@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
|
@@ -106,12 +106,133 @@ var init_config = __esm({
|
|
|
106
106
|
}
|
|
107
107
|
});
|
|
108
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 readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
150
|
+
import path3 from "path";
|
|
151
|
+
function loadAgentConfig() {
|
|
152
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
153
|
+
try {
|
|
154
|
+
return JSON.parse(readFileSync3(AGENT_CONFIG_PATH, "utf-8"));
|
|
155
|
+
} catch {
|
|
156
|
+
return {};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function saveAgentConfig(config) {
|
|
160
|
+
const dir = path3.dirname(AGENT_CONFIG_PATH);
|
|
161
|
+
if (!existsSync3(dir)) mkdirSync2(dir, { recursive: true });
|
|
162
|
+
writeFileSync2(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 = path3.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
|
+
|
|
109
221
|
// src/lib/employees.ts
|
|
110
222
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
111
|
-
import { existsSync as
|
|
223
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync4, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
112
224
|
import { execSync } from "child_process";
|
|
113
|
-
import
|
|
225
|
+
import path4 from "path";
|
|
114
226
|
import os2 from "os";
|
|
227
|
+
function normalizeRole(role) {
|
|
228
|
+
return (role ?? "").trim().toLowerCase();
|
|
229
|
+
}
|
|
230
|
+
function isCoordinatorRole(role) {
|
|
231
|
+
return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
|
|
232
|
+
}
|
|
233
|
+
function getCoordinatorEmployee(employees) {
|
|
234
|
+
return employees.find((e) => isCoordinatorRole(e.role));
|
|
235
|
+
}
|
|
115
236
|
function validateEmployeeName(name) {
|
|
116
237
|
if (!name) {
|
|
117
238
|
return { valid: false, error: "Name is required" };
|
|
@@ -128,7 +249,7 @@ function validateEmployeeName(name) {
|
|
|
128
249
|
return { valid: true };
|
|
129
250
|
}
|
|
130
251
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
131
|
-
if (!
|
|
252
|
+
if (!existsSync4(employeesPath)) {
|
|
132
253
|
return [];
|
|
133
254
|
}
|
|
134
255
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -139,9 +260,17 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
139
260
|
}
|
|
140
261
|
}
|
|
141
262
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
142
|
-
await mkdir2(
|
|
263
|
+
await mkdir2(path4.dirname(employeesPath), { recursive: true });
|
|
143
264
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
144
265
|
}
|
|
266
|
+
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
267
|
+
if (!existsSync4(employeesPath)) return [];
|
|
268
|
+
try {
|
|
269
|
+
return JSON.parse(readFileSync4(employeesPath, "utf-8"));
|
|
270
|
+
} catch {
|
|
271
|
+
return [];
|
|
272
|
+
}
|
|
273
|
+
}
|
|
145
274
|
function addEmployee(employees, employee) {
|
|
146
275
|
const normalized = { ...employee, name: employee.name.toLowerCase() };
|
|
147
276
|
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
@@ -149,6 +278,52 @@ function addEmployee(employees, employee) {
|
|
|
149
278
|
}
|
|
150
279
|
return [...employees, normalized];
|
|
151
280
|
}
|
|
281
|
+
function appendToCoordinatorTeam(employee) {
|
|
282
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
283
|
+
if (!coordinator) return;
|
|
284
|
+
const idPath = path4.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
285
|
+
if (!existsSync4(idPath)) return;
|
|
286
|
+
const content = readFileSync4(idPath, "utf-8");
|
|
287
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
288
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
289
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
290
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
291
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
292
|
+
const entry = `
|
|
293
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
294
|
+
`;
|
|
295
|
+
let updated;
|
|
296
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
297
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
298
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
299
|
+
} else {
|
|
300
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
301
|
+
}
|
|
302
|
+
writeFileSync3(idPath, updated, "utf-8");
|
|
303
|
+
}
|
|
304
|
+
function capitalize(s) {
|
|
305
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
306
|
+
}
|
|
307
|
+
async function hireEmployee(employee) {
|
|
308
|
+
const employees = await loadEmployees();
|
|
309
|
+
const updated = addEmployee(employees, employee);
|
|
310
|
+
await saveEmployees(updated);
|
|
311
|
+
try {
|
|
312
|
+
appendToCoordinatorTeam(employee);
|
|
313
|
+
} catch {
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
317
|
+
const config = loadAgentConfig2();
|
|
318
|
+
const name = employee.name.toLowerCase();
|
|
319
|
+
if (!config[name] && config["default"]) {
|
|
320
|
+
config[name] = { ...config["default"] };
|
|
321
|
+
saveAgentConfig2(config);
|
|
322
|
+
}
|
|
323
|
+
} catch {
|
|
324
|
+
}
|
|
325
|
+
return updated;
|
|
326
|
+
}
|
|
152
327
|
function findExeBin() {
|
|
153
328
|
try {
|
|
154
329
|
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
@@ -165,7 +340,7 @@ function registerBinSymlinks(name) {
|
|
|
165
340
|
errors.push("Could not find 'exe-os' in PATH");
|
|
166
341
|
return { created, skipped, errors };
|
|
167
342
|
}
|
|
168
|
-
const binDir =
|
|
343
|
+
const binDir = path4.dirname(exeBinPath);
|
|
169
344
|
let target;
|
|
170
345
|
try {
|
|
171
346
|
target = readlinkSync(exeBinPath);
|
|
@@ -175,8 +350,8 @@ function registerBinSymlinks(name) {
|
|
|
175
350
|
}
|
|
176
351
|
for (const suffix of ["", "-opencode"]) {
|
|
177
352
|
const linkName = `${name}${suffix}`;
|
|
178
|
-
const linkPath =
|
|
179
|
-
if (
|
|
353
|
+
const linkPath = path4.join(binDir, linkName);
|
|
354
|
+
if (existsSync4(linkPath)) {
|
|
180
355
|
skipped.push(linkName);
|
|
181
356
|
continue;
|
|
182
357
|
}
|
|
@@ -189,12 +364,15 @@ function registerBinSymlinks(name) {
|
|
|
189
364
|
}
|
|
190
365
|
return { created, skipped, errors };
|
|
191
366
|
}
|
|
192
|
-
var EMPLOYEES_PATH;
|
|
367
|
+
var EMPLOYEES_PATH, COORDINATOR_ROLE, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
193
368
|
var init_employees = __esm({
|
|
194
369
|
"src/lib/employees.ts"() {
|
|
195
370
|
"use strict";
|
|
196
371
|
init_config();
|
|
197
|
-
EMPLOYEES_PATH =
|
|
372
|
+
EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
|
|
373
|
+
COORDINATOR_ROLE = "COO";
|
|
374
|
+
IDENTITY_DIR = path4.join(EXE_AI_DIR, "identity");
|
|
375
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
198
376
|
}
|
|
199
377
|
});
|
|
200
378
|
|
|
@@ -205,12 +383,36 @@ var init_db_retry = __esm({
|
|
|
205
383
|
}
|
|
206
384
|
});
|
|
207
385
|
|
|
386
|
+
// src/lib/database-adapter.ts
|
|
387
|
+
import os3 from "os";
|
|
388
|
+
import path5 from "path";
|
|
389
|
+
import { createRequire } from "module";
|
|
390
|
+
import { pathToFileURL } from "url";
|
|
391
|
+
var BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES;
|
|
392
|
+
var init_database_adapter = __esm({
|
|
393
|
+
"src/lib/database-adapter.ts"() {
|
|
394
|
+
"use strict";
|
|
395
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
396
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
397
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
398
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
399
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
400
|
+
};
|
|
401
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
402
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
208
407
|
// src/lib/database.ts
|
|
209
408
|
import { createClient } from "@libsql/client";
|
|
210
409
|
function getClient() {
|
|
211
|
-
if (!
|
|
410
|
+
if (!_adapterClient) {
|
|
212
411
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
213
412
|
}
|
|
413
|
+
if (process.env.DATABASE_URL) {
|
|
414
|
+
return _adapterClient;
|
|
415
|
+
}
|
|
214
416
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
215
417
|
return _resilientClient;
|
|
216
418
|
}
|
|
@@ -219,14 +421,16 @@ function getClient() {
|
|
|
219
421
|
}
|
|
220
422
|
return _resilientClient;
|
|
221
423
|
}
|
|
222
|
-
var _resilientClient, _daemonClient;
|
|
424
|
+
var _resilientClient, _daemonClient, _adapterClient;
|
|
223
425
|
var init_database = __esm({
|
|
224
426
|
"src/lib/database.ts"() {
|
|
225
427
|
"use strict";
|
|
226
428
|
init_db_retry();
|
|
227
429
|
init_employees();
|
|
430
|
+
init_database_adapter();
|
|
228
431
|
_resilientClient = null;
|
|
229
432
|
_daemonClient = null;
|
|
433
|
+
_adapterClient = null;
|
|
230
434
|
}
|
|
231
435
|
});
|
|
232
436
|
|
|
@@ -780,17 +984,17 @@ __export(identity_exports, {
|
|
|
780
984
|
listIdentities: () => listIdentities,
|
|
781
985
|
updateIdentity: () => updateIdentity
|
|
782
986
|
});
|
|
783
|
-
import { existsSync as
|
|
987
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
784
988
|
import { readdirSync as readdirSync2 } from "fs";
|
|
785
|
-
import
|
|
989
|
+
import path8 from "path";
|
|
786
990
|
import { createHash } from "crypto";
|
|
787
991
|
function ensureDir() {
|
|
788
|
-
if (!
|
|
789
|
-
|
|
992
|
+
if (!existsSync7(IDENTITY_DIR2)) {
|
|
993
|
+
mkdirSync4(IDENTITY_DIR2, { recursive: true });
|
|
790
994
|
}
|
|
791
995
|
}
|
|
792
996
|
function identityPath(agentId) {
|
|
793
|
-
return
|
|
997
|
+
return path8.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
794
998
|
}
|
|
795
999
|
function parseFrontmatter(raw) {
|
|
796
1000
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -831,8 +1035,8 @@ function contentHash(content) {
|
|
|
831
1035
|
}
|
|
832
1036
|
function getIdentity(agentId) {
|
|
833
1037
|
const filePath = identityPath(agentId);
|
|
834
|
-
if (!
|
|
835
|
-
const raw =
|
|
1038
|
+
if (!existsSync7(filePath)) return null;
|
|
1039
|
+
const raw = readFileSync7(filePath, "utf-8");
|
|
836
1040
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
837
1041
|
return {
|
|
838
1042
|
agentId,
|
|
@@ -846,7 +1050,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
846
1050
|
ensureDir();
|
|
847
1051
|
const filePath = identityPath(agentId);
|
|
848
1052
|
const hash = contentHash(content);
|
|
849
|
-
|
|
1053
|
+
writeFileSync5(filePath, content, "utf-8");
|
|
850
1054
|
try {
|
|
851
1055
|
const client = getClient();
|
|
852
1056
|
await client.execute({
|
|
@@ -863,7 +1067,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
863
1067
|
}
|
|
864
1068
|
function listIdentities() {
|
|
865
1069
|
ensureDir();
|
|
866
|
-
const files = readdirSync2(
|
|
1070
|
+
const files = readdirSync2(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
|
|
867
1071
|
const results = [];
|
|
868
1072
|
for (const file of files) {
|
|
869
1073
|
const agentId = file.replace(".md", "");
|
|
@@ -896,40 +1100,40 @@ ${teamLines.join("\n")}`);
|
|
|
896
1100
|
}
|
|
897
1101
|
return parts.join("\n\n");
|
|
898
1102
|
}
|
|
899
|
-
var
|
|
1103
|
+
var IDENTITY_DIR2;
|
|
900
1104
|
var init_identity = __esm({
|
|
901
1105
|
"src/lib/identity.ts"() {
|
|
902
1106
|
"use strict";
|
|
903
1107
|
init_config();
|
|
904
1108
|
init_database();
|
|
905
|
-
|
|
1109
|
+
IDENTITY_DIR2 = path8.join(EXE_AI_DIR, "identity");
|
|
906
1110
|
}
|
|
907
1111
|
});
|
|
908
1112
|
|
|
909
1113
|
// src/lib/agent-symlinks.ts
|
|
910
|
-
import
|
|
911
|
-
import
|
|
1114
|
+
import os4 from "os";
|
|
1115
|
+
import path9 from "path";
|
|
912
1116
|
import {
|
|
913
|
-
existsSync as
|
|
1117
|
+
existsSync as existsSync8,
|
|
914
1118
|
lstatSync,
|
|
915
|
-
mkdirSync as
|
|
1119
|
+
mkdirSync as mkdirSync5,
|
|
916
1120
|
readlinkSync as readlinkSync2,
|
|
917
1121
|
symlinkSync as symlinkSync2
|
|
918
1122
|
} from "fs";
|
|
919
1123
|
function claudeAgentsDir(homeDir) {
|
|
920
|
-
return
|
|
1124
|
+
return path9.join(homeDir, ".claude", "agents");
|
|
921
1125
|
}
|
|
922
1126
|
function identitySourcePath(homeDir, agentId) {
|
|
923
|
-
return
|
|
1127
|
+
return path9.join(homeDir, ".exe-os", "identity", `${agentId}.md`);
|
|
924
1128
|
}
|
|
925
1129
|
function claudeAgentLinkPath(homeDir, agentId) {
|
|
926
|
-
return
|
|
1130
|
+
return path9.join(claudeAgentsDir(homeDir), `${agentId}.md`);
|
|
927
1131
|
}
|
|
928
|
-
function ensureAgentSymlink(agentId, homeDir =
|
|
1132
|
+
function ensureAgentSymlink(agentId, homeDir = os4.homedir()) {
|
|
929
1133
|
const target = identitySourcePath(homeDir, agentId);
|
|
930
1134
|
const link = claudeAgentLinkPath(homeDir, agentId);
|
|
931
|
-
|
|
932
|
-
if (
|
|
1135
|
+
mkdirSync5(claudeAgentsDir(homeDir), { recursive: true });
|
|
1136
|
+
if (existsSync8(link)) {
|
|
933
1137
|
let stat;
|
|
934
1138
|
try {
|
|
935
1139
|
stat = lstatSync(link);
|
|
@@ -963,7 +1167,7 @@ function ensureAgentSymlink(agentId, homeDir = os3.homedir()) {
|
|
|
963
1167
|
}
|
|
964
1168
|
return { agentId, action: "created", target, link };
|
|
965
1169
|
}
|
|
966
|
-
async function ensureAllAgentSymlinks(homeDir =
|
|
1170
|
+
async function ensureAllAgentSymlinks(homeDir = os4.homedir()) {
|
|
967
1171
|
const employees = await loadEmployees();
|
|
968
1172
|
return employees.map((emp) => ensureAgentSymlink(emp.name, homeDir));
|
|
969
1173
|
}
|
|
@@ -998,14 +1202,14 @@ var init_mcp_prefix = __esm({
|
|
|
998
1202
|
});
|
|
999
1203
|
|
|
1000
1204
|
// src/lib/preferences.ts
|
|
1001
|
-
import { existsSync as
|
|
1002
|
-
import
|
|
1003
|
-
import
|
|
1004
|
-
function loadPreferences(homeDir =
|
|
1005
|
-
const configPath =
|
|
1006
|
-
if (!
|
|
1205
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
|
|
1206
|
+
import path10 from "path";
|
|
1207
|
+
import os5 from "os";
|
|
1208
|
+
function loadPreferences(homeDir = os5.homedir()) {
|
|
1209
|
+
const configPath = path10.join(homeDir, ".exe-os", "config.json");
|
|
1210
|
+
if (!existsSync9(configPath)) return {};
|
|
1007
1211
|
try {
|
|
1008
|
-
const config = JSON.parse(
|
|
1212
|
+
const config = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
1009
1213
|
return config.preferences ?? {};
|
|
1010
1214
|
} catch {
|
|
1011
1215
|
return {};
|
|
@@ -1031,52 +1235,52 @@ __export(installer_exports, {
|
|
|
1031
1235
|
setupTmux: () => setupTmux
|
|
1032
1236
|
});
|
|
1033
1237
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
|
|
1034
|
-
import { existsSync as
|
|
1035
|
-
import
|
|
1036
|
-
import
|
|
1238
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync7, copyFileSync, mkdirSync as mkdirSync7 } from "fs";
|
|
1239
|
+
import path11 from "path";
|
|
1240
|
+
import os6 from "os";
|
|
1037
1241
|
import { execSync as execSync2 } from "child_process";
|
|
1038
1242
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1039
1243
|
function resolvePackageRoot() {
|
|
1040
1244
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
1041
|
-
let dir =
|
|
1042
|
-
const root =
|
|
1245
|
+
let dir = path11.dirname(thisFile);
|
|
1246
|
+
const root = path11.parse(dir).root;
|
|
1043
1247
|
while (dir !== root) {
|
|
1044
|
-
const pkgPath =
|
|
1045
|
-
if (
|
|
1248
|
+
const pkgPath = path11.join(dir, "package.json");
|
|
1249
|
+
if (existsSync10(pkgPath)) {
|
|
1046
1250
|
try {
|
|
1047
|
-
const pkg = JSON.parse(
|
|
1251
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
1048
1252
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
1049
1253
|
} catch {
|
|
1050
1254
|
}
|
|
1051
1255
|
}
|
|
1052
|
-
dir =
|
|
1256
|
+
dir = path11.dirname(dir);
|
|
1053
1257
|
}
|
|
1054
|
-
return
|
|
1258
|
+
return path11.resolve(path11.dirname(thisFile), "..", "..", "..");
|
|
1055
1259
|
}
|
|
1056
|
-
async function copySlashCommands(packageRoot, homeDir =
|
|
1260
|
+
async function copySlashCommands(packageRoot, homeDir = os6.homedir()) {
|
|
1057
1261
|
let copied = 0;
|
|
1058
1262
|
let skipped = 0;
|
|
1059
|
-
const skillsBase =
|
|
1060
|
-
const exeDir =
|
|
1061
|
-
if (
|
|
1263
|
+
const skillsBase = path11.join(homeDir, ".claude", "skills");
|
|
1264
|
+
const exeDir = path11.join(packageRoot, "src", "commands", "exe");
|
|
1265
|
+
if (existsSync10(exeDir)) {
|
|
1062
1266
|
const entries = await readdir(exeDir);
|
|
1063
1267
|
const mdFiles = entries.filter((f) => f.endsWith(".md"));
|
|
1064
1268
|
for (const file of mdFiles) {
|
|
1065
1269
|
const name = file.replace(".md", "");
|
|
1066
|
-
const destDir =
|
|
1270
|
+
const destDir = path11.join(skillsBase, `exe-${name}`);
|
|
1067
1271
|
await mkdir3(destDir, { recursive: true });
|
|
1068
|
-
const srcPath =
|
|
1069
|
-
const destPath =
|
|
1272
|
+
const srcPath = path11.join(exeDir, file);
|
|
1273
|
+
const destPath = path11.join(destDir, "SKILL.md");
|
|
1070
1274
|
const result = await copyAsSkill(srcPath, destPath, `exe-${name}`);
|
|
1071
1275
|
if (result) copied++;
|
|
1072
1276
|
else skipped++;
|
|
1073
1277
|
}
|
|
1074
1278
|
}
|
|
1075
|
-
const topLevelSrc =
|
|
1076
|
-
if (
|
|
1077
|
-
const destDir =
|
|
1279
|
+
const topLevelSrc = path11.join(packageRoot, "src", "commands", "exe.md");
|
|
1280
|
+
if (existsSync10(topLevelSrc)) {
|
|
1281
|
+
const destDir = path11.join(skillsBase, "exe");
|
|
1078
1282
|
await mkdir3(destDir, { recursive: true });
|
|
1079
|
-
const destPath =
|
|
1283
|
+
const destPath = path11.join(destDir, "SKILL.md");
|
|
1080
1284
|
const result = await copyAsSkill(topLevelSrc, destPath, "exe");
|
|
1081
1285
|
if (result) copied++;
|
|
1082
1286
|
else skipped++;
|
|
@@ -1099,17 +1303,17 @@ name: ${skillName}
|
|
|
1099
1303
|
`);
|
|
1100
1304
|
}
|
|
1101
1305
|
}
|
|
1102
|
-
if (
|
|
1306
|
+
if (existsSync10(destPath)) {
|
|
1103
1307
|
const existing = await readFile3(destPath, "utf-8");
|
|
1104
1308
|
if (existing === content) return false;
|
|
1105
1309
|
}
|
|
1106
1310
|
await writeFile3(destPath, content);
|
|
1107
1311
|
return true;
|
|
1108
1312
|
}
|
|
1109
|
-
async function registerMcpServer(packageRoot, homeDir =
|
|
1110
|
-
const claudeJsonPath =
|
|
1313
|
+
async function registerMcpServer(packageRoot, homeDir = os6.homedir()) {
|
|
1314
|
+
const claudeJsonPath = path11.join(homeDir, ".claude.json");
|
|
1111
1315
|
let claudeJson = {};
|
|
1112
|
-
if (
|
|
1316
|
+
if (existsSync10(claudeJsonPath)) {
|
|
1113
1317
|
try {
|
|
1114
1318
|
claudeJson = JSON.parse(await readFile3(claudeJsonPath, "utf-8"));
|
|
1115
1319
|
} catch {
|
|
@@ -1122,7 +1326,7 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
1122
1326
|
const newEntry = {
|
|
1123
1327
|
type: "stdio",
|
|
1124
1328
|
command: "node",
|
|
1125
|
-
args: [
|
|
1329
|
+
args: [path11.join(packageRoot, "dist", "mcp", "server.js")],
|
|
1126
1330
|
env: {}
|
|
1127
1331
|
};
|
|
1128
1332
|
const currentMem = claudeJson.mcpServers[MCP_LEGACY_KEY];
|
|
@@ -1130,17 +1334,17 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
1130
1334
|
const memMatches = currentMem && JSON.stringify(currentMem) === JSON.stringify(newEntry);
|
|
1131
1335
|
const osMatches = currentOs && JSON.stringify(currentOs) === JSON.stringify(newEntry);
|
|
1132
1336
|
if (memMatches && osMatches) {
|
|
1133
|
-
await cleanSettingsJsonMcp(
|
|
1337
|
+
await cleanSettingsJsonMcp(path11.join(homeDir, ".claude", "settings.json"));
|
|
1134
1338
|
return false;
|
|
1135
1339
|
}
|
|
1136
1340
|
claudeJson.mcpServers[MCP_LEGACY_KEY] = newEntry;
|
|
1137
1341
|
claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
|
|
1138
1342
|
await writeFile3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
1139
|
-
await cleanSettingsJsonMcp(
|
|
1343
|
+
await cleanSettingsJsonMcp(path11.join(homeDir, ".claude", "settings.json"));
|
|
1140
1344
|
return true;
|
|
1141
1345
|
}
|
|
1142
1346
|
async function cleanSettingsJsonMcp(settingsPath) {
|
|
1143
|
-
if (!
|
|
1347
|
+
if (!existsSync10(settingsPath)) return;
|
|
1144
1348
|
try {
|
|
1145
1349
|
const settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1146
1350
|
const servers = settings.mcpServers;
|
|
@@ -1160,14 +1364,14 @@ async function cleanSettingsJsonMcp(settingsPath) {
|
|
|
1160
1364
|
} catch {
|
|
1161
1365
|
}
|
|
1162
1366
|
}
|
|
1163
|
-
async function mergeHooks(packageRoot, homeDir =
|
|
1164
|
-
const settingsPath =
|
|
1165
|
-
const logsDir =
|
|
1166
|
-
const hookLogPath =
|
|
1367
|
+
async function mergeHooks(packageRoot, homeDir = os6.homedir()) {
|
|
1368
|
+
const settingsPath = path11.join(homeDir, ".claude", "settings.json");
|
|
1369
|
+
const logsDir = path11.join(homeDir, ".exe-os", "logs");
|
|
1370
|
+
const hookLogPath = path11.join(logsDir, "hooks.log");
|
|
1167
1371
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
1168
1372
|
await mkdir3(logsDir, { recursive: true });
|
|
1169
1373
|
let settings = {};
|
|
1170
|
-
if (
|
|
1374
|
+
if (existsSync10(settingsPath)) {
|
|
1171
1375
|
try {
|
|
1172
1376
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1173
1377
|
} catch {
|
|
@@ -1189,11 +1393,11 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1189
1393
|
hooks: [
|
|
1190
1394
|
{
|
|
1191
1395
|
type: "command",
|
|
1192
|
-
command: `node "${
|
|
1396
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
|
|
1193
1397
|
},
|
|
1194
1398
|
{
|
|
1195
1399
|
type: "command",
|
|
1196
|
-
command: `node "${
|
|
1400
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
|
|
1197
1401
|
}
|
|
1198
1402
|
]
|
|
1199
1403
|
},
|
|
@@ -1205,7 +1409,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1205
1409
|
hooks: [
|
|
1206
1410
|
{
|
|
1207
1411
|
type: "command",
|
|
1208
|
-
command: `node "${
|
|
1412
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
1209
1413
|
timeout: 1e4
|
|
1210
1414
|
}
|
|
1211
1415
|
]
|
|
@@ -1218,7 +1422,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1218
1422
|
hooks: [
|
|
1219
1423
|
{
|
|
1220
1424
|
type: "command",
|
|
1221
|
-
command: `node "${
|
|
1425
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
1222
1426
|
}
|
|
1223
1427
|
]
|
|
1224
1428
|
},
|
|
@@ -1230,7 +1434,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1230
1434
|
hooks: [
|
|
1231
1435
|
{
|
|
1232
1436
|
type: "command",
|
|
1233
|
-
command: `node "${
|
|
1437
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
|
|
1234
1438
|
timeout: 5e3
|
|
1235
1439
|
}
|
|
1236
1440
|
]
|
|
@@ -1243,7 +1447,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1243
1447
|
hooks: [
|
|
1244
1448
|
{
|
|
1245
1449
|
type: "command",
|
|
1246
|
-
command: `node "${
|
|
1450
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
1247
1451
|
}
|
|
1248
1452
|
]
|
|
1249
1453
|
},
|
|
@@ -1256,7 +1460,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1256
1460
|
hooks: [
|
|
1257
1461
|
{
|
|
1258
1462
|
type: "command",
|
|
1259
|
-
command: `node "${
|
|
1463
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
1260
1464
|
}
|
|
1261
1465
|
]
|
|
1262
1466
|
},
|
|
@@ -1268,7 +1472,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1268
1472
|
hooks: [
|
|
1269
1473
|
{
|
|
1270
1474
|
type: "command",
|
|
1271
|
-
command: `node "${
|
|
1475
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "subagent-stop.js")}"${logSuffix}`
|
|
1272
1476
|
}
|
|
1273
1477
|
]
|
|
1274
1478
|
},
|
|
@@ -1280,7 +1484,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1280
1484
|
hooks: [
|
|
1281
1485
|
{
|
|
1282
1486
|
type: "command",
|
|
1283
|
-
command: `node "${
|
|
1487
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "pre-compact.js")}"${logSuffix}`,
|
|
1284
1488
|
timeout: 1e4
|
|
1285
1489
|
}
|
|
1286
1490
|
]
|
|
@@ -1293,7 +1497,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1293
1497
|
hooks: [
|
|
1294
1498
|
{
|
|
1295
1499
|
type: "command",
|
|
1296
|
-
command: `node "${
|
|
1500
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "session-end.js")}"${logSuffix}`
|
|
1297
1501
|
}
|
|
1298
1502
|
]
|
|
1299
1503
|
},
|
|
@@ -1305,7 +1509,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1305
1509
|
hooks: [
|
|
1306
1510
|
{
|
|
1307
1511
|
type: "command",
|
|
1308
|
-
command: `node "${
|
|
1512
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "notification.js")}"${logSuffix}`
|
|
1309
1513
|
}
|
|
1310
1514
|
]
|
|
1311
1515
|
},
|
|
@@ -1317,7 +1521,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1317
1521
|
hooks: [
|
|
1318
1522
|
{
|
|
1319
1523
|
type: "command",
|
|
1320
|
-
command: `node "${
|
|
1524
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "post-compact.js")}"${logSuffix}`,
|
|
1321
1525
|
timeout: 1e4
|
|
1322
1526
|
}
|
|
1323
1527
|
]
|
|
@@ -1330,7 +1534,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1330
1534
|
hooks: [
|
|
1331
1535
|
{
|
|
1332
1536
|
type: "command",
|
|
1333
|
-
command: `node "${
|
|
1537
|
+
command: `node "${path11.join(packageRoot, "dist", "hooks", "instructions-loaded.js")}"${logSuffix}`
|
|
1334
1538
|
}
|
|
1335
1539
|
]
|
|
1336
1540
|
},
|
|
@@ -1429,13 +1633,13 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1429
1633
|
allowList.push(tool);
|
|
1430
1634
|
}
|
|
1431
1635
|
}
|
|
1432
|
-
await mkdir3(
|
|
1636
|
+
await mkdir3(path11.dirname(settingsPath), { recursive: true });
|
|
1433
1637
|
await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1434
1638
|
return { added, skipped };
|
|
1435
1639
|
}
|
|
1436
|
-
async function cleanOldShellFunctions(homeDir =
|
|
1437
|
-
const rosterPath =
|
|
1438
|
-
if (!
|
|
1640
|
+
async function cleanOldShellFunctions(homeDir = os6.homedir()) {
|
|
1641
|
+
const rosterPath = path11.join(homeDir, ".exe-os", "exe-employees.json");
|
|
1642
|
+
if (!existsSync10(rosterPath)) return 0;
|
|
1439
1643
|
let employees;
|
|
1440
1644
|
try {
|
|
1441
1645
|
employees = JSON.parse(await readFile3(rosterPath, "utf-8"));
|
|
@@ -1450,13 +1654,13 @@ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
|
|
|
1450
1654
|
return { name: n, funcDef, forLoop };
|
|
1451
1655
|
});
|
|
1452
1656
|
const rcFiles = [
|
|
1453
|
-
|
|
1454
|
-
|
|
1657
|
+
path11.join(homeDir, ".zshrc"),
|
|
1658
|
+
path11.join(homeDir, ".bashrc")
|
|
1455
1659
|
];
|
|
1456
1660
|
const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
|
|
1457
1661
|
let totalRemoved = 0;
|
|
1458
1662
|
for (const rcPath of rcFiles) {
|
|
1459
|
-
if (!
|
|
1663
|
+
if (!existsSync10(rcPath)) continue;
|
|
1460
1664
|
let content;
|
|
1461
1665
|
try {
|
|
1462
1666
|
content = await readFile3(rcPath, "utf-8");
|
|
@@ -1560,8 +1764,8 @@ function escapeRegExp(s) {
|
|
|
1560
1764
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1561
1765
|
}
|
|
1562
1766
|
async function injectOrchestrationRules(homeDir) {
|
|
1563
|
-
const claudeDir =
|
|
1564
|
-
const claudeMdPath =
|
|
1767
|
+
const claudeDir = path11.join(homeDir, ".claude");
|
|
1768
|
+
const claudeMdPath = path11.join(claudeDir, "CLAUDE.md");
|
|
1565
1769
|
await mkdir3(claudeDir, { recursive: true });
|
|
1566
1770
|
let existing = "";
|
|
1567
1771
|
try {
|
|
@@ -1583,19 +1787,19 @@ async function injectOrchestrationRules(homeDir) {
|
|
|
1583
1787
|
await writeFile3(claudeMdPath, existing + separator + ORCHESTRATION_RULES + "\n", "utf-8");
|
|
1584
1788
|
return "injected";
|
|
1585
1789
|
}
|
|
1586
|
-
async function installStatusLine(packageRoot, homeDir =
|
|
1790
|
+
async function installStatusLine(packageRoot, homeDir = os6.homedir()) {
|
|
1587
1791
|
const prefs = loadPreferences(homeDir);
|
|
1588
1792
|
if (prefs.ccStatusLine === false) return "opted-out";
|
|
1589
|
-
const claudeDir =
|
|
1793
|
+
const claudeDir = path11.join(homeDir, ".claude");
|
|
1590
1794
|
await mkdir3(claudeDir, { recursive: true });
|
|
1591
|
-
const assetPath =
|
|
1592
|
-
if (!
|
|
1593
|
-
const destScript =
|
|
1795
|
+
const assetPath = path11.join(packageRoot, "dist", "assets", "statusline-command.sh");
|
|
1796
|
+
if (!existsSync10(assetPath)) return "asset-missing";
|
|
1797
|
+
const destScript = path11.join(claudeDir, "statusline-command.sh");
|
|
1594
1798
|
const assetContent = await readFile3(assetPath, "utf-8");
|
|
1595
1799
|
await writeFile3(destScript, assetContent, { mode: 493 });
|
|
1596
|
-
const settingsPath =
|
|
1800
|
+
const settingsPath = path11.join(claudeDir, "settings.json");
|
|
1597
1801
|
let settings = {};
|
|
1598
|
-
if (
|
|
1802
|
+
if (existsSync10(settingsPath)) {
|
|
1599
1803
|
try {
|
|
1600
1804
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1601
1805
|
} catch {
|
|
@@ -1632,13 +1836,13 @@ async function runInstaller(homeDir) {
|
|
|
1632
1836
|
`Hooks: ${hookResult.added} added, ${hookResult.skipped} unchanged
|
|
1633
1837
|
`
|
|
1634
1838
|
);
|
|
1635
|
-
const resolvedHome = homeDir ??
|
|
1636
|
-
const exeWorkspace =
|
|
1637
|
-
if (!
|
|
1839
|
+
const resolvedHome = homeDir ?? os6.homedir();
|
|
1840
|
+
const exeWorkspace = path11.join(resolvedHome, "exe");
|
|
1841
|
+
if (!existsSync10(exeWorkspace)) {
|
|
1638
1842
|
try {
|
|
1639
|
-
await mkdir3(
|
|
1640
|
-
await mkdir3(
|
|
1641
|
-
await mkdir3(
|
|
1843
|
+
await mkdir3(path11.join(exeWorkspace, "content"), { recursive: true });
|
|
1844
|
+
await mkdir3(path11.join(exeWorkspace, "operations"), { recursive: true });
|
|
1845
|
+
await mkdir3(path11.join(exeWorkspace, "output"), { recursive: true });
|
|
1642
1846
|
process.stderr.write(
|
|
1643
1847
|
`Created ~/exe/ \u2014 your automation workspace for non-code projects
|
|
1644
1848
|
`
|
|
@@ -1673,34 +1877,34 @@ exe-os installed successfully.
|
|
|
1673
1877
|
`);
|
|
1674
1878
|
}
|
|
1675
1879
|
function setupTmux(home) {
|
|
1676
|
-
const homeDir = home ??
|
|
1677
|
-
const exeDir =
|
|
1678
|
-
const exeTmuxConf =
|
|
1679
|
-
const userTmuxConf =
|
|
1680
|
-
const backupPath =
|
|
1880
|
+
const homeDir = home ?? os6.homedir();
|
|
1881
|
+
const exeDir = path11.join(homeDir, ".exe-os");
|
|
1882
|
+
const exeTmuxConf = path11.join(exeDir, "tmux.conf");
|
|
1883
|
+
const userTmuxConf = path11.join(homeDir, ".tmux.conf");
|
|
1884
|
+
const backupPath = path11.join(homeDir, ".tmux.conf.backup");
|
|
1681
1885
|
const sourceLine = "source-file ~/.exe-os/tmux.conf";
|
|
1682
1886
|
const pkgRoot = resolvePackageRoot();
|
|
1683
|
-
const assetPath =
|
|
1684
|
-
if (!
|
|
1887
|
+
const assetPath = path11.join(pkgRoot, "dist", "assets", "tmux.conf");
|
|
1888
|
+
if (!existsSync10(assetPath)) {
|
|
1685
1889
|
process.stderr.write(`exe-os: tmux.conf asset not found at ${assetPath} \u2014 skipping tmux setup
|
|
1686
1890
|
`);
|
|
1687
1891
|
return;
|
|
1688
1892
|
}
|
|
1689
|
-
|
|
1893
|
+
mkdirSync7(exeDir, { recursive: true });
|
|
1690
1894
|
copyFileSync(assetPath, exeTmuxConf);
|
|
1691
|
-
if (
|
|
1692
|
-
const existing =
|
|
1895
|
+
if (existsSync10(userTmuxConf)) {
|
|
1896
|
+
const existing = readFileSync9(userTmuxConf, "utf8");
|
|
1693
1897
|
if (!existing.includes(sourceLine)) {
|
|
1694
|
-
if (!
|
|
1898
|
+
if (!existsSync10(backupPath)) {
|
|
1695
1899
|
copyFileSync(userTmuxConf, backupPath);
|
|
1696
1900
|
process.stderr.write(`exe-os: backed up existing tmux config to ${backupPath}
|
|
1697
1901
|
`);
|
|
1698
1902
|
}
|
|
1699
|
-
|
|
1903
|
+
writeFileSync7(userTmuxConf, `${sourceLine}
|
|
1700
1904
|
${existing}`);
|
|
1701
1905
|
}
|
|
1702
1906
|
} else {
|
|
1703
|
-
|
|
1907
|
+
writeFileSync7(userTmuxConf, `# Exe OS tmux defaults \u2014 remove this line to use your own config
|
|
1704
1908
|
${sourceLine}
|
|
1705
1909
|
`);
|
|
1706
1910
|
}
|
|
@@ -1711,10 +1915,10 @@ ${sourceLine}
|
|
|
1711
1915
|
process.stderr.write("exe-os: tmux config installed\n");
|
|
1712
1916
|
}
|
|
1713
1917
|
function setupGhostty(home) {
|
|
1714
|
-
const homeDir = home ??
|
|
1715
|
-
const xdgConfig =
|
|
1716
|
-
const macConfig =
|
|
1717
|
-
const ghosttyInstalled =
|
|
1918
|
+
const homeDir = home ?? os6.homedir();
|
|
1919
|
+
const xdgConfig = path11.join(homeDir, ".config", "ghostty");
|
|
1920
|
+
const macConfig = path11.join(homeDir, "Library", "Application Support", "com.mitchellh.ghostty");
|
|
1921
|
+
const ghosttyInstalled = existsSync10(xdgConfig) || existsSync10(macConfig) || (() => {
|
|
1718
1922
|
try {
|
|
1719
1923
|
execSync2("which ghostty 2>/dev/null");
|
|
1720
1924
|
return true;
|
|
@@ -1726,47 +1930,47 @@ function setupGhostty(home) {
|
|
|
1726
1930
|
return;
|
|
1727
1931
|
}
|
|
1728
1932
|
const pkgRoot = resolvePackageRoot();
|
|
1729
|
-
const assetPath =
|
|
1730
|
-
if (!
|
|
1933
|
+
const assetPath = path11.join(pkgRoot, "dist", "assets", "ghostty.conf");
|
|
1934
|
+
if (!existsSync10(assetPath)) {
|
|
1731
1935
|
process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
|
|
1732
1936
|
return;
|
|
1733
1937
|
}
|
|
1734
1938
|
const configDir = xdgConfig;
|
|
1735
|
-
const configPath =
|
|
1736
|
-
const backupPath =
|
|
1737
|
-
|
|
1939
|
+
const configPath = path11.join(configDir, "config");
|
|
1940
|
+
const backupPath = path11.join(configDir, "config.backup");
|
|
1941
|
+
mkdirSync7(configDir, { recursive: true });
|
|
1738
1942
|
const START_MARKER = "# \u2500\u2500 exe-os:ghostty-start \u2500\u2500";
|
|
1739
1943
|
const END_MARKER = "# \u2500\u2500 exe-os:ghostty-end \u2500\u2500";
|
|
1740
|
-
const assetContent =
|
|
1944
|
+
const assetContent = readFileSync9(assetPath, "utf8").trim();
|
|
1741
1945
|
const markedSection = `${START_MARKER}
|
|
1742
1946
|
${assetContent}
|
|
1743
1947
|
${END_MARKER}`;
|
|
1744
|
-
if (
|
|
1745
|
-
const existing =
|
|
1948
|
+
if (existsSync10(configPath)) {
|
|
1949
|
+
const existing = readFileSync9(configPath, "utf8");
|
|
1746
1950
|
if (existing.includes(START_MARKER) && existing.includes(END_MARKER)) {
|
|
1747
1951
|
const before = existing.slice(0, existing.indexOf(START_MARKER));
|
|
1748
1952
|
const after = existing.slice(existing.indexOf(END_MARKER) + END_MARKER.length);
|
|
1749
|
-
|
|
1953
|
+
writeFileSync7(configPath, `${before}${markedSection}${after}`);
|
|
1750
1954
|
} else if (existing.includes("Exe OS")) {
|
|
1751
|
-
if (!
|
|
1955
|
+
if (!existsSync10(backupPath)) {
|
|
1752
1956
|
copyFileSync(configPath, backupPath);
|
|
1753
1957
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1754
1958
|
`);
|
|
1755
1959
|
}
|
|
1756
|
-
|
|
1960
|
+
writeFileSync7(configPath, `${markedSection}
|
|
1757
1961
|
`);
|
|
1758
1962
|
} else {
|
|
1759
|
-
if (!
|
|
1963
|
+
if (!existsSync10(backupPath)) {
|
|
1760
1964
|
copyFileSync(configPath, backupPath);
|
|
1761
1965
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1762
1966
|
`);
|
|
1763
1967
|
}
|
|
1764
|
-
|
|
1968
|
+
writeFileSync7(configPath, `${markedSection}
|
|
1765
1969
|
|
|
1766
1970
|
${existing}`);
|
|
1767
1971
|
}
|
|
1768
1972
|
} else {
|
|
1769
|
-
|
|
1973
|
+
writeFileSync7(configPath, `${markedSection}
|
|
1770
1974
|
`);
|
|
1771
1975
|
}
|
|
1772
1976
|
process.stderr.write("exe-os: Ghostty config installed\n");
|
|
@@ -1807,8 +2011,8 @@ ${EXE_SECTION_END}`;
|
|
|
1807
2011
|
});
|
|
1808
2012
|
|
|
1809
2013
|
// src/bin/exe-new-employee.ts
|
|
1810
|
-
import { existsSync as
|
|
1811
|
-
import
|
|
2014
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8 } from "fs";
|
|
2015
|
+
import path12 from "path";
|
|
1812
2016
|
|
|
1813
2017
|
// src/lib/session-wrappers.ts
|
|
1814
2018
|
import {
|
|
@@ -2400,18 +2604,18 @@ function isMainModule(importMetaUrl) {
|
|
|
2400
2604
|
// src/lib/plan-limits.ts
|
|
2401
2605
|
init_database();
|
|
2402
2606
|
init_employees();
|
|
2403
|
-
import { readFileSync as
|
|
2404
|
-
import
|
|
2607
|
+
import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
|
|
2608
|
+
import path7 from "path";
|
|
2405
2609
|
|
|
2406
2610
|
// src/lib/license.ts
|
|
2407
2611
|
init_config();
|
|
2408
|
-
import { readFileSync as
|
|
2612
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
2409
2613
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2410
|
-
import
|
|
2614
|
+
import path6 from "path";
|
|
2411
2615
|
import { jwtVerify, importSPKI } from "jose";
|
|
2412
|
-
var LICENSE_PATH =
|
|
2413
|
-
var CACHE_PATH =
|
|
2414
|
-
var DEVICE_ID_PATH =
|
|
2616
|
+
var LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
|
|
2617
|
+
var CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
|
|
2618
|
+
var DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
|
|
2415
2619
|
var API_BASE = "https://askexe.com/cloud";
|
|
2416
2620
|
var RETRY_DELAY_MS = 500;
|
|
2417
2621
|
async function fetchRetry(url, init) {
|
|
@@ -2444,37 +2648,37 @@ var FREE_LICENSE = {
|
|
|
2444
2648
|
memoryLimit: 5e3
|
|
2445
2649
|
};
|
|
2446
2650
|
function loadDeviceId() {
|
|
2447
|
-
const deviceJsonPath =
|
|
2651
|
+
const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
|
|
2448
2652
|
try {
|
|
2449
|
-
if (
|
|
2450
|
-
const data = JSON.parse(
|
|
2653
|
+
if (existsSync5(deviceJsonPath)) {
|
|
2654
|
+
const data = JSON.parse(readFileSync5(deviceJsonPath, "utf8"));
|
|
2451
2655
|
if (data.deviceId) return data.deviceId;
|
|
2452
2656
|
}
|
|
2453
2657
|
} catch {
|
|
2454
2658
|
}
|
|
2455
2659
|
try {
|
|
2456
|
-
if (
|
|
2457
|
-
const id2 =
|
|
2660
|
+
if (existsSync5(DEVICE_ID_PATH)) {
|
|
2661
|
+
const id2 = readFileSync5(DEVICE_ID_PATH, "utf8").trim();
|
|
2458
2662
|
if (id2) return id2;
|
|
2459
2663
|
}
|
|
2460
2664
|
} catch {
|
|
2461
2665
|
}
|
|
2462
2666
|
const id = randomUUID2();
|
|
2463
|
-
|
|
2464
|
-
|
|
2667
|
+
mkdirSync3(EXE_AI_DIR, { recursive: true });
|
|
2668
|
+
writeFileSync4(DEVICE_ID_PATH, id, "utf8");
|
|
2465
2669
|
return id;
|
|
2466
2670
|
}
|
|
2467
2671
|
function loadLicense() {
|
|
2468
2672
|
try {
|
|
2469
|
-
if (!
|
|
2470
|
-
return
|
|
2673
|
+
if (!existsSync5(LICENSE_PATH)) return null;
|
|
2674
|
+
return readFileSync5(LICENSE_PATH, "utf8").trim();
|
|
2471
2675
|
} catch {
|
|
2472
2676
|
return null;
|
|
2473
2677
|
}
|
|
2474
2678
|
}
|
|
2475
2679
|
function saveLicense(apiKey) {
|
|
2476
|
-
|
|
2477
|
-
|
|
2680
|
+
mkdirSync3(EXE_AI_DIR, { recursive: true });
|
|
2681
|
+
writeFileSync4(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
2478
2682
|
}
|
|
2479
2683
|
async function verifyLicenseJwt(token) {
|
|
2480
2684
|
try {
|
|
@@ -2500,8 +2704,8 @@ async function verifyLicenseJwt(token) {
|
|
|
2500
2704
|
}
|
|
2501
2705
|
async function getCachedLicense() {
|
|
2502
2706
|
try {
|
|
2503
|
-
if (!
|
|
2504
|
-
const raw = JSON.parse(
|
|
2707
|
+
if (!existsSync5(CACHE_PATH)) return null;
|
|
2708
|
+
const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
|
|
2505
2709
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
2506
2710
|
return await verifyLicenseJwt(raw.token);
|
|
2507
2711
|
} catch {
|
|
@@ -2510,8 +2714,8 @@ async function getCachedLicense() {
|
|
|
2510
2714
|
}
|
|
2511
2715
|
function readCachedToken() {
|
|
2512
2716
|
try {
|
|
2513
|
-
if (!
|
|
2514
|
-
const raw = JSON.parse(
|
|
2717
|
+
if (!existsSync5(CACHE_PATH)) return null;
|
|
2718
|
+
const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
|
|
2515
2719
|
return typeof raw.token === "string" ? raw.token : null;
|
|
2516
2720
|
} catch {
|
|
2517
2721
|
return null;
|
|
@@ -2545,7 +2749,7 @@ function getRawCachedPlan() {
|
|
|
2545
2749
|
}
|
|
2546
2750
|
function cacheResponse(token) {
|
|
2547
2751
|
try {
|
|
2548
|
-
|
|
2752
|
+
writeFileSync4(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
2549
2753
|
} catch {
|
|
2550
2754
|
}
|
|
2551
2755
|
}
|
|
@@ -2610,9 +2814,9 @@ async function checkLicense() {
|
|
|
2610
2814
|
let key = loadLicense();
|
|
2611
2815
|
if (!key) {
|
|
2612
2816
|
try {
|
|
2613
|
-
const configPath =
|
|
2614
|
-
if (
|
|
2615
|
-
const raw = JSON.parse(
|
|
2817
|
+
const configPath = path6.join(EXE_AI_DIR, "config.json");
|
|
2818
|
+
if (existsSync5(configPath)) {
|
|
2819
|
+
const raw = JSON.parse(readFileSync5(configPath, "utf8"));
|
|
2616
2820
|
const cloud = raw.cloud;
|
|
2617
2821
|
if (cloud?.apiKey) {
|
|
2618
2822
|
key = cloud.apiKey;
|
|
@@ -2637,7 +2841,7 @@ var PlanLimitError = class extends Error {
|
|
|
2637
2841
|
this.name = "PlanLimitError";
|
|
2638
2842
|
}
|
|
2639
2843
|
};
|
|
2640
|
-
var CACHE_PATH2 =
|
|
2844
|
+
var CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
2641
2845
|
async function assertEmployeeLimit(license, rosterPath) {
|
|
2642
2846
|
const lic = license ?? await checkLicense();
|
|
2643
2847
|
if (lic.employeeLimit < 0) return;
|
|
@@ -2671,7 +2875,6 @@ async function main() {
|
|
|
2671
2875
|
console.error(`Invalid name: ${validation.error}`);
|
|
2672
2876
|
process.exit(1);
|
|
2673
2877
|
}
|
|
2674
|
-
const employees = await loadEmployees();
|
|
2675
2878
|
let newEmployee;
|
|
2676
2879
|
const effectiveTemplate = templateName ?? name;
|
|
2677
2880
|
const template = getTemplate(effectiveTemplate);
|
|
@@ -2709,14 +2912,12 @@ async function main() {
|
|
|
2709
2912
|
}
|
|
2710
2913
|
throw err;
|
|
2711
2914
|
}
|
|
2712
|
-
let updated;
|
|
2713
2915
|
try {
|
|
2714
|
-
|
|
2916
|
+
await hireEmployee(newEmployee);
|
|
2715
2917
|
} catch (err) {
|
|
2716
2918
|
console.error(err instanceof Error ? err.message : String(err));
|
|
2717
2919
|
process.exit(1);
|
|
2718
2920
|
}
|
|
2719
|
-
await saveEmployees(updated);
|
|
2720
2921
|
try {
|
|
2721
2922
|
const { getTemplate: getIdentityTemplate } = await Promise.resolve().then(() => (init_identity_templates(), identity_templates_exports));
|
|
2722
2923
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
@@ -2733,7 +2934,7 @@ async function main() {
|
|
|
2733
2934
|
const identityTemplate = templateKey ? getIdentityTemplate(templateKey) : null;
|
|
2734
2935
|
if (identityTemplate) {
|
|
2735
2936
|
const idPath = identityPath2(name);
|
|
2736
|
-
const dir =
|
|
2937
|
+
const dir = path12.dirname(idPath);
|
|
2737
2938
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
2738
2939
|
const content = identityTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`);
|
|
2739
2940
|
fs.writeFileSync(idPath, content, "utf-8");
|
|
@@ -2741,9 +2942,9 @@ async function main() {
|
|
|
2741
2942
|
}
|
|
2742
2943
|
} catch {
|
|
2743
2944
|
}
|
|
2744
|
-
const taskDir =
|
|
2745
|
-
if (!
|
|
2746
|
-
|
|
2945
|
+
const taskDir = path12.join(process.cwd(), "exe", name);
|
|
2946
|
+
if (!existsSync11(taskDir)) {
|
|
2947
|
+
mkdirSync8(taskDir, { recursive: true });
|
|
2747
2948
|
}
|
|
2748
2949
|
const bins = registerBinSymlinks(newEmployee.name);
|
|
2749
2950
|
if (bins.created.length > 0) {
|