@askexenow/exe-os 0.8.21 → 0.8.23
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 +120 -13
- package/dist/bin/cli.js +22 -10
- package/dist/bin/install.js +8 -4
- package/package.json +1 -1
|
@@ -446,11 +446,110 @@ var init_shard_manager = __esm({
|
|
|
446
446
|
}
|
|
447
447
|
});
|
|
448
448
|
|
|
449
|
+
// src/lib/employees.ts
|
|
450
|
+
var employees_exports = {};
|
|
451
|
+
__export(employees_exports, {
|
|
452
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
453
|
+
addEmployee: () => addEmployee,
|
|
454
|
+
getEmployee: () => getEmployee,
|
|
455
|
+
loadEmployees: () => loadEmployees,
|
|
456
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
457
|
+
saveEmployees: () => saveEmployees,
|
|
458
|
+
validateEmployeeName: () => validateEmployeeName
|
|
459
|
+
});
|
|
460
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
461
|
+
import { existsSync as existsSync5, symlinkSync, readlinkSync } from "fs";
|
|
462
|
+
import { execSync } from "child_process";
|
|
463
|
+
import path5 from "path";
|
|
464
|
+
function validateEmployeeName(name) {
|
|
465
|
+
if (!name) {
|
|
466
|
+
return { valid: false, error: "Name is required" };
|
|
467
|
+
}
|
|
468
|
+
if (name.length > 32) {
|
|
469
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
470
|
+
}
|
|
471
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
472
|
+
return {
|
|
473
|
+
valid: false,
|
|
474
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
return { valid: true };
|
|
478
|
+
}
|
|
479
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
480
|
+
if (!existsSync5(employeesPath)) {
|
|
481
|
+
return [];
|
|
482
|
+
}
|
|
483
|
+
const raw = await readFile3(employeesPath, "utf-8");
|
|
484
|
+
try {
|
|
485
|
+
return JSON.parse(raw);
|
|
486
|
+
} catch {
|
|
487
|
+
return [];
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
491
|
+
await mkdir3(path5.dirname(employeesPath), { recursive: true });
|
|
492
|
+
await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
493
|
+
}
|
|
494
|
+
function getEmployee(employees, name) {
|
|
495
|
+
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
496
|
+
}
|
|
497
|
+
function addEmployee(employees, employee) {
|
|
498
|
+
const normalized = { ...employee, name: employee.name.toLowerCase() };
|
|
499
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
500
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
501
|
+
}
|
|
502
|
+
return [...employees, normalized];
|
|
503
|
+
}
|
|
504
|
+
function registerBinSymlinks(name) {
|
|
505
|
+
const created = [];
|
|
506
|
+
const skipped = [];
|
|
507
|
+
const errors = [];
|
|
508
|
+
let exeBinPath;
|
|
509
|
+
try {
|
|
510
|
+
exeBinPath = execSync("which exe", { encoding: "utf-8" }).trim();
|
|
511
|
+
} catch {
|
|
512
|
+
errors.push("Could not find 'exe' in PATH");
|
|
513
|
+
return { created, skipped, errors };
|
|
514
|
+
}
|
|
515
|
+
const binDir = path5.dirname(exeBinPath);
|
|
516
|
+
let target;
|
|
517
|
+
try {
|
|
518
|
+
target = readlinkSync(exeBinPath);
|
|
519
|
+
} catch {
|
|
520
|
+
errors.push("Could not read 'exe' symlink");
|
|
521
|
+
return { created, skipped, errors };
|
|
522
|
+
}
|
|
523
|
+
for (const suffix of ["", "-opencode"]) {
|
|
524
|
+
const linkName = `${name}${suffix}`;
|
|
525
|
+
const linkPath = path5.join(binDir, linkName);
|
|
526
|
+
if (existsSync5(linkPath)) {
|
|
527
|
+
skipped.push(linkName);
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
try {
|
|
531
|
+
symlinkSync(target, linkPath);
|
|
532
|
+
created.push(linkName);
|
|
533
|
+
} catch (err) {
|
|
534
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return { created, skipped, errors };
|
|
538
|
+
}
|
|
539
|
+
var EMPLOYEES_PATH;
|
|
540
|
+
var init_employees = __esm({
|
|
541
|
+
"src/lib/employees.ts"() {
|
|
542
|
+
"use strict";
|
|
543
|
+
init_config();
|
|
544
|
+
EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
|
|
449
548
|
// src/bin/backfill-conversations.ts
|
|
450
549
|
import crypto2 from "crypto";
|
|
451
550
|
import { createReadStream } from "fs";
|
|
452
551
|
import { readdir, stat } from "fs/promises";
|
|
453
|
-
import
|
|
552
|
+
import path6 from "path";
|
|
454
553
|
import { createInterface } from "readline";
|
|
455
554
|
import { homedir } from "os";
|
|
456
555
|
|
|
@@ -1876,7 +1975,7 @@ function isMainModule(importMetaUrl) {
|
|
|
1876
1975
|
// src/bin/backfill-conversations.ts
|
|
1877
1976
|
process.env.EXE_EMBED_PRIORITY = "low";
|
|
1878
1977
|
async function findJsonlFiles(cutoffMs) {
|
|
1879
|
-
const projectsDir =
|
|
1978
|
+
const projectsDir = path6.join(homedir(), ".claude", "projects");
|
|
1880
1979
|
const files = [];
|
|
1881
1980
|
async function walk(dir) {
|
|
1882
1981
|
let entries;
|
|
@@ -1886,7 +1985,7 @@ async function findJsonlFiles(cutoffMs) {
|
|
|
1886
1985
|
return;
|
|
1887
1986
|
}
|
|
1888
1987
|
for (const entry of entries) {
|
|
1889
|
-
const full =
|
|
1988
|
+
const full = path6.join(dir, entry.name);
|
|
1890
1989
|
if (entry.isDirectory()) {
|
|
1891
1990
|
await walk(full);
|
|
1892
1991
|
} else if (entry.name.endsWith(".jsonl")) {
|
|
@@ -1902,9 +2001,9 @@ async function findJsonlFiles(cutoffMs) {
|
|
|
1902
2001
|
return files;
|
|
1903
2002
|
}
|
|
1904
2003
|
function projectNameFromPath(filePath) {
|
|
1905
|
-
const projectsDir =
|
|
1906
|
-
const relative =
|
|
1907
|
-
const projectDir = relative.split(
|
|
2004
|
+
const projectsDir = path6.join(homedir(), ".claude", "projects");
|
|
2005
|
+
const relative = path6.relative(projectsDir, filePath);
|
|
2006
|
+
const projectDir = relative.split(path6.sep)[0] ?? relative;
|
|
1908
2007
|
const homeEncoded = homedir().replaceAll("/", "-");
|
|
1909
2008
|
if (projectDir.startsWith(homeEncoded + "-")) {
|
|
1910
2009
|
return projectDir.slice(homeEncoded.length + 1);
|
|
@@ -1967,14 +2066,14 @@ async function extractConversationPairs(filePath, projectFilter) {
|
|
|
1967
2066
|
} else if (entry.type === "assistant" && entry.message?.content) {
|
|
1968
2067
|
const assistantText = extractAssistantText(entry.message.content);
|
|
1969
2068
|
if (!assistantText.trim()) continue;
|
|
1970
|
-
const resolvedProject = fileCwd ?
|
|
2069
|
+
const resolvedProject = fileCwd ? path6.basename(fileCwd) : fallbackProject;
|
|
1971
2070
|
const userText = pendingUser?.text ?? "";
|
|
1972
2071
|
const timestamp = entry.timestamp ?? pendingUser?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1973
2072
|
pairs.push({
|
|
1974
2073
|
userText,
|
|
1975
2074
|
assistantText,
|
|
1976
2075
|
timestamp,
|
|
1977
|
-
sessionId: fileSessionId ||
|
|
2076
|
+
sessionId: fileSessionId || path6.basename(filePath, ".jsonl"),
|
|
1978
2077
|
project: resolvedProject,
|
|
1979
2078
|
cwd: fileCwd || fallbackProject
|
|
1980
2079
|
});
|
|
@@ -2013,7 +2112,7 @@ async function loadExistingHashes(cutoffIso) {
|
|
|
2013
2112
|
}
|
|
2014
2113
|
var MAX_CONTENT_LENGTH = 8e3;
|
|
2015
2114
|
var MIN_ASSISTANT_LENGTH = 50;
|
|
2016
|
-
async function storePair(pair, daemonConnected, stats) {
|
|
2115
|
+
async function storePair(pair, daemonConnected, stats, agentId) {
|
|
2017
2116
|
const client = getClient();
|
|
2018
2117
|
const id = crypto2.randomUUID();
|
|
2019
2118
|
const userTrunc = pair.userText.length > MAX_CONTENT_LENGTH ? pair.userText.slice(0, MAX_CONTENT_LENGTH) : pair.userText;
|
|
@@ -2062,8 +2161,8 @@ async function storePair(pair, daemonConnected, stats) {
|
|
|
2062
2161
|
}
|
|
2063
2162
|
await writeMemory({
|
|
2064
2163
|
id: crypto2.randomUUID(),
|
|
2065
|
-
agent_id:
|
|
2066
|
-
agent_role: "
|
|
2164
|
+
agent_id: agentId,
|
|
2165
|
+
agent_role: "coo",
|
|
2067
2166
|
session_id: pair.sessionId,
|
|
2068
2167
|
timestamp: pair.timestamp,
|
|
2069
2168
|
tool_name: "ConversationBackfill",
|
|
@@ -2086,7 +2185,15 @@ async function backfillConversations(options) {
|
|
|
2086
2185
|
};
|
|
2087
2186
|
const cutoffMs = Date.now() - options.days * 24 * 60 * 60 * 1e3;
|
|
2088
2187
|
const cutoffIso = new Date(cutoffMs).toISOString();
|
|
2089
|
-
|
|
2188
|
+
let cooAgentId = "exe";
|
|
2189
|
+
try {
|
|
2190
|
+
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
2191
|
+
const employees = await loadEmployees2();
|
|
2192
|
+
const coo = employees.find((e) => e.role === "COO");
|
|
2193
|
+
if (coo) cooAgentId = coo.name;
|
|
2194
|
+
} catch {
|
|
2195
|
+
}
|
|
2196
|
+
process.stderr.write(`[backfill-conversations] Scanning last ${options.days} days (agent: ${cooAgentId})...
|
|
2090
2197
|
`);
|
|
2091
2198
|
if (!options.dryRun) {
|
|
2092
2199
|
process.stderr.write("[backfill-conversations] Initializing store...\n");
|
|
@@ -2123,7 +2230,7 @@ async function backfillConversations(options) {
|
|
|
2123
2230
|
}
|
|
2124
2231
|
seenHashes.add(hash);
|
|
2125
2232
|
if (!options.dryRun) {
|
|
2126
|
-
await storePair(pair, daemonConnected, stats);
|
|
2233
|
+
await storePair(pair, daemonConnected, stats, cooAgentId);
|
|
2127
2234
|
} else {
|
|
2128
2235
|
stats.conversationsStored++;
|
|
2129
2236
|
stats.memoriesStored++;
|
package/dist/bin/cli.js
CHANGED
|
@@ -751,13 +751,17 @@ async function mergeHooks(packageRoot, homeDir = os3.homedir()) {
|
|
|
751
751
|
settings.hooks[event] = [];
|
|
752
752
|
}
|
|
753
753
|
const existing = settings.hooks[event];
|
|
754
|
-
const
|
|
755
|
-
|
|
754
|
+
const correctCommand = group.hooks[0]?.command ?? "";
|
|
755
|
+
const alreadyCorrect = existing.some(
|
|
756
|
+
(g) => g.hooks.some((h) => h.command === correctCommand)
|
|
756
757
|
);
|
|
757
|
-
if (
|
|
758
|
+
if (alreadyCorrect) {
|
|
758
759
|
skipped++;
|
|
759
760
|
} else {
|
|
760
|
-
existing.
|
|
761
|
+
settings.hooks[event] = existing.filter(
|
|
762
|
+
(g) => !g.hooks.some((h) => h.command.includes(marker))
|
|
763
|
+
);
|
|
764
|
+
settings.hooks[event].push(group);
|
|
761
765
|
added++;
|
|
762
766
|
}
|
|
763
767
|
}
|
|
@@ -3085,7 +3089,7 @@ async function loadExistingHashes(cutoffIso) {
|
|
|
3085
3089
|
}
|
|
3086
3090
|
return hashes;
|
|
3087
3091
|
}
|
|
3088
|
-
async function storePair(pair, daemonConnected, stats) {
|
|
3092
|
+
async function storePair(pair, daemonConnected, stats, agentId) {
|
|
3089
3093
|
const client = getClient();
|
|
3090
3094
|
const id = crypto2.randomUUID();
|
|
3091
3095
|
const userTrunc = pair.userText.length > MAX_CONTENT_LENGTH ? pair.userText.slice(0, MAX_CONTENT_LENGTH) : pair.userText;
|
|
@@ -3134,8 +3138,8 @@ async function storePair(pair, daemonConnected, stats) {
|
|
|
3134
3138
|
}
|
|
3135
3139
|
await writeMemory({
|
|
3136
3140
|
id: crypto2.randomUUID(),
|
|
3137
|
-
agent_id:
|
|
3138
|
-
agent_role: "
|
|
3141
|
+
agent_id: agentId,
|
|
3142
|
+
agent_role: "coo",
|
|
3139
3143
|
session_id: pair.sessionId,
|
|
3140
3144
|
timestamp: pair.timestamp,
|
|
3141
3145
|
tool_name: "ConversationBackfill",
|
|
@@ -3158,7 +3162,15 @@ async function backfillConversations(options) {
|
|
|
3158
3162
|
};
|
|
3159
3163
|
const cutoffMs = Date.now() - options.days * 24 * 60 * 60 * 1e3;
|
|
3160
3164
|
const cutoffIso = new Date(cutoffMs).toISOString();
|
|
3161
|
-
|
|
3165
|
+
let cooAgentId = "exe";
|
|
3166
|
+
try {
|
|
3167
|
+
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
3168
|
+
const employees = await loadEmployees2();
|
|
3169
|
+
const coo = employees.find((e) => e.role === "COO");
|
|
3170
|
+
if (coo) cooAgentId = coo.name;
|
|
3171
|
+
} catch {
|
|
3172
|
+
}
|
|
3173
|
+
process.stderr.write(`[backfill-conversations] Scanning last ${options.days} days (agent: ${cooAgentId})...
|
|
3162
3174
|
`);
|
|
3163
3175
|
if (!options.dryRun) {
|
|
3164
3176
|
process.stderr.write("[backfill-conversations] Initializing store...\n");
|
|
@@ -3195,7 +3207,7 @@ async function backfillConversations(options) {
|
|
|
3195
3207
|
}
|
|
3196
3208
|
seenHashes.add(hash);
|
|
3197
3209
|
if (!options.dryRun) {
|
|
3198
|
-
await storePair(pair, daemonConnected, stats);
|
|
3210
|
+
await storePair(pair, daemonConnected, stats, cooAgentId);
|
|
3199
3211
|
} else {
|
|
3200
3212
|
stats.conversationsStored++;
|
|
3201
3213
|
stats.memoriesStored++;
|
|
@@ -21447,7 +21459,7 @@ async function runClaudeUninstall() {
|
|
|
21447
21459
|
return {
|
|
21448
21460
|
...g,
|
|
21449
21461
|
hooks: g.hooks.filter(
|
|
21450
|
-
(h) => !(typeof h.command === "string" && h.command.includes("exe-os"))
|
|
21462
|
+
(h) => !(typeof h.command === "string" && (h.command.includes("exe-os") || h.command.includes("askexenow") || h.command.includes("exe-mem")))
|
|
21451
21463
|
)
|
|
21452
21464
|
};
|
|
21453
21465
|
}).filter(
|
package/dist/bin/install.js
CHANGED
|
@@ -478,13 +478,17 @@ async function mergeHooks(packageRoot, homeDir = os3.homedir()) {
|
|
|
478
478
|
settings.hooks[event] = [];
|
|
479
479
|
}
|
|
480
480
|
const existing = settings.hooks[event];
|
|
481
|
-
const
|
|
482
|
-
|
|
481
|
+
const correctCommand = group.hooks[0]?.command ?? "";
|
|
482
|
+
const alreadyCorrect = existing.some(
|
|
483
|
+
(g) => g.hooks.some((h) => h.command === correctCommand)
|
|
483
484
|
);
|
|
484
|
-
if (
|
|
485
|
+
if (alreadyCorrect) {
|
|
485
486
|
skipped++;
|
|
486
487
|
} else {
|
|
487
|
-
existing.
|
|
488
|
+
settings.hooks[event] = existing.filter(
|
|
489
|
+
(g) => !g.hooks.some((h) => h.command.includes(marker))
|
|
490
|
+
);
|
|
491
|
+
settings.hooks[event].push(group);
|
|
488
492
|
added++;
|
|
489
493
|
}
|
|
490
494
|
}
|
package/package.json
CHANGED