@askexenow/exe-os 0.8.37 → 0.8.38
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 +66 -60
- package/dist/bin/backfill-responses.js +7 -8
- package/dist/bin/backfill-vectors.js +1 -8
- package/dist/bin/cleanup-stale-review-tasks.js +1 -8
- package/dist/bin/cli.js +520 -325
- package/dist/bin/exe-assign.js +7 -8
- package/dist/bin/exe-boot.js +54 -21
- package/dist/bin/exe-call.js +9 -4
- package/dist/bin/exe-cloud.js +37 -3
- package/dist/bin/exe-doctor.js +1 -8
- package/dist/bin/exe-export-behaviors.js +4 -11
- package/dist/bin/exe-forget.js +1 -8
- package/dist/bin/exe-gateway.js +72 -30
- package/dist/bin/exe-heartbeat.js +4 -11
- package/dist/bin/exe-kill.js +1 -8
- package/dist/bin/exe-launch-agent.js +51 -14
- package/dist/bin/exe-link.js +13 -3
- package/dist/bin/exe-new-employee.js +35 -10
- package/dist/bin/exe-pending-messages.js +1 -8
- package/dist/bin/exe-pending-notifications.js +1 -8
- package/dist/bin/exe-pending-reviews.js +4 -11
- package/dist/bin/exe-review.js +7 -8
- package/dist/bin/exe-search.js +10 -11
- package/dist/bin/exe-session-cleanup.js +11 -12
- package/dist/bin/exe-status.js +1 -8
- package/dist/bin/exe-team.js +1 -8
- package/dist/bin/git-sweep.js +7 -8
- package/dist/bin/graph-backfill.js +1 -8
- package/dist/bin/graph-export.js +1 -8
- package/dist/bin/install.js +9 -0
- package/dist/bin/scan-tasks.js +7 -8
- package/dist/bin/setup.js +396 -245
- package/dist/bin/shard-migrate.js +1 -8
- package/dist/bin/wiki-sync.js +1 -8
- package/dist/gateway/index.js +30 -30
- package/dist/hooks/bug-report-worker.js +4 -11
- package/dist/hooks/commit-complete.js +7 -8
- package/dist/hooks/error-recall.js +11 -12
- package/dist/hooks/ingest-worker.js +24 -9
- package/dist/hooks/instructions-loaded.js +7 -8
- package/dist/hooks/notification.js +7 -8
- package/dist/hooks/post-compact.js +7 -8
- package/dist/hooks/pre-compact.js +7 -8
- package/dist/hooks/pre-tool-use.js +7 -8
- package/dist/hooks/prompt-ingest-worker.js +19 -4
- package/dist/hooks/prompt-submit.js +14 -9
- package/dist/hooks/response-ingest-worker.js +20 -5
- package/dist/hooks/session-end.js +11 -12
- package/dist/hooks/session-start.js +11 -12
- package/dist/hooks/stop.js +7 -8
- package/dist/hooks/subagent-stop.js +7 -8
- package/dist/hooks/summary-worker.js +24 -9
- package/dist/index.js +11 -5
- package/dist/lib/cloud-sync.js +19 -2
- package/dist/lib/employee-templates.js +5 -0
- package/dist/lib/exe-daemon.js +24 -8
- package/dist/lib/hybrid-search.js +10 -11
- package/dist/lib/identity-templates.js +16 -7
- package/dist/lib/license.js +43 -2
- package/dist/lib/schedules.js +1 -8
- package/dist/lib/store.js +7 -8
- package/dist/mcp/server.js +184 -113
- package/dist/mcp/tools/list-tasks.js +35 -27
- package/dist/runtime/index.js +7 -2
- package/dist/tui/App.js +44 -5
- package/package.json +4 -2
package/dist/bin/cli.js
CHANGED
|
@@ -621,6 +621,10 @@ async function mergeHooks(packageRoot, homeDir = os3.homedir()) {
|
|
|
621
621
|
if (!settings.hooks) {
|
|
622
622
|
settings.hooks = {};
|
|
623
623
|
}
|
|
624
|
+
if (settings.hooks && typeof settings.hooks !== "object") {
|
|
625
|
+
console.warn("[exe-os] Unexpected hooks schema in settings.json \u2014 skipping hook installation");
|
|
626
|
+
return { added: 0, skipped: 0 };
|
|
627
|
+
}
|
|
624
628
|
const hooksToRegister = [
|
|
625
629
|
{
|
|
626
630
|
event: "PostToolUse",
|
|
@@ -783,6 +787,11 @@ async function mergeHooks(packageRoot, homeDir = os3.homedir()) {
|
|
|
783
787
|
if (!settings.hooks[event]) {
|
|
784
788
|
settings.hooks[event] = [];
|
|
785
789
|
}
|
|
790
|
+
if (!Array.isArray(settings.hooks[event])) {
|
|
791
|
+
console.warn(`[exe-os] Hook event "${event}" has unexpected structure \u2014 skipping`);
|
|
792
|
+
skipped++;
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
786
795
|
const existing = settings.hooks[event];
|
|
787
796
|
const correctCommand = group.hooks[0]?.command ?? "";
|
|
788
797
|
const alreadyCorrect = existing.some(
|
|
@@ -2054,7 +2063,7 @@ __export(shard_manager_exports, {
|
|
|
2054
2063
|
shardExists: () => shardExists
|
|
2055
2064
|
});
|
|
2056
2065
|
import path6 from "path";
|
|
2057
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
2066
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2058
2067
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2059
2068
|
function initShardManager(encryptionKey) {
|
|
2060
2069
|
_encryptionKey = encryptionKey;
|
|
@@ -2093,8 +2102,7 @@ function shardExists(projectName) {
|
|
|
2093
2102
|
}
|
|
2094
2103
|
function listShards() {
|
|
2095
2104
|
if (!existsSync6(SHARDS_DIR)) return [];
|
|
2096
|
-
|
|
2097
|
-
return readdirSync5(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2105
|
+
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2098
2106
|
}
|
|
2099
2107
|
async function ensureShardSchema(client) {
|
|
2100
2108
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
@@ -2377,6 +2385,12 @@ async function writeMemory(record) {
|
|
|
2377
2385
|
supersedes_id: record.supersedes_id ?? null
|
|
2378
2386
|
};
|
|
2379
2387
|
_pendingRecords.push(dbRow);
|
|
2388
|
+
const MAX_PENDING = 1e3;
|
|
2389
|
+
if (_pendingRecords.length > MAX_PENDING) {
|
|
2390
|
+
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
2391
|
+
_pendingRecords = _pendingRecords.slice(-MAX_PENDING);
|
|
2392
|
+
console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
|
|
2393
|
+
}
|
|
2380
2394
|
if (_flushTimer === null) {
|
|
2381
2395
|
_flushTimer = setInterval(() => {
|
|
2382
2396
|
void flushBatch();
|
|
@@ -3347,65 +3361,72 @@ async function backfillConversations(options) {
|
|
|
3347
3361
|
process.stderr.write(`[backfill-conversations] Found ${files.length} JSONL files to process
|
|
3348
3362
|
`);
|
|
3349
3363
|
process.env.EXE_EMBED_PRIORITY = "low";
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
+
const BATCH_SIZE = 50;
|
|
3365
|
+
const MAX_DEDUP_SIZE = 5e4;
|
|
3366
|
+
for (let batchStart = 0; batchStart < files.length; batchStart += BATCH_SIZE) {
|
|
3367
|
+
const batch = files.slice(batchStart, batchStart + BATCH_SIZE);
|
|
3368
|
+
for (const file of batch) {
|
|
3369
|
+
stats.filesScanned++;
|
|
3370
|
+
if (existingPaths.size < MAX_DEDUP_SIZE && existingPaths.has(file)) {
|
|
3371
|
+
stats.skippedDedup++;
|
|
3372
|
+
continue;
|
|
3373
|
+
}
|
|
3374
|
+
const conv = await parseConversation(file);
|
|
3375
|
+
if (conv.totalMessages < MIN_MESSAGES) {
|
|
3376
|
+
stats.skippedTooShort++;
|
|
3377
|
+
continue;
|
|
3378
|
+
}
|
|
3379
|
+
const summary = buildSummary(conv);
|
|
3380
|
+
if (options.dryRun) {
|
|
3381
|
+
process.stdout.write(`
|
|
3364
3382
|
\u2500\u2500\u2500 ${file} \u2500\u2500\u2500
|
|
3365
3383
|
`);
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3384
|
+
process.stdout.write(`Project: ${conv.projectName} | Messages: ${conv.totalMessages}`);
|
|
3385
|
+
process.stdout.write(` | Tools: ${Object.keys(conv.toolCounts).length}`);
|
|
3386
|
+
process.stdout.write(` | Files: ${conv.filesTouched.size}
|
|
3369
3387
|
`);
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3388
|
+
const firstPrompt = conv.userMessages[0];
|
|
3389
|
+
if (firstPrompt) {
|
|
3390
|
+
process.stdout.write(`First prompt: ${firstPrompt.slice(0, 120)}
|
|
3373
3391
|
`);
|
|
3392
|
+
}
|
|
3393
|
+
stats.conversationsStored++;
|
|
3394
|
+
continue;
|
|
3395
|
+
}
|
|
3396
|
+
let vector = null;
|
|
3397
|
+
if (daemonConnected) {
|
|
3398
|
+
try {
|
|
3399
|
+
vector = await embedViaClient(summary, "low");
|
|
3400
|
+
if (!vector) stats.embedFailed++;
|
|
3401
|
+
} catch {
|
|
3402
|
+
stats.embedFailed++;
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
await writeMemory({
|
|
3406
|
+
id: crypto2.randomUUID(),
|
|
3407
|
+
agent_id: conv.agentId,
|
|
3408
|
+
agent_role: conv.agentId === "exe" ? "COO" : "specialist",
|
|
3409
|
+
session_id: conv.sessionId,
|
|
3410
|
+
timestamp: conv.startTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
3411
|
+
tool_name: TOOL_NAME,
|
|
3412
|
+
project_name: conv.projectName,
|
|
3413
|
+
has_error: conv.errorCount > 0,
|
|
3414
|
+
raw_text: summary,
|
|
3415
|
+
vector,
|
|
3416
|
+
source_path: file,
|
|
3417
|
+
source_type: "conversation"
|
|
3418
|
+
});
|
|
3419
|
+
if (existingPaths.size < MAX_DEDUP_SIZE) {
|
|
3420
|
+
existingPaths.add(file);
|
|
3374
3421
|
}
|
|
3375
3422
|
stats.conversationsStored++;
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
if (daemonConnected) {
|
|
3380
|
-
try {
|
|
3381
|
-
vector = await embedViaClient(summary, "low");
|
|
3382
|
-
if (!vector) stats.embedFailed++;
|
|
3383
|
-
} catch {
|
|
3384
|
-
stats.embedFailed++;
|
|
3385
|
-
}
|
|
3386
|
-
}
|
|
3387
|
-
await writeMemory({
|
|
3388
|
-
id: crypto2.randomUUID(),
|
|
3389
|
-
agent_id: conv.agentId,
|
|
3390
|
-
agent_role: conv.agentId === "exe" ? "COO" : "specialist",
|
|
3391
|
-
session_id: conv.sessionId,
|
|
3392
|
-
timestamp: conv.startTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
3393
|
-
tool_name: TOOL_NAME,
|
|
3394
|
-
project_name: conv.projectName,
|
|
3395
|
-
has_error: conv.errorCount > 0,
|
|
3396
|
-
raw_text: summary,
|
|
3397
|
-
vector,
|
|
3398
|
-
source_path: file,
|
|
3399
|
-
source_type: "conversation"
|
|
3400
|
-
});
|
|
3401
|
-
existingPaths.add(file);
|
|
3402
|
-
stats.conversationsStored++;
|
|
3403
|
-
if (stats.filesScanned % 50 === 0) {
|
|
3404
|
-
process.stderr.write(
|
|
3405
|
-
`[backfill-conversations] Progress: ${stats.filesScanned}/${files.length} files, ${stats.conversationsStored} stored
|
|
3423
|
+
if (stats.filesScanned % 50 === 0) {
|
|
3424
|
+
process.stderr.write(
|
|
3425
|
+
`[backfill-conversations] Progress: ${stats.filesScanned}/${files.length} files, ${stats.conversationsStored} stored
|
|
3406
3426
|
`
|
|
3407
|
-
|
|
3408
|
-
|
|
3427
|
+
);
|
|
3428
|
+
await flushBatch();
|
|
3429
|
+
}
|
|
3409
3430
|
}
|
|
3410
3431
|
}
|
|
3411
3432
|
if (!options.dryRun) {
|
|
@@ -3463,6 +3484,7 @@ __export(employee_templates_exports, {
|
|
|
3463
3484
|
buildCustomEmployeePrompt: () => buildCustomEmployeePrompt,
|
|
3464
3485
|
getSessionPrompt: () => getSessionPrompt,
|
|
3465
3486
|
getTemplate: () => getTemplate,
|
|
3487
|
+
getTemplateByRole: () => getTemplateByRole,
|
|
3466
3488
|
personalizePrompt: () => personalizePrompt,
|
|
3467
3489
|
renderClientCOOTemplate: () => renderClientCOOTemplate
|
|
3468
3490
|
});
|
|
@@ -3478,6 +3500,10 @@ function buildCustomEmployeePrompt(name, role) {
|
|
|
3478
3500
|
function getTemplate(name) {
|
|
3479
3501
|
return TEMPLATES[name];
|
|
3480
3502
|
}
|
|
3503
|
+
function getTemplateByRole(role) {
|
|
3504
|
+
const lower = role.toLowerCase();
|
|
3505
|
+
return Object.values(TEMPLATES).find((t) => t.role.toLowerCase() === lower);
|
|
3506
|
+
}
|
|
3481
3507
|
function personalizePrompt(prompt, templateName, actualName) {
|
|
3482
3508
|
if (templateName === actualName) return prompt;
|
|
3483
3509
|
const escaped = templateName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -4262,48 +4288,67 @@ async function downloadModel(opts) {
|
|
|
4262
4288
|
const tmpPath = destPath + ".tmp";
|
|
4263
4289
|
await mkdir5(destDir, { recursive: true });
|
|
4264
4290
|
if (existsSync9(destPath)) {
|
|
4265
|
-
const
|
|
4266
|
-
if (
|
|
4291
|
+
const hash = await fileHash(destPath);
|
|
4292
|
+
if (hash === EXPECTED_SHA256) {
|
|
4267
4293
|
return destPath;
|
|
4268
4294
|
}
|
|
4269
4295
|
}
|
|
4270
|
-
|
|
4271
|
-
const
|
|
4272
|
-
|
|
4273
|
-
throw new Error(`Download failed: HTTP ${response.status}`);
|
|
4274
|
-
}
|
|
4275
|
-
const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
|
|
4296
|
+
const MAX_RETRIES2 = 3;
|
|
4297
|
+
const DOWNLOAD_TIMEOUT_MS = 3e5;
|
|
4298
|
+
let lastErr;
|
|
4276
4299
|
let downloaded = 0;
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
if (!
|
|
4285
|
-
|
|
4300
|
+
for (let attempt = 1; attempt <= MAX_RETRIES2; attempt++) {
|
|
4301
|
+
try {
|
|
4302
|
+
if (existsSync9(tmpPath)) unlinkSync3(tmpPath);
|
|
4303
|
+
const response = await fetchFn(GGUF_URL, {
|
|
4304
|
+
redirect: "follow",
|
|
4305
|
+
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
|
|
4306
|
+
});
|
|
4307
|
+
if (!response.ok || !response.body) {
|
|
4308
|
+
throw new Error(`Download failed: HTTP ${response.status}`);
|
|
4309
|
+
}
|
|
4310
|
+
const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
|
|
4311
|
+
const hash = createHash("sha256");
|
|
4312
|
+
const fileStream = createWriteStream(tmpPath);
|
|
4313
|
+
const reader = response.body.getReader();
|
|
4314
|
+
try {
|
|
4315
|
+
while (true) {
|
|
4316
|
+
const { done, value } = await reader.read();
|
|
4317
|
+
if (done) break;
|
|
4318
|
+
if (!fileStream.write(value)) {
|
|
4319
|
+
await new Promise((resolve) => fileStream.once("drain", resolve));
|
|
4320
|
+
}
|
|
4321
|
+
hash.update(value);
|
|
4322
|
+
downloaded += value.byteLength;
|
|
4323
|
+
onProgress?.(downloaded, contentLength);
|
|
4324
|
+
}
|
|
4325
|
+
} finally {
|
|
4326
|
+
fileStream.end();
|
|
4327
|
+
await new Promise((resolve, reject) => {
|
|
4328
|
+
fileStream.on("finish", resolve);
|
|
4329
|
+
fileStream.on("error", reject);
|
|
4330
|
+
});
|
|
4331
|
+
}
|
|
4332
|
+
const actualHash = hash.digest("hex");
|
|
4333
|
+
if (actualHash !== EXPECTED_SHA256) {
|
|
4334
|
+
unlinkSync3(tmpPath);
|
|
4335
|
+
throw new Error(
|
|
4336
|
+
`SHA256 mismatch: expected ${EXPECTED_SHA256}, got ${actualHash}`
|
|
4337
|
+
);
|
|
4338
|
+
}
|
|
4339
|
+
renameSync3(tmpPath, destPath);
|
|
4340
|
+
return destPath;
|
|
4341
|
+
} catch (err) {
|
|
4342
|
+
lastErr = err instanceof Error ? err : new Error(String(err));
|
|
4343
|
+
if (attempt < MAX_RETRIES2) {
|
|
4344
|
+
process.stderr.write(`
|
|
4345
|
+
Download attempt ${attempt} failed, retrying...
|
|
4346
|
+
`);
|
|
4347
|
+
if (existsSync9(tmpPath)) unlinkSync3(tmpPath);
|
|
4286
4348
|
}
|
|
4287
|
-
hash.update(value);
|
|
4288
|
-
downloaded += value.byteLength;
|
|
4289
|
-
onProgress?.(downloaded, contentLength);
|
|
4290
4349
|
}
|
|
4291
|
-
} finally {
|
|
4292
|
-
fileStream.end();
|
|
4293
|
-
await new Promise((resolve, reject) => {
|
|
4294
|
-
fileStream.on("finish", resolve);
|
|
4295
|
-
fileStream.on("error", reject);
|
|
4296
|
-
});
|
|
4297
|
-
}
|
|
4298
|
-
const actualHash = hash.digest("hex");
|
|
4299
|
-
if (actualHash !== EXPECTED_SHA256) {
|
|
4300
|
-
unlinkSync3(tmpPath);
|
|
4301
|
-
throw new Error(
|
|
4302
|
-
`SHA256 mismatch: expected ${EXPECTED_SHA256}, got ${actualHash}`
|
|
4303
|
-
);
|
|
4304
4350
|
}
|
|
4305
|
-
|
|
4306
|
-
return destPath;
|
|
4351
|
+
throw lastErr;
|
|
4307
4352
|
}
|
|
4308
4353
|
async function fileHash(filePath) {
|
|
4309
4354
|
return new Promise((resolve, reject) => {
|
|
@@ -4406,6 +4451,8 @@ __export(license_exports, {
|
|
|
4406
4451
|
loadLicense: () => loadLicense,
|
|
4407
4452
|
mirrorLicenseKey: () => mirrorLicenseKey,
|
|
4408
4453
|
saveLicense: () => saveLicense,
|
|
4454
|
+
startLicenseRevalidation: () => startLicenseRevalidation,
|
|
4455
|
+
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
4409
4456
|
validateLicense: () => validateLicense
|
|
4410
4457
|
});
|
|
4411
4458
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync3 } from "fs";
|
|
@@ -4530,14 +4577,23 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
4530
4577
|
} catch {
|
|
4531
4578
|
const cached = await getCachedLicense();
|
|
4532
4579
|
if (cached) return cached;
|
|
4533
|
-
return FREE_LICENSE;
|
|
4580
|
+
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4583
|
+
function getCacheAgeMs() {
|
|
4584
|
+
try {
|
|
4585
|
+
const { statSync: statSync2 } = __require("fs");
|
|
4586
|
+
const s = statSync2(CACHE_PATH);
|
|
4587
|
+
return Date.now() - s.mtimeMs;
|
|
4588
|
+
} catch {
|
|
4589
|
+
return Infinity;
|
|
4534
4590
|
}
|
|
4535
4591
|
}
|
|
4536
4592
|
async function checkLicense() {
|
|
4537
4593
|
const key = loadLicense();
|
|
4538
4594
|
if (!key) return FREE_LICENSE;
|
|
4539
4595
|
const cached = await getCachedLicense();
|
|
4540
|
-
if (cached) return cached;
|
|
4596
|
+
if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
|
|
4541
4597
|
const deviceId = loadDeviceId();
|
|
4542
4598
|
return validateLicense(key, deviceId);
|
|
4543
4599
|
}
|
|
@@ -4658,7 +4714,28 @@ async function assertVpsLicense(opts) {
|
|
|
4658
4714
|
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
|
|
4659
4715
|
);
|
|
4660
4716
|
}
|
|
4661
|
-
|
|
4717
|
+
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
4718
|
+
if (_revalTimer) return;
|
|
4719
|
+
_revalTimer = setInterval(async () => {
|
|
4720
|
+
try {
|
|
4721
|
+
const license = await checkLicense();
|
|
4722
|
+
if (!license.valid) {
|
|
4723
|
+
process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
|
|
4724
|
+
}
|
|
4725
|
+
} catch {
|
|
4726
|
+
}
|
|
4727
|
+
}, intervalMs);
|
|
4728
|
+
if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
|
|
4729
|
+
_revalTimer.unref();
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
function stopLicenseRevalidation() {
|
|
4733
|
+
if (_revalTimer) {
|
|
4734
|
+
clearInterval(_revalTimer);
|
|
4735
|
+
_revalTimer = null;
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
|
|
4662
4739
|
var init_license = __esm({
|
|
4663
4740
|
"src/lib/license.ts"() {
|
|
4664
4741
|
"use strict";
|
|
@@ -4688,6 +4765,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
4688
4765
|
employeeLimit: 1,
|
|
4689
4766
|
memoryLimit: 5e3
|
|
4690
4767
|
};
|
|
4768
|
+
CACHE_MAX_AGE_MS = 36e5;
|
|
4769
|
+
_revalTimer = null;
|
|
4691
4770
|
}
|
|
4692
4771
|
});
|
|
4693
4772
|
|
|
@@ -4701,7 +4780,7 @@ __export(identity_exports, {
|
|
|
4701
4780
|
updateIdentity: () => updateIdentity
|
|
4702
4781
|
});
|
|
4703
4782
|
import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
|
|
4704
|
-
import { readdirSync } from "fs";
|
|
4783
|
+
import { readdirSync as readdirSync2 } from "fs";
|
|
4705
4784
|
import path12 from "path";
|
|
4706
4785
|
import { createHash as createHash2 } from "crypto";
|
|
4707
4786
|
function ensureDir() {
|
|
@@ -4783,7 +4862,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
4783
4862
|
}
|
|
4784
4863
|
function listIdentities() {
|
|
4785
4864
|
ensureDir();
|
|
4786
|
-
const files =
|
|
4865
|
+
const files = readdirSync2(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
|
|
4787
4866
|
const results = [];
|
|
4788
4867
|
for (const file of files) {
|
|
4789
4868
|
const agentId = file.replace(".md", "");
|
|
@@ -4830,6 +4909,7 @@ var init_identity = __esm({
|
|
|
4830
4909
|
var identity_templates_exports = {};
|
|
4831
4910
|
__export(identity_templates_exports, {
|
|
4832
4911
|
IDENTITY_TEMPLATES: () => IDENTITY_TEMPLATES,
|
|
4912
|
+
PLAN_MODE_COMPAT: () => PLAN_MODE_COMPAT,
|
|
4833
4913
|
POST_WORK_CHECKLIST: () => POST_WORK_CHECKLIST,
|
|
4834
4914
|
getTemplate: () => getTemplate2,
|
|
4835
4915
|
getTemplateForTitle: () => getTemplateForTitle
|
|
@@ -4849,10 +4929,18 @@ function getTemplateForTitle(title) {
|
|
|
4849
4929
|
if (t.includes("review") || t.includes("audit") || t.includes("qa")) return IDENTITY_TEMPLATES["staff-code-reviewer"];
|
|
4850
4930
|
return null;
|
|
4851
4931
|
}
|
|
4852
|
-
var POST_WORK_CHECKLIST, IDENTITY_TEMPLATES;
|
|
4932
|
+
var PLAN_MODE_COMPAT, POST_WORK_CHECKLIST, IDENTITY_TEMPLATES;
|
|
4853
4933
|
var init_identity_templates = __esm({
|
|
4854
4934
|
"src/lib/identity-templates.ts"() {
|
|
4855
4935
|
"use strict";
|
|
4936
|
+
PLAN_MODE_COMPAT = `
|
|
4937
|
+
## Plan Mode Compatibility
|
|
4938
|
+
If tool execution is unavailable (e.g., CC plan mode), switch to planning:
|
|
4939
|
+
- Reason about the task and create a written plan
|
|
4940
|
+
- Document what tools you would call and with what parameters
|
|
4941
|
+
- Output structured text that can be acted on when tools become available
|
|
4942
|
+
Do not repeatedly attempt tool calls that fail \u2014 switch to planning mode.
|
|
4943
|
+
`;
|
|
4856
4944
|
POST_WORK_CHECKLIST = `
|
|
4857
4945
|
5. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
|
|
4858
4946
|
6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
|
|
@@ -4932,7 +5020,7 @@ Never say "I have no memories" without first searching broadly. Your memory may
|
|
|
4932
5020
|
- **update_identity** \u2014 rewrite any agent's identity when role/responsibilities change (exe/founder only)
|
|
4933
5021
|
- **get_identity** \u2014 read any agent's identity for coordination
|
|
4934
5022
|
- **send_message** \u2014 direct intercom to employees
|
|
4935
|
-
|
|
5023
|
+
${PLAN_MODE_COMPAT}
|
|
4936
5024
|
## Completion Workflow
|
|
4937
5025
|
|
|
4938
5026
|
1. Read the task file and verify the deliverable matches the brief
|
|
@@ -5003,7 +5091,7 @@ You are \${agent_id}. CTO. You hold deep context on the entire codebase, archite
|
|
|
5003
5091
|
- **store_behavior** \u2014 record corrections for engineers (p0 = always injected)
|
|
5004
5092
|
- **get_identity** \u2014 read any agent's identity for review context
|
|
5005
5093
|
- **query_relationships** \u2014 GraphRAG entity connections for architecture analysis
|
|
5006
|
-
|
|
5094
|
+
${PLAN_MODE_COMPAT}
|
|
5007
5095
|
## Completion Workflow
|
|
5008
5096
|
|
|
5009
5097
|
1. Read ARCHITECTURE.md before starting work on any repo
|
|
@@ -5070,7 +5158,7 @@ You are \${agent_id}. CMO. You hold deep context on design, branding, storytelli
|
|
|
5070
5158
|
- **update_task** \u2014 mark tasks done with result summary
|
|
5071
5159
|
- **store_memory** \u2014 report completions with brand alignment notes, SEO considerations
|
|
5072
5160
|
- **get_identity** \u2014 read team identities for brand-consistent communication
|
|
5073
|
-
|
|
5161
|
+
${PLAN_MODE_COMPAT}
|
|
5074
5162
|
## Completion Workflow
|
|
5075
5163
|
|
|
5076
5164
|
1. Read the task file and understand the brief \u2014 tone, format, channel requirements
|
|
@@ -5137,7 +5225,7 @@ You are a principal engineer. You write production-grade code with zero shortcut
|
|
|
5137
5225
|
- **recall_my_memory** \u2014 check past work, patterns, gotchas in this project
|
|
5138
5226
|
- **store_memory** \u2014 report completions for org visibility
|
|
5139
5227
|
- **ask_team_memory** \u2014 pull context from colleagues when specs reference their work
|
|
5140
|
-
|
|
5228
|
+
${PLAN_MODE_COMPAT}
|
|
5141
5229
|
## Completion Workflow
|
|
5142
5230
|
|
|
5143
5231
|
1. Read ARCHITECTURE.md if it exists \u2014 understand architecture before changing anything
|
|
@@ -5197,7 +5285,7 @@ You are the content production specialist. You turn scripts and creative briefs
|
|
|
5197
5285
|
- **update_task** \u2014 mark tasks done with result summary
|
|
5198
5286
|
- **recall_my_memory** \u2014 check past work: which models worked, which prompts produced good results
|
|
5199
5287
|
- **store_memory** \u2014 report completions with production decisions for future reference
|
|
5200
|
-
|
|
5288
|
+
${PLAN_MODE_COMPAT}
|
|
5201
5289
|
## Completion Workflow
|
|
5202
5290
|
|
|
5203
5291
|
1. Read the task file \u2014 understand the brief, check budget constraints
|
|
@@ -5269,7 +5357,7 @@ You are the AI Product Lead \u2014 the competitive intelligence engine. You stud
|
|
|
5269
5357
|
- **update_task** \u2014 mark tasks done with analysis results
|
|
5270
5358
|
- **store_memory** \u2014 persist competitive analyses, evaluations, recommendations
|
|
5271
5359
|
- **create_task** \u2014 when a feature is worth building, spec it for the CTO
|
|
5272
|
-
|
|
5360
|
+
${PLAN_MODE_COMPAT}
|
|
5273
5361
|
## Completion Workflow
|
|
5274
5362
|
|
|
5275
5363
|
1. Read the task \u2014 understand what capability is needed
|
|
@@ -5332,7 +5420,7 @@ You are \${agent_id}. Staff Code Reviewer and System Auditor. Last line of defen
|
|
|
5332
5420
|
- **store_behavior** \u2014 record new patterns
|
|
5333
5421
|
- **update_task** \u2014 mark reviews done with structured findings
|
|
5334
5422
|
- **create_task** \u2014 assign fixes to the CTO
|
|
5335
|
-
|
|
5423
|
+
${PLAN_MODE_COMPAT}
|
|
5336
5424
|
## Completion Workflow
|
|
5337
5425
|
|
|
5338
5426
|
1. Read the task brief and understand the audit scope
|
|
@@ -5354,10 +5442,27 @@ __export(setup_wizard_exports, {
|
|
|
5354
5442
|
validateModel: () => validateModel
|
|
5355
5443
|
});
|
|
5356
5444
|
import crypto3 from "crypto";
|
|
5357
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
5445
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync4, unlinkSync as unlinkSync4 } from "fs";
|
|
5358
5446
|
import os4 from "os";
|
|
5359
5447
|
import path13 from "path";
|
|
5360
5448
|
import { createInterface as createInterface2 } from "readline";
|
|
5449
|
+
function loadSetupState() {
|
|
5450
|
+
try {
|
|
5451
|
+
return JSON.parse(readFileSync8(SETUP_STATE_PATH, "utf8"));
|
|
5452
|
+
} catch {
|
|
5453
|
+
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5454
|
+
}
|
|
5455
|
+
}
|
|
5456
|
+
function saveSetupState(state) {
|
|
5457
|
+
mkdirSync5(path13.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
5458
|
+
writeFileSync4(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
5459
|
+
}
|
|
5460
|
+
function clearSetupState() {
|
|
5461
|
+
try {
|
|
5462
|
+
unlinkSync4(SETUP_STATE_PATH);
|
|
5463
|
+
} catch {
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5361
5466
|
function ask(rl, prompt) {
|
|
5362
5467
|
return new Promise((resolve) => {
|
|
5363
5468
|
const doAsk = () => {
|
|
@@ -5397,88 +5502,122 @@ async function runSetupWizard(opts = {}) {
|
|
|
5397
5502
|
rl.close();
|
|
5398
5503
|
return;
|
|
5399
5504
|
}
|
|
5505
|
+
const state = loadSetupState();
|
|
5506
|
+
if (state.completedSteps.length > 0) {
|
|
5507
|
+
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
5508
|
+
}
|
|
5400
5509
|
if (existsSync12(LEGACY_LANCE_PATH)) {
|
|
5401
5510
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
5402
5511
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
5403
5512
|
log(" The old directory will not be modified or deleted.");
|
|
5404
5513
|
log("");
|
|
5405
5514
|
}
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5515
|
+
if (!state.completedSteps.includes(1)) {
|
|
5516
|
+
const existingKey = await getMasterKey();
|
|
5517
|
+
if (existingKey) {
|
|
5518
|
+
log("Encryption key already exists \u2014 skipping generation.");
|
|
5519
|
+
} else {
|
|
5520
|
+
log("Generating 256-bit encryption key...");
|
|
5521
|
+
const key = crypto3.randomBytes(32);
|
|
5522
|
+
await setMasterKey(key);
|
|
5523
|
+
log("Encryption key generated and stored securely.");
|
|
5524
|
+
}
|
|
5525
|
+
state.completedSteps.push(1);
|
|
5526
|
+
saveSetupState(state);
|
|
5409
5527
|
} else {
|
|
5410
|
-
log("
|
|
5411
|
-
const key = crypto3.randomBytes(32);
|
|
5412
|
-
await setMasterKey(key);
|
|
5413
|
-
log("Encryption key generated and stored securely.");
|
|
5528
|
+
log("Step 1 already complete \u2014 skipping.");
|
|
5414
5529
|
}
|
|
5415
5530
|
log("");
|
|
5416
|
-
log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
|
|
5417
|
-
log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
|
|
5418
|
-
log("only your encryption key can decrypt it.");
|
|
5419
5531
|
let cloudConfig;
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5532
|
+
if (!state.completedSteps.includes(2)) {
|
|
5533
|
+
log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
|
|
5534
|
+
log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
|
|
5535
|
+
log("only your encryption key can decrypt it.");
|
|
5536
|
+
try {
|
|
5537
|
+
const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
5538
|
+
const deviceId = loadDeviceId2();
|
|
5539
|
+
const res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
|
|
5540
|
+
method: "POST",
|
|
5541
|
+
headers: { "Content-Type": "application/json" },
|
|
5542
|
+
body: JSON.stringify({ deviceId }),
|
|
5543
|
+
signal: AbortSignal.timeout(1e4)
|
|
5544
|
+
});
|
|
5545
|
+
if (res.ok) {
|
|
5546
|
+
const data = await res.json();
|
|
5547
|
+
if (data.apiKey) {
|
|
5548
|
+
cloudConfig = { apiKey: data.apiKey, endpoint: "https://askexe.com/cloud" };
|
|
5549
|
+
const { saveLicense: saveLicense3, mirrorLicenseKey: mirrorLicenseKey3 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
5550
|
+
saveLicense3(data.apiKey);
|
|
5551
|
+
mirrorLicenseKey3(data.apiKey);
|
|
5552
|
+
log("Cloud sync activated automatically.");
|
|
5553
|
+
}
|
|
5437
5554
|
}
|
|
5555
|
+
} catch {
|
|
5556
|
+
log("Cloud sync will activate when online.");
|
|
5438
5557
|
}
|
|
5439
|
-
|
|
5440
|
-
|
|
5558
|
+
state.completedSteps.push(2);
|
|
5559
|
+
saveSetupState(state);
|
|
5560
|
+
} else {
|
|
5561
|
+
log("Step 2 already complete \u2014 skipping.");
|
|
5441
5562
|
}
|
|
5442
5563
|
log("");
|
|
5443
|
-
if (!
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5564
|
+
if (!state.completedSteps.includes(3)) {
|
|
5565
|
+
if (!skipModel) {
|
|
5566
|
+
log("Note: jina-embeddings-v5-text-small is licensed CC-BY-NC-4.0 (non-commercial)");
|
|
5567
|
+
log("");
|
|
5568
|
+
await downloadModel({
|
|
5569
|
+
destDir: MODELS_DIR,
|
|
5570
|
+
onProgress: (downloaded, total) => {
|
|
5571
|
+
const pct = (downloaded / total * 100).toFixed(1);
|
|
5572
|
+
const dlMB = (downloaded / 1e6).toFixed(0);
|
|
5573
|
+
const totalMB = (total / 1e6).toFixed(0);
|
|
5574
|
+
process.stderr.write(`\rDownloading model: ${pct}% (${dlMB}/${totalMB} MB)`);
|
|
5575
|
+
}
|
|
5576
|
+
});
|
|
5577
|
+
process.stderr.write("\n");
|
|
5578
|
+
log("Model downloaded and verified.");
|
|
5579
|
+
}
|
|
5580
|
+
state.completedSteps.push(3);
|
|
5581
|
+
saveSetupState(state);
|
|
5582
|
+
} else {
|
|
5583
|
+
log("Step 3 already complete \u2014 skipping.");
|
|
5457
5584
|
}
|
|
5458
|
-
if (!
|
|
5459
|
-
|
|
5585
|
+
if (!state.completedSteps.includes(4)) {
|
|
5586
|
+
if (!skipModel && !skipModelValidation) {
|
|
5587
|
+
await validateModel(log);
|
|
5588
|
+
}
|
|
5589
|
+
state.completedSteps.push(4);
|
|
5590
|
+
saveSetupState(state);
|
|
5591
|
+
} else {
|
|
5592
|
+
log("Step 4 already complete \u2014 skipping.");
|
|
5460
5593
|
}
|
|
5461
5594
|
const config = await loadConfig();
|
|
5462
|
-
if (
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
const claudeJsonPath = path13.join(os4.homedir(), ".claude.json");
|
|
5469
|
-
let claudeJson = {};
|
|
5595
|
+
if (!state.completedSteps.includes(5)) {
|
|
5596
|
+
if (cloudConfig) {
|
|
5597
|
+
config.cloud = cloudConfig;
|
|
5598
|
+
}
|
|
5599
|
+
await saveConfig(config);
|
|
5600
|
+
log("");
|
|
5470
5601
|
try {
|
|
5471
|
-
|
|
5602
|
+
const claudeJsonPath = path13.join(os4.homedir(), ".claude.json");
|
|
5603
|
+
let claudeJson = {};
|
|
5604
|
+
try {
|
|
5605
|
+
claudeJson = JSON.parse(readFileSync8(claudeJsonPath, "utf8"));
|
|
5606
|
+
} catch {
|
|
5607
|
+
}
|
|
5608
|
+
if (!claudeJson.projects) claudeJson.projects = {};
|
|
5609
|
+
const projects = claudeJson.projects;
|
|
5610
|
+
for (const dir of [process.cwd(), os4.homedir()]) {
|
|
5611
|
+
if (!projects[dir]) projects[dir] = {};
|
|
5612
|
+
projects[dir].hasTrustDialogAccepted = true;
|
|
5613
|
+
}
|
|
5614
|
+
writeFileSync4(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5472
5615
|
} catch {
|
|
5473
5616
|
}
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
projects[dir].hasTrustDialogAccepted = true;
|
|
5479
|
-
}
|
|
5480
|
-
writeFileSync4(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5481
|
-
} catch {
|
|
5617
|
+
state.completedSteps.push(5);
|
|
5618
|
+
saveSetupState(state);
|
|
5619
|
+
} else {
|
|
5620
|
+
log("Step 5 already complete \u2014 skipping.");
|
|
5482
5621
|
}
|
|
5483
5622
|
const {
|
|
5484
5623
|
loadEmployees: loadEmployees2,
|
|
@@ -5487,7 +5626,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5487
5626
|
registerBinSymlinks: registerBinSymlinks2,
|
|
5488
5627
|
EMPLOYEES_PATH: EMPLOYEES_PATH2
|
|
5489
5628
|
} = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
5490
|
-
const {
|
|
5629
|
+
const { getTemplateByRole: getTemplateByRole2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5491
5630
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
5492
5631
|
const { getTemplate: getIdentityTemplate } = await Promise.resolve().then(() => (init_identity_templates(), identity_templates_exports));
|
|
5493
5632
|
const {
|
|
@@ -5497,152 +5636,178 @@ async function runSetupWizard(opts = {}) {
|
|
|
5497
5636
|
validateLicense: validateLicense2
|
|
5498
5637
|
} = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
5499
5638
|
const createdEmployees = [];
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
log("They hold the big picture: priorities, progress, and blockers.");
|
|
5504
|
-
log("You talk to them. They coordinate everyone else.");
|
|
5505
|
-
log("");
|
|
5506
|
-
const cooNameInput = await ask(rl, "Name your COO (default: exe): ");
|
|
5507
|
-
const cooName = (cooNameInput || "exe").toLowerCase();
|
|
5508
|
-
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5509
|
-
if (!employees.some((e) => e.name === cooName)) {
|
|
5510
|
-
const { DEFAULT_EXE: DEFAULT_EXE2, personalizePrompt: personalizePrompt2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5511
|
-
const cooEmployee = {
|
|
5512
|
-
name: cooName,
|
|
5513
|
-
role: "COO",
|
|
5514
|
-
systemPrompt: personalizePrompt2(DEFAULT_EXE2.systemPrompt, "exe", cooName),
|
|
5515
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5516
|
-
templateName: "exe",
|
|
5517
|
-
templateVersion: 1
|
|
5518
|
-
};
|
|
5519
|
-
employees = addEmployee2(employees, cooEmployee);
|
|
5520
|
-
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5521
|
-
}
|
|
5522
|
-
const cooIdentityContent = getIdentityTemplate("coo");
|
|
5523
|
-
if (cooIdentityContent) {
|
|
5524
|
-
const cooIdPath = identityPath2(cooName);
|
|
5525
|
-
mkdirSync5(path13.dirname(cooIdPath), { recursive: true });
|
|
5526
|
-
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
5527
|
-
writeFileSync4(cooIdPath, replaced, "utf-8");
|
|
5528
|
-
}
|
|
5529
|
-
registerBinSymlinks2(cooName);
|
|
5530
|
-
createdEmployees.push({ name: cooName, role: "COO" });
|
|
5531
|
-
log("");
|
|
5532
|
-
log("=== Meet Your Specialists ===");
|
|
5533
|
-
log("");
|
|
5534
|
-
log("Your COO coordinates specialists. Here's who you can hire:");
|
|
5535
|
-
log("");
|
|
5536
|
-
log(" CTO (default: yoshi)");
|
|
5537
|
-
log(" Your head of engineering. Architecture, code reviews, tech decisions.");
|
|
5538
|
-
log(" Manages your projects and delegates to engineers when there's parallel work.");
|
|
5539
|
-
log("");
|
|
5540
|
-
log(" CMO (default: mari)");
|
|
5541
|
-
log(" Design, brand, content, SEO. Builds your visual identity, writes");
|
|
5542
|
-
log(" your copy, and gets you found online. Delegates to content specialists.");
|
|
5543
|
-
log("");
|
|
5544
|
-
log("Why separate roles instead of one AI that does everything?");
|
|
5545
|
-
log("");
|
|
5546
|
-
log("Memory saturates. One agent juggling architecture decisions AND landing page");
|
|
5547
|
-
log("copy AND CI/CD AND social media loses context on all of them. Competing");
|
|
5548
|
-
log("priorities \u2014 should I fix the auth bug or write the blog post? When you split");
|
|
5549
|
-
log("responsibilities, each specialist stays sharp because they stay focused.");
|
|
5550
|
-
log("Your COO connects them so nothing falls through the cracks.");
|
|
5551
|
-
log("");
|
|
5552
|
-
log("This is how real companies scale \u2014 you're just doing it with AI");
|
|
5553
|
-
log("instead of headcount.");
|
|
5554
|
-
log("");
|
|
5555
|
-
await ask(rl, "Press Enter to continue. ");
|
|
5556
|
-
let license;
|
|
5557
|
-
try {
|
|
5558
|
-
license = await checkLicense2();
|
|
5559
|
-
} catch {
|
|
5560
|
-
license = { valid: true, plan: "free", email: "", expiresAt: null, deviceLimit: 1, employeeLimit: 2, memoryLimit: 1e3 };
|
|
5561
|
-
}
|
|
5562
|
-
if (license.plan === "free") {
|
|
5563
|
-
log("Your plan: Free \u2014 1 employee (your COO)");
|
|
5639
|
+
let cooName = "exe";
|
|
5640
|
+
if (!state.completedSteps.includes(6)) {
|
|
5641
|
+
log("=== Your Team ===");
|
|
5564
5642
|
log("");
|
|
5565
|
-
log("
|
|
5566
|
-
log("
|
|
5567
|
-
log("
|
|
5643
|
+
log("Every install starts with a COO \u2014 your right-hand operator.");
|
|
5644
|
+
log("They hold the big picture: priorities, progress, and blockers.");
|
|
5645
|
+
log("You talk to them. They coordinate everyone else.");
|
|
5568
5646
|
log("");
|
|
5569
|
-
const
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
}
|
|
5647
|
+
const cooNameInput = await ask(rl, "Name your COO (default: exe): ");
|
|
5648
|
+
cooName = (cooNameInput || "exe").toLowerCase();
|
|
5649
|
+
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5650
|
+
if (!employees.some((e) => e.name === cooName)) {
|
|
5651
|
+
const { DEFAULT_EXE: DEFAULT_EXE2, personalizePrompt: personalizePrompt2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5652
|
+
const cooEmployee = {
|
|
5653
|
+
name: cooName,
|
|
5654
|
+
role: "COO",
|
|
5655
|
+
systemPrompt: personalizePrompt2(DEFAULT_EXE2.systemPrompt, "exe", cooName),
|
|
5656
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5657
|
+
templateName: "exe",
|
|
5658
|
+
templateVersion: 1
|
|
5659
|
+
};
|
|
5660
|
+
employees = addEmployee2(employees, cooEmployee);
|
|
5661
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5662
|
+
}
|
|
5663
|
+
const cooIdentityContent = getIdentityTemplate("coo");
|
|
5664
|
+
if (cooIdentityContent) {
|
|
5665
|
+
const cooIdPath = identityPath2(cooName);
|
|
5666
|
+
mkdirSync5(path13.dirname(cooIdPath), { recursive: true });
|
|
5667
|
+
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
5668
|
+
writeFileSync4(cooIdPath, replaced, "utf-8");
|
|
5669
|
+
}
|
|
5670
|
+
registerBinSymlinks2(cooName);
|
|
5671
|
+
createdEmployees.push({ name: cooName, role: "COO" });
|
|
5672
|
+
state.completedSteps.push(6);
|
|
5673
|
+
saveSetupState(state);
|
|
5674
|
+
} else {
|
|
5675
|
+
log("Step 6 already complete \u2014 skipping.");
|
|
5676
|
+
const roster = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5677
|
+
const existingCoo = roster.find((e) => e.role === "COO");
|
|
5678
|
+
if (existingCoo) cooName = existingCoo.name;
|
|
5585
5679
|
}
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
log(
|
|
5680
|
+
log("");
|
|
5681
|
+
if (!state.completedSteps.includes(7)) {
|
|
5682
|
+
log("=== Meet Your Specialists ===");
|
|
5589
5683
|
log("");
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5684
|
+
log("Your COO coordinates specialists. Here's who you can hire:");
|
|
5685
|
+
log("");
|
|
5686
|
+
log(" CTO (default: yoshi)");
|
|
5687
|
+
log(" Your head of engineering. Architecture, code reviews, tech decisions.");
|
|
5688
|
+
log(" Manages your projects and delegates to engineers when there's parallel work.");
|
|
5689
|
+
log("");
|
|
5690
|
+
log(" CMO (default: mari)");
|
|
5691
|
+
log(" Design, brand, content, SEO. Builds your visual identity, writes");
|
|
5692
|
+
log(" your copy, and gets you found online. Delegates to content specialists.");
|
|
5693
|
+
log("");
|
|
5694
|
+
log("Why separate roles instead of one AI that does everything?");
|
|
5695
|
+
log("");
|
|
5696
|
+
log("Memory saturates. One agent juggling architecture decisions AND landing page");
|
|
5697
|
+
log("copy AND CI/CD AND social media loses context on all of them. Competing");
|
|
5698
|
+
log("priorities \u2014 should I fix the auth bug or write the blog post? When you split");
|
|
5699
|
+
log("responsibilities, each specialist stays sharp because they stay focused.");
|
|
5700
|
+
log("Your COO connects them so nothing falls through the cracks.");
|
|
5701
|
+
log("");
|
|
5702
|
+
log("This is how real companies scale \u2014 you're just doing it with AI");
|
|
5703
|
+
log("instead of headcount.");
|
|
5704
|
+
log("");
|
|
5705
|
+
await ask(rl, "Press Enter to continue. ");
|
|
5706
|
+
state.completedSteps.push(7);
|
|
5707
|
+
saveSetupState(state);
|
|
5708
|
+
} else {
|
|
5709
|
+
log("Step 7 already complete \u2014 skipping.");
|
|
5710
|
+
}
|
|
5711
|
+
if (!state.completedSteps.includes(8)) {
|
|
5712
|
+
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
5713
|
+
let license;
|
|
5714
|
+
try {
|
|
5715
|
+
license = await checkLicense2();
|
|
5716
|
+
} catch {
|
|
5717
|
+
license = { valid: true, plan: "free", email: "", expiresAt: null, deviceLimit: 1, employeeLimit: 2, memoryLimit: 1e3 };
|
|
5718
|
+
}
|
|
5719
|
+
if (license.plan === "free") {
|
|
5720
|
+
log("Your plan: Free \u2014 1 employee (your COO)");
|
|
5721
|
+
log("");
|
|
5722
|
+
log("The CTO and CMO are available on Solopreneur ($97/mo) \u2014 full engineering");
|
|
5723
|
+
log("and marketing team, unlimited tasks, priority support.");
|
|
5724
|
+
log("Get your key at https://askexe.com, then paste it here.");
|
|
5725
|
+
log("");
|
|
5726
|
+
const licenseInput = await ask(rl, "License key (or press Enter to skip): ");
|
|
5727
|
+
if (licenseInput.startsWith("exe_sk_")) {
|
|
5728
|
+
saveLicense2(licenseInput);
|
|
5729
|
+
mirrorLicenseKey2(licenseInput);
|
|
5730
|
+
try {
|
|
5731
|
+
license = await validateLicense2(licenseInput);
|
|
5732
|
+
} catch {
|
|
5733
|
+
log("Couldn't reach the license server \u2014 your key has been saved.");
|
|
5734
|
+
log("Run exe-os --activate <key> anytime to finish activation.");
|
|
5735
|
+
}
|
|
5736
|
+
} else if (!licenseInput) {
|
|
5737
|
+
log("You can activate anytime with: exe-os --activate <key>");
|
|
5738
|
+
} else {
|
|
5739
|
+
log("That doesn't look like a license key (should start with exe_sk_).");
|
|
5740
|
+
log("You can activate anytime with: exe-os --activate <key>");
|
|
5741
|
+
}
|
|
5742
|
+
}
|
|
5743
|
+
if (license.plan !== "free") {
|
|
5744
|
+
const planName = license.plan === "pro" ? "Solopreneur" : license.plan.charAt(0).toUpperCase() + license.plan.slice(1);
|
|
5745
|
+
log(`Your plan: ${planName} (up to ${license.employeeLimit} employees)`);
|
|
5746
|
+
log("");
|
|
5747
|
+
const createCto = await ask(rl, "Create your CTO? (Y/n): ");
|
|
5748
|
+
if (createCto.toLowerCase() !== "n") {
|
|
5749
|
+
const ctoTemplate = getTemplateByRole2("CTO");
|
|
5750
|
+
const ctoDefault = ctoTemplate?.name ?? "cto";
|
|
5751
|
+
const ctoNameInput = await ask(rl, `Name your CTO (default: ${ctoDefault}): `);
|
|
5752
|
+
const ctoName = (ctoNameInput || ctoDefault).toLowerCase();
|
|
5753
|
+
if (!employees.some((e) => e.name === ctoName)) {
|
|
5754
|
+
const { personalizePrompt: personalizeCto } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5755
|
+
const ctoEmployee = {
|
|
5756
|
+
name: ctoName,
|
|
5757
|
+
role: "CTO",
|
|
5758
|
+
systemPrompt: personalizeCto(ctoTemplate?.systemPrompt ?? "", ctoDefault, ctoName),
|
|
5759
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5760
|
+
};
|
|
5761
|
+
employees = addEmployee2(employees, ctoEmployee);
|
|
5762
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5763
|
+
}
|
|
5764
|
+
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
5765
|
+
if (ctoIdentityContent) {
|
|
5766
|
+
const ctoIdPath = identityPath2(ctoName);
|
|
5767
|
+
mkdirSync5(path13.dirname(ctoIdPath), { recursive: true });
|
|
5768
|
+
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
5769
|
+
writeFileSync4(ctoIdPath, replaced, "utf-8");
|
|
5770
|
+
}
|
|
5771
|
+
registerBinSymlinks2(ctoName);
|
|
5772
|
+
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
5773
|
+
log(`Created ${ctoName} (CTO)`);
|
|
5774
|
+
}
|
|
5775
|
+
const createCmo = await ask(rl, "Create your CMO? (Y/n): ");
|
|
5776
|
+
if (createCmo.toLowerCase() !== "n") {
|
|
5777
|
+
const cmoTemplate = getTemplateByRole2("CMO");
|
|
5778
|
+
const cmoDefault = cmoTemplate?.name ?? "cmo";
|
|
5779
|
+
const cmoNameInput = await ask(rl, `Name your CMO (default: ${cmoDefault}): `);
|
|
5780
|
+
const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
|
|
5781
|
+
if (!employees.some((e) => e.name === cmoName)) {
|
|
5782
|
+
const { personalizePrompt: personalizeCmo } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
5783
|
+
const cmoEmployee = {
|
|
5784
|
+
name: cmoName,
|
|
5785
|
+
role: "CMO",
|
|
5786
|
+
systemPrompt: personalizeCmo(cmoTemplate?.systemPrompt ?? "", cmoDefault, cmoName),
|
|
5787
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5788
|
+
};
|
|
5789
|
+
employees = addEmployee2(employees, cmoEmployee);
|
|
5790
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
5791
|
+
}
|
|
5792
|
+
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
5793
|
+
if (cmoIdentityContent) {
|
|
5794
|
+
const cmoIdPath = identityPath2(cmoName);
|
|
5795
|
+
mkdirSync5(path13.dirname(cmoIdPath), { recursive: true });
|
|
5796
|
+
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
5797
|
+
writeFileSync4(cmoIdPath, replaced, "utf-8");
|
|
5798
|
+
}
|
|
5799
|
+
registerBinSymlinks2(cmoName);
|
|
5800
|
+
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
5801
|
+
log(`Created ${cmoName} (CMO)`);
|
|
5639
5802
|
}
|
|
5640
|
-
|
|
5641
|
-
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
5642
|
-
log(`Created ${cmoName} (CMO)`);
|
|
5803
|
+
log("");
|
|
5643
5804
|
}
|
|
5644
|
-
|
|
5805
|
+
state.completedSteps.push(8);
|
|
5806
|
+
saveSetupState(state);
|
|
5807
|
+
} else {
|
|
5808
|
+
log("Step 8 already complete \u2014 skipping.");
|
|
5645
5809
|
}
|
|
5810
|
+
clearSetupState();
|
|
5646
5811
|
log("=== Two Ways to Work ===");
|
|
5647
5812
|
log("");
|
|
5648
5813
|
log(" 1. Claude Code mode");
|
|
@@ -5679,12 +5844,14 @@ async function runSetupWizard(opts = {}) {
|
|
|
5679
5844
|
rl.close();
|
|
5680
5845
|
}
|
|
5681
5846
|
}
|
|
5847
|
+
var SETUP_STATE_PATH;
|
|
5682
5848
|
var init_setup_wizard = __esm({
|
|
5683
5849
|
"src/lib/setup-wizard.ts"() {
|
|
5684
5850
|
"use strict";
|
|
5685
5851
|
init_config();
|
|
5686
5852
|
init_keychain();
|
|
5687
5853
|
init_model_downloader();
|
|
5854
|
+
SETUP_STATE_PATH = path13.join(os4.homedir(), ".exe-os", "setup-state.json");
|
|
5688
5855
|
}
|
|
5689
5856
|
});
|
|
5690
5857
|
|
|
@@ -16354,8 +16521,8 @@ import path23 from "path";
|
|
|
16354
16521
|
import os8 from "os";
|
|
16355
16522
|
import {
|
|
16356
16523
|
readFileSync as readFileSync13,
|
|
16357
|
-
readdirSync as
|
|
16358
|
-
unlinkSync as
|
|
16524
|
+
readdirSync as readdirSync3,
|
|
16525
|
+
unlinkSync as unlinkSync5,
|
|
16359
16526
|
existsSync as existsSync17,
|
|
16360
16527
|
rmdirSync
|
|
16361
16528
|
} from "fs";
|
|
@@ -16837,7 +17004,7 @@ var init_tasks_crud = __esm({
|
|
|
16837
17004
|
|
|
16838
17005
|
// src/lib/tasks-review.ts
|
|
16839
17006
|
import path25 from "path";
|
|
16840
|
-
import { existsSync as existsSync19, readdirSync as
|
|
17007
|
+
import { existsSync as existsSync19, readdirSync as readdirSync4, unlinkSync as unlinkSync6 } from "fs";
|
|
16841
17008
|
async function countPendingReviews() {
|
|
16842
17009
|
const client = getClient();
|
|
16843
17010
|
const result = await client.execute({
|
|
@@ -16959,9 +17126,9 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
16959
17126
|
try {
|
|
16960
17127
|
const cacheDir = path25.join(EXE_AI_DIR, "session-cache");
|
|
16961
17128
|
if (existsSync19(cacheDir)) {
|
|
16962
|
-
for (const f of
|
|
17129
|
+
for (const f of readdirSync4(cacheDir)) {
|
|
16963
17130
|
if (f.startsWith("review-notified-")) {
|
|
16964
|
-
|
|
17131
|
+
unlinkSync6(path25.join(cacheDir, f));
|
|
16965
17132
|
}
|
|
16966
17133
|
}
|
|
16967
17134
|
}
|
|
@@ -17562,7 +17729,7 @@ __export(tasks_exports, {
|
|
|
17562
17729
|
writeCheckpoint: () => writeCheckpoint
|
|
17563
17730
|
});
|
|
17564
17731
|
import path28 from "path";
|
|
17565
|
-
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, unlinkSync as
|
|
17732
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
|
|
17566
17733
|
async function createTask(input) {
|
|
17567
17734
|
const result = await createTaskCore(input);
|
|
17568
17735
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -17588,7 +17755,7 @@ async function updateTask(input) {
|
|
|
17588
17755
|
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
17589
17756
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
17590
17757
|
try {
|
|
17591
|
-
|
|
17758
|
+
unlinkSync7(cachePath);
|
|
17592
17759
|
} catch {
|
|
17593
17760
|
}
|
|
17594
17761
|
}
|
|
@@ -18007,7 +18174,7 @@ import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, mkdirS
|
|
|
18007
18174
|
import path29 from "path";
|
|
18008
18175
|
import os9 from "os";
|
|
18009
18176
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
18010
|
-
import { unlinkSync as
|
|
18177
|
+
import { unlinkSync as unlinkSync8 } from "fs";
|
|
18011
18178
|
function spawnLockPath(sessionName) {
|
|
18012
18179
|
return path29.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
18013
18180
|
}
|
|
@@ -18039,7 +18206,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
18039
18206
|
}
|
|
18040
18207
|
function releaseSpawnLock2(sessionName) {
|
|
18041
18208
|
try {
|
|
18042
|
-
|
|
18209
|
+
unlinkSync8(spawnLockPath(sessionName));
|
|
18043
18210
|
} catch {
|
|
18044
18211
|
}
|
|
18045
18212
|
}
|
|
@@ -21827,7 +21994,7 @@ var init_update = __esm({
|
|
|
21827
21994
|
});
|
|
21828
21995
|
|
|
21829
21996
|
// src/bin/cli.ts
|
|
21830
|
-
import { existsSync as existsSync21, readFileSync as readFileSync17, writeFileSync as writeFileSync9, readdirSync as
|
|
21997
|
+
import { existsSync as existsSync21, readFileSync as readFileSync17, writeFileSync as writeFileSync9, readdirSync as readdirSync5, rmSync } from "fs";
|
|
21831
21998
|
import path31 from "path";
|
|
21832
21999
|
import os10 from "os";
|
|
21833
22000
|
var args = process.argv.slice(2);
|
|
@@ -21907,7 +22074,14 @@ async function runClaudeCheck() {
|
|
|
21907
22074
|
const claudeJsonPath = path31.join(os10.homedir(), ".claude.json");
|
|
21908
22075
|
let ok = true;
|
|
21909
22076
|
if (existsSync21(settingsPath)) {
|
|
21910
|
-
|
|
22077
|
+
let settings;
|
|
22078
|
+
try {
|
|
22079
|
+
settings = JSON.parse(readFileSync17(settingsPath, "utf8"));
|
|
22080
|
+
} catch {
|
|
22081
|
+
console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
|
|
22082
|
+
ok = false;
|
|
22083
|
+
settings = {};
|
|
22084
|
+
}
|
|
21911
22085
|
const hasHooks = settings.hooks && Object.keys(settings.hooks).some((k) => {
|
|
21912
22086
|
const groups = settings.hooks[k];
|
|
21913
22087
|
return Array.isArray(groups) && groups.some((g) => {
|
|
@@ -21929,7 +22103,14 @@ async function runClaudeCheck() {
|
|
|
21929
22103
|
ok = false;
|
|
21930
22104
|
}
|
|
21931
22105
|
if (existsSync21(claudeJsonPath)) {
|
|
21932
|
-
|
|
22106
|
+
let claudeJson;
|
|
22107
|
+
try {
|
|
22108
|
+
claudeJson = JSON.parse(readFileSync17(claudeJsonPath, "utf8"));
|
|
22109
|
+
} catch {
|
|
22110
|
+
console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
|
|
22111
|
+
ok = false;
|
|
22112
|
+
claudeJson = {};
|
|
22113
|
+
}
|
|
21933
22114
|
const hasMcp = claudeJson.mcpServers && (claudeJson.mcpServers["exe-mem"] || claudeJson.mcpServers["exe-os"]);
|
|
21934
22115
|
if (hasMcp) {
|
|
21935
22116
|
console.log("\x1B[32m\u2713\x1B[0m MCP server configured in claude.json");
|
|
@@ -21967,8 +22148,19 @@ async function runClaudeUninstall(flags = []) {
|
|
|
21967
22148
|
const exeOsDir = path31.join(homeDir, ".exe-os");
|
|
21968
22149
|
let removed = 0;
|
|
21969
22150
|
const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
|
|
22151
|
+
let settings = {};
|
|
21970
22152
|
if (existsSync21(settingsPath)) {
|
|
21971
|
-
|
|
22153
|
+
try {
|
|
22154
|
+
settings = JSON.parse(readFileSync17(settingsPath, "utf8"));
|
|
22155
|
+
} catch {
|
|
22156
|
+
console.error("Your ~/.claude/settings.json appears malformed.");
|
|
22157
|
+
if (purge) {
|
|
22158
|
+
console.error("Skipping settings cleanup (--purge).");
|
|
22159
|
+
} else {
|
|
22160
|
+
console.error("Try running: exe-os claude install");
|
|
22161
|
+
process.exit(1);
|
|
22162
|
+
}
|
|
22163
|
+
}
|
|
21972
22164
|
if (settings.hooks) {
|
|
21973
22165
|
for (const key of Object.keys(settings.hooks)) {
|
|
21974
22166
|
const groups = settings.hooks[key];
|
|
@@ -22027,7 +22219,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
22027
22219
|
if (existsSync21(skillsDir)) {
|
|
22028
22220
|
let skillCount = 0;
|
|
22029
22221
|
try {
|
|
22030
|
-
const entries =
|
|
22222
|
+
const entries = readdirSync5(skillsDir);
|
|
22031
22223
|
for (const entry of entries) {
|
|
22032
22224
|
if (entry.startsWith("exe")) {
|
|
22033
22225
|
const fullPath = path31.join(skillsDir, entry);
|
|
@@ -22060,7 +22252,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
22060
22252
|
if (existsSync21(agentsDir)) {
|
|
22061
22253
|
let agentCount = 0;
|
|
22062
22254
|
try {
|
|
22063
|
-
const entries =
|
|
22255
|
+
const entries = readdirSync5(agentsDir).filter((f) => f.endsWith(".md"));
|
|
22064
22256
|
let knownNames = /* @__PURE__ */ new Set();
|
|
22065
22257
|
const rosterPath = path31.join(exeOsDir, "exe-employees.json");
|
|
22066
22258
|
if (existsSync21(rosterPath)) {
|
|
@@ -22088,7 +22280,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
22088
22280
|
if (existsSync21(projectsDir)) {
|
|
22089
22281
|
let projectCount = 0;
|
|
22090
22282
|
try {
|
|
22091
|
-
const projects =
|
|
22283
|
+
const projects = readdirSync5(projectsDir);
|
|
22092
22284
|
for (const proj of projects) {
|
|
22093
22285
|
const projSettings = path31.join(projectsDir, proj, "settings.json");
|
|
22094
22286
|
if (!existsSync21(projSettings)) continue;
|
|
@@ -22195,13 +22387,16 @@ async function runActivate(key) {
|
|
|
22195
22387
|
mirrorLicenseKey2(key);
|
|
22196
22388
|
try {
|
|
22197
22389
|
const license = await validateLicense2(key);
|
|
22198
|
-
if (!license.valid) {
|
|
22390
|
+
if (!license.valid && license.error === "offline") {
|
|
22391
|
+
console.log("\u26A0 Activated in offline mode \u2014 will verify when connected");
|
|
22392
|
+
} else if (!license.valid) {
|
|
22199
22393
|
process.stderr.write(`License invalid: ${license.plan || "unknown"}
|
|
22200
22394
|
`);
|
|
22201
22395
|
process.exit(1);
|
|
22396
|
+
} else {
|
|
22397
|
+
const planLabel = license.plan === "pro" ? "Solopreneur" : license.plan.charAt(0).toUpperCase() + license.plan.slice(1);
|
|
22398
|
+
console.log(`Plan activated: ${planLabel}`);
|
|
22202
22399
|
}
|
|
22203
|
-
const planLabel = license.plan === "pro" ? "Solopreneur" : license.plan.charAt(0).toUpperCase() + license.plan.slice(1);
|
|
22204
|
-
console.log(`Plan activated: ${planLabel}`);
|
|
22205
22400
|
const rl = createInterface4({ input: process.stdin, output: process.stdout });
|
|
22206
22401
|
const ask2 = (prompt) => new Promise((resolve) => rl.question(prompt, (a) => resolve(a.trim())));
|
|
22207
22402
|
let employees = await loadEmployees2();
|