@askexenow/exe-os 0.9.7 → 0.9.8
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 +754 -79
- package/dist/bin/backfill-responses.js +752 -77
- package/dist/bin/backfill-vectors.js +752 -77
- package/dist/bin/cleanup-stale-review-tasks.js +657 -35
- package/dist/bin/cli.js +1388 -605
- package/dist/bin/exe-agent-config.js +123 -95
- package/dist/bin/exe-agent.js +41 -25
- package/dist/bin/exe-assign.js +732 -57
- package/dist/bin/exe-boot.js +784 -153
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +692 -70
- package/dist/bin/exe-doctor.js +648 -26
- package/dist/bin/exe-export-behaviors.js +650 -20
- package/dist/bin/exe-forget.js +635 -13
- package/dist/bin/exe-gateway.js +1053 -271
- package/dist/bin/exe-heartbeat.js +665 -43
- package/dist/bin/exe-kill.js +646 -16
- package/dist/bin/exe-launch-agent.js +887 -97
- package/dist/bin/exe-link.js +658 -43
- package/dist/bin/exe-new-employee.js +378 -177
- package/dist/bin/exe-pending-messages.js +656 -34
- package/dist/bin/exe-pending-notifications.js +635 -13
- package/dist/bin/exe-pending-reviews.js +659 -37
- package/dist/bin/exe-rename.js +645 -30
- package/dist/bin/exe-review.js +635 -13
- package/dist/bin/exe-search.js +771 -88
- package/dist/bin/exe-session-cleanup.js +834 -150
- package/dist/bin/exe-settings.js +127 -91
- package/dist/bin/exe-start-codex.js +729 -94
- package/dist/bin/exe-start-opencode.js +717 -82
- package/dist/bin/exe-status.js +657 -35
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +720 -89
- package/dist/bin/graph-backfill.js +643 -13
- package/dist/bin/graph-export.js +646 -16
- package/dist/bin/install.js +596 -193
- package/dist/bin/scan-tasks.js +724 -93
- package/dist/bin/setup.js +1038 -210
- package/dist/bin/shard-migrate.js +645 -15
- package/dist/bin/wiki-sync.js +646 -16
- package/dist/gateway/index.js +1027 -245
- package/dist/hooks/bug-report-worker.js +891 -170
- package/dist/hooks/commit-complete.js +718 -87
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +840 -156
- package/dist/hooks/ingest.js +90 -73
- package/dist/hooks/instructions-loaded.js +669 -38
- package/dist/hooks/notification.js +661 -30
- package/dist/hooks/post-compact.js +674 -43
- package/dist/hooks/pre-compact.js +718 -87
- package/dist/hooks/pre-tool-use.js +872 -125
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1060 -319
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +721 -90
- package/dist/hooks/session-start.js +1031 -207
- package/dist/hooks/stop.js +680 -49
- package/dist/hooks/subagent-stop.js +674 -43
- package/dist/hooks/summary-worker.js +816 -132
- package/dist/index.js +1015 -232
- package/dist/lib/cloud-sync.js +663 -48
- package/dist/lib/consolidation.js +26 -3
- package/dist/lib/database.js +626 -18
- package/dist/lib/db.js +2261 -0
- package/dist/lib/device-registry.js +640 -25
- package/dist/lib/embedder.js +96 -43
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +259 -83
- package/dist/lib/exe-daemon-client.js +101 -63
- package/dist/lib/exe-daemon.js +894 -162
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +55 -28
- package/dist/lib/reminders.js +21 -1
- package/dist/lib/schedules.js +636 -14
- package/dist/lib/skill-learning.js +21 -1
- package/dist/lib/store.js +643 -13
- package/dist/lib/task-router.js +82 -71
- package/dist/lib/tasks.js +98 -71
- package/dist/lib/tmux-routing.js +87 -60
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1784 -458
- package/dist/mcp/tools/complete-reminder.js +21 -1
- package/dist/mcp/tools/create-reminder.js +21 -1
- package/dist/mcp/tools/create-task.js +290 -164
- package/dist/mcp/tools/deactivate-behavior.js +24 -4
- package/dist/mcp/tools/list-reminders.js +21 -1
- package/dist/mcp/tools/list-tasks.js +195 -38
- package/dist/mcp/tools/send-message.js +58 -31
- package/dist/mcp/tools/update-task.js +75 -48
- package/dist/runtime/index.js +720 -89
- package/dist/tui/App.js +853 -123
- 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
|
};
|
|
@@ -100,6 +106,118 @@ var init_config = __esm({
|
|
|
100
106
|
}
|
|
101
107
|
});
|
|
102
108
|
|
|
109
|
+
// src/lib/runtime-table.ts
|
|
110
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
111
|
+
var init_runtime_table = __esm({
|
|
112
|
+
"src/lib/runtime-table.ts"() {
|
|
113
|
+
"use strict";
|
|
114
|
+
RUNTIME_TABLE = {
|
|
115
|
+
codex: {
|
|
116
|
+
binary: "codex",
|
|
117
|
+
launchMode: "interactive",
|
|
118
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
119
|
+
inlineFlag: "--no-alt-screen",
|
|
120
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
121
|
+
defaultModel: "gpt-5.4"
|
|
122
|
+
},
|
|
123
|
+
opencode: {
|
|
124
|
+
binary: "opencode",
|
|
125
|
+
launchMode: "exec",
|
|
126
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
127
|
+
inlineFlag: "",
|
|
128
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
129
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
DEFAULT_RUNTIME = "claude";
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// src/lib/agent-config.ts
|
|
137
|
+
var agent_config_exports = {};
|
|
138
|
+
__export(agent_config_exports, {
|
|
139
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
140
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
141
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
142
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
143
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
144
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
145
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
146
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
147
|
+
setAgentRuntime: () => setAgentRuntime
|
|
148
|
+
});
|
|
149
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
150
|
+
import path2 from "path";
|
|
151
|
+
function loadAgentConfig() {
|
|
152
|
+
if (!existsSync2(AGENT_CONFIG_PATH)) return {};
|
|
153
|
+
try {
|
|
154
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
155
|
+
} catch {
|
|
156
|
+
return {};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function saveAgentConfig(config) {
|
|
160
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
161
|
+
if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
|
|
162
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
163
|
+
}
|
|
164
|
+
function getAgentRuntime(agentId) {
|
|
165
|
+
const config = loadAgentConfig();
|
|
166
|
+
const entry = config[agentId];
|
|
167
|
+
if (entry) return entry;
|
|
168
|
+
const orgDefault = config["default"];
|
|
169
|
+
if (orgDefault) return orgDefault;
|
|
170
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
171
|
+
}
|
|
172
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
173
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
174
|
+
if (!knownModels) {
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (!knownModels.includes(model)) {
|
|
181
|
+
return {
|
|
182
|
+
ok: false,
|
|
183
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const config = loadAgentConfig();
|
|
187
|
+
config[agentId] = { runtime, model };
|
|
188
|
+
saveAgentConfig(config);
|
|
189
|
+
return { ok: true };
|
|
190
|
+
}
|
|
191
|
+
function clearAgentRuntime(agentId) {
|
|
192
|
+
const config = loadAgentConfig();
|
|
193
|
+
delete config[agentId];
|
|
194
|
+
saveAgentConfig(config);
|
|
195
|
+
}
|
|
196
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
197
|
+
var init_agent_config = __esm({
|
|
198
|
+
"src/lib/agent-config.ts"() {
|
|
199
|
+
"use strict";
|
|
200
|
+
init_config();
|
|
201
|
+
init_runtime_table();
|
|
202
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
203
|
+
KNOWN_RUNTIMES = {
|
|
204
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
205
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
206
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
207
|
+
};
|
|
208
|
+
RUNTIME_LABELS = {
|
|
209
|
+
claude: "Claude Code (Anthropic)",
|
|
210
|
+
codex: "Codex (OpenAI)",
|
|
211
|
+
opencode: "OpenCode (open source)"
|
|
212
|
+
};
|
|
213
|
+
DEFAULT_MODELS = {
|
|
214
|
+
claude: "claude-opus-4",
|
|
215
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
216
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
103
221
|
// src/lib/employees.ts
|
|
104
222
|
var employees_exports = {};
|
|
105
223
|
__export(employees_exports, {
|
|
@@ -115,6 +233,7 @@ __export(employees_exports, {
|
|
|
115
233
|
getEmployeeByRole: () => getEmployeeByRole,
|
|
116
234
|
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
117
235
|
hasRole: () => hasRole,
|
|
236
|
+
hireEmployee: () => hireEmployee,
|
|
118
237
|
isCoordinatorName: () => isCoordinatorName,
|
|
119
238
|
isCoordinatorRole: () => isCoordinatorRole,
|
|
120
239
|
isMultiInstance: () => isMultiInstance,
|
|
@@ -127,9 +246,9 @@ __export(employees_exports, {
|
|
|
127
246
|
validateEmployeeName: () => validateEmployeeName
|
|
128
247
|
});
|
|
129
248
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
130
|
-
import { existsSync as
|
|
249
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
131
250
|
import { execSync } from "child_process";
|
|
132
|
-
import
|
|
251
|
+
import path3 from "path";
|
|
133
252
|
import os2 from "os";
|
|
134
253
|
function normalizeRole(role) {
|
|
135
254
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -166,7 +285,7 @@ function validateEmployeeName(name) {
|
|
|
166
285
|
return { valid: true };
|
|
167
286
|
}
|
|
168
287
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
169
|
-
if (!
|
|
288
|
+
if (!existsSync3(employeesPath)) {
|
|
170
289
|
return [];
|
|
171
290
|
}
|
|
172
291
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -177,13 +296,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
177
296
|
}
|
|
178
297
|
}
|
|
179
298
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
180
|
-
await mkdir2(
|
|
299
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
181
300
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
182
301
|
}
|
|
183
302
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
184
|
-
if (!
|
|
303
|
+
if (!existsSync3(employeesPath)) return [];
|
|
185
304
|
try {
|
|
186
|
-
return JSON.parse(
|
|
305
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
187
306
|
} catch {
|
|
188
307
|
return [];
|
|
189
308
|
}
|
|
@@ -225,6 +344,52 @@ function addEmployee(employees, employee) {
|
|
|
225
344
|
}
|
|
226
345
|
return [...employees, normalized];
|
|
227
346
|
}
|
|
347
|
+
function appendToCoordinatorTeam(employee) {
|
|
348
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
349
|
+
if (!coordinator) return;
|
|
350
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
351
|
+
if (!existsSync3(idPath)) return;
|
|
352
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
353
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
354
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
355
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
356
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
357
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
358
|
+
const entry = `
|
|
359
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
360
|
+
`;
|
|
361
|
+
let updated;
|
|
362
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
363
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
364
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
365
|
+
} else {
|
|
366
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
367
|
+
}
|
|
368
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
369
|
+
}
|
|
370
|
+
function capitalize(s) {
|
|
371
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
372
|
+
}
|
|
373
|
+
async function hireEmployee(employee) {
|
|
374
|
+
const employees = await loadEmployees();
|
|
375
|
+
const updated = addEmployee(employees, employee);
|
|
376
|
+
await saveEmployees(updated);
|
|
377
|
+
try {
|
|
378
|
+
appendToCoordinatorTeam(employee);
|
|
379
|
+
} catch {
|
|
380
|
+
}
|
|
381
|
+
try {
|
|
382
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
383
|
+
const config = loadAgentConfig2();
|
|
384
|
+
const name = employee.name.toLowerCase();
|
|
385
|
+
if (!config[name] && config["default"]) {
|
|
386
|
+
config[name] = { ...config["default"] };
|
|
387
|
+
saveAgentConfig2(config);
|
|
388
|
+
}
|
|
389
|
+
} catch {
|
|
390
|
+
}
|
|
391
|
+
return updated;
|
|
392
|
+
}
|
|
228
393
|
async function normalizeRosterCase(rosterPath) {
|
|
229
394
|
const employees = await loadEmployees(rosterPath);
|
|
230
395
|
let changed = false;
|
|
@@ -234,14 +399,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
234
399
|
emp.name = emp.name.toLowerCase();
|
|
235
400
|
changed = true;
|
|
236
401
|
try {
|
|
237
|
-
const identityDir =
|
|
238
|
-
const oldPath =
|
|
239
|
-
const newPath =
|
|
240
|
-
if (
|
|
402
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
403
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
404
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
405
|
+
if (existsSync3(oldPath) && !existsSync3(newPath)) {
|
|
241
406
|
renameSync2(oldPath, newPath);
|
|
242
|
-
} else if (
|
|
243
|
-
const content =
|
|
244
|
-
|
|
407
|
+
} else if (existsSync3(oldPath) && oldPath !== newPath) {
|
|
408
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
409
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
245
410
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
246
411
|
unlinkSync(oldPath);
|
|
247
412
|
}
|
|
@@ -271,7 +436,7 @@ function registerBinSymlinks(name) {
|
|
|
271
436
|
errors.push("Could not find 'exe-os' in PATH");
|
|
272
437
|
return { created, skipped, errors };
|
|
273
438
|
}
|
|
274
|
-
const binDir =
|
|
439
|
+
const binDir = path3.dirname(exeBinPath);
|
|
275
440
|
let target;
|
|
276
441
|
try {
|
|
277
442
|
target = readlinkSync(exeBinPath);
|
|
@@ -281,8 +446,8 @@ function registerBinSymlinks(name) {
|
|
|
281
446
|
}
|
|
282
447
|
for (const suffix of ["", "-opencode"]) {
|
|
283
448
|
const linkName = `${name}${suffix}`;
|
|
284
|
-
const linkPath =
|
|
285
|
-
if (
|
|
449
|
+
const linkPath = path3.join(binDir, linkName);
|
|
450
|
+
if (existsSync3(linkPath)) {
|
|
286
451
|
skipped.push(linkName);
|
|
287
452
|
continue;
|
|
288
453
|
}
|
|
@@ -295,51 +460,44 @@ function registerBinSymlinks(name) {
|
|
|
295
460
|
}
|
|
296
461
|
return { created, skipped, errors };
|
|
297
462
|
}
|
|
298
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
463
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
299
464
|
var init_employees = __esm({
|
|
300
465
|
"src/lib/employees.ts"() {
|
|
301
466
|
"use strict";
|
|
302
467
|
init_config();
|
|
303
|
-
EMPLOYEES_PATH =
|
|
468
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
304
469
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
305
470
|
COORDINATOR_ROLE = "COO";
|
|
306
471
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
472
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
473
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
307
474
|
}
|
|
308
475
|
});
|
|
309
476
|
|
|
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
477
|
// src/lib/agent-symlinks.ts
|
|
319
|
-
init_employees();
|
|
320
478
|
import os3 from "os";
|
|
321
|
-
import
|
|
479
|
+
import path4 from "path";
|
|
322
480
|
import {
|
|
323
|
-
existsSync as
|
|
481
|
+
existsSync as existsSync4,
|
|
324
482
|
lstatSync,
|
|
325
|
-
mkdirSync,
|
|
483
|
+
mkdirSync as mkdirSync2,
|
|
326
484
|
readlinkSync as readlinkSync2,
|
|
327
485
|
symlinkSync as symlinkSync2
|
|
328
486
|
} from "fs";
|
|
329
487
|
function claudeAgentsDir(homeDir) {
|
|
330
|
-
return
|
|
488
|
+
return path4.join(homeDir, ".claude", "agents");
|
|
331
489
|
}
|
|
332
490
|
function identitySourcePath(homeDir, agentId) {
|
|
333
|
-
return
|
|
491
|
+
return path4.join(homeDir, ".exe-os", "identity", `${agentId}.md`);
|
|
334
492
|
}
|
|
335
493
|
function claudeAgentLinkPath(homeDir, agentId) {
|
|
336
|
-
return
|
|
494
|
+
return path4.join(claudeAgentsDir(homeDir), `${agentId}.md`);
|
|
337
495
|
}
|
|
338
496
|
function ensureAgentSymlink(agentId, homeDir = os3.homedir()) {
|
|
339
497
|
const target = identitySourcePath(homeDir, agentId);
|
|
340
498
|
const link = claudeAgentLinkPath(homeDir, agentId);
|
|
341
|
-
|
|
342
|
-
if (
|
|
499
|
+
mkdirSync2(claudeAgentsDir(homeDir), { recursive: true });
|
|
500
|
+
if (existsSync4(link)) {
|
|
343
501
|
let stat;
|
|
344
502
|
try {
|
|
345
503
|
stat = lstatSync(link);
|
|
@@ -377,14 +535,14 @@ async function ensureAllAgentSymlinks(homeDir = os3.homedir()) {
|
|
|
377
535
|
const employees = await loadEmployees();
|
|
378
536
|
return employees.map((emp) => ensureAgentSymlink(emp.name, homeDir));
|
|
379
537
|
}
|
|
538
|
+
var init_agent_symlinks = __esm({
|
|
539
|
+
"src/lib/agent-symlinks.ts"() {
|
|
540
|
+
"use strict";
|
|
541
|
+
init_employees();
|
|
542
|
+
}
|
|
543
|
+
});
|
|
380
544
|
|
|
381
545
|
// 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
546
|
function expandDualPrefixTools(shortNames) {
|
|
389
547
|
const out = [];
|
|
390
548
|
for (const name of shortNames) {
|
|
@@ -394,64 +552,87 @@ function expandDualPrefixTools(shortNames) {
|
|
|
394
552
|
}
|
|
395
553
|
return out;
|
|
396
554
|
}
|
|
555
|
+
var MCP_PRIMARY_KEY, MCP_LEGACY_KEY, MCP_TOOL_PREFIXES;
|
|
556
|
+
var init_mcp_prefix = __esm({
|
|
557
|
+
"src/lib/mcp-prefix.ts"() {
|
|
558
|
+
"use strict";
|
|
559
|
+
MCP_PRIMARY_KEY = "exe-os";
|
|
560
|
+
MCP_LEGACY_KEY = "exe-mem";
|
|
561
|
+
MCP_TOOL_PREFIXES = [
|
|
562
|
+
`mcp__${MCP_PRIMARY_KEY}__`,
|
|
563
|
+
`mcp__${MCP_LEGACY_KEY}__`
|
|
564
|
+
];
|
|
565
|
+
}
|
|
566
|
+
});
|
|
397
567
|
|
|
398
568
|
// src/lib/preferences.ts
|
|
399
|
-
import { existsSync as
|
|
400
|
-
import
|
|
569
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
570
|
+
import path5 from "path";
|
|
401
571
|
import os4 from "os";
|
|
402
572
|
function loadPreferences(homeDir = os4.homedir()) {
|
|
403
|
-
const configPath =
|
|
404
|
-
if (!
|
|
573
|
+
const configPath = path5.join(homeDir, ".exe-os", "config.json");
|
|
574
|
+
if (!existsSync5(configPath)) return {};
|
|
405
575
|
try {
|
|
406
|
-
const config = JSON.parse(
|
|
576
|
+
const config = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
407
577
|
return config.preferences ?? {};
|
|
408
578
|
} catch {
|
|
409
579
|
return {};
|
|
410
580
|
}
|
|
411
581
|
}
|
|
582
|
+
var init_preferences = __esm({
|
|
583
|
+
"src/lib/preferences.ts"() {
|
|
584
|
+
"use strict";
|
|
585
|
+
}
|
|
586
|
+
});
|
|
412
587
|
|
|
413
588
|
// src/adapters/claude/installer.ts
|
|
589
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
|
|
590
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4, copyFileSync, mkdirSync as mkdirSync4 } from "fs";
|
|
591
|
+
import path6 from "path";
|
|
592
|
+
import os5 from "os";
|
|
593
|
+
import { execSync as execSync2 } from "child_process";
|
|
594
|
+
import { fileURLToPath } from "url";
|
|
414
595
|
function resolvePackageRoot() {
|
|
415
596
|
const thisFile = fileURLToPath(import.meta.url);
|
|
416
|
-
let dir =
|
|
417
|
-
const root =
|
|
597
|
+
let dir = path6.dirname(thisFile);
|
|
598
|
+
const root = path6.parse(dir).root;
|
|
418
599
|
while (dir !== root) {
|
|
419
|
-
const pkgPath =
|
|
420
|
-
if (
|
|
600
|
+
const pkgPath = path6.join(dir, "package.json");
|
|
601
|
+
if (existsSync6(pkgPath)) {
|
|
421
602
|
try {
|
|
422
|
-
const pkg = JSON.parse(
|
|
603
|
+
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
423
604
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
424
605
|
} catch {
|
|
425
606
|
}
|
|
426
607
|
}
|
|
427
|
-
dir =
|
|
608
|
+
dir = path6.dirname(dir);
|
|
428
609
|
}
|
|
429
|
-
return
|
|
610
|
+
return path6.resolve(path6.dirname(thisFile), "..", "..", "..");
|
|
430
611
|
}
|
|
431
612
|
async function copySlashCommands(packageRoot, homeDir = os5.homedir()) {
|
|
432
613
|
let copied = 0;
|
|
433
614
|
let skipped = 0;
|
|
434
|
-
const skillsBase =
|
|
435
|
-
const exeDir =
|
|
436
|
-
if (
|
|
615
|
+
const skillsBase = path6.join(homeDir, ".claude", "skills");
|
|
616
|
+
const exeDir = path6.join(packageRoot, "src", "commands", "exe");
|
|
617
|
+
if (existsSync6(exeDir)) {
|
|
437
618
|
const entries = await readdir(exeDir);
|
|
438
619
|
const mdFiles = entries.filter((f) => f.endsWith(".md"));
|
|
439
620
|
for (const file of mdFiles) {
|
|
440
621
|
const name = file.replace(".md", "");
|
|
441
|
-
const destDir =
|
|
622
|
+
const destDir = path6.join(skillsBase, `exe-${name}`);
|
|
442
623
|
await mkdir3(destDir, { recursive: true });
|
|
443
|
-
const srcPath =
|
|
444
|
-
const destPath =
|
|
624
|
+
const srcPath = path6.join(exeDir, file);
|
|
625
|
+
const destPath = path6.join(destDir, "SKILL.md");
|
|
445
626
|
const result = await copyAsSkill(srcPath, destPath, `exe-${name}`);
|
|
446
627
|
if (result) copied++;
|
|
447
628
|
else skipped++;
|
|
448
629
|
}
|
|
449
630
|
}
|
|
450
|
-
const topLevelSrc =
|
|
451
|
-
if (
|
|
452
|
-
const destDir =
|
|
631
|
+
const topLevelSrc = path6.join(packageRoot, "src", "commands", "exe.md");
|
|
632
|
+
if (existsSync6(topLevelSrc)) {
|
|
633
|
+
const destDir = path6.join(skillsBase, "exe");
|
|
453
634
|
await mkdir3(destDir, { recursive: true });
|
|
454
|
-
const destPath =
|
|
635
|
+
const destPath = path6.join(destDir, "SKILL.md");
|
|
455
636
|
const result = await copyAsSkill(topLevelSrc, destPath, "exe");
|
|
456
637
|
if (result) copied++;
|
|
457
638
|
else skipped++;
|
|
@@ -474,7 +655,7 @@ name: ${skillName}
|
|
|
474
655
|
`);
|
|
475
656
|
}
|
|
476
657
|
}
|
|
477
|
-
if (
|
|
658
|
+
if (existsSync6(destPath)) {
|
|
478
659
|
const existing = await readFile3(destPath, "utf-8");
|
|
479
660
|
if (existing === content) return false;
|
|
480
661
|
}
|
|
@@ -482,9 +663,9 @@ name: ${skillName}
|
|
|
482
663
|
return true;
|
|
483
664
|
}
|
|
484
665
|
async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
485
|
-
const claudeJsonPath =
|
|
666
|
+
const claudeJsonPath = path6.join(homeDir, ".claude.json");
|
|
486
667
|
let claudeJson = {};
|
|
487
|
-
if (
|
|
668
|
+
if (existsSync6(claudeJsonPath)) {
|
|
488
669
|
try {
|
|
489
670
|
claudeJson = JSON.parse(await readFile3(claudeJsonPath, "utf-8"));
|
|
490
671
|
} catch {
|
|
@@ -497,7 +678,7 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
497
678
|
const newEntry = {
|
|
498
679
|
type: "stdio",
|
|
499
680
|
command: "node",
|
|
500
|
-
args: [
|
|
681
|
+
args: [path6.join(packageRoot, "dist", "mcp", "server.js")],
|
|
501
682
|
env: {}
|
|
502
683
|
};
|
|
503
684
|
const currentMem = claudeJson.mcpServers[MCP_LEGACY_KEY];
|
|
@@ -505,17 +686,17 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
505
686
|
const memMatches = currentMem && JSON.stringify(currentMem) === JSON.stringify(newEntry);
|
|
506
687
|
const osMatches = currentOs && JSON.stringify(currentOs) === JSON.stringify(newEntry);
|
|
507
688
|
if (memMatches && osMatches) {
|
|
508
|
-
await cleanSettingsJsonMcp(
|
|
689
|
+
await cleanSettingsJsonMcp(path6.join(homeDir, ".claude", "settings.json"));
|
|
509
690
|
return false;
|
|
510
691
|
}
|
|
511
692
|
claudeJson.mcpServers[MCP_LEGACY_KEY] = newEntry;
|
|
512
693
|
claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
|
|
513
694
|
await writeFile3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
514
|
-
await cleanSettingsJsonMcp(
|
|
695
|
+
await cleanSettingsJsonMcp(path6.join(homeDir, ".claude", "settings.json"));
|
|
515
696
|
return true;
|
|
516
697
|
}
|
|
517
698
|
async function cleanSettingsJsonMcp(settingsPath) {
|
|
518
|
-
if (!
|
|
699
|
+
if (!existsSync6(settingsPath)) return;
|
|
519
700
|
try {
|
|
520
701
|
const settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
521
702
|
const servers = settings.mcpServers;
|
|
@@ -536,13 +717,13 @@ async function cleanSettingsJsonMcp(settingsPath) {
|
|
|
536
717
|
}
|
|
537
718
|
}
|
|
538
719
|
async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
539
|
-
const settingsPath =
|
|
540
|
-
const logsDir =
|
|
541
|
-
const hookLogPath =
|
|
720
|
+
const settingsPath = path6.join(homeDir, ".claude", "settings.json");
|
|
721
|
+
const logsDir = path6.join(homeDir, ".exe-os", "logs");
|
|
722
|
+
const hookLogPath = path6.join(logsDir, "hooks.log");
|
|
542
723
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
543
724
|
await mkdir3(logsDir, { recursive: true });
|
|
544
725
|
let settings = {};
|
|
545
|
-
if (
|
|
726
|
+
if (existsSync6(settingsPath)) {
|
|
546
727
|
try {
|
|
547
728
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
548
729
|
} catch {
|
|
@@ -564,11 +745,11 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
564
745
|
hooks: [
|
|
565
746
|
{
|
|
566
747
|
type: "command",
|
|
567
|
-
command: `node "${
|
|
748
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
|
|
568
749
|
},
|
|
569
750
|
{
|
|
570
751
|
type: "command",
|
|
571
|
-
command: `node "${
|
|
752
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
|
|
572
753
|
}
|
|
573
754
|
]
|
|
574
755
|
},
|
|
@@ -580,7 +761,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
580
761
|
hooks: [
|
|
581
762
|
{
|
|
582
763
|
type: "command",
|
|
583
|
-
command: `node "${
|
|
764
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
584
765
|
timeout: 1e4
|
|
585
766
|
}
|
|
586
767
|
]
|
|
@@ -593,7 +774,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
593
774
|
hooks: [
|
|
594
775
|
{
|
|
595
776
|
type: "command",
|
|
596
|
-
command: `node "${
|
|
777
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
597
778
|
}
|
|
598
779
|
]
|
|
599
780
|
},
|
|
@@ -605,7 +786,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
605
786
|
hooks: [
|
|
606
787
|
{
|
|
607
788
|
type: "command",
|
|
608
|
-
command: `node "${
|
|
789
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
|
|
609
790
|
timeout: 5e3
|
|
610
791
|
}
|
|
611
792
|
]
|
|
@@ -618,7 +799,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
618
799
|
hooks: [
|
|
619
800
|
{
|
|
620
801
|
type: "command",
|
|
621
|
-
command: `node "${
|
|
802
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
622
803
|
}
|
|
623
804
|
]
|
|
624
805
|
},
|
|
@@ -631,7 +812,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
631
812
|
hooks: [
|
|
632
813
|
{
|
|
633
814
|
type: "command",
|
|
634
|
-
command: `node "${
|
|
815
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
635
816
|
}
|
|
636
817
|
]
|
|
637
818
|
},
|
|
@@ -643,7 +824,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
643
824
|
hooks: [
|
|
644
825
|
{
|
|
645
826
|
type: "command",
|
|
646
|
-
command: `node "${
|
|
827
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "subagent-stop.js")}"${logSuffix}`
|
|
647
828
|
}
|
|
648
829
|
]
|
|
649
830
|
},
|
|
@@ -655,7 +836,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
655
836
|
hooks: [
|
|
656
837
|
{
|
|
657
838
|
type: "command",
|
|
658
|
-
command: `node "${
|
|
839
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "pre-compact.js")}"${logSuffix}`,
|
|
659
840
|
timeout: 1e4
|
|
660
841
|
}
|
|
661
842
|
]
|
|
@@ -668,7 +849,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
668
849
|
hooks: [
|
|
669
850
|
{
|
|
670
851
|
type: "command",
|
|
671
|
-
command: `node "${
|
|
852
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "session-end.js")}"${logSuffix}`
|
|
672
853
|
}
|
|
673
854
|
]
|
|
674
855
|
},
|
|
@@ -680,7 +861,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
680
861
|
hooks: [
|
|
681
862
|
{
|
|
682
863
|
type: "command",
|
|
683
|
-
command: `node "${
|
|
864
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "notification.js")}"${logSuffix}`
|
|
684
865
|
}
|
|
685
866
|
]
|
|
686
867
|
},
|
|
@@ -692,7 +873,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
692
873
|
hooks: [
|
|
693
874
|
{
|
|
694
875
|
type: "command",
|
|
695
|
-
command: `node "${
|
|
876
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "post-compact.js")}"${logSuffix}`,
|
|
696
877
|
timeout: 1e4
|
|
697
878
|
}
|
|
698
879
|
]
|
|
@@ -705,7 +886,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
705
886
|
hooks: [
|
|
706
887
|
{
|
|
707
888
|
type: "command",
|
|
708
|
-
command: `node "${
|
|
889
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "instructions-loaded.js")}"${logSuffix}`
|
|
709
890
|
}
|
|
710
891
|
]
|
|
711
892
|
},
|
|
@@ -804,13 +985,13 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
804
985
|
allowList.push(tool);
|
|
805
986
|
}
|
|
806
987
|
}
|
|
807
|
-
await mkdir3(
|
|
988
|
+
await mkdir3(path6.dirname(settingsPath), { recursive: true });
|
|
808
989
|
await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
809
990
|
return { added, skipped };
|
|
810
991
|
}
|
|
811
992
|
async function cleanOldShellFunctions(homeDir = os5.homedir()) {
|
|
812
|
-
const rosterPath =
|
|
813
|
-
if (!
|
|
993
|
+
const rosterPath = path6.join(homeDir, ".exe-os", "exe-employees.json");
|
|
994
|
+
if (!existsSync6(rosterPath)) return 0;
|
|
814
995
|
let employees;
|
|
815
996
|
try {
|
|
816
997
|
employees = JSON.parse(await readFile3(rosterPath, "utf-8"));
|
|
@@ -825,13 +1006,13 @@ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
|
|
|
825
1006
|
return { name: n, funcDef, forLoop };
|
|
826
1007
|
});
|
|
827
1008
|
const rcFiles = [
|
|
828
|
-
|
|
829
|
-
|
|
1009
|
+
path6.join(homeDir, ".zshrc"),
|
|
1010
|
+
path6.join(homeDir, ".bashrc")
|
|
830
1011
|
];
|
|
831
1012
|
const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
|
|
832
1013
|
let totalRemoved = 0;
|
|
833
1014
|
for (const rcPath of rcFiles) {
|
|
834
|
-
if (!
|
|
1015
|
+
if (!existsSync6(rcPath)) continue;
|
|
835
1016
|
let content;
|
|
836
1017
|
try {
|
|
837
1018
|
content = await readFile3(rcPath, "utf-8");
|
|
@@ -934,22 +1115,9 @@ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
|
|
|
934
1115
|
function escapeRegExp(s) {
|
|
935
1116
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
936
1117
|
}
|
|
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
1118
|
async function injectOrchestrationRules(homeDir) {
|
|
951
|
-
const claudeDir =
|
|
952
|
-
const claudeMdPath =
|
|
1119
|
+
const claudeDir = path6.join(homeDir, ".claude");
|
|
1120
|
+
const claudeMdPath = path6.join(claudeDir, "CLAUDE.md");
|
|
953
1121
|
await mkdir3(claudeDir, { recursive: true });
|
|
954
1122
|
let existing = "";
|
|
955
1123
|
try {
|
|
@@ -974,16 +1142,16 @@ async function injectOrchestrationRules(homeDir) {
|
|
|
974
1142
|
async function installStatusLine(packageRoot, homeDir = os5.homedir()) {
|
|
975
1143
|
const prefs = loadPreferences(homeDir);
|
|
976
1144
|
if (prefs.ccStatusLine === false) return "opted-out";
|
|
977
|
-
const claudeDir =
|
|
1145
|
+
const claudeDir = path6.join(homeDir, ".claude");
|
|
978
1146
|
await mkdir3(claudeDir, { recursive: true });
|
|
979
|
-
const assetPath =
|
|
980
|
-
if (!
|
|
981
|
-
const destScript =
|
|
1147
|
+
const assetPath = path6.join(packageRoot, "dist", "assets", "statusline-command.sh");
|
|
1148
|
+
if (!existsSync6(assetPath)) return "asset-missing";
|
|
1149
|
+
const destScript = path6.join(claudeDir, "statusline-command.sh");
|
|
982
1150
|
const assetContent = await readFile3(assetPath, "utf-8");
|
|
983
1151
|
await writeFile3(destScript, assetContent, { mode: 493 });
|
|
984
|
-
const settingsPath =
|
|
1152
|
+
const settingsPath = path6.join(claudeDir, "settings.json");
|
|
985
1153
|
let settings = {};
|
|
986
|
-
if (
|
|
1154
|
+
if (existsSync6(settingsPath)) {
|
|
987
1155
|
try {
|
|
988
1156
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
989
1157
|
} catch {
|
|
@@ -1021,12 +1189,12 @@ async function runInstaller(homeDir) {
|
|
|
1021
1189
|
`
|
|
1022
1190
|
);
|
|
1023
1191
|
const resolvedHome = homeDir ?? os5.homedir();
|
|
1024
|
-
const exeWorkspace =
|
|
1025
|
-
if (!
|
|
1192
|
+
const exeWorkspace = path6.join(resolvedHome, "exe");
|
|
1193
|
+
if (!existsSync6(exeWorkspace)) {
|
|
1026
1194
|
try {
|
|
1027
|
-
await mkdir3(
|
|
1028
|
-
await mkdir3(
|
|
1029
|
-
await mkdir3(
|
|
1195
|
+
await mkdir3(path6.join(exeWorkspace, "content"), { recursive: true });
|
|
1196
|
+
await mkdir3(path6.join(exeWorkspace, "operations"), { recursive: true });
|
|
1197
|
+
await mkdir3(path6.join(exeWorkspace, "output"), { recursive: true });
|
|
1030
1198
|
process.stderr.write(
|
|
1031
1199
|
`Created ~/exe/ \u2014 your automation workspace for non-code projects
|
|
1032
1200
|
`
|
|
@@ -1062,33 +1230,33 @@ exe-os installed successfully.
|
|
|
1062
1230
|
}
|
|
1063
1231
|
function setupTmux(home) {
|
|
1064
1232
|
const homeDir = home ?? os5.homedir();
|
|
1065
|
-
const exeDir =
|
|
1066
|
-
const exeTmuxConf =
|
|
1067
|
-
const userTmuxConf =
|
|
1068
|
-
const backupPath =
|
|
1233
|
+
const exeDir = path6.join(homeDir, ".exe-os");
|
|
1234
|
+
const exeTmuxConf = path6.join(exeDir, "tmux.conf");
|
|
1235
|
+
const userTmuxConf = path6.join(homeDir, ".tmux.conf");
|
|
1236
|
+
const backupPath = path6.join(homeDir, ".tmux.conf.backup");
|
|
1069
1237
|
const sourceLine = "source-file ~/.exe-os/tmux.conf";
|
|
1070
1238
|
const pkgRoot = resolvePackageRoot();
|
|
1071
|
-
const assetPath =
|
|
1072
|
-
if (!
|
|
1239
|
+
const assetPath = path6.join(pkgRoot, "dist", "assets", "tmux.conf");
|
|
1240
|
+
if (!existsSync6(assetPath)) {
|
|
1073
1241
|
process.stderr.write(`exe-os: tmux.conf asset not found at ${assetPath} \u2014 skipping tmux setup
|
|
1074
1242
|
`);
|
|
1075
1243
|
return;
|
|
1076
1244
|
}
|
|
1077
|
-
|
|
1245
|
+
mkdirSync4(exeDir, { recursive: true });
|
|
1078
1246
|
copyFileSync(assetPath, exeTmuxConf);
|
|
1079
|
-
if (
|
|
1080
|
-
const existing =
|
|
1247
|
+
if (existsSync6(userTmuxConf)) {
|
|
1248
|
+
const existing = readFileSync5(userTmuxConf, "utf8");
|
|
1081
1249
|
if (!existing.includes(sourceLine)) {
|
|
1082
|
-
if (!
|
|
1250
|
+
if (!existsSync6(backupPath)) {
|
|
1083
1251
|
copyFileSync(userTmuxConf, backupPath);
|
|
1084
1252
|
process.stderr.write(`exe-os: backed up existing tmux config to ${backupPath}
|
|
1085
1253
|
`);
|
|
1086
1254
|
}
|
|
1087
|
-
|
|
1255
|
+
writeFileSync4(userTmuxConf, `${sourceLine}
|
|
1088
1256
|
${existing}`);
|
|
1089
1257
|
}
|
|
1090
1258
|
} else {
|
|
1091
|
-
|
|
1259
|
+
writeFileSync4(userTmuxConf, `# Exe OS tmux defaults \u2014 remove this line to use your own config
|
|
1092
1260
|
${sourceLine}
|
|
1093
1261
|
`);
|
|
1094
1262
|
}
|
|
@@ -1100,9 +1268,9 @@ ${sourceLine}
|
|
|
1100
1268
|
}
|
|
1101
1269
|
function setupGhostty(home) {
|
|
1102
1270
|
const homeDir = home ?? os5.homedir();
|
|
1103
|
-
const xdgConfig =
|
|
1104
|
-
const macConfig =
|
|
1105
|
-
const ghosttyInstalled =
|
|
1271
|
+
const xdgConfig = path6.join(homeDir, ".config", "ghostty");
|
|
1272
|
+
const macConfig = path6.join(homeDir, "Library", "Application Support", "com.mitchellh.ghostty");
|
|
1273
|
+
const ghosttyInstalled = existsSync6(xdgConfig) || existsSync6(macConfig) || (() => {
|
|
1106
1274
|
try {
|
|
1107
1275
|
execSync2("which ghostty 2>/dev/null");
|
|
1108
1276
|
return true;
|
|
@@ -1114,47 +1282,47 @@ function setupGhostty(home) {
|
|
|
1114
1282
|
return;
|
|
1115
1283
|
}
|
|
1116
1284
|
const pkgRoot = resolvePackageRoot();
|
|
1117
|
-
const assetPath =
|
|
1118
|
-
if (!
|
|
1285
|
+
const assetPath = path6.join(pkgRoot, "dist", "assets", "ghostty.conf");
|
|
1286
|
+
if (!existsSync6(assetPath)) {
|
|
1119
1287
|
process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
|
|
1120
1288
|
return;
|
|
1121
1289
|
}
|
|
1122
1290
|
const configDir = xdgConfig;
|
|
1123
|
-
const configPath =
|
|
1124
|
-
const backupPath =
|
|
1125
|
-
|
|
1291
|
+
const configPath = path6.join(configDir, "config");
|
|
1292
|
+
const backupPath = path6.join(configDir, "config.backup");
|
|
1293
|
+
mkdirSync4(configDir, { recursive: true });
|
|
1126
1294
|
const START_MARKER = "# \u2500\u2500 exe-os:ghostty-start \u2500\u2500";
|
|
1127
1295
|
const END_MARKER = "# \u2500\u2500 exe-os:ghostty-end \u2500\u2500";
|
|
1128
|
-
const assetContent =
|
|
1296
|
+
const assetContent = readFileSync5(assetPath, "utf8").trim();
|
|
1129
1297
|
const markedSection = `${START_MARKER}
|
|
1130
1298
|
${assetContent}
|
|
1131
1299
|
${END_MARKER}`;
|
|
1132
|
-
if (
|
|
1133
|
-
const existing =
|
|
1300
|
+
if (existsSync6(configPath)) {
|
|
1301
|
+
const existing = readFileSync5(configPath, "utf8");
|
|
1134
1302
|
if (existing.includes(START_MARKER) && existing.includes(END_MARKER)) {
|
|
1135
1303
|
const before = existing.slice(0, existing.indexOf(START_MARKER));
|
|
1136
1304
|
const after = existing.slice(existing.indexOf(END_MARKER) + END_MARKER.length);
|
|
1137
|
-
|
|
1305
|
+
writeFileSync4(configPath, `${before}${markedSection}${after}`);
|
|
1138
1306
|
} else if (existing.includes("Exe OS")) {
|
|
1139
|
-
if (!
|
|
1307
|
+
if (!existsSync6(backupPath)) {
|
|
1140
1308
|
copyFileSync(configPath, backupPath);
|
|
1141
1309
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1142
1310
|
`);
|
|
1143
1311
|
}
|
|
1144
|
-
|
|
1312
|
+
writeFileSync4(configPath, `${markedSection}
|
|
1145
1313
|
`);
|
|
1146
1314
|
} else {
|
|
1147
|
-
if (!
|
|
1315
|
+
if (!existsSync6(backupPath)) {
|
|
1148
1316
|
copyFileSync(configPath, backupPath);
|
|
1149
1317
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1150
1318
|
`);
|
|
1151
1319
|
}
|
|
1152
|
-
|
|
1320
|
+
writeFileSync4(configPath, `${markedSection}
|
|
1153
1321
|
|
|
1154
1322
|
${existing}`);
|
|
1155
1323
|
}
|
|
1156
1324
|
} else {
|
|
1157
|
-
|
|
1325
|
+
writeFileSync4(configPath, `${markedSection}
|
|
1158
1326
|
`);
|
|
1159
1327
|
}
|
|
1160
1328
|
process.stderr.write("exe-os: Ghostty config installed\n");
|
|
@@ -1171,46 +1339,275 @@ function summarizeSymlinkResults(results) {
|
|
|
1171
1339
|
}
|
|
1172
1340
|
return summary;
|
|
1173
1341
|
}
|
|
1342
|
+
var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
|
|
1343
|
+
var init_installer = __esm({
|
|
1344
|
+
"src/adapters/claude/installer.ts"() {
|
|
1345
|
+
"use strict";
|
|
1346
|
+
init_agent_symlinks();
|
|
1347
|
+
init_mcp_prefix();
|
|
1348
|
+
init_preferences();
|
|
1349
|
+
EXE_SECTION_START = "<!-- exe-os:orchestration-start -->";
|
|
1350
|
+
EXE_SECTION_END = "<!-- exe-os:orchestration-end -->";
|
|
1351
|
+
ORCHESTRATION_RULES = `${EXE_SECTION_START}
|
|
1352
|
+
## exe-os Multi-Agent Orchestration (auto-managed \u2014 do not edit)
|
|
1353
|
+
|
|
1354
|
+
These rules are injected by exe-os and override default behavior for multi-agent coordination.
|
|
1355
|
+
|
|
1356
|
+
- **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.
|
|
1357
|
+
- **Always use create_task to assign work.** Never use messages, tmux send-keys, or ad-hoc instructions as a substitute for tasks.
|
|
1358
|
+
- **Never modify another agent's in-progress task.** Create a new task instead. Modifying active tasks causes race conditions.
|
|
1359
|
+
- **Chain of command:** founder -> COO -> managers -> specialists. The COO does not bypass managers for specialist work.
|
|
1360
|
+
- **Verify dispatch.** After every create_task, confirm the employee received and started the task via tmux capture-pane.
|
|
1361
|
+
${EXE_SECTION_END}`;
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1365
|
+
// src/adapters/codex/installer.ts
|
|
1366
|
+
var installer_exports = {};
|
|
1367
|
+
__export(installer_exports, {
|
|
1368
|
+
installCodexStatusLine: () => installCodexStatusLine,
|
|
1369
|
+
mergeCodexHooks: () => mergeCodexHooks,
|
|
1370
|
+
runCodexInstaller: () => runCodexInstaller,
|
|
1371
|
+
verifyCodexHooks: () => verifyCodexHooks
|
|
1372
|
+
});
|
|
1373
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
1374
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1375
|
+
import path8 from "path";
|
|
1376
|
+
import os6 from "os";
|
|
1377
|
+
async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
1378
|
+
const codexDir = path8.join(homeDir, ".codex");
|
|
1379
|
+
const hooksPath = path8.join(codexDir, "hooks.json");
|
|
1380
|
+
const logsDir = path8.join(homeDir, ".exe-os", "logs");
|
|
1381
|
+
const hookLogPath = path8.join(logsDir, "hooks.log");
|
|
1382
|
+
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
1383
|
+
await mkdir4(codexDir, { recursive: true });
|
|
1384
|
+
await mkdir4(logsDir, { recursive: true });
|
|
1385
|
+
let hooksJson = {};
|
|
1386
|
+
if (existsSync8(hooksPath)) {
|
|
1387
|
+
try {
|
|
1388
|
+
hooksJson = JSON.parse(await readFile4(hooksPath, "utf-8"));
|
|
1389
|
+
} catch {
|
|
1390
|
+
hooksJson = {};
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
if (!hooksJson.hooks) {
|
|
1394
|
+
hooksJson.hooks = {};
|
|
1395
|
+
}
|
|
1396
|
+
const hooksToRegister = [
|
|
1397
|
+
{
|
|
1398
|
+
event: "SessionStart",
|
|
1399
|
+
group: {
|
|
1400
|
+
hooks: [
|
|
1401
|
+
{
|
|
1402
|
+
type: "command",
|
|
1403
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
1404
|
+
timeout: 30,
|
|
1405
|
+
statusMessage: "exe-os: loading memory brief"
|
|
1406
|
+
}
|
|
1407
|
+
]
|
|
1408
|
+
},
|
|
1409
|
+
marker: "dist/hooks/session-start.js"
|
|
1410
|
+
},
|
|
1411
|
+
{
|
|
1412
|
+
event: "PostToolUse",
|
|
1413
|
+
group: {
|
|
1414
|
+
matcher: "Bash|apply_patch|Edit|Write|Read|Glob|Grep|mcp__.*",
|
|
1415
|
+
hooks: [
|
|
1416
|
+
{
|
|
1417
|
+
type: "command",
|
|
1418
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
|
|
1419
|
+
},
|
|
1420
|
+
{
|
|
1421
|
+
type: "command",
|
|
1422
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
|
|
1423
|
+
}
|
|
1424
|
+
]
|
|
1425
|
+
},
|
|
1426
|
+
marker: "dist/hooks/ingest.js"
|
|
1427
|
+
},
|
|
1428
|
+
{
|
|
1429
|
+
event: "UserPromptSubmit",
|
|
1430
|
+
group: {
|
|
1431
|
+
hooks: [
|
|
1432
|
+
{
|
|
1433
|
+
type: "command",
|
|
1434
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
1435
|
+
},
|
|
1436
|
+
{
|
|
1437
|
+
type: "command",
|
|
1438
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
|
|
1439
|
+
timeout: 5
|
|
1440
|
+
}
|
|
1441
|
+
]
|
|
1442
|
+
},
|
|
1443
|
+
marker: "dist/hooks/prompt-submit.js"
|
|
1444
|
+
},
|
|
1445
|
+
{
|
|
1446
|
+
event: "Stop",
|
|
1447
|
+
group: {
|
|
1448
|
+
hooks: [
|
|
1449
|
+
{
|
|
1450
|
+
type: "command",
|
|
1451
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
1452
|
+
}
|
|
1453
|
+
]
|
|
1454
|
+
},
|
|
1455
|
+
marker: "dist/hooks/stop.js"
|
|
1456
|
+
},
|
|
1457
|
+
{
|
|
1458
|
+
event: "PreToolUse",
|
|
1459
|
+
group: {
|
|
1460
|
+
matcher: "Bash|apply_patch",
|
|
1461
|
+
hooks: [
|
|
1462
|
+
{
|
|
1463
|
+
type: "command",
|
|
1464
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
1465
|
+
}
|
|
1466
|
+
]
|
|
1467
|
+
},
|
|
1468
|
+
marker: "dist/hooks/pre-tool-use.js"
|
|
1469
|
+
}
|
|
1470
|
+
];
|
|
1471
|
+
let added = 0;
|
|
1472
|
+
let skipped = 0;
|
|
1473
|
+
for (const { event, group, marker } of hooksToRegister) {
|
|
1474
|
+
if (!hooksJson.hooks[event]) {
|
|
1475
|
+
hooksJson.hooks[event] = [];
|
|
1476
|
+
}
|
|
1477
|
+
const existing = hooksJson.hooks[event];
|
|
1478
|
+
const correctCommand = group.hooks[0]?.command ?? "";
|
|
1479
|
+
const alreadyCorrect = existing.some(
|
|
1480
|
+
(g) => g.hooks.some((h) => h.command === correctCommand)
|
|
1481
|
+
);
|
|
1482
|
+
if (alreadyCorrect) {
|
|
1483
|
+
skipped++;
|
|
1484
|
+
} else {
|
|
1485
|
+
hooksJson.hooks[event] = existing.filter(
|
|
1486
|
+
(g) => !g.hooks.some((h) => h.command.includes(marker))
|
|
1487
|
+
);
|
|
1488
|
+
hooksJson.hooks[event].push(group);
|
|
1489
|
+
added++;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
await writeFile4(hooksPath, JSON.stringify(hooksJson, null, 2) + "\n");
|
|
1493
|
+
return { added, skipped };
|
|
1494
|
+
}
|
|
1495
|
+
function verifyCodexHooks(homeDir = os6.homedir()) {
|
|
1496
|
+
const hooksPath = path8.join(homeDir, ".codex", "hooks.json");
|
|
1497
|
+
if (!existsSync8(hooksPath)) return false;
|
|
1498
|
+
try {
|
|
1499
|
+
const hooksJson = JSON.parse(
|
|
1500
|
+
__require("fs").readFileSync(hooksPath, "utf-8")
|
|
1501
|
+
);
|
|
1502
|
+
if (!hooksJson.hooks) return false;
|
|
1503
|
+
const required = ["SessionStart", "PostToolUse", "UserPromptSubmit", "Stop", "PreToolUse"];
|
|
1504
|
+
for (const event of required) {
|
|
1505
|
+
const groups = hooksJson.hooks[event];
|
|
1506
|
+
if (!groups || !groups.some(
|
|
1507
|
+
(g) => g.hooks.some((h) => h.command.includes("dist/hooks/"))
|
|
1508
|
+
)) {
|
|
1509
|
+
return false;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return true;
|
|
1513
|
+
} catch {
|
|
1514
|
+
return false;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
async function installCodexStatusLine(homeDir = os6.homedir()) {
|
|
1518
|
+
const prefs = loadPreferences(homeDir);
|
|
1519
|
+
if (prefs.codexStatusLine === false) return "opted-out";
|
|
1520
|
+
const codexDir = path8.join(homeDir, ".codex");
|
|
1521
|
+
const configPath = path8.join(codexDir, "config.toml");
|
|
1522
|
+
await mkdir4(codexDir, { recursive: true });
|
|
1523
|
+
let content = "";
|
|
1524
|
+
if (existsSync8(configPath)) {
|
|
1525
|
+
content = await readFile4(configPath, "utf-8");
|
|
1526
|
+
if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
|
|
1527
|
+
return "already-configured";
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
const statusLineToml = `[tui]
|
|
1531
|
+
status_line = [${DEFAULT_CODEX_STATUS_LINE.map((s) => `"${s}"`).join(", ")}]`;
|
|
1532
|
+
if (content.includes("[tui]")) {
|
|
1533
|
+
content = content.replace(/\[tui\]/, `${statusLineToml}`);
|
|
1534
|
+
} else {
|
|
1535
|
+
const separator = content.length > 0 && !content.endsWith("\n") ? "\n\n" : content.length > 0 ? "\n" : "";
|
|
1536
|
+
content = content + separator + statusLineToml + "\n";
|
|
1537
|
+
}
|
|
1538
|
+
await writeFile4(configPath, content);
|
|
1539
|
+
return "installed";
|
|
1540
|
+
}
|
|
1541
|
+
async function runCodexInstaller(homeDir) {
|
|
1542
|
+
const packageRoot = resolvePackageRoot();
|
|
1543
|
+
const result = await mergeCodexHooks(packageRoot, homeDir);
|
|
1544
|
+
process.stderr.write(
|
|
1545
|
+
`[exe-os] Codex hooks: ${result.added} added, ${result.skipped} unchanged
|
|
1546
|
+
`
|
|
1547
|
+
);
|
|
1548
|
+
const statusResult = await installCodexStatusLine(homeDir);
|
|
1549
|
+
process.stderr.write(
|
|
1550
|
+
`[exe-os] Codex status line: ${statusResult}
|
|
1551
|
+
`
|
|
1552
|
+
);
|
|
1553
|
+
}
|
|
1554
|
+
var DEFAULT_CODEX_STATUS_LINE;
|
|
1555
|
+
var init_installer2 = __esm({
|
|
1556
|
+
"src/adapters/codex/installer.ts"() {
|
|
1557
|
+
"use strict";
|
|
1558
|
+
init_installer();
|
|
1559
|
+
init_preferences();
|
|
1560
|
+
DEFAULT_CODEX_STATUS_LINE = [
|
|
1561
|
+
"model-with-reasoning",
|
|
1562
|
+
"current-dir",
|
|
1563
|
+
"project-name",
|
|
1564
|
+
"git-branch",
|
|
1565
|
+
"run-state",
|
|
1566
|
+
"context-used"
|
|
1567
|
+
];
|
|
1568
|
+
}
|
|
1569
|
+
});
|
|
1174
1570
|
|
|
1175
1571
|
// src/bin/install.ts
|
|
1176
|
-
|
|
1572
|
+
init_installer();
|
|
1573
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, unlinkSync as unlinkSync3, readdirSync as readdirSync2, openSync, closeSync } from "fs";
|
|
1177
1574
|
import { spawn, execSync as execSync3 } from "child_process";
|
|
1178
|
-
import
|
|
1179
|
-
import
|
|
1575
|
+
import path9 from "path";
|
|
1576
|
+
import os7 from "os";
|
|
1180
1577
|
|
|
1181
1578
|
// src/lib/session-wrappers.ts
|
|
1182
1579
|
import {
|
|
1183
|
-
existsSync as
|
|
1184
|
-
readFileSync as
|
|
1185
|
-
writeFileSync as
|
|
1186
|
-
mkdirSync as
|
|
1580
|
+
existsSync as existsSync7,
|
|
1581
|
+
readFileSync as readFileSync6,
|
|
1582
|
+
writeFileSync as writeFileSync5,
|
|
1583
|
+
mkdirSync as mkdirSync5,
|
|
1187
1584
|
chmodSync,
|
|
1188
1585
|
readdirSync,
|
|
1189
1586
|
unlinkSync as unlinkSync2
|
|
1190
1587
|
} from "fs";
|
|
1191
|
-
import
|
|
1588
|
+
import path7 from "path";
|
|
1192
1589
|
import { homedir } from "os";
|
|
1193
1590
|
var MAX_N = 9;
|
|
1194
1591
|
function generateSessionWrappers(packageRoot, homeDir) {
|
|
1195
1592
|
const home = homeDir ?? homedir();
|
|
1196
|
-
const binDir =
|
|
1197
|
-
const rosterPath =
|
|
1198
|
-
|
|
1199
|
-
const exeStartDst =
|
|
1593
|
+
const binDir = path7.join(home, ".exe-os", "bin");
|
|
1594
|
+
const rosterPath = path7.join(home, ".exe-os", "exe-employees.json");
|
|
1595
|
+
mkdirSync5(binDir, { recursive: true });
|
|
1596
|
+
const exeStartDst = path7.join(binDir, "exe-start");
|
|
1200
1597
|
const candidates = [
|
|
1201
|
-
|
|
1202
|
-
|
|
1598
|
+
path7.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
1599
|
+
path7.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
1203
1600
|
];
|
|
1204
1601
|
for (const src of candidates) {
|
|
1205
|
-
if (
|
|
1206
|
-
|
|
1602
|
+
if (existsSync7(src)) {
|
|
1603
|
+
writeFileSync5(exeStartDst, readFileSync6(src));
|
|
1207
1604
|
chmodSync(exeStartDst, 493);
|
|
1208
1605
|
break;
|
|
1209
1606
|
}
|
|
1210
1607
|
}
|
|
1211
1608
|
let employees = [];
|
|
1212
1609
|
try {
|
|
1213
|
-
employees = JSON.parse(
|
|
1610
|
+
employees = JSON.parse(readFileSync6(rosterPath, "utf8"));
|
|
1214
1611
|
} catch {
|
|
1215
1612
|
return { created: 0, pathConfigured: false };
|
|
1216
1613
|
}
|
|
@@ -1220,9 +1617,9 @@ function generateSessionWrappers(packageRoot, homeDir) {
|
|
|
1220
1617
|
try {
|
|
1221
1618
|
for (const f of readdirSync(binDir)) {
|
|
1222
1619
|
if (f === "exe-start") continue;
|
|
1223
|
-
const fPath =
|
|
1620
|
+
const fPath = path7.join(binDir, f);
|
|
1224
1621
|
try {
|
|
1225
|
-
const content =
|
|
1622
|
+
const content = readFileSync6(fPath, "utf8");
|
|
1226
1623
|
if (content.includes("exe-start")) {
|
|
1227
1624
|
unlinkSync2(fPath);
|
|
1228
1625
|
}
|
|
@@ -1237,30 +1634,30 @@ exec "${exeStartDst}" "$0" "$@"
|
|
|
1237
1634
|
`;
|
|
1238
1635
|
for (const emp of employees) {
|
|
1239
1636
|
for (let n = 1; n <= MAX_N; n++) {
|
|
1240
|
-
const wrapperPath =
|
|
1241
|
-
|
|
1637
|
+
const wrapperPath = path7.join(binDir, `${emp.name}${n}`);
|
|
1638
|
+
writeFileSync5(wrapperPath, wrapperContent);
|
|
1242
1639
|
chmodSync(wrapperPath, 493);
|
|
1243
1640
|
created++;
|
|
1244
1641
|
}
|
|
1245
1642
|
}
|
|
1246
1643
|
const codexLauncherCandidates = [
|
|
1247
|
-
|
|
1248
|
-
|
|
1644
|
+
path7.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
|
|
1645
|
+
path7.join(packageRoot, "src", "bin", "exe-start-codex.ts")
|
|
1249
1646
|
];
|
|
1250
1647
|
let codexLauncher = null;
|
|
1251
1648
|
for (const c of codexLauncherCandidates) {
|
|
1252
|
-
if (
|
|
1649
|
+
if (existsSync7(c)) {
|
|
1253
1650
|
codexLauncher = c;
|
|
1254
1651
|
break;
|
|
1255
1652
|
}
|
|
1256
1653
|
}
|
|
1257
1654
|
if (codexLauncher) {
|
|
1258
1655
|
for (const emp of employees) {
|
|
1259
|
-
const wrapperPath =
|
|
1656
|
+
const wrapperPath = path7.join(binDir, `${emp.name}-codex`);
|
|
1260
1657
|
const content = `#!/bin/bash
|
|
1261
1658
|
exec node "${codexLauncher}" --agent ${emp.name} "$@"
|
|
1262
1659
|
`;
|
|
1263
|
-
|
|
1660
|
+
writeFileSync5(wrapperPath, content);
|
|
1264
1661
|
chmodSync(wrapperPath, 493);
|
|
1265
1662
|
created++;
|
|
1266
1663
|
}
|
|
@@ -1279,24 +1676,24 @@ export PATH="${binDir}:$PATH"
|
|
|
1279
1676
|
const shell = process.env.SHELL ?? "/bin/bash";
|
|
1280
1677
|
const profilePaths = [];
|
|
1281
1678
|
if (shell.includes("zsh")) {
|
|
1282
|
-
profilePaths.push(
|
|
1679
|
+
profilePaths.push(path7.join(home, ".zshrc"));
|
|
1283
1680
|
} else if (shell.includes("bash")) {
|
|
1284
|
-
profilePaths.push(
|
|
1285
|
-
profilePaths.push(
|
|
1681
|
+
profilePaths.push(path7.join(home, ".bashrc"));
|
|
1682
|
+
profilePaths.push(path7.join(home, ".bash_profile"));
|
|
1286
1683
|
} else {
|
|
1287
|
-
profilePaths.push(
|
|
1684
|
+
profilePaths.push(path7.join(home, ".profile"));
|
|
1288
1685
|
}
|
|
1289
1686
|
for (const profilePath of profilePaths) {
|
|
1290
1687
|
try {
|
|
1291
1688
|
let content = "";
|
|
1292
1689
|
try {
|
|
1293
|
-
content =
|
|
1690
|
+
content = readFileSync6(profilePath, "utf8");
|
|
1294
1691
|
} catch {
|
|
1295
1692
|
}
|
|
1296
1693
|
if (content.includes(".exe-os/bin")) {
|
|
1297
1694
|
return false;
|
|
1298
1695
|
}
|
|
1299
|
-
|
|
1696
|
+
writeFileSync5(profilePath, content + exportLine);
|
|
1300
1697
|
return true;
|
|
1301
1698
|
} catch {
|
|
1302
1699
|
continue;
|
|
@@ -1306,14 +1703,14 @@ export PATH="${binDir}:$PATH"
|
|
|
1306
1703
|
}
|
|
1307
1704
|
|
|
1308
1705
|
// src/bin/install.ts
|
|
1309
|
-
var homedir2 =
|
|
1310
|
-
var EXE_DIR =
|
|
1706
|
+
var homedir2 = os7.homedir;
|
|
1707
|
+
var EXE_DIR = path9.join(homedir2(), ".exe-os");
|
|
1311
1708
|
function restartDaemon() {
|
|
1312
|
-
const pidPath =
|
|
1313
|
-
const sockPath =
|
|
1709
|
+
const pidPath = path9.join(EXE_DIR, "exed.pid");
|
|
1710
|
+
const sockPath = path9.join(EXE_DIR, "exed.sock");
|
|
1314
1711
|
try {
|
|
1315
|
-
if (
|
|
1316
|
-
const pid = parseInt(
|
|
1712
|
+
if (existsSync9(pidPath)) {
|
|
1713
|
+
const pid = parseInt(readFileSync7(pidPath, "utf8").trim(), 10);
|
|
1317
1714
|
if (!isNaN(pid) && pid > 0) {
|
|
1318
1715
|
try {
|
|
1319
1716
|
process.kill(pid, "SIGKILL");
|
|
@@ -1354,11 +1751,11 @@ function restartDaemon() {
|
|
|
1354
1751
|
} catch {
|
|
1355
1752
|
}
|
|
1356
1753
|
try {
|
|
1357
|
-
const wpDir =
|
|
1358
|
-
if (
|
|
1754
|
+
const wpDir = path9.join(EXE_DIR, "worker-pids");
|
|
1755
|
+
if (existsSync9(wpDir)) {
|
|
1359
1756
|
for (const f of readdirSync2(wpDir)) {
|
|
1360
1757
|
try {
|
|
1361
|
-
unlinkSync3(
|
|
1758
|
+
unlinkSync3(path9.join(wpDir, f));
|
|
1362
1759
|
} catch {
|
|
1363
1760
|
}
|
|
1364
1761
|
}
|
|
@@ -1375,7 +1772,7 @@ function restartDaemon() {
|
|
|
1375
1772
|
}
|
|
1376
1773
|
} catch {
|
|
1377
1774
|
}
|
|
1378
|
-
const totalGB =
|
|
1775
|
+
const totalGB = os7.totalmem() / (1024 * 1024 * 1024);
|
|
1379
1776
|
if (totalGB <= 8) {
|
|
1380
1777
|
process.stderr.write(
|
|
1381
1778
|
`exe-os: ${totalGB.toFixed(0)}GB system \u2014 skipping daemon spawn (keyword search mode)
|
|
@@ -1385,13 +1782,13 @@ function restartDaemon() {
|
|
|
1385
1782
|
}
|
|
1386
1783
|
try {
|
|
1387
1784
|
const pkgRoot = resolvePackageRoot();
|
|
1388
|
-
const daemonPath =
|
|
1389
|
-
if (!
|
|
1785
|
+
const daemonPath = path9.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1786
|
+
if (!existsSync9(daemonPath)) {
|
|
1390
1787
|
process.stderr.write(`exe-os: daemon not found at ${daemonPath} \u2014 skipping respawn
|
|
1391
1788
|
`);
|
|
1392
1789
|
return;
|
|
1393
1790
|
}
|
|
1394
|
-
const logPath =
|
|
1791
|
+
const logPath = path9.join(EXE_DIR, "exed.log");
|
|
1395
1792
|
let stderrFd = "ignore";
|
|
1396
1793
|
try {
|
|
1397
1794
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1461,6 +1858,12 @@ if (args.includes("--commands-only")) {
|
|
|
1461
1858
|
} catch (err) {
|
|
1462
1859
|
process.stderr.write(`exe-os: session wrapper generation failed: ${err instanceof Error ? err.message : String(err)}
|
|
1463
1860
|
`);
|
|
1861
|
+
}
|
|
1862
|
+
try {
|
|
1863
|
+
execSync3("which codex", { encoding: "utf8", timeout: 5e3 });
|
|
1864
|
+
const { runCodexInstaller: runCodexInstaller2 } = await Promise.resolve().then(() => (init_installer2(), installer_exports));
|
|
1865
|
+
await runCodexInstaller2();
|
|
1866
|
+
} catch {
|
|
1464
1867
|
}
|
|
1465
1868
|
restartDaemon();
|
|
1466
1869
|
} catch (err) {
|