@askexenow/exe-os 0.9.85 → 0.9.87
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/agentic-ontology-backfill.js +50 -14
- package/dist/bin/agentic-reflection-backfill.js +50 -14
- package/dist/bin/agentic-semantic-label.js +50 -14
- package/dist/bin/backfill-conversations.js +50 -14
- package/dist/bin/backfill-responses.js +50 -14
- package/dist/bin/backfill-vectors.js +50 -14
- package/dist/bin/bulk-sync-postgres.js +50 -14
- package/dist/bin/cleanup-stale-review-tasks.js +53 -17
- package/dist/bin/cli.js +339 -81
- package/dist/bin/exe-agent.js +18 -0
- package/dist/bin/exe-assign.js +50 -14
- package/dist/bin/exe-boot.js +75 -39
- package/dist/bin/exe-call.js +18 -0
- package/dist/bin/exe-cloud.js +40 -4
- package/dist/bin/exe-dispatch.js +61 -25
- package/dist/bin/exe-doctor.js +40 -4
- package/dist/bin/exe-export-behaviors.js +50 -14
- package/dist/bin/exe-forget.js +50 -14
- package/dist/bin/exe-gateway.js +65 -29
- package/dist/bin/exe-heartbeat.js +55 -19
- package/dist/bin/exe-kill.js +54 -18
- package/dist/bin/exe-launch-agent.js +58 -22
- package/dist/bin/exe-new-employee.js +33 -2
- package/dist/bin/exe-pending-messages.js +53 -17
- package/dist/bin/exe-pending-notifications.js +53 -17
- package/dist/bin/exe-pending-reviews.js +55 -19
- package/dist/bin/exe-rename.js +52 -16
- package/dist/bin/exe-review.js +50 -14
- package/dist/bin/exe-search.js +58 -22
- package/dist/bin/exe-session-cleanup.js +85 -44
- package/dist/bin/exe-start-codex.js +57 -21
- package/dist/bin/exe-start-opencode.js +55 -19
- package/dist/bin/exe-status.js +62 -26
- package/dist/bin/exe-team.js +50 -14
- package/dist/bin/git-sweep.js +63 -27
- package/dist/bin/graph-backfill.js +50 -14
- package/dist/bin/graph-export.js +50 -14
- package/dist/bin/install.js +9 -0
- package/dist/bin/intercom-check.js +67 -31
- package/dist/bin/scan-tasks.js +63 -27
- package/dist/bin/setup.js +53 -13
- package/dist/bin/shard-migrate.js +50 -14
- package/dist/bin/stack-update.js +59 -2
- package/dist/bin/update.js +1 -1
- package/dist/gateway/index.js +65 -29
- package/dist/hooks/bug-report-worker.js +65 -29
- package/dist/hooks/codex-stop-task-finalizer.js +59 -23
- package/dist/hooks/commit-complete.js +64 -28
- package/dist/hooks/error-recall.js +62 -26
- package/dist/hooks/ingest-worker.js +4 -4
- package/dist/hooks/ingest.js +56 -20
- package/dist/hooks/instructions-loaded.js +50 -14
- package/dist/hooks/notification.js +50 -14
- package/dist/hooks/post-compact.js +50 -14
- package/dist/hooks/post-tool-combined.js +63 -27
- package/dist/hooks/pre-compact.js +61 -25
- package/dist/hooks/pre-tool-use.js +58 -22
- package/dist/hooks/prompt-submit.js +78 -42
- package/dist/hooks/session-end.js +66 -30
- package/dist/hooks/session-start.js +68 -32
- package/dist/hooks/stop.js +53 -17
- package/dist/hooks/subagent-stop.js +50 -14
- package/dist/hooks/summary-worker.js +55 -19
- package/dist/index.js +61 -25
- package/dist/lib/cloud-sync.js +32 -14
- package/dist/lib/database.js +22 -4
- package/dist/lib/db-daemon-client.js +16 -4
- package/dist/lib/db.js +22 -4
- package/dist/lib/device-registry.js +22 -4
- package/dist/lib/embedder.js +16 -4
- package/dist/lib/employee-templates.js +18 -0
- package/dist/lib/exe-daemon-client.js +16 -4
- package/dist/lib/exe-daemon.js +874 -232
- package/dist/lib/hybrid-search.js +58 -22
- package/dist/lib/identity-templates.js +6 -2
- package/dist/lib/schedules.js +53 -17
- package/dist/lib/skill-learning.js +16 -4
- package/dist/lib/store.js +50 -14
- package/dist/lib/tasks.js +16 -4
- package/dist/lib/tmux-routing.js +18 -6
- package/dist/mcp/server.js +809 -200
- package/dist/mcp/tools/create-task.js +24 -8
- package/dist/mcp/tools/update-task.js +18 -6
- package/dist/runtime/index.js +61 -25
- package/dist/tui/App.js +91 -55
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -390,7 +390,7 @@ __export(exe_daemon_client_exports, {
|
|
|
390
390
|
});
|
|
391
391
|
import net from "net";
|
|
392
392
|
import os2 from "os";
|
|
393
|
-
import { spawn } from "child_process";
|
|
393
|
+
import { spawn, execSync } from "child_process";
|
|
394
394
|
import { randomUUID } from "crypto";
|
|
395
395
|
import { existsSync as existsSync4, unlinkSync, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
396
396
|
import path3 from "path";
|
|
@@ -420,6 +420,14 @@ function handleData(chunk) {
|
|
|
420
420
|
}
|
|
421
421
|
}
|
|
422
422
|
}
|
|
423
|
+
function isZombie(pid) {
|
|
424
|
+
try {
|
|
425
|
+
const state = execSync(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
|
|
426
|
+
return state.startsWith("Z");
|
|
427
|
+
} catch {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
423
431
|
function cleanupStaleFiles() {
|
|
424
432
|
if (existsSync4(PID_PATH)) {
|
|
425
433
|
try {
|
|
@@ -427,7 +435,11 @@ function cleanupStaleFiles() {
|
|
|
427
435
|
if (pid > 0) {
|
|
428
436
|
try {
|
|
429
437
|
process.kill(pid, 0);
|
|
430
|
-
|
|
438
|
+
if (!isZombie(pid)) {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
process.stderr.write(`[exed-client] PID ${pid} is a zombie \u2014 cleaning up stale files
|
|
442
|
+
`);
|
|
431
443
|
} catch {
|
|
432
444
|
}
|
|
433
445
|
}
|
|
@@ -455,8 +467,8 @@ function findPackageRoot() {
|
|
|
455
467
|
function getAvailableMemoryGB() {
|
|
456
468
|
if (process.platform === "darwin") {
|
|
457
469
|
try {
|
|
458
|
-
const { execSync:
|
|
459
|
-
const vmstat =
|
|
470
|
+
const { execSync: execSync16 } = __require("child_process");
|
|
471
|
+
const vmstat = execSync16("vm_stat", { encoding: "utf8" });
|
|
460
472
|
const pageSize = 16384;
|
|
461
473
|
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
462
474
|
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
@@ -877,8 +889,8 @@ async function embedDirect(text3) {
|
|
|
877
889
|
const llamaCpp = await import("node-llama-cpp");
|
|
878
890
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
879
891
|
const { existsSync: existsSync42 } = await import("fs");
|
|
880
|
-
const
|
|
881
|
-
const modelPath =
|
|
892
|
+
const path56 = await import("path");
|
|
893
|
+
const modelPath = path56.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
882
894
|
if (!existsSync42(modelPath)) {
|
|
883
895
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
884
896
|
}
|
|
@@ -1107,7 +1119,7 @@ __export(employees_exports, {
|
|
|
1107
1119
|
});
|
|
1108
1120
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
1109
1121
|
import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync5, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
1110
|
-
import { execSync } from "child_process";
|
|
1122
|
+
import { execSync as execSync2 } from "child_process";
|
|
1111
1123
|
import path5 from "path";
|
|
1112
1124
|
import os3 from "os";
|
|
1113
1125
|
function normalizeRole(role) {
|
|
@@ -1283,7 +1295,7 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
1283
1295
|
}
|
|
1284
1296
|
function findExeBin() {
|
|
1285
1297
|
try {
|
|
1286
|
-
return
|
|
1298
|
+
return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
1287
1299
|
} catch {
|
|
1288
1300
|
return null;
|
|
1289
1301
|
}
|
|
@@ -3526,6 +3538,12 @@ async function disposeDatabase() {
|
|
|
3526
3538
|
clearInterval(_walCheckpointTimer);
|
|
3527
3539
|
_walCheckpointTimer = null;
|
|
3528
3540
|
}
|
|
3541
|
+
if (_client) {
|
|
3542
|
+
try {
|
|
3543
|
+
await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
3544
|
+
} catch {
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3529
3547
|
if (_daemonClient) {
|
|
3530
3548
|
_daemonClient.close();
|
|
3531
3549
|
_daemonClient = null;
|
|
@@ -3571,7 +3589,7 @@ __export(keychain_exports, {
|
|
|
3571
3589
|
});
|
|
3572
3590
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3573
3591
|
import { existsSync as existsSync7, statSync as statSync2 } from "fs";
|
|
3574
|
-
import { execSync as
|
|
3592
|
+
import { execSync as execSync3 } from "child_process";
|
|
3575
3593
|
import path7 from "path";
|
|
3576
3594
|
import os5 from "os";
|
|
3577
3595
|
function getKeyDir() {
|
|
@@ -3588,13 +3606,13 @@ function linuxSecretAvailable() {
|
|
|
3588
3606
|
if (process.platform !== "linux") return false;
|
|
3589
3607
|
if (linuxSecretAvailability !== null) return linuxSecretAvailability;
|
|
3590
3608
|
try {
|
|
3591
|
-
|
|
3609
|
+
execSync3("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
|
|
3592
3610
|
} catch {
|
|
3593
3611
|
linuxSecretAvailability = false;
|
|
3594
3612
|
return false;
|
|
3595
3613
|
}
|
|
3596
3614
|
try {
|
|
3597
|
-
|
|
3615
|
+
execSync3("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
|
|
3598
3616
|
linuxSecretAvailability = true;
|
|
3599
3617
|
} catch {
|
|
3600
3618
|
linuxSecretAvailability = false;
|
|
@@ -3618,7 +3636,7 @@ function macKeychainGet(service = SERVICE) {
|
|
|
3618
3636
|
if (!nativeKeychainAllowed()) return null;
|
|
3619
3637
|
if (process.platform !== "darwin") return null;
|
|
3620
3638
|
try {
|
|
3621
|
-
return
|
|
3639
|
+
return execSync3(
|
|
3622
3640
|
`security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
|
|
3623
3641
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
3624
3642
|
).trim();
|
|
@@ -3631,13 +3649,13 @@ function macKeychainSet(value, service = SERVICE) {
|
|
|
3631
3649
|
if (process.platform !== "darwin") return false;
|
|
3632
3650
|
try {
|
|
3633
3651
|
try {
|
|
3634
|
-
|
|
3652
|
+
execSync3(
|
|
3635
3653
|
`security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3636
3654
|
{ timeout: 5e3 }
|
|
3637
3655
|
);
|
|
3638
3656
|
} catch {
|
|
3639
3657
|
}
|
|
3640
|
-
|
|
3658
|
+
execSync3(
|
|
3641
3659
|
`security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
|
|
3642
3660
|
{ timeout: 5e3 }
|
|
3643
3661
|
);
|
|
@@ -3650,7 +3668,7 @@ function macKeychainDelete(service = SERVICE) {
|
|
|
3650
3668
|
if (!nativeKeychainAllowed()) return false;
|
|
3651
3669
|
if (process.platform !== "darwin") return false;
|
|
3652
3670
|
try {
|
|
3653
|
-
|
|
3671
|
+
execSync3(
|
|
3654
3672
|
`security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3655
3673
|
{ timeout: 5e3 }
|
|
3656
3674
|
);
|
|
@@ -3662,7 +3680,7 @@ function macKeychainDelete(service = SERVICE) {
|
|
|
3662
3680
|
function linuxSecretGet(service = SERVICE) {
|
|
3663
3681
|
if (!linuxSecretAvailable()) return null;
|
|
3664
3682
|
try {
|
|
3665
|
-
return
|
|
3683
|
+
return execSync3(
|
|
3666
3684
|
`secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3667
3685
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
3668
3686
|
).trim();
|
|
@@ -3673,7 +3691,7 @@ function linuxSecretGet(service = SERVICE) {
|
|
|
3673
3691
|
function linuxSecretSet(value, service = SERVICE) {
|
|
3674
3692
|
if (!linuxSecretAvailable()) return false;
|
|
3675
3693
|
try {
|
|
3676
|
-
|
|
3694
|
+
execSync3(
|
|
3677
3695
|
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3678
3696
|
{ timeout: 5e3 }
|
|
3679
3697
|
);
|
|
@@ -3686,7 +3704,7 @@ function linuxSecretDelete(service = SERVICE) {
|
|
|
3686
3704
|
if (!nativeKeychainAllowed()) return false;
|
|
3687
3705
|
if (process.platform !== "linux") return false;
|
|
3688
3706
|
try {
|
|
3689
|
-
|
|
3707
|
+
execSync3(
|
|
3690
3708
|
`secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3691
3709
|
{ timeout: 5e3 }
|
|
3692
3710
|
);
|
|
@@ -3705,7 +3723,7 @@ async function tryKeytar() {
|
|
|
3705
3723
|
}
|
|
3706
3724
|
function deriveMachineKey() {
|
|
3707
3725
|
try {
|
|
3708
|
-
const
|
|
3726
|
+
const crypto21 = __require("crypto");
|
|
3709
3727
|
const material = [
|
|
3710
3728
|
os5.hostname(),
|
|
3711
3729
|
os5.userInfo().username,
|
|
@@ -3714,7 +3732,7 @@ function deriveMachineKey() {
|
|
|
3714
3732
|
// Machine ID on Linux (stable across reboots)
|
|
3715
3733
|
process.platform === "linux" ? readMachineId() : ""
|
|
3716
3734
|
].join("|");
|
|
3717
|
-
return
|
|
3735
|
+
return crypto21.createHash("sha256").update(material).digest();
|
|
3718
3736
|
} catch {
|
|
3719
3737
|
return null;
|
|
3720
3738
|
}
|
|
@@ -3728,9 +3746,9 @@ function readMachineId() {
|
|
|
3728
3746
|
}
|
|
3729
3747
|
}
|
|
3730
3748
|
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3731
|
-
const
|
|
3732
|
-
const iv =
|
|
3733
|
-
const cipher =
|
|
3749
|
+
const crypto21 = __require("crypto");
|
|
3750
|
+
const iv = crypto21.randomBytes(12);
|
|
3751
|
+
const cipher = crypto21.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3734
3752
|
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3735
3753
|
encrypted += cipher.final("base64");
|
|
3736
3754
|
const authTag = cipher.getAuthTag().toString("base64");
|
|
@@ -3739,13 +3757,13 @@ function encryptWithMachineKey(plaintext, machineKey) {
|
|
|
3739
3757
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3740
3758
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3741
3759
|
try {
|
|
3742
|
-
const
|
|
3760
|
+
const crypto21 = __require("crypto");
|
|
3743
3761
|
const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
|
|
3744
3762
|
if (parts.length !== 3) return null;
|
|
3745
3763
|
const [ivB64, tagB64, cipherB64] = parts;
|
|
3746
3764
|
const iv = Buffer.from(ivB64, "base64");
|
|
3747
3765
|
const authTag = Buffer.from(tagB64, "base64");
|
|
3748
|
-
const decipher =
|
|
3766
|
+
const decipher = crypto21.createDecipheriv("aes-256-gcm", machineKey, iv);
|
|
3749
3767
|
decipher.setAuthTag(authTag);
|
|
3750
3768
|
let decrypted = decipher.update(cipherB64, "base64", "utf-8");
|
|
3751
3769
|
decrypted += decipher.final("utf-8");
|
|
@@ -4783,6 +4801,24 @@ var init_platform_procedures = __esm({
|
|
|
4783
4801
|
priority: "p0",
|
|
4784
4802
|
content: "When an agent encounters a suspected Exe OS bug, update breakage, MCP/tool failure, installer issue, memory/orchestration defect, or customer-local patch need, it MUST use create_bug_report. Do this before or alongside any local workaround so the report reaches AskExe support directly via the customer's license. Do NOT ask the founder for permission to file a required bug report. If create_bug_report is deferred/lazy-loaded, load it and call it. If it is unavailable in the live MCP surface, report 'create_bug_report unavailable in this session' and save a local report in exe/output \u2014 never claim the tool does not exist unless the live MCP surface was checked. If upstream delivery fails, call support_test (MCP) and include its result in the local report so AskExe can distinguish customer setup, license provisioning, and server intake issues; only ask the founder to run `exe-os support test` if MCP is disconnected/unavailable. Classify first: upstream_bug = reproducible exe-os/platform defect; customer_customization = identity, behavior, procedure, config, branding, workflow preference that belongs in customer-owned layers; emergency_hotfix = temporary local patch. For upstream bugs/emergency hotfixes include version, repro steps, expected/actual, files changed, workaround, and local diff summary. Avoid permanent platform-code patches unless founder approves; if a hotfix is unavoidable, document it in the bug report and re-check after npm update."
|
|
4785
4803
|
},
|
|
4804
|
+
{
|
|
4805
|
+
title: "Bug report status check \u2014 surface available fixes on boot",
|
|
4806
|
+
domain: "support",
|
|
4807
|
+
priority: "p1",
|
|
4808
|
+
content: "Once per session (COO boot only, never repeat), call list_my_bug_reports to check if any previously filed bug reports have been fixed by AskExe. If any report has status 'fixed' with a fixed_version, surface it to the founder immediately: '\u{1F527} N bug fix(es) available \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no reports exist or none are fixed, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
|
|
4809
|
+
},
|
|
4810
|
+
{
|
|
4811
|
+
title: "Feature request triage \u2014 upstream feature vs local customization",
|
|
4812
|
+
domain: "support",
|
|
4813
|
+
priority: "p0",
|
|
4814
|
+
content: "When an agent or founder identifies a desired capability that exe-os does not yet provide, the COO (or equivalent coordinator) must decide: is this a local customization (identity, behavior, procedure, config, branding, workflow preference that can be configured in customer-owned layers) or an upstream feature request (a platform capability that requires changes to exe-os code, shipped via npm update)? Local customizations: implement immediately using store_behavior, update_identity, company_procedure, or config changes. Upstream features: use create_feature_request to submit to AskExe. Include use case, business impact, and current workaround. Do NOT ask the founder for permission to file a feature request \u2014 file it proactively when the need is clear."
|
|
4815
|
+
},
|
|
4816
|
+
{
|
|
4817
|
+
title: "Feature request status check \u2014 surface shipped features on boot",
|
|
4818
|
+
domain: "support",
|
|
4819
|
+
priority: "p1",
|
|
4820
|
+
content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
|
|
4821
|
+
},
|
|
4786
4822
|
// --- Operations ---
|
|
4787
4823
|
{
|
|
4788
4824
|
title: "Managers must supervise deployed workers",
|
|
@@ -6246,7 +6282,7 @@ __export(project_name_exports, {
|
|
|
6246
6282
|
_resetCache: () => _resetCache,
|
|
6247
6283
|
getProjectName: () => getProjectName
|
|
6248
6284
|
});
|
|
6249
|
-
import { execSync as
|
|
6285
|
+
import { execSync as execSync4 } from "child_process";
|
|
6250
6286
|
import path10 from "path";
|
|
6251
6287
|
function getProjectName(cwd) {
|
|
6252
6288
|
const dir = cwd ?? process.cwd();
|
|
@@ -6254,7 +6290,7 @@ function getProjectName(cwd) {
|
|
|
6254
6290
|
try {
|
|
6255
6291
|
let repoRoot;
|
|
6256
6292
|
try {
|
|
6257
|
-
const gitCommonDir =
|
|
6293
|
+
const gitCommonDir = execSync4("git rev-parse --path-format=absolute --git-common-dir", {
|
|
6258
6294
|
cwd: dir,
|
|
6259
6295
|
encoding: "utf8",
|
|
6260
6296
|
timeout: 2e3,
|
|
@@ -6262,7 +6298,7 @@ function getProjectName(cwd) {
|
|
|
6262
6298
|
}).trim();
|
|
6263
6299
|
repoRoot = path10.dirname(gitCommonDir);
|
|
6264
6300
|
} catch {
|
|
6265
|
-
repoRoot =
|
|
6301
|
+
repoRoot = execSync4("git rev-parse --show-toplevel", {
|
|
6266
6302
|
cwd: dir,
|
|
6267
6303
|
encoding: "utf8",
|
|
6268
6304
|
timeout: 2e3,
|
|
@@ -6296,14 +6332,14 @@ var file_grep_exports = {};
|
|
|
6296
6332
|
__export(file_grep_exports, {
|
|
6297
6333
|
grepProjectFiles: () => grepProjectFiles
|
|
6298
6334
|
});
|
|
6299
|
-
import { execSync as
|
|
6335
|
+
import { execSync as execSync5 } from "child_process";
|
|
6300
6336
|
import { readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync4, existsSync as existsSync10 } from "fs";
|
|
6301
6337
|
import path11 from "path";
|
|
6302
6338
|
import crypto2 from "crypto";
|
|
6303
6339
|
function hasRipgrep() {
|
|
6304
6340
|
if (_hasRg === null) {
|
|
6305
6341
|
try {
|
|
6306
|
-
|
|
6342
|
+
execSync5("rg --version", { stdio: "ignore", timeout: 2e3 });
|
|
6307
6343
|
_hasRg = true;
|
|
6308
6344
|
} catch {
|
|
6309
6345
|
_hasRg = false;
|
|
@@ -6369,7 +6405,7 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
|
|
|
6369
6405
|
const globs = (patterns ?? DEFAULT_PATTERNS).map((p) => `--glob '${p}'`).join(" ");
|
|
6370
6406
|
const excludes = EXCLUDE_DIRS.map((d) => `--glob '!${d}'`).join(" ");
|
|
6371
6407
|
const cmd = `rg -i -c --hidden --no-config --no-ignore '${pattern.replace(/'/g, "\\'")}' . ${globs} ${excludes} --max-filesize ${MAX_FILE_SIZE} 2>/dev/null || true`;
|
|
6372
|
-
const output =
|
|
6408
|
+
const output = execSync5(cmd, {
|
|
6373
6409
|
cwd: projectRoot,
|
|
6374
6410
|
encoding: "utf8",
|
|
6375
6411
|
timeout: 3e3,
|
|
@@ -6384,12 +6420,12 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
|
|
|
6384
6420
|
const matchCount = parseInt(line.slice(colonIdx + 1));
|
|
6385
6421
|
if (isNaN(matchCount) || matchCount === 0) continue;
|
|
6386
6422
|
try {
|
|
6387
|
-
const firstMatch =
|
|
6423
|
+
const firstMatch = execSync5(
|
|
6388
6424
|
`rg -i -n --hidden '${pattern.replace(/'/g, "\\'")}' '${filePath}' --max-count 1 2>/dev/null | head -1`,
|
|
6389
6425
|
{ cwd: projectRoot, encoding: "utf8", timeout: 1e3 }
|
|
6390
6426
|
).trim();
|
|
6391
6427
|
const lineNum = parseInt(firstMatch.split(":")[0] ?? "1");
|
|
6392
|
-
const totalLines =
|
|
6428
|
+
const totalLines = execSync5(`wc -l < '${filePath}'`, {
|
|
6393
6429
|
cwd: projectRoot,
|
|
6394
6430
|
encoding: "utf8",
|
|
6395
6431
|
timeout: 1e3
|
|
@@ -7303,10 +7339,10 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
7303
7339
|
};
|
|
7304
7340
|
try {
|
|
7305
7341
|
const fs = await import("fs");
|
|
7306
|
-
const
|
|
7342
|
+
const path56 = await import("path");
|
|
7307
7343
|
const os21 = await import("os");
|
|
7308
|
-
const logPath =
|
|
7309
|
-
fs.mkdirSync(
|
|
7344
|
+
const logPath = path56.join(os21.homedir(), ".exe-os", "search-quality.jsonl");
|
|
7345
|
+
fs.mkdirSync(path56.dirname(logPath), { recursive: true });
|
|
7310
7346
|
fs.appendFileSync(logPath, JSON.stringify(logEntry) + "\n");
|
|
7311
7347
|
} catch {
|
|
7312
7348
|
}
|
|
@@ -7740,7 +7776,7 @@ var init_hybrid_search = __esm({
|
|
|
7740
7776
|
});
|
|
7741
7777
|
|
|
7742
7778
|
// src/lib/session-key.ts
|
|
7743
|
-
import { execSync as
|
|
7779
|
+
import { execSync as execSync6 } from "child_process";
|
|
7744
7780
|
function normalizeCommand(command) {
|
|
7745
7781
|
const trimmed = command.trim().toLowerCase();
|
|
7746
7782
|
const parts = trimmed.split(/[\\/]/);
|
|
@@ -7759,7 +7795,7 @@ function resolveRuntimeProcess() {
|
|
|
7759
7795
|
let pid = process.ppid;
|
|
7760
7796
|
for (let i = 0; i < 10; i++) {
|
|
7761
7797
|
try {
|
|
7762
|
-
const info =
|
|
7798
|
+
const info = execSync6(`ps -p ${pid} -o ppid=,comm=`, {
|
|
7763
7799
|
encoding: "utf8",
|
|
7764
7800
|
timeout: 2e3
|
|
7765
7801
|
}).trim();
|
|
@@ -7831,7 +7867,7 @@ __export(active_agent_exports, {
|
|
|
7831
7867
|
writeActiveAgent: () => writeActiveAgent
|
|
7832
7868
|
});
|
|
7833
7869
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
|
|
7834
|
-
import { execSync as
|
|
7870
|
+
import { execSync as execSync7 } from "child_process";
|
|
7835
7871
|
import path12 from "path";
|
|
7836
7872
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
7837
7873
|
if (candidate === baseName) return true;
|
|
@@ -7925,7 +7961,7 @@ function getActiveAgent() {
|
|
|
7925
7961
|
} catch {
|
|
7926
7962
|
}
|
|
7927
7963
|
try {
|
|
7928
|
-
const sessionName =
|
|
7964
|
+
const sessionName = execSync7(
|
|
7929
7965
|
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
7930
7966
|
{ encoding: "utf8", timeout: 2e3 }
|
|
7931
7967
|
).trim();
|
|
@@ -8599,8 +8635,8 @@ __export(wiki_client_exports, {
|
|
|
8599
8635
|
listDocuments: () => listDocuments,
|
|
8600
8636
|
listWorkspaces: () => listWorkspaces
|
|
8601
8637
|
});
|
|
8602
|
-
async function wikiFetch(config2,
|
|
8603
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
8638
|
+
async function wikiFetch(config2, path56, method = "GET", body) {
|
|
8639
|
+
const url = `${config2.baseUrl}/api/v1${path56}`;
|
|
8604
8640
|
const headers = {
|
|
8605
8641
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
8606
8642
|
"Content-Type": "application/json"
|
|
@@ -8633,7 +8669,7 @@ async function wikiFetch(config2, path55, method = "GET", body) {
|
|
|
8633
8669
|
}
|
|
8634
8670
|
}
|
|
8635
8671
|
if (!response.ok) {
|
|
8636
|
-
throw new Error(`Wiki API ${method} ${
|
|
8672
|
+
throw new Error(`Wiki API ${method} ${path56}: ${response.status} ${response.statusText}`);
|
|
8637
8673
|
}
|
|
8638
8674
|
return response.json();
|
|
8639
8675
|
} finally {
|
|
@@ -9291,14 +9327,14 @@ var init_transport = __esm({
|
|
|
9291
9327
|
});
|
|
9292
9328
|
|
|
9293
9329
|
// src/lib/cc-agent-support.ts
|
|
9294
|
-
import { execSync as
|
|
9330
|
+
import { execSync as execSync8 } from "child_process";
|
|
9295
9331
|
function _resetCcAgentSupportCache() {
|
|
9296
9332
|
_cachedSupport = null;
|
|
9297
9333
|
}
|
|
9298
9334
|
function claudeSupportsAgentFlag() {
|
|
9299
9335
|
if (_cachedSupport !== null) return _cachedSupport;
|
|
9300
9336
|
try {
|
|
9301
|
-
const helpOutput =
|
|
9337
|
+
const helpOutput = execSync8("claude --help 2>&1", {
|
|
9302
9338
|
encoding: "utf-8",
|
|
9303
9339
|
timeout: 5e3
|
|
9304
9340
|
});
|
|
@@ -10235,7 +10271,7 @@ __export(tmux_routing_exports, {
|
|
|
10235
10271
|
spawnEmployee: () => spawnEmployee,
|
|
10236
10272
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
10237
10273
|
});
|
|
10238
|
-
import { execFileSync as execFileSync2, execSync as
|
|
10274
|
+
import { execFileSync as execFileSync2, execSync as execSync9 } from "child_process";
|
|
10239
10275
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync10, mkdirSync as mkdirSync8, existsSync as existsSync17, appendFileSync, readdirSync as readdirSync5 } from "fs";
|
|
10240
10276
|
import path21 from "path";
|
|
10241
10277
|
import os10 from "os";
|
|
@@ -10956,7 +10992,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
10956
10992
|
let booted = false;
|
|
10957
10993
|
for (let i = 0; i < 30; i++) {
|
|
10958
10994
|
try {
|
|
10959
|
-
|
|
10995
|
+
execSync9("sleep 0.5");
|
|
10960
10996
|
} catch {
|
|
10961
10997
|
}
|
|
10962
10998
|
try {
|
|
@@ -11204,7 +11240,7 @@ __export(tasks_crud_exports, {
|
|
|
11204
11240
|
import crypto7 from "crypto";
|
|
11205
11241
|
import path23 from "path";
|
|
11206
11242
|
import os12 from "os";
|
|
11207
|
-
import { execSync as
|
|
11243
|
+
import { execSync as execSync10 } from "child_process";
|
|
11208
11244
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
11209
11245
|
import { existsSync as existsSync19, readFileSync as readFileSync14 } from "fs";
|
|
11210
11246
|
async function writeCheckpoint(input) {
|
|
@@ -11549,14 +11585,14 @@ function isTmuxSessionAlive(identifier) {
|
|
|
11549
11585
|
if (!identifier || identifier === "unknown") return true;
|
|
11550
11586
|
try {
|
|
11551
11587
|
if (identifier.startsWith("%")) {
|
|
11552
|
-
const output =
|
|
11588
|
+
const output = execSync10("tmux list-panes -a -F '#{pane_id}'", {
|
|
11553
11589
|
timeout: 2e3,
|
|
11554
11590
|
encoding: "utf8",
|
|
11555
11591
|
stdio: ["pipe", "pipe", "pipe"]
|
|
11556
11592
|
});
|
|
11557
11593
|
return output.split("\n").some((l) => l.trim() === identifier);
|
|
11558
11594
|
} else {
|
|
11559
|
-
|
|
11595
|
+
execSync10(`tmux has-session -t ${JSON.stringify(identifier)}`, {
|
|
11560
11596
|
timeout: 2e3,
|
|
11561
11597
|
stdio: ["pipe", "pipe", "pipe"]
|
|
11562
11598
|
});
|
|
@@ -11565,7 +11601,7 @@ function isTmuxSessionAlive(identifier) {
|
|
|
11565
11601
|
} catch {
|
|
11566
11602
|
if (identifier.startsWith("%")) return true;
|
|
11567
11603
|
try {
|
|
11568
|
-
|
|
11604
|
+
execSync10("tmux list-sessions", {
|
|
11569
11605
|
timeout: 2e3,
|
|
11570
11606
|
stdio: ["pipe", "pipe", "pipe"]
|
|
11571
11607
|
});
|
|
@@ -11580,12 +11616,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
|
|
|
11580
11616
|
if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
|
|
11581
11617
|
try {
|
|
11582
11618
|
const since = new Date(taskCreatedAt).toISOString();
|
|
11583
|
-
const branch =
|
|
11619
|
+
const branch = execSync10(
|
|
11584
11620
|
"git rev-parse --abbrev-ref HEAD 2>/dev/null",
|
|
11585
11621
|
{ encoding: "utf8", timeout: 3e3 }
|
|
11586
11622
|
).trim();
|
|
11587
11623
|
const branchArg = branch && branch !== "HEAD" ? branch : "";
|
|
11588
|
-
const commitCount =
|
|
11624
|
+
const commitCount = execSync10(
|
|
11589
11625
|
`git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
|
|
11590
11626
|
{ encoding: "utf8", timeout: 5e3 }
|
|
11591
11627
|
).trim();
|
|
@@ -12768,12 +12804,14 @@ On EVERY new conversation, before doing anything else:
|
|
|
12768
12804
|
1. **Memory scan**: Run recall_my_memory with broad queries \u2014 "project", "client", "pipeline", "campaign", "deal", "decision", "blocker". Summarize what you find.
|
|
12769
12805
|
2. **Task scan**: Run list_tasks to see what's open, in progress, blocked, or needs review across all employees.
|
|
12770
12806
|
3. **Team check**: Run ask_team_memory for recent activity from CTO/CMO/engineers.
|
|
12771
|
-
4. **
|
|
12807
|
+
4. **Bug fix check** (one-time, never repeat): Call list_my_bug_reports to see if AskExe has fixed any previously filed bugs. If any have status "fixed" with a fixed_version, tell the founder: "\u{1F527} N bug fix(es) available \u2014 run \`exe-os update\` to get version X.Y.Z." Skip silently if none or if the call fails.
|
|
12808
|
+
5. **Present the brief**: Give the founder a concise status report:
|
|
12772
12809
|
- What's active and progressing
|
|
12773
12810
|
- What's blocked and needs attention
|
|
12774
12811
|
- What decisions are pending
|
|
12812
|
+
- Available bug fixes (from step 4, if any)
|
|
12775
12813
|
- What you recommend doing next
|
|
12776
|
-
|
|
12814
|
+
6. Then ask: "What's the priority?"
|
|
12777
12815
|
|
|
12778
12816
|
If this is your FIRST ever conversation (few or no prior memories):
|
|
12779
12817
|
- Search more broadly: "product", "SEO", "meeting", "strategy", "revenue"
|
|
@@ -12793,6 +12831,8 @@ Never say "I have no memories" without first searching broadly. Your memory may
|
|
|
12793
12831
|
- **get_identity** \u2014 read any agent's identity for coordination
|
|
12794
12832
|
- **set_agent_config** \u2014 view or change which tool (Claude Code, Codex, OpenCode) and model each agent uses. Call with no args to show all agents' current settings. Call with agent_id + runtime + model to change.
|
|
12795
12833
|
- **send_message** \u2014 direct intercom to employees
|
|
12834
|
+
- **create_bug_report** \u2014 file a bug when you encounter an Exe OS platform issue
|
|
12835
|
+
- **list_my_bug_reports** \u2014 check status of filed bugs (boot check: surface available fixes to founder)
|
|
12796
12836
|
${PLAN_MODE_COMPAT}
|
|
12797
12837
|
## Completion Workflow
|
|
12798
12838
|
|
|
@@ -13957,13 +13997,13 @@ var init_crdt_sync = __esm({
|
|
|
13957
13997
|
});
|
|
13958
13998
|
|
|
13959
13999
|
// src/lib/tmux-status.ts
|
|
13960
|
-
import { execSync as
|
|
14000
|
+
import { execSync as execSync13 } from "child_process";
|
|
13961
14001
|
function inTmux() {
|
|
13962
14002
|
if (process.env.TMUX || process.env.TMUX_PANE) return true;
|
|
13963
14003
|
const term = process.env.TERM ?? "";
|
|
13964
14004
|
if (term.startsWith("tmux") || term.startsWith("screen")) return true;
|
|
13965
14005
|
try {
|
|
13966
|
-
|
|
14006
|
+
execSync13("tmux display-message -p '#{session_name}' 2>/dev/null", {
|
|
13967
14007
|
encoding: "utf8",
|
|
13968
14008
|
timeout: 2e3
|
|
13969
14009
|
});
|
|
@@ -13973,12 +14013,12 @@ function inTmux() {
|
|
|
13973
14013
|
try {
|
|
13974
14014
|
let pid = process.ppid;
|
|
13975
14015
|
for (let depth = 0; depth < 8 && pid > 1; depth++) {
|
|
13976
|
-
const comm =
|
|
14016
|
+
const comm = execSync13(`ps -p ${pid} -o comm= 2>/dev/null`, {
|
|
13977
14017
|
encoding: "utf8",
|
|
13978
14018
|
timeout: 1e3
|
|
13979
14019
|
}).trim();
|
|
13980
14020
|
if (/tmux/.test(comm)) return true;
|
|
13981
|
-
const ppid =
|
|
14021
|
+
const ppid = execSync13(`ps -p ${pid} -o ppid= 2>/dev/null`, {
|
|
13982
14022
|
encoding: "utf8",
|
|
13983
14023
|
timeout: 1e3
|
|
13984
14024
|
}).trim();
|
|
@@ -13991,7 +14031,7 @@ function inTmux() {
|
|
|
13991
14031
|
}
|
|
13992
14032
|
function listTmuxSessions() {
|
|
13993
14033
|
try {
|
|
13994
|
-
const out =
|
|
14034
|
+
const out = execSync13("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
|
|
13995
14035
|
encoding: "utf8",
|
|
13996
14036
|
timeout: 3e3
|
|
13997
14037
|
});
|
|
@@ -14002,7 +14042,7 @@ function listTmuxSessions() {
|
|
|
14002
14042
|
}
|
|
14003
14043
|
function capturePaneLines(windowName, lines = 10) {
|
|
14004
14044
|
try {
|
|
14005
|
-
const out =
|
|
14045
|
+
const out = execSync13(
|
|
14006
14046
|
`tmux capture-pane -t ${JSON.stringify(windowName)} -p 2>/dev/null | tail -${lines}`,
|
|
14007
14047
|
{ encoding: "utf8", timeout: 3e3 }
|
|
14008
14048
|
);
|
|
@@ -14013,7 +14053,7 @@ function capturePaneLines(windowName, lines = 10) {
|
|
|
14013
14053
|
}
|
|
14014
14054
|
function getPaneCwd(windowName) {
|
|
14015
14055
|
try {
|
|
14016
|
-
const out =
|
|
14056
|
+
const out = execSync13(
|
|
14017
14057
|
`tmux display-message -t ${JSON.stringify(windowName)} -p '#{pane_current_path}' 2>/dev/null`,
|
|
14018
14058
|
{ encoding: "utf8", timeout: 3e3 }
|
|
14019
14059
|
);
|
|
@@ -14024,7 +14064,7 @@ function getPaneCwd(windowName) {
|
|
|
14024
14064
|
}
|
|
14025
14065
|
function projectFromPath(dir) {
|
|
14026
14066
|
try {
|
|
14027
|
-
const root =
|
|
14067
|
+
const root = execSync13("git -C " + JSON.stringify(dir) + " rev-parse --show-toplevel 2>/dev/null", {
|
|
14028
14068
|
encoding: "utf8",
|
|
14029
14069
|
timeout: 3e3
|
|
14030
14070
|
}).trim();
|
|
@@ -14083,7 +14123,7 @@ function getEmployeeStatuses(employeeNames) {
|
|
|
14083
14123
|
}
|
|
14084
14124
|
let paneAlive = true;
|
|
14085
14125
|
try {
|
|
14086
|
-
const paneStatus =
|
|
14126
|
+
const paneStatus = execSync13(
|
|
14087
14127
|
`tmux list-panes -t ${JSON.stringify(sessionName)} -F '#{pane_dead}' 2>/dev/null`,
|
|
14088
14128
|
{ encoding: "utf8", timeout: 3e3 }
|
|
14089
14129
|
).trim();
|
|
@@ -14190,7 +14230,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
14190
14230
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
14191
14231
|
import { spawn as spawn4 } from "child_process";
|
|
14192
14232
|
import { existsSync as existsSync41, openSync as openSync4, mkdirSync as mkdirSync23, closeSync as closeSync4, readFileSync as readFileSync36 } from "fs";
|
|
14193
|
-
import
|
|
14233
|
+
import path55 from "path";
|
|
14194
14234
|
import os20 from "os";
|
|
14195
14235
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
14196
14236
|
|
|
@@ -18578,12 +18618,12 @@ function registerExportGraph(server2) {
|
|
|
18578
18618
|
}
|
|
18579
18619
|
const html = await exportGraphHTML(client);
|
|
18580
18620
|
const fs = await import("fs");
|
|
18581
|
-
const
|
|
18621
|
+
const path56 = await import("path");
|
|
18582
18622
|
const os21 = await import("os");
|
|
18583
|
-
const outDir =
|
|
18623
|
+
const outDir = path56.join(os21.homedir(), ".exe-os", "exports");
|
|
18584
18624
|
fs.mkdirSync(outDir, { recursive: true });
|
|
18585
18625
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
18586
|
-
const filePath =
|
|
18626
|
+
const filePath = path56.join(outDir, `graph-${timestamp}.html`);
|
|
18587
18627
|
fs.writeFileSync(filePath, html, "utf-8");
|
|
18588
18628
|
return {
|
|
18589
18629
|
content: [
|
|
@@ -19953,7 +19993,7 @@ import { z as z55 } from "zod";
|
|
|
19953
19993
|
init_tmux_routing();
|
|
19954
19994
|
init_task_scope();
|
|
19955
19995
|
init_employees();
|
|
19956
|
-
import { execSync as
|
|
19996
|
+
import { execSync as execSync11 } from "child_process";
|
|
19957
19997
|
import { existsSync as existsSync25, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "fs";
|
|
19958
19998
|
import { homedir as homedir5 } from "os";
|
|
19959
19999
|
import { join as join3 } from "path";
|
|
@@ -23562,9 +23602,9 @@ var HostingerApiClient = class {
|
|
|
23562
23602
|
}
|
|
23563
23603
|
this.lastRequestTime = Date.now();
|
|
23564
23604
|
}
|
|
23565
|
-
async request(method,
|
|
23605
|
+
async request(method, path56, body) {
|
|
23566
23606
|
await this.rateLimit();
|
|
23567
|
-
const url = `${this.baseUrl}${
|
|
23607
|
+
const url = `${this.baseUrl}${path56}`;
|
|
23568
23608
|
const headers = {
|
|
23569
23609
|
Authorization: `Bearer ${this.apiKey}`,
|
|
23570
23610
|
"Content-Type": "application/json",
|
|
@@ -23637,8 +23677,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
|
|
|
23637
23677
|
}
|
|
23638
23678
|
return envelope.result;
|
|
23639
23679
|
}
|
|
23640
|
-
function buildUrl(zoneId,
|
|
23641
|
-
const normalizedPath =
|
|
23680
|
+
function buildUrl(zoneId, path56 = "/dns_records", query) {
|
|
23681
|
+
const normalizedPath = path56.startsWith("/") ? path56 : `/${path56}`;
|
|
23642
23682
|
const url = new URL(
|
|
23643
23683
|
`${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
|
|
23644
23684
|
);
|
|
@@ -24288,7 +24328,7 @@ function isScheduledTrigger(trigger) {
|
|
|
24288
24328
|
init_database();
|
|
24289
24329
|
init_store();
|
|
24290
24330
|
import crypto16 from "crypto";
|
|
24291
|
-
import { execSync as
|
|
24331
|
+
import { execSync as execSync12 } from "child_process";
|
|
24292
24332
|
var CRON_FIELD = /^[\d*/,\-]+$/;
|
|
24293
24333
|
function isValidCron(cron) {
|
|
24294
24334
|
const fields = cron.trim().split(/\s+/);
|
|
@@ -24414,7 +24454,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
|
|
|
24414
24454
|
const cwd = projectDir ? `cd ${JSON.stringify(projectDir)} && ` : "";
|
|
24415
24455
|
const escapedPrompt = prompt.replace(/"/g, '\\"');
|
|
24416
24456
|
const entry = `${cron} ${cwd}claude -p --dangerously-skip-permissions "${escapedPrompt}" # exe-schedule:${id}`;
|
|
24417
|
-
|
|
24457
|
+
execSync12(
|
|
24418
24458
|
`(crontab -l 2>/dev/null; echo ${JSON.stringify(entry)}) | crontab -`,
|
|
24419
24459
|
{ timeout: 5e3, stdio: "ignore" }
|
|
24420
24460
|
);
|
|
@@ -27349,14 +27389,206 @@ Upstream status: ${upstreamStatus}`
|
|
|
27349
27389
|
);
|
|
27350
27390
|
}
|
|
27351
27391
|
|
|
27352
|
-
// src/mcp/tools/
|
|
27392
|
+
// src/mcp/tools/create-feature-request.ts
|
|
27393
|
+
init_embedder();
|
|
27394
|
+
init_active_agent();
|
|
27395
|
+
init_config();
|
|
27396
|
+
init_license();
|
|
27397
|
+
init_store();
|
|
27353
27398
|
import { z as z89 } from "zod";
|
|
27399
|
+
import crypto19 from "crypto";
|
|
27400
|
+
import { mkdir as mkdir7, writeFile as writeFile8 } from "fs/promises";
|
|
27401
|
+
import path50 from "path";
|
|
27402
|
+
var CATEGORY = z89.enum([
|
|
27403
|
+
"upstream_feature",
|
|
27404
|
+
"local_customization",
|
|
27405
|
+
"integration",
|
|
27406
|
+
"unclear"
|
|
27407
|
+
]);
|
|
27408
|
+
var PRIORITY = z89.enum(["p0", "p1", "p2", "p3"]);
|
|
27409
|
+
function slugify3(input) {
|
|
27410
|
+
return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "feature-request";
|
|
27411
|
+
}
|
|
27412
|
+
function section2(title, body) {
|
|
27413
|
+
return `## ${title}
|
|
27414
|
+
|
|
27415
|
+
${body?.trim() || "Not provided"}`;
|
|
27416
|
+
}
|
|
27417
|
+
function buildMarkdown2(input) {
|
|
27418
|
+
return [
|
|
27419
|
+
`# Feature Request \u2014 ${input.title}`,
|
|
27420
|
+
"",
|
|
27421
|
+
`id: ${input.id}`,
|
|
27422
|
+
`category: ${input.category}`,
|
|
27423
|
+
`priority: ${input.priority}`,
|
|
27424
|
+
`filed_by: ${input.agentId} (${input.agentRole})`,
|
|
27425
|
+
`package_version: ${input.packageVersion}`,
|
|
27426
|
+
`project: ${input.projectName ?? "unknown"}`,
|
|
27427
|
+
`created_at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
27428
|
+
"",
|
|
27429
|
+
section2("Description", input.description),
|
|
27430
|
+
section2("Use Case", input.useCase),
|
|
27431
|
+
section2("Current Workaround", input.currentWorkaround),
|
|
27432
|
+
section2("Proposed Solution", input.proposedSolution),
|
|
27433
|
+
section2("Business Impact", input.businessImpact)
|
|
27434
|
+
].join("\n");
|
|
27435
|
+
}
|
|
27436
|
+
async function maybeSendUpstream2(payload) {
|
|
27437
|
+
const config2 = await loadConfig();
|
|
27438
|
+
const routerUrl = process.env.API_ROUTER_URL?.replace(/\/+$/, "");
|
|
27439
|
+
const endpoint2 = config2.support?.featureRequestEndpoint || process.env.EXE_FEATURE_REQUEST_ENDPOINT || (routerUrl ? `${routerUrl}/v1/support/feature-requests` : "https://askexe.com/v1/support/feature-requests");
|
|
27440
|
+
const token = config2.support?.featureRequestToken || process.env.EXE_FEATURE_REQUEST_TOKEN;
|
|
27441
|
+
const licenseKey = loadLicense() || process.env.EXE_LICENSE_KEY || config2.cloud?.apiKey;
|
|
27442
|
+
const licenseToken = readCachedLicenseToken();
|
|
27443
|
+
if (!endpoint2) {
|
|
27444
|
+
return "not_configured";
|
|
27445
|
+
}
|
|
27446
|
+
try {
|
|
27447
|
+
const parsed = new URL(endpoint2);
|
|
27448
|
+
if (parsed.protocol !== "https:" && !["localhost", "127.0.0.1", "::1"].includes(parsed.hostname)) {
|
|
27449
|
+
return "failed: insecure endpoint rejected";
|
|
27450
|
+
}
|
|
27451
|
+
const response = await fetch(parsed, {
|
|
27452
|
+
method: "POST",
|
|
27453
|
+
headers: {
|
|
27454
|
+
"content-type": "application/json",
|
|
27455
|
+
...token ? { authorization: `Bearer ${token}` } : {},
|
|
27456
|
+
...licenseKey ? { "x-exe-license-key": licenseKey } : {},
|
|
27457
|
+
...licenseToken ? { "x-exe-license-token": licenseToken } : {}
|
|
27458
|
+
},
|
|
27459
|
+
body: JSON.stringify(payload),
|
|
27460
|
+
signal: AbortSignal.timeout(1e4)
|
|
27461
|
+
});
|
|
27462
|
+
if (!response.ok) return `failed: HTTP ${response.status}`;
|
|
27463
|
+
return "sent";
|
|
27464
|
+
} catch (err) {
|
|
27465
|
+
return `failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
27466
|
+
}
|
|
27467
|
+
}
|
|
27468
|
+
function registerCreateFeatureRequest(server2) {
|
|
27469
|
+
server2.registerTool(
|
|
27470
|
+
"create_feature_request",
|
|
27471
|
+
{
|
|
27472
|
+
title: "Create Feature Request",
|
|
27473
|
+
description: "File a feature request for exe-os: upstream_feature (requires platform changes), local_customization (configurable in customer layers), integration (third-party), or unclear. Writes a local report, stores memory, and optionally sends to AskExe.",
|
|
27474
|
+
inputSchema: {
|
|
27475
|
+
title: z89.string().min(3).describe("Short descriptive title"),
|
|
27476
|
+
category: CATEGORY.describe(
|
|
27477
|
+
"upstream_feature = platform capability change; local_customization = configurable in customer layers; integration = third-party connector; unclear = needs product triage"
|
|
27478
|
+
),
|
|
27479
|
+
priority: PRIORITY.default("p2").describe("p0 critical \u2192 p3 low"),
|
|
27480
|
+
description: z89.string().min(10).describe("What capability is needed and why"),
|
|
27481
|
+
use_case: z89.string().optional().describe("Concrete scenario where this feature would be used"),
|
|
27482
|
+
current_workaround: z89.string().optional().describe("How the need is currently addressed, if at all"),
|
|
27483
|
+
proposed_solution: z89.string().optional().describe("Suggested implementation approach"),
|
|
27484
|
+
business_impact: z89.string().optional().describe("Impact on business/workflow if this ships"),
|
|
27485
|
+
package_version: z89.string().optional().describe("Installed @askexenow/exe-os version"),
|
|
27486
|
+
project_name: z89.string().optional().describe("Project/customer context"),
|
|
27487
|
+
send_upstream: z89.boolean().default(true).describe("Attempt to POST to configured AskExe support endpoint")
|
|
27488
|
+
}
|
|
27489
|
+
},
|
|
27490
|
+
async ({
|
|
27491
|
+
title,
|
|
27492
|
+
category,
|
|
27493
|
+
priority,
|
|
27494
|
+
description,
|
|
27495
|
+
use_case,
|
|
27496
|
+
current_workaround,
|
|
27497
|
+
proposed_solution,
|
|
27498
|
+
business_impact,
|
|
27499
|
+
package_version,
|
|
27500
|
+
project_name,
|
|
27501
|
+
send_upstream
|
|
27502
|
+
}) => {
|
|
27503
|
+
const { agentId, agentRole } = getActiveAgent();
|
|
27504
|
+
const id = crypto19.randomUUID();
|
|
27505
|
+
const version = package_version ?? "unknown";
|
|
27506
|
+
const markdown = buildMarkdown2({
|
|
27507
|
+
id,
|
|
27508
|
+
title,
|
|
27509
|
+
category,
|
|
27510
|
+
priority,
|
|
27511
|
+
agentId,
|
|
27512
|
+
agentRole,
|
|
27513
|
+
packageVersion: version,
|
|
27514
|
+
description,
|
|
27515
|
+
useCase: use_case,
|
|
27516
|
+
currentWorkaround: current_workaround,
|
|
27517
|
+
proposedSolution: proposed_solution,
|
|
27518
|
+
businessImpact: business_impact,
|
|
27519
|
+
projectName: project_name
|
|
27520
|
+
});
|
|
27521
|
+
const outDir = path50.join(EXE_AI_DIR, "feature-requests");
|
|
27522
|
+
await mkdir7(outDir, { recursive: true });
|
|
27523
|
+
const reportPath = path50.join(outDir, `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}-${slugify3(title)}-${id.slice(0, 8)}.md`);
|
|
27524
|
+
await writeFile8(reportPath, markdown, "utf-8");
|
|
27525
|
+
let vector = null;
|
|
27526
|
+
try {
|
|
27527
|
+
vector = await embed(markdown);
|
|
27528
|
+
} catch {
|
|
27529
|
+
vector = null;
|
|
27530
|
+
}
|
|
27531
|
+
await writeMemory({
|
|
27532
|
+
id,
|
|
27533
|
+
agent_id: agentId,
|
|
27534
|
+
agent_role: agentRole,
|
|
27535
|
+
session_id: process.env.SESSION_ID ?? "manual",
|
|
27536
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27537
|
+
tool_name: "create_feature_request",
|
|
27538
|
+
project_name: project_name ?? "support",
|
|
27539
|
+
has_error: false,
|
|
27540
|
+
raw_text: markdown,
|
|
27541
|
+
vector,
|
|
27542
|
+
source_path: reportPath,
|
|
27543
|
+
source_type: "feature_request",
|
|
27544
|
+
memory_type: "feature_request",
|
|
27545
|
+
tier: 1,
|
|
27546
|
+
importance: priority === "p0" ? 10 : priority === "p1" ? 9 : priority === "p2" ? 7 : 5,
|
|
27547
|
+
intent: "request",
|
|
27548
|
+
domain: "support",
|
|
27549
|
+
file_paths: null
|
|
27550
|
+
});
|
|
27551
|
+
await flushBatch();
|
|
27552
|
+
const upstreamStatus = send_upstream ? await maybeSendUpstream2({
|
|
27553
|
+
id,
|
|
27554
|
+
title,
|
|
27555
|
+
category,
|
|
27556
|
+
priority,
|
|
27557
|
+
description,
|
|
27558
|
+
use_case,
|
|
27559
|
+
current_workaround,
|
|
27560
|
+
proposed_solution,
|
|
27561
|
+
business_impact,
|
|
27562
|
+
package_version: version,
|
|
27563
|
+
project_name,
|
|
27564
|
+
agent_id: agentId,
|
|
27565
|
+
agent_role: agentRole
|
|
27566
|
+
}) : "skipped";
|
|
27567
|
+
return {
|
|
27568
|
+
content: [
|
|
27569
|
+
{
|
|
27570
|
+
type: "text",
|
|
27571
|
+
text: `Feature request created.
|
|
27572
|
+
ID: ${id}
|
|
27573
|
+
Category: ${category}
|
|
27574
|
+
Local report: ${reportPath}
|
|
27575
|
+
Memory stored: ${id}
|
|
27576
|
+
Upstream status: ${upstreamStatus}`
|
|
27577
|
+
}
|
|
27578
|
+
]
|
|
27579
|
+
};
|
|
27580
|
+
}
|
|
27581
|
+
);
|
|
27582
|
+
}
|
|
27583
|
+
|
|
27584
|
+
// src/mcp/tools/support.ts
|
|
27585
|
+
import { z as z90 } from "zod";
|
|
27354
27586
|
|
|
27355
27587
|
// src/bin/exe-support.ts
|
|
27356
27588
|
init_config();
|
|
27357
27589
|
init_license();
|
|
27358
27590
|
import { mkdirSync as mkdirSync21, readFileSync as readFileSync32, unlinkSync as unlinkSync12, writeFileSync as writeFileSync23 } from "fs";
|
|
27359
|
-
import
|
|
27591
|
+
import path51 from "path";
|
|
27360
27592
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
27361
27593
|
var DEFAULT_BUG_ENDPOINT = "https://askexe.com/v1/support/bug-reports";
|
|
27362
27594
|
var DEFAULT_ADMIN_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
|
|
@@ -27477,8 +27709,8 @@ async function resolveEndpoints() {
|
|
|
27477
27709
|
return { bugEndpoint, healthEndpoint, adminEndpoint };
|
|
27478
27710
|
}
|
|
27479
27711
|
function checkLocalWrite() {
|
|
27480
|
-
const dir =
|
|
27481
|
-
const testPath =
|
|
27712
|
+
const dir = path51.join(EXE_AI_DIR, "bug-reports");
|
|
27713
|
+
const testPath = path51.join(dir, ".support-write-test");
|
|
27482
27714
|
try {
|
|
27483
27715
|
mkdirSync21(dir, { recursive: true, mode: 448 });
|
|
27484
27716
|
writeFileSync23(testPath, "ok\n", { mode: 384 });
|
|
@@ -27494,10 +27726,10 @@ function checkLocalWrite() {
|
|
|
27494
27726
|
}
|
|
27495
27727
|
}
|
|
27496
27728
|
function writeLocalTestReport(id, project, version) {
|
|
27497
|
-
const dir =
|
|
27729
|
+
const dir = path51.join(EXE_AI_DIR, "bug-reports");
|
|
27498
27730
|
mkdirSync21(dir, { recursive: true, mode: 448 });
|
|
27499
27731
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
27500
|
-
const filePath =
|
|
27732
|
+
const filePath = path51.join(dir, `${date}-support-intake-test-${id.slice(0, 8)}.md`);
|
|
27501
27733
|
writeFileSync23(filePath, `# TEST \u2014 ${project} support intake
|
|
27502
27734
|
|
|
27503
27735
|
Report ID: ${id}
|
|
@@ -27539,15 +27771,15 @@ async function maybeCloseAdmin(id, adminEndpoint, version) {
|
|
|
27539
27771
|
}
|
|
27540
27772
|
}
|
|
27541
27773
|
function readPackageVersion2() {
|
|
27542
|
-
let dir =
|
|
27774
|
+
let dir = path51.dirname(new URL(import.meta.url).pathname);
|
|
27543
27775
|
for (let i = 0; i < 6; i++) {
|
|
27544
|
-
const pkg =
|
|
27776
|
+
const pkg = path51.join(dir, "package.json");
|
|
27545
27777
|
try {
|
|
27546
27778
|
const parsed = JSON.parse(readFileSync32(pkg, "utf8"));
|
|
27547
27779
|
if (parsed.version) return parsed.version;
|
|
27548
27780
|
} catch {
|
|
27549
27781
|
}
|
|
27550
|
-
dir =
|
|
27782
|
+
dir = path51.dirname(dir);
|
|
27551
27783
|
}
|
|
27552
27784
|
return "unknown";
|
|
27553
27785
|
}
|
|
@@ -27573,6 +27805,7 @@ function nextForPostFailure(status2) {
|
|
|
27573
27805
|
}
|
|
27574
27806
|
|
|
27575
27807
|
// src/mcp/tools/support.ts
|
|
27808
|
+
init_license();
|
|
27576
27809
|
function formatRows(rows, mode) {
|
|
27577
27810
|
const lines = [`exe-os support ${mode}`, ""];
|
|
27578
27811
|
for (const row of rows) {
|
|
@@ -27613,7 +27846,7 @@ function registerSupportTools(server2) {
|
|
|
27613
27846
|
title: "Support Test",
|
|
27614
27847
|
description: "End-to-end support intake smoke test. Files a clearly marked test report upstream and auto-closes it on AskExe machines with admin credentials.",
|
|
27615
27848
|
inputSchema: {
|
|
27616
|
-
project:
|
|
27849
|
+
project: z90.string().default("support-smoke").describe("Customer/project name, e.g. hygo")
|
|
27617
27850
|
}
|
|
27618
27851
|
},
|
|
27619
27852
|
async ({ project }) => {
|
|
@@ -27625,12 +27858,139 @@ function registerSupportTools(server2) {
|
|
|
27625
27858
|
};
|
|
27626
27859
|
}
|
|
27627
27860
|
);
|
|
27861
|
+
server2.registerTool(
|
|
27862
|
+
"list_my_bug_reports",
|
|
27863
|
+
{
|
|
27864
|
+
title: "My Bug Reports",
|
|
27865
|
+
description: "List bug reports you've filed, scoped to your license. Shows status (open/triaged/fixed/closed), severity, and fixed_version so you know when to update.",
|
|
27866
|
+
inputSchema: {
|
|
27867
|
+
status: z90.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("all").describe("Filter by status. Default: all"),
|
|
27868
|
+
limit: z90.number().min(1).max(50).default(25).describe("Max results")
|
|
27869
|
+
}
|
|
27870
|
+
},
|
|
27871
|
+
async ({ status: status2, limit }) => {
|
|
27872
|
+
const licenseKey = loadLicense();
|
|
27873
|
+
const licenseToken = readCachedLicenseToken();
|
|
27874
|
+
if (!licenseKey && !licenseToken) {
|
|
27875
|
+
return {
|
|
27876
|
+
content: [{ type: "text", text: "No license key found. Run `exe-os setup` or `exe-os cloud setup` first." }],
|
|
27877
|
+
isError: true
|
|
27878
|
+
};
|
|
27879
|
+
}
|
|
27880
|
+
const endpoint2 = new URL("https://askexe.com/v1/support/my-reports");
|
|
27881
|
+
endpoint2.searchParams.set("status", status2);
|
|
27882
|
+
endpoint2.searchParams.set("limit", String(limit));
|
|
27883
|
+
const headers = { "content-type": "application/json" };
|
|
27884
|
+
if (licenseKey) headers["x-exe-license-key"] = licenseKey;
|
|
27885
|
+
if (licenseToken) headers["x-exe-license-token"] = licenseToken;
|
|
27886
|
+
try {
|
|
27887
|
+
const res = await fetch(endpoint2.toString(), { method: "GET", headers, signal: AbortSignal.timeout(15e3) });
|
|
27888
|
+
if (!res.ok) {
|
|
27889
|
+
const body = await res.text().catch(() => "");
|
|
27890
|
+
return {
|
|
27891
|
+
content: [{ type: "text", text: `Failed to fetch bug reports: HTTP ${res.status}${body ? ` \u2014 ${body}` : ""}` }],
|
|
27892
|
+
isError: true
|
|
27893
|
+
};
|
|
27894
|
+
}
|
|
27895
|
+
const data = await res.json();
|
|
27896
|
+
if (data.count === 0) {
|
|
27897
|
+
return {
|
|
27898
|
+
content: [{ type: "text", text: `No bug reports found${status2 !== "all" ? ` with status '${status2}'` : ""}.` }],
|
|
27899
|
+
structuredContent: { items: [], count: 0 }
|
|
27900
|
+
};
|
|
27901
|
+
}
|
|
27902
|
+
const lines = [`Bug reports (${data.count}):`, ""];
|
|
27903
|
+
for (const r of data.items) {
|
|
27904
|
+
const sevIcon = r.severity === "p0" ? "\u{1F534}" : r.severity === "p1" ? "\u{1F534}" : r.severity === "p2" ? "\u{1F7E0}" : "\u{1F7E2}";
|
|
27905
|
+
const statusIcon = r.status === "fixed" ? "\u2705" : r.status === "closed" ? "\u2611\uFE0F" : r.status === "triaged" ? "\u{1F50D}" : r.status === "wontfix" ? "\u26D4" : "\u{1F535}";
|
|
27906
|
+
lines.push(`${sevIcon} ${r.severity.toUpperCase()} ${statusIcon} ${r.status} \u2014 ${r.title}`);
|
|
27907
|
+
lines.push(` ID: ${r.id} | Filed: ${r.created_at?.slice(0, 10) ?? "?"}`);
|
|
27908
|
+
if (r.fixed_version) lines.push(` \u{1F527} Fixed in: ${r.fixed_version} \u2014 run \`exe-os update\` to get this fix`);
|
|
27909
|
+
if (r.triage_notes) lines.push(` \u{1F4DD} AskExe: ${r.triage_notes}`);
|
|
27910
|
+
lines.push("");
|
|
27911
|
+
}
|
|
27912
|
+
return {
|
|
27913
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
27914
|
+
structuredContent: data
|
|
27915
|
+
};
|
|
27916
|
+
} catch (err) {
|
|
27917
|
+
return {
|
|
27918
|
+
content: [{ type: "text", text: `Error fetching bug reports: ${err instanceof Error ? err.message : String(err)}` }],
|
|
27919
|
+
isError: true
|
|
27920
|
+
};
|
|
27921
|
+
}
|
|
27922
|
+
}
|
|
27923
|
+
);
|
|
27924
|
+
server2.registerTool(
|
|
27925
|
+
"list_my_feature_requests",
|
|
27926
|
+
{
|
|
27927
|
+
title: "My Feature Requests",
|
|
27928
|
+
description: "List feature requests you've filed, scoped to your license. Shows status (open/planned/in_progress/shipped/closed), priority, and shipped_version so you know when to update.",
|
|
27929
|
+
inputSchema: {
|
|
27930
|
+
status: z90.enum(["all", "open", "planned", "in_progress", "shipped", "closed", "wontdo"]).default("all").describe("Filter by status. Default: all"),
|
|
27931
|
+
limit: z90.number().min(1).max(50).default(25).describe("Max results")
|
|
27932
|
+
}
|
|
27933
|
+
},
|
|
27934
|
+
async ({ status: status2, limit }) => {
|
|
27935
|
+
const licenseKey = loadLicense();
|
|
27936
|
+
const licenseToken = readCachedLicenseToken();
|
|
27937
|
+
if (!licenseKey && !licenseToken) {
|
|
27938
|
+
return {
|
|
27939
|
+
content: [{ type: "text", text: "No license key found. Run `exe-os setup` or `exe-os cloud setup` first." }],
|
|
27940
|
+
isError: true
|
|
27941
|
+
};
|
|
27942
|
+
}
|
|
27943
|
+
const endpoint2 = new URL("https://askexe.com/v1/support/my-feature-requests");
|
|
27944
|
+
endpoint2.searchParams.set("status", status2);
|
|
27945
|
+
endpoint2.searchParams.set("limit", String(limit));
|
|
27946
|
+
const headers = { "content-type": "application/json" };
|
|
27947
|
+
if (licenseKey) headers["x-exe-license-key"] = licenseKey;
|
|
27948
|
+
if (licenseToken) headers["x-exe-license-token"] = licenseToken;
|
|
27949
|
+
try {
|
|
27950
|
+
const res = await fetch(endpoint2.toString(), { method: "GET", headers, signal: AbortSignal.timeout(15e3) });
|
|
27951
|
+
if (!res.ok) {
|
|
27952
|
+
const body = await res.text().catch(() => "");
|
|
27953
|
+
return {
|
|
27954
|
+
content: [{ type: "text", text: `Failed to fetch feature requests: HTTP ${res.status}${body ? ` \u2014 ${body}` : ""}` }],
|
|
27955
|
+
isError: true
|
|
27956
|
+
};
|
|
27957
|
+
}
|
|
27958
|
+
const data = await res.json();
|
|
27959
|
+
if (data.count === 0) {
|
|
27960
|
+
return {
|
|
27961
|
+
content: [{ type: "text", text: `No feature requests found${status2 !== "all" ? ` with status '${status2}'` : ""}.` }],
|
|
27962
|
+
structuredContent: { items: [], count: 0 }
|
|
27963
|
+
};
|
|
27964
|
+
}
|
|
27965
|
+
const lines = [`Feature requests (${data.count}):`, ""];
|
|
27966
|
+
for (const r of data.items) {
|
|
27967
|
+
const priIcon = r.priority === "p0" ? "\u{1F534}" : r.priority === "p1" ? "\u{1F534}" : r.priority === "p2" ? "\u{1F7E0}" : "\u{1F7E2}";
|
|
27968
|
+
const statusIcon = r.status === "shipped" ? "\u2705" : r.status === "closed" ? "\u2611\uFE0F" : r.status === "planned" ? "\u{1F4CB}" : r.status === "in_progress" ? "\u{1F528}" : r.status === "wontdo" ? "\u26D4" : "\u{1F535}";
|
|
27969
|
+
lines.push(`${priIcon} ${r.priority.toUpperCase()} ${statusIcon} ${r.status} \u2014 ${r.title}`);
|
|
27970
|
+
lines.push(` ID: ${r.id} | Filed: ${r.created_at?.slice(0, 10) ?? "?"}`);
|
|
27971
|
+
if (r.shipped_version) lines.push(` \u{1F680} Shipped in: ${r.shipped_version} \u2014 run \`exe-os update\` to get this feature`);
|
|
27972
|
+
if (r.target_version) lines.push(` \u{1F3AF} Target: ${r.target_version}`);
|
|
27973
|
+
if (r.response_notes) lines.push(` \u{1F4DD} AskExe: ${r.response_notes}`);
|
|
27974
|
+
lines.push("");
|
|
27975
|
+
}
|
|
27976
|
+
return {
|
|
27977
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
27978
|
+
structuredContent: data
|
|
27979
|
+
};
|
|
27980
|
+
} catch (err) {
|
|
27981
|
+
return {
|
|
27982
|
+
content: [{ type: "text", text: `Error fetching feature requests: ${err instanceof Error ? err.message : String(err)}` }],
|
|
27983
|
+
isError: true
|
|
27984
|
+
};
|
|
27985
|
+
}
|
|
27986
|
+
}
|
|
27987
|
+
);
|
|
27628
27988
|
}
|
|
27629
27989
|
|
|
27630
27990
|
// src/mcp/tools/cli-parity.ts
|
|
27631
27991
|
import { execFile as execFile2 } from "child_process";
|
|
27632
27992
|
import { promisify as promisify2 } from "util";
|
|
27633
|
-
import { z as
|
|
27993
|
+
import { z as z91 } from "zod";
|
|
27634
27994
|
|
|
27635
27995
|
// src/bin/exe-status.ts
|
|
27636
27996
|
init_employees();
|
|
@@ -27691,22 +28051,22 @@ if (isMainModule(import.meta.url)) {
|
|
|
27691
28051
|
|
|
27692
28052
|
// src/bin/exe-healthcheck.ts
|
|
27693
28053
|
import { existsSync as existsSync39, readFileSync as readFileSync33, readdirSync as readdirSync14 } from "fs";
|
|
27694
|
-
import
|
|
27695
|
-
import { execSync as
|
|
28054
|
+
import path52 from "path";
|
|
28055
|
+
import { execSync as execSync14 } from "child_process";
|
|
27696
28056
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
27697
28057
|
init_config();
|
|
27698
28058
|
function findPackageRoot2() {
|
|
27699
|
-
let dir =
|
|
27700
|
-
const { root } =
|
|
28059
|
+
let dir = path52.dirname(fileURLToPath6(import.meta.url));
|
|
28060
|
+
const { root } = path52.parse(dir);
|
|
27701
28061
|
while (dir !== root) {
|
|
27702
|
-
if (existsSync39(
|
|
27703
|
-
dir =
|
|
28062
|
+
if (existsSync39(path52.join(dir, "package.json"))) return dir;
|
|
28063
|
+
dir = path52.dirname(dir);
|
|
27704
28064
|
}
|
|
27705
28065
|
throw new Error("Cannot find package root");
|
|
27706
28066
|
}
|
|
27707
28067
|
function checkBuildIntegrity(pkgRoot) {
|
|
27708
28068
|
const results = [];
|
|
27709
|
-
const tsupConfig =
|
|
28069
|
+
const tsupConfig = path52.join(pkgRoot, "tsup.config.ts");
|
|
27710
28070
|
if (!existsSync39(tsupConfig)) {
|
|
27711
28071
|
return [{ name: "build/tsup-config", pass: false, detail: "tsup.config.ts not found" }];
|
|
27712
28072
|
}
|
|
@@ -27716,7 +28076,7 @@ function checkBuildIntegrity(pkgRoot) {
|
|
|
27716
28076
|
let total = 0;
|
|
27717
28077
|
for (const match of entryMatches) {
|
|
27718
28078
|
const outputKey = match[1];
|
|
27719
|
-
const expectedPath =
|
|
28079
|
+
const expectedPath = path52.join(pkgRoot, "dist", `${outputKey}.js`);
|
|
27720
28080
|
total++;
|
|
27721
28081
|
if (!existsSync39(expectedPath)) {
|
|
27722
28082
|
missing.push(`dist/${outputKey}.js`);
|
|
@@ -27740,7 +28100,7 @@ function checkBuildIntegrity(pkgRoot) {
|
|
|
27740
28100
|
}
|
|
27741
28101
|
function checkEmbedPipeline(pkgRoot) {
|
|
27742
28102
|
const results = [];
|
|
27743
|
-
const daemonPath =
|
|
28103
|
+
const daemonPath = path52.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
27744
28104
|
if (!existsSync39(daemonPath)) {
|
|
27745
28105
|
results.push({
|
|
27746
28106
|
name: "exed/daemon-exists",
|
|
@@ -27752,17 +28112,17 @@ function checkEmbedPipeline(pkgRoot) {
|
|
|
27752
28112
|
results.push({ name: "exed/daemon-exists", pass: true, detail: "dist/lib/exe-daemon.js exists" });
|
|
27753
28113
|
const entryDirs = ["dist/hooks", "dist/bin", "dist/mcp"];
|
|
27754
28114
|
for (const dir of entryDirs) {
|
|
27755
|
-
const fullDir =
|
|
28115
|
+
const fullDir = path52.join(pkgRoot, dir);
|
|
27756
28116
|
if (!existsSync39(fullDir)) continue;
|
|
27757
28117
|
let walkDir = fullDir;
|
|
27758
|
-
const { root } =
|
|
28118
|
+
const { root } = path52.parse(walkDir);
|
|
27759
28119
|
let foundRoot = null;
|
|
27760
28120
|
while (walkDir !== root) {
|
|
27761
|
-
if (existsSync39(
|
|
28121
|
+
if (existsSync39(path52.join(walkDir, "package.json"))) {
|
|
27762
28122
|
foundRoot = walkDir;
|
|
27763
28123
|
break;
|
|
27764
28124
|
}
|
|
27765
|
-
walkDir =
|
|
28125
|
+
walkDir = path52.dirname(walkDir);
|
|
27766
28126
|
}
|
|
27767
28127
|
if (!foundRoot) {
|
|
27768
28128
|
results.push({
|
|
@@ -27772,7 +28132,7 @@ function checkEmbedPipeline(pkgRoot) {
|
|
|
27772
28132
|
});
|
|
27773
28133
|
continue;
|
|
27774
28134
|
}
|
|
27775
|
-
const resolvedDaemon =
|
|
28135
|
+
const resolvedDaemon = path52.join(foundRoot, "dist", "lib", "exe-daemon.js");
|
|
27776
28136
|
const reachable = existsSync39(resolvedDaemon);
|
|
27777
28137
|
results.push({
|
|
27778
28138
|
name: `exed/reachable-from-${dir}`,
|
|
@@ -27784,13 +28144,13 @@ function checkEmbedPipeline(pkgRoot) {
|
|
|
27784
28144
|
}
|
|
27785
28145
|
function checkTaskSystem(pkgRoot) {
|
|
27786
28146
|
const results = [];
|
|
27787
|
-
const scannerPath =
|
|
28147
|
+
const scannerPath = path52.join(pkgRoot, "dist", "bin", "scan-tasks.js");
|
|
27788
28148
|
if (!existsSync39(scannerPath)) {
|
|
27789
28149
|
results.push({ name: "tasks/scanner", pass: false, detail: "scan-tasks.js not found" });
|
|
27790
28150
|
return results;
|
|
27791
28151
|
}
|
|
27792
28152
|
try {
|
|
27793
|
-
|
|
28153
|
+
execSync14(`node "${scannerPath}" /tmp/nonexistent-healthcheck-test --format=json 2>/dev/null`, {
|
|
27794
28154
|
timeout: 1e4,
|
|
27795
28155
|
encoding: "utf-8"
|
|
27796
28156
|
});
|
|
@@ -27807,13 +28167,13 @@ function checkTaskSystem(pkgRoot) {
|
|
|
27807
28167
|
}
|
|
27808
28168
|
function checkWorkerSpawning(pkgRoot) {
|
|
27809
28169
|
const results = [];
|
|
27810
|
-
const workerPath =
|
|
28170
|
+
const workerPath = path52.join(pkgRoot, "dist", "hooks", "ingest-worker.js");
|
|
27811
28171
|
if (!existsSync39(workerPath)) {
|
|
27812
28172
|
results.push({ name: "workers/ingest-worker", pass: false, detail: "ingest-worker.js not found" });
|
|
27813
28173
|
return results;
|
|
27814
28174
|
}
|
|
27815
28175
|
try {
|
|
27816
|
-
|
|
28176
|
+
execSync14(`node --check "${workerPath}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
|
|
27817
28177
|
results.push({ name: "workers/ingest-worker", pass: true, detail: "ingest-worker.js parses OK" });
|
|
27818
28178
|
} catch (err) {
|
|
27819
28179
|
results.push({
|
|
@@ -27822,14 +28182,14 @@ function checkWorkerSpawning(pkgRoot) {
|
|
|
27822
28182
|
detail: `Parse error: ${err instanceof Error ? err.message.slice(0, 200) : String(err)}`
|
|
27823
28183
|
});
|
|
27824
28184
|
}
|
|
27825
|
-
const hooksDir =
|
|
28185
|
+
const hooksDir = path52.join(pkgRoot, "dist", "hooks");
|
|
27826
28186
|
if (existsSync39(hooksDir)) {
|
|
27827
28187
|
const hookFiles = readdirSync14(hooksDir).filter((f) => f.endsWith(".js") && !f.endsWith(".js.map"));
|
|
27828
28188
|
let hooksPassed = 0;
|
|
27829
28189
|
const hooksFailed = [];
|
|
27830
28190
|
for (const hook of hookFiles) {
|
|
27831
28191
|
try {
|
|
27832
|
-
|
|
28192
|
+
execSync14(`node --check "${path52.join(hooksDir, hook)}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
|
|
27833
28193
|
hooksPassed++;
|
|
27834
28194
|
} catch {
|
|
27835
28195
|
hooksFailed.push(hook);
|
|
@@ -27853,8 +28213,8 @@ function checkWorkerSpawning(pkgRoot) {
|
|
|
27853
28213
|
}
|
|
27854
28214
|
function checkMcpTransport() {
|
|
27855
28215
|
const results = [];
|
|
27856
|
-
const pidPath =
|
|
27857
|
-
const tokenPath =
|
|
28216
|
+
const pidPath = path52.join(EXE_AI_DIR, "exed.pid");
|
|
28217
|
+
const tokenPath = path52.join(EXE_AI_DIR, "exed.token");
|
|
27858
28218
|
let daemonAlive = false;
|
|
27859
28219
|
if (existsSync39(pidPath)) {
|
|
27860
28220
|
try {
|
|
@@ -27871,7 +28231,7 @@ function checkMcpTransport() {
|
|
|
27871
28231
|
if (daemonAlive && existsSync39(tokenPath)) {
|
|
27872
28232
|
try {
|
|
27873
28233
|
const token = readFileSync33(tokenPath, "utf8").trim();
|
|
27874
|
-
const response =
|
|
28234
|
+
const response = execSync14(
|
|
27875
28235
|
`curl -sS -i -m 2 -H "Authorization: Bearer ${token}" -H 'Accept: application/json, text/event-stream' http://127.0.0.1:48739/mcp`,
|
|
27876
28236
|
{ encoding: "utf8", timeout: 3e3 }
|
|
27877
28237
|
);
|
|
@@ -27901,7 +28261,7 @@ function checkMcpTransport() {
|
|
|
27901
28261
|
results.push({
|
|
27902
28262
|
name: "mcp/monitor-summary",
|
|
27903
28263
|
pass: true,
|
|
27904
|
-
detail: `Privacy-safe local monitor summary: ${
|
|
28264
|
+
detail: `Privacy-safe local monitor summary: ${path52.join(EXE_AI_DIR, "monitor", "mcp-transport-summary.json")}`
|
|
27905
28265
|
});
|
|
27906
28266
|
return results;
|
|
27907
28267
|
}
|
|
@@ -27922,7 +28282,7 @@ function checkClaudeCodeInstall() {
|
|
|
27922
28282
|
});
|
|
27923
28283
|
}
|
|
27924
28284
|
try {
|
|
27925
|
-
const claudePath =
|
|
28285
|
+
const claudePath = execSync14("which claude 2>/dev/null || true", { encoding: "utf8", timeout: 5e3 }).trim();
|
|
27926
28286
|
if (!claudePath) {
|
|
27927
28287
|
results.push({
|
|
27928
28288
|
name: "cc/cli-binary",
|
|
@@ -27932,7 +28292,7 @@ function checkClaudeCodeInstall() {
|
|
|
27932
28292
|
} else {
|
|
27933
28293
|
let resolved = claudePath;
|
|
27934
28294
|
try {
|
|
27935
|
-
resolved =
|
|
28295
|
+
resolved = execSync14(`readlink -f "${claudePath}" 2>/dev/null || readlink "${claudePath}" 2>/dev/null || echo "${claudePath}"`, {
|
|
27936
28296
|
encoding: "utf8",
|
|
27937
28297
|
timeout: 5e3
|
|
27938
28298
|
}).trim();
|
|
@@ -27959,7 +28319,7 @@ function checkClaudeCodeInstall() {
|
|
|
27959
28319
|
detail: "Failed to check claude binary path"
|
|
27960
28320
|
});
|
|
27961
28321
|
}
|
|
27962
|
-
const versionsDir =
|
|
28322
|
+
const versionsDir = path52.join(
|
|
27963
28323
|
process.env.HOME ?? process.env.USERPROFILE ?? "",
|
|
27964
28324
|
".local",
|
|
27965
28325
|
"share",
|
|
@@ -28016,7 +28376,7 @@ function checkClaudeCodeInstall() {
|
|
|
28016
28376
|
});
|
|
28017
28377
|
}
|
|
28018
28378
|
try {
|
|
28019
|
-
const ccVersion =
|
|
28379
|
+
const ccVersion = execSync14("claude --version 2>/dev/null || echo unknown", {
|
|
28020
28380
|
encoding: "utf8",
|
|
28021
28381
|
timeout: 5e3
|
|
28022
28382
|
}).trim();
|
|
@@ -28064,17 +28424,17 @@ if (isMainModule(import.meta.url)) {
|
|
|
28064
28424
|
}
|
|
28065
28425
|
|
|
28066
28426
|
// src/lib/update-check.ts
|
|
28067
|
-
import { execSync as
|
|
28427
|
+
import { execSync as execSync15 } from "child_process";
|
|
28068
28428
|
import { readFileSync as readFileSync34 } from "fs";
|
|
28069
|
-
import
|
|
28429
|
+
import path53 from "path";
|
|
28070
28430
|
function getLocalVersion(packageRoot) {
|
|
28071
|
-
const pkgPath =
|
|
28431
|
+
const pkgPath = path53.join(packageRoot, "package.json");
|
|
28072
28432
|
const pkg = JSON.parse(readFileSync34(pkgPath, "utf-8"));
|
|
28073
28433
|
return pkg.version;
|
|
28074
28434
|
}
|
|
28075
28435
|
function getRemoteVersion() {
|
|
28076
28436
|
try {
|
|
28077
|
-
const output =
|
|
28437
|
+
const output = execSync15("npm view @askexenow/exe-os version", {
|
|
28078
28438
|
encoding: "utf-8",
|
|
28079
28439
|
timeout: 15e3,
|
|
28080
28440
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -28136,12 +28496,12 @@ function registerCliParityTools(server2) {
|
|
|
28136
28496
|
title: "Doctor",
|
|
28137
28497
|
description: "Run exe-os doctor audit. Defaults to read-only diagnostics; optional dry-run/fix flags mirror CLI.",
|
|
28138
28498
|
inputSchema: {
|
|
28139
|
-
agent:
|
|
28140
|
-
project:
|
|
28141
|
-
verbose:
|
|
28142
|
-
conflicts:
|
|
28143
|
-
dry_run:
|
|
28144
|
-
fix:
|
|
28499
|
+
agent: z91.string().optional(),
|
|
28500
|
+
project: z91.string().optional(),
|
|
28501
|
+
verbose: z91.boolean().default(false),
|
|
28502
|
+
conflicts: z91.boolean().default(false),
|
|
28503
|
+
dry_run: z91.boolean().default(false),
|
|
28504
|
+
fix: z91.boolean().default(false)
|
|
28145
28505
|
}
|
|
28146
28506
|
}, async ({ agent, project, verbose, conflicts, dry_run, fix }) => {
|
|
28147
28507
|
const args = [];
|
|
@@ -28158,9 +28518,9 @@ function registerCliParityTools(server2) {
|
|
|
28158
28518
|
title: "Rename Employee",
|
|
28159
28519
|
description: "Rename an employee using the same path as `exe-os rename <old> <new>`. Use for customer roster/identity renames.",
|
|
28160
28520
|
inputSchema: {
|
|
28161
|
-
old_name:
|
|
28162
|
-
new_name:
|
|
28163
|
-
dry_run:
|
|
28521
|
+
old_name: z91.string().min(1),
|
|
28522
|
+
new_name: z91.string().min(1),
|
|
28523
|
+
dry_run: z91.boolean().default(false)
|
|
28164
28524
|
}
|
|
28165
28525
|
}, async ({ old_name, new_name, dry_run }) => {
|
|
28166
28526
|
if (dry_run) {
|
|
@@ -28173,7 +28533,7 @@ function registerCliParityTools(server2) {
|
|
|
28173
28533
|
server2.registerTool("status_brief", {
|
|
28174
28534
|
title: "Status Brief",
|
|
28175
28535
|
description: "Return current employee/tmux status. Mirrors `exe-status` and supports optional deep view for one employee.",
|
|
28176
|
-
inputSchema: { employee:
|
|
28536
|
+
inputSchema: { employee: z91.string().optional() }
|
|
28177
28537
|
}, async ({ employee }) => {
|
|
28178
28538
|
const text3 = await status(employee);
|
|
28179
28539
|
return result2(text3, { ok: true, employee: employee ?? null });
|
|
@@ -28181,7 +28541,7 @@ function registerCliParityTools(server2) {
|
|
|
28181
28541
|
server2.registerTool("pending_work_summary", {
|
|
28182
28542
|
title: "Pending Work Summary",
|
|
28183
28543
|
description: "Return pending reviews, messages, and notifications using the same summaries as the CLI tools.",
|
|
28184
|
-
inputSchema: { agent:
|
|
28544
|
+
inputSchema: { agent: z91.string().optional() }
|
|
28185
28545
|
}, async ({ agent }) => {
|
|
28186
28546
|
const parts = await Promise.all([
|
|
28187
28547
|
runCommand("exe-pending-reviews", [], 3e4),
|
|
@@ -28202,7 +28562,7 @@ function registerCliParityTools(server2) {
|
|
|
28202
28562
|
server2.registerTool("key_rotation_preflight", {
|
|
28203
28563
|
title: "Key Rotation Preflight",
|
|
28204
28564
|
description: "Dry-run key rotation/update preflight. No destructive changes.",
|
|
28205
|
-
inputSchema: { mode:
|
|
28565
|
+
inputSchema: { mode: z91.enum(["rotate", "update"]).default("rotate") }
|
|
28206
28566
|
}, async ({ mode }) => {
|
|
28207
28567
|
const out = await runCommand("exe-os", ["key", mode, "--dry-run"], 6e4);
|
|
28208
28568
|
return result2(out.text, { ok: out.ok, command: out.command, mode }, !out.ok);
|
|
@@ -28221,11 +28581,11 @@ function registerCliParityTools(server2) {
|
|
|
28221
28581
|
title: "Stack Update Check",
|
|
28222
28582
|
description: "Plan/check a customer stack update without applying Docker changes. Mirrors `exe-os stack-update --check`.",
|
|
28223
28583
|
inputSchema: {
|
|
28224
|
-
target:
|
|
28225
|
-
manifest:
|
|
28226
|
-
compose_file:
|
|
28227
|
-
env_file:
|
|
28228
|
-
deployment_persona:
|
|
28584
|
+
target: z91.string().optional(),
|
|
28585
|
+
manifest: z91.string().optional(),
|
|
28586
|
+
compose_file: z91.string().optional(),
|
|
28587
|
+
env_file: z91.string().optional(),
|
|
28588
|
+
deployment_persona: z91.enum(["customer", "askexe-control-plane"]).default("customer")
|
|
28229
28589
|
}
|
|
28230
28590
|
}, async ({ target, manifest, compose_file, env_file, deployment_persona }) => {
|
|
28231
28591
|
const args = ["stack-update", "--check", "--deployment-persona", deployment_persona];
|
|
@@ -28249,7 +28609,7 @@ function registerCliParityTools(server2) {
|
|
|
28249
28609
|
// src/mcp/tools/get-session-events.ts
|
|
28250
28610
|
init_active_agent();
|
|
28251
28611
|
init_fast_db_init();
|
|
28252
|
-
import { z as
|
|
28612
|
+
import { z as z92 } from "zod";
|
|
28253
28613
|
|
|
28254
28614
|
// src/lib/session-events.ts
|
|
28255
28615
|
init_task_scope();
|
|
@@ -28336,7 +28696,7 @@ async function listRecentSessionEvents(client, options) {
|
|
|
28336
28696
|
}
|
|
28337
28697
|
|
|
28338
28698
|
// src/mcp/tools/get-session-events.ts
|
|
28339
|
-
var EVENT_TYPE =
|
|
28699
|
+
var EVENT_TYPE = z92.enum([
|
|
28340
28700
|
"user_prompt",
|
|
28341
28701
|
"assistant_response",
|
|
28342
28702
|
"tool_call",
|
|
@@ -28366,11 +28726,11 @@ function registerGetSessionEvents(server2) {
|
|
|
28366
28726
|
title: "Get Session Events",
|
|
28367
28727
|
description: "Return exact recent chronological user prompts, assistant responses, and tool calls from the append-only session journal. Use this when asked what happened last, not semantic memory search.",
|
|
28368
28728
|
inputSchema: {
|
|
28369
|
-
agent_id:
|
|
28370
|
-
session_id:
|
|
28729
|
+
agent_id: z92.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
|
|
28730
|
+
session_id: z92.string().optional().describe("Optional exact runtime session id."),
|
|
28371
28731
|
event_type: EVENT_TYPE.optional().describe("Filter to one event type."),
|
|
28372
|
-
project_name:
|
|
28373
|
-
limit:
|
|
28732
|
+
project_name: z92.string().optional().describe("Optional project filter. Pass 'all' for all projects."),
|
|
28733
|
+
limit: z92.number().int().min(1).max(100).default(20).describe("Number of events to return.")
|
|
28374
28734
|
}
|
|
28375
28735
|
},
|
|
28376
28736
|
async ({ agent_id, session_id, event_type, project_name, limit }) => {
|
|
@@ -28406,8 +28766,8 @@ function registerGetLastAssistantResponse(server2) {
|
|
|
28406
28766
|
title: "Get Last Assistant Response",
|
|
28407
28767
|
description: "Return the exact last assistant response for an agent from the session event journal. Use for 'what was the last thing you said?'",
|
|
28408
28768
|
inputSchema: {
|
|
28409
|
-
agent_id:
|
|
28410
|
-
project_name:
|
|
28769
|
+
agent_id: z92.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
|
|
28770
|
+
project_name: z92.string().optional().describe("Optional project filter. Pass 'all' for all projects.")
|
|
28411
28771
|
}
|
|
28412
28772
|
},
|
|
28413
28773
|
async ({ agent_id, project_name }) => {
|
|
@@ -28437,32 +28797,106 @@ function registerGetLastAssistantResponse(server2) {
|
|
|
28437
28797
|
}
|
|
28438
28798
|
|
|
28439
28799
|
// src/mcp/tools/code-context.ts
|
|
28440
|
-
import { z as
|
|
28800
|
+
import { z as z93 } from "zod";
|
|
28441
28801
|
|
|
28442
28802
|
// src/lib/code-context-index.ts
|
|
28443
28803
|
init_config();
|
|
28444
|
-
import
|
|
28445
|
-
import
|
|
28804
|
+
import crypto20 from "crypto";
|
|
28805
|
+
import path54 from "path";
|
|
28446
28806
|
import { existsSync as existsSync40, mkdirSync as mkdirSync22, readFileSync as readFileSync35, readdirSync as readdirSync15, statSync as statSync9, writeFileSync as writeFileSync24 } from "fs";
|
|
28447
28807
|
import { spawnSync } from "child_process";
|
|
28808
|
+
init_exe_daemon_client();
|
|
28809
|
+
var VECTOR_STORE_VERSION = 1;
|
|
28810
|
+
var EMBED_BATCH_SIZE = 64;
|
|
28811
|
+
function vectorStorePath(projectRoot) {
|
|
28812
|
+
const rootHash = hashText(projectRoot).slice(0, 16);
|
|
28813
|
+
return path54.join(indexDir(), `${rootHash}.vectors.json`);
|
|
28814
|
+
}
|
|
28815
|
+
function loadVectorStore(projectRoot) {
|
|
28816
|
+
const file = vectorStorePath(projectRoot);
|
|
28817
|
+
if (!existsSync40(file)) return null;
|
|
28818
|
+
try {
|
|
28819
|
+
const parsed = JSON.parse(readFileSync35(file, "utf8"));
|
|
28820
|
+
if (parsed.version !== VECTOR_STORE_VERSION) return null;
|
|
28821
|
+
return parsed;
|
|
28822
|
+
} catch {
|
|
28823
|
+
return null;
|
|
28824
|
+
}
|
|
28825
|
+
}
|
|
28826
|
+
function saveVectorStore(projectRoot, store) {
|
|
28827
|
+
writeFileSync24(vectorStorePath(projectRoot), JSON.stringify(store));
|
|
28828
|
+
}
|
|
28829
|
+
function cosineSimilarity3(a, b) {
|
|
28830
|
+
let dot = 0, normA = 0, normB = 0;
|
|
28831
|
+
for (let i = 0; i < a.length; i++) {
|
|
28832
|
+
dot += a[i] * b[i];
|
|
28833
|
+
normA += a[i] * a[i];
|
|
28834
|
+
normB += b[i] * b[i];
|
|
28835
|
+
}
|
|
28836
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
28837
|
+
return denom === 0 ? 0 : dot / denom;
|
|
28838
|
+
}
|
|
28839
|
+
async function embedSymbols(index) {
|
|
28840
|
+
const rootHash = hashText(index.projectRoot).slice(0, 16);
|
|
28841
|
+
const existing = loadVectorStore(index.projectRoot);
|
|
28842
|
+
const store = {
|
|
28843
|
+
version: VECTOR_STORE_VERSION,
|
|
28844
|
+
projectRootHash: rootHash,
|
|
28845
|
+
vectors: {}
|
|
28846
|
+
};
|
|
28847
|
+
const allSymbols = [];
|
|
28848
|
+
for (const file of Object.values(index.files)) {
|
|
28849
|
+
for (const symbol of file.symbols) {
|
|
28850
|
+
if (existing?.vectors[symbol.id]) {
|
|
28851
|
+
store.vectors[symbol.id] = existing.vectors[symbol.id];
|
|
28852
|
+
} else {
|
|
28853
|
+
allSymbols.push({ id: symbol.id, text: symbol.summary || `${symbol.kind} ${symbol.name} in ${symbol.filePath}` });
|
|
28854
|
+
}
|
|
28855
|
+
}
|
|
28856
|
+
}
|
|
28857
|
+
if (allSymbols.length === 0) {
|
|
28858
|
+
saveVectorStore(index.projectRoot, store);
|
|
28859
|
+
return store;
|
|
28860
|
+
}
|
|
28861
|
+
const connected = await connectEmbedDaemon().catch(() => false);
|
|
28862
|
+
if (!connected) {
|
|
28863
|
+
saveVectorStore(index.projectRoot, store);
|
|
28864
|
+
return store;
|
|
28865
|
+
}
|
|
28866
|
+
for (let i = 0; i < allSymbols.length; i += EMBED_BATCH_SIZE) {
|
|
28867
|
+
const batch = allSymbols.slice(i, i + EMBED_BATCH_SIZE);
|
|
28868
|
+
const texts = batch.map((s) => s.text);
|
|
28869
|
+
try {
|
|
28870
|
+
const vectors = await embedBatchViaClient(texts, "low");
|
|
28871
|
+
if (vectors && vectors.length === batch.length) {
|
|
28872
|
+
for (let j = 0; j < batch.length; j++) {
|
|
28873
|
+
store.vectors[batch[j].id] = vectors[j];
|
|
28874
|
+
}
|
|
28875
|
+
}
|
|
28876
|
+
} catch {
|
|
28877
|
+
}
|
|
28878
|
+
}
|
|
28879
|
+
saveVectorStore(index.projectRoot, store);
|
|
28880
|
+
return store;
|
|
28881
|
+
}
|
|
28448
28882
|
var INDEX_VERSION = 2;
|
|
28449
28883
|
var DEFAULT_MAX_FILES = 5e3;
|
|
28450
28884
|
var IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build", "target", "vendor"]);
|
|
28451
28885
|
function normalizeProjectRoot(projectRoot) {
|
|
28452
|
-
return
|
|
28886
|
+
return path54.resolve(projectRoot || process.cwd());
|
|
28453
28887
|
}
|
|
28454
28888
|
function hashText(text3) {
|
|
28455
|
-
return
|
|
28889
|
+
return crypto20.createHash("sha256").update(text3).digest("hex");
|
|
28456
28890
|
}
|
|
28457
28891
|
function indexDir() {
|
|
28458
|
-
const dir =
|
|
28892
|
+
const dir = path54.join(EXE_AI_DIR, "code-context");
|
|
28459
28893
|
mkdirSync22(dir, { recursive: true });
|
|
28460
28894
|
return dir;
|
|
28461
28895
|
}
|
|
28462
28896
|
function getCodeContextIndexPath(projectRoot) {
|
|
28463
28897
|
const root = normalizeProjectRoot(projectRoot);
|
|
28464
28898
|
const rootHash = hashText(root).slice(0, 16);
|
|
28465
|
-
return
|
|
28899
|
+
return path54.join(indexDir(), `${rootHash}.json`);
|
|
28466
28900
|
}
|
|
28467
28901
|
function currentBranch(projectRoot) {
|
|
28468
28902
|
const result3 = spawnSync("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
|
|
@@ -28475,8 +28909,8 @@ function shouldIgnore(relPath) {
|
|
|
28475
28909
|
}
|
|
28476
28910
|
function listRecursive(projectRoot, dir = projectRoot, out = []) {
|
|
28477
28911
|
for (const entry of readdirSync15(dir, { withFileTypes: true })) {
|
|
28478
|
-
const abs =
|
|
28479
|
-
const rel =
|
|
28912
|
+
const abs = path54.join(dir, entry.name);
|
|
28913
|
+
const rel = path54.relative(projectRoot, abs).replaceAll(path54.sep, "/");
|
|
28480
28914
|
if (shouldIgnore(rel)) continue;
|
|
28481
28915
|
if (entry.isDirectory()) listRecursive(projectRoot, abs, out);
|
|
28482
28916
|
else if (entry.isFile()) out.push(rel);
|
|
@@ -28492,7 +28926,7 @@ function listCodeFiles(projectRoot, maxFiles) {
|
|
|
28492
28926
|
const rg = spawnSync("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
|
|
28493
28927
|
files = rg.status === 0 && rg.stdout.trim() ? rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean) : listRecursive(projectRoot);
|
|
28494
28928
|
}
|
|
28495
|
-
return files.map((file) => file.replaceAll(
|
|
28929
|
+
return files.map((file) => file.replaceAll(path54.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
|
|
28496
28930
|
}
|
|
28497
28931
|
function parseImportPaths(importText) {
|
|
28498
28932
|
const paths = [];
|
|
@@ -28505,13 +28939,13 @@ function parseImportPaths(importText) {
|
|
|
28505
28939
|
}
|
|
28506
28940
|
function resolveImport(fromFile, importPath, allFiles) {
|
|
28507
28941
|
if (!importPath.startsWith(".")) return null;
|
|
28508
|
-
const base =
|
|
28942
|
+
const base = path54.posix.normalize(path54.posix.join(path54.posix.dirname(fromFile.replaceAll(path54.sep, "/")), importPath));
|
|
28509
28943
|
const withoutKnownExt = base.replace(/\.(?:[a-z0-9]+)$/i, "");
|
|
28510
28944
|
const candidates = [base];
|
|
28511
28945
|
for (const ext of ["ts", "tsx", "js", "jsx", "py", "rs", "go", "java", "cs", "cpp", "c", "rb", "php", "swift", "kt", "scala", "sql", "md", "json", "yaml", "yml"]) {
|
|
28512
28946
|
candidates.push(`${withoutKnownExt}.${ext}`, `${base}.${ext}`);
|
|
28513
28947
|
}
|
|
28514
|
-
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(
|
|
28948
|
+
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(path54.posix.join(base, indexName));
|
|
28515
28949
|
return candidates.find((candidate) => allFiles.has(candidate)) ?? null;
|
|
28516
28950
|
}
|
|
28517
28951
|
function symbolId(filePath, chunk) {
|
|
@@ -28532,7 +28966,7 @@ function saveIndex(index) {
|
|
|
28532
28966
|
writeFileSync24(getCodeContextIndexPath(index.projectRoot), JSON.stringify(index, null, 2));
|
|
28533
28967
|
}
|
|
28534
28968
|
function buildFileRecord(projectRoot, relPath, allFiles, previous) {
|
|
28535
|
-
const absPath =
|
|
28969
|
+
const absPath = path54.join(projectRoot, relPath);
|
|
28536
28970
|
let stat;
|
|
28537
28971
|
try {
|
|
28538
28972
|
stat = statSync9(absPath);
|
|
@@ -28570,13 +29004,13 @@ function buildCodeContextIndex(options = {}) {
|
|
|
28570
29004
|
const branch = currentBranch(projectRoot);
|
|
28571
29005
|
const previous = options.force ? null : loadIndex(projectRoot);
|
|
28572
29006
|
const files = listCodeFiles(projectRoot, maxFiles);
|
|
28573
|
-
const allFiles = new Set(files.map((file) => file.replaceAll(
|
|
29007
|
+
const allFiles = new Set(files.map((file) => file.replaceAll(path54.sep, "/")));
|
|
28574
29008
|
const fileRecords = {};
|
|
28575
29009
|
let rebuiltFiles = 0;
|
|
28576
29010
|
let reusedFiles = 0;
|
|
28577
29011
|
let skippedFiles = 0;
|
|
28578
29012
|
for (const rel of files) {
|
|
28579
|
-
const normalized = rel.replaceAll(
|
|
29013
|
+
const normalized = rel.replaceAll(path54.sep, "/");
|
|
28580
29014
|
const { record, reused } = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
|
|
28581
29015
|
if (record) {
|
|
28582
29016
|
fileRecords[normalized] = record;
|
|
@@ -28605,11 +29039,11 @@ function loadOrBuildCodeContextIndex(options = {}) {
|
|
|
28605
29039
|
if (loaded) {
|
|
28606
29040
|
const currentFiles = listCodeFiles(projectRoot, options.maxFiles ?? DEFAULT_MAX_FILES);
|
|
28607
29041
|
const unchanged = currentFiles.every((rel) => {
|
|
28608
|
-
const normalized = rel.replaceAll(
|
|
29042
|
+
const normalized = rel.replaceAll(path54.sep, "/");
|
|
28609
29043
|
const existing = loaded.files[normalized];
|
|
28610
29044
|
if (!existing) return false;
|
|
28611
29045
|
try {
|
|
28612
|
-
const stat = statSync9(
|
|
29046
|
+
const stat = statSync9(path54.join(projectRoot, normalized));
|
|
28613
29047
|
return stat.mtimeMs === existing.mtimeMs && stat.size === existing.size;
|
|
28614
29048
|
} catch {
|
|
28615
29049
|
return false;
|
|
@@ -28662,9 +29096,9 @@ function globToRegex(pattern) {
|
|
|
28662
29096
|
}
|
|
28663
29097
|
function matchesPath(filePath, patterns) {
|
|
28664
29098
|
if (!patterns || patterns.length === 0) return true;
|
|
28665
|
-
const normalized = filePath.replaceAll(
|
|
29099
|
+
const normalized = filePath.replaceAll(path54.sep, "/");
|
|
28666
29100
|
return patterns.some((pattern) => {
|
|
28667
|
-
const p = pattern.replaceAll(
|
|
29101
|
+
const p = pattern.replaceAll(path54.sep, "/").replace(/^\.\//, "");
|
|
28668
29102
|
return normalized === p || normalized.startsWith(`${p}/`) || normalized.endsWith(p) || globToRegex(p).test(normalized);
|
|
28669
29103
|
});
|
|
28670
29104
|
}
|
|
@@ -28723,7 +29157,7 @@ function filteredFiles(index, options = {}) {
|
|
|
28723
29157
|
return matchesPath(file.path, options.paths);
|
|
28724
29158
|
});
|
|
28725
29159
|
}
|
|
28726
|
-
function
|
|
29160
|
+
function lexicalSearch(query, options = {}) {
|
|
28727
29161
|
const terms = tokenize(query);
|
|
28728
29162
|
if (terms.length === 0) return [];
|
|
28729
29163
|
const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
|
|
@@ -28749,6 +29183,82 @@ function searchCodeContext(query, options = {}) {
|
|
|
28749
29183
|
const limit = options.limit ?? 20;
|
|
28750
29184
|
return results.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
|
|
28751
29185
|
}
|
|
29186
|
+
function searchCodeContext(query, options = {}) {
|
|
29187
|
+
return lexicalSearch(query, options);
|
|
29188
|
+
}
|
|
29189
|
+
var RRF_K2 = 60;
|
|
29190
|
+
async function searchCodeContextSemantic(query, options = {}) {
|
|
29191
|
+
const terms = tokenize(query);
|
|
29192
|
+
if (terms.length === 0) return [];
|
|
29193
|
+
const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
|
|
29194
|
+
const projectRoot = normalizeProjectRoot(options.projectRoot);
|
|
29195
|
+
const vectorStore = loadVectorStore(projectRoot);
|
|
29196
|
+
if (!vectorStore || Object.keys(vectorStore.vectors).length === 0) {
|
|
29197
|
+
return lexicalSearch(query, options);
|
|
29198
|
+
}
|
|
29199
|
+
let queryVector = null;
|
|
29200
|
+
try {
|
|
29201
|
+
const connected = await connectEmbedDaemon().catch(() => false);
|
|
29202
|
+
if (connected) {
|
|
29203
|
+
const result3 = await embedBatchViaClient([query], "high");
|
|
29204
|
+
if (result3 && result3.length === 1) queryVector = result3[0];
|
|
29205
|
+
}
|
|
29206
|
+
} catch {
|
|
29207
|
+
}
|
|
29208
|
+
if (!queryVector) return lexicalSearch(query, options);
|
|
29209
|
+
const files = filteredFiles(index, options);
|
|
29210
|
+
const candidates = [];
|
|
29211
|
+
for (const file of files) {
|
|
29212
|
+
for (const symbol of file.symbols) {
|
|
29213
|
+
const lexical = scoreSymbol(symbol, terms);
|
|
29214
|
+
const vec = vectorStore.vectors[symbol.id];
|
|
29215
|
+
const vectorScore = vec ? cosineSimilarity3(queryVector, vec) : 0;
|
|
29216
|
+
if (lexical.score > 0 || vectorScore > 0.3) {
|
|
29217
|
+
candidates.push({
|
|
29218
|
+
symbol,
|
|
29219
|
+
lexicalScore: lexical.score,
|
|
29220
|
+
vectorScore,
|
|
29221
|
+
matches: lexical.matches
|
|
29222
|
+
});
|
|
29223
|
+
}
|
|
29224
|
+
}
|
|
29225
|
+
}
|
|
29226
|
+
const byLexical = [...candidates].sort((a, b) => b.lexicalScore - a.lexicalScore);
|
|
29227
|
+
const byVector = [...candidates].sort((a, b) => b.vectorScore - a.vectorScore);
|
|
29228
|
+
const lexicalRank = /* @__PURE__ */ new Map();
|
|
29229
|
+
const vectorRank = /* @__PURE__ */ new Map();
|
|
29230
|
+
byLexical.forEach((c, i) => lexicalRank.set(c.symbol.id, i + 1));
|
|
29231
|
+
byVector.forEach((c, i) => vectorRank.set(c.symbol.id, i + 1));
|
|
29232
|
+
const VECTOR_WEIGHT = 0.6;
|
|
29233
|
+
const LEXICAL_WEIGHT = 0.4;
|
|
29234
|
+
const fused = candidates.map((c) => {
|
|
29235
|
+
const lRank = lexicalRank.get(c.symbol.id) ?? candidates.length + 1;
|
|
29236
|
+
const vRank = vectorRank.get(c.symbol.id) ?? candidates.length + 1;
|
|
29237
|
+
const rrfScore = VECTOR_WEIGHT * (1 / (RRF_K2 + vRank)) + LEXICAL_WEIGHT * (1 / (RRF_K2 + lRank));
|
|
29238
|
+
return {
|
|
29239
|
+
symbol: c.symbol,
|
|
29240
|
+
score: rrfScore,
|
|
29241
|
+
matches: c.vectorScore > 0.3 ? [...c.matches, `semantic=${c.vectorScore.toFixed(3)}`] : c.matches,
|
|
29242
|
+
filePath: c.symbol.filePath,
|
|
29243
|
+
language: c.symbol.language,
|
|
29244
|
+
content: c.symbol.text,
|
|
29245
|
+
startLine: c.symbol.startLine,
|
|
29246
|
+
endLine: c.symbol.endLine
|
|
29247
|
+
};
|
|
29248
|
+
});
|
|
29249
|
+
const offset = Math.max(0, options.offset ?? 0);
|
|
29250
|
+
const limit = options.limit ?? 20;
|
|
29251
|
+
return fused.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
|
|
29252
|
+
}
|
|
29253
|
+
async function buildCodeContextIndexWithEmbeddings(options = {}) {
|
|
29254
|
+
const index = buildCodeContextIndex(options);
|
|
29255
|
+
const existingStore = loadVectorStore(index.projectRoot);
|
|
29256
|
+
const existingCount = existingStore ? Object.keys(existingStore.vectors).length : 0;
|
|
29257
|
+
const store = await embedSymbols(index);
|
|
29258
|
+
const vectorCount = Object.keys(store.vectors).length;
|
|
29259
|
+
const newEmbeddings = vectorCount - Math.min(existingCount, vectorCount);
|
|
29260
|
+
return { index, vectorCount, newEmbeddings };
|
|
29261
|
+
}
|
|
28752
29262
|
function dependentsMap(index) {
|
|
28753
29263
|
const map = /* @__PURE__ */ new Map();
|
|
28754
29264
|
for (const file of Object.values(index.files)) {
|
|
@@ -28782,7 +29292,7 @@ function traceCodeSymbol(symbolName, options = {}) {
|
|
|
28782
29292
|
}
|
|
28783
29293
|
function resolveTargetFile(index, input) {
|
|
28784
29294
|
if (input.filePath) {
|
|
28785
|
-
const normalized = input.filePath.replaceAll(
|
|
29295
|
+
const normalized = input.filePath.replaceAll(path54.sep, "/").replace(/^\.\//, "");
|
|
28786
29296
|
if (index.files[normalized]) return { filePath: normalized, target: normalized };
|
|
28787
29297
|
const suffix = Object.keys(index.files).find((file) => file.endsWith(normalized));
|
|
28788
29298
|
if (suffix) return { filePath: suffix, target: input.filePath };
|
|
@@ -28812,7 +29322,7 @@ function analyzeBlastRadius(input) {
|
|
|
28812
29322
|
}
|
|
28813
29323
|
}
|
|
28814
29324
|
}
|
|
28815
|
-
const targetBase =
|
|
29325
|
+
const targetBase = path54.basename(target.filePath).replace(/\.[^.]+$/, "").toLowerCase();
|
|
28816
29326
|
const symbolLower = input.symbol?.toLowerCase();
|
|
28817
29327
|
const tests = Object.keys(index.files).filter((file) => {
|
|
28818
29328
|
const lower = file.toLowerCase();
|
|
@@ -28851,23 +29361,24 @@ function jsonResult(value) {
|
|
|
28851
29361
|
function registerCodeContext(server2) {
|
|
28852
29362
|
server2.registerTool("code_context", {
|
|
28853
29363
|
title: "Code Context",
|
|
28854
|
-
description: "Persistent codebase context engine. One consolidated tool to avoid MCP bloat. Actions: index, search, trace, blast_radius, stats.",
|
|
29364
|
+
description: "Persistent codebase context engine with semantic vector search. One consolidated tool to avoid MCP bloat. Actions: index (structural only), index_embed (structural + vector embeddings), search (semantic+lexical hybrid), trace, blast_radius, stats. Search uses RRF fusion of Jina v5 embeddings (cosine similarity) + lexical scoring. Falls back to lexical if daemon unavailable.",
|
|
28855
29365
|
inputSchema: {
|
|
28856
|
-
action:
|
|
28857
|
-
project_root:
|
|
28858
|
-
query:
|
|
28859
|
-
symbol:
|
|
28860
|
-
file_path:
|
|
28861
|
-
force:
|
|
28862
|
-
limit:
|
|
28863
|
-
offset:
|
|
28864
|
-
refresh_index:
|
|
28865
|
-
languages:
|
|
28866
|
-
paths:
|
|
28867
|
-
depth:
|
|
28868
|
-
max_files:
|
|
28869
|
-
|
|
28870
|
-
|
|
29366
|
+
action: z93.enum(["index", "index_embed", "search", "trace", "blast_radius", "stats"]).describe("Code context operation. index_embed = index + generate embeddings for semantic search."),
|
|
29367
|
+
project_root: z93.string().optional().describe("Repository root. Defaults to current working directory."),
|
|
29368
|
+
query: z93.string().optional().describe("Natural language search query (e.g. 'authentication logic', 'database migration'). Semantic search finds conceptual matches, not just keywords."),
|
|
29369
|
+
symbol: z93.string().optional().describe("Symbol/function/class/type name for trace or blast_radius"),
|
|
29370
|
+
file_path: z93.string().optional().describe("File path for blast_radius"),
|
|
29371
|
+
force: z93.boolean().optional().describe("Force rebuild before answering"),
|
|
29372
|
+
limit: z93.coerce.number().int().min(1).max(100).optional().describe("Max results"),
|
|
29373
|
+
offset: z93.coerce.number().int().min(0).optional().describe("Search pagination offset"),
|
|
29374
|
+
refresh_index: z93.boolean().optional().describe("Refresh/rebuild index before searching"),
|
|
29375
|
+
languages: z93.array(z93.string()).optional().describe('Language filters, e.g. ["python", "typescript"]'),
|
|
29376
|
+
paths: z93.array(z93.string()).optional().describe("Path/glob filters"),
|
|
29377
|
+
depth: z93.coerce.number().int().min(1).max(5).optional().describe("Dependent traversal depth for blast_radius"),
|
|
29378
|
+
max_files: z93.coerce.number().int().min(1).max(1e4).optional().describe("Max code files to index"),
|
|
29379
|
+
lexical_only: z93.boolean().optional().describe("Force lexical-only search (skip vector similarity). Default: false \u2014 uses semantic hybrid search.")
|
|
29380
|
+
}
|
|
29381
|
+
}, async ({ action, project_root, query, symbol, file_path, force, limit, offset, refresh_index, languages, paths, depth, max_files, lexical_only }) => {
|
|
28871
29382
|
const opts = { projectRoot: project_root, force, maxFiles: max_files };
|
|
28872
29383
|
if (action === "index") {
|
|
28873
29384
|
const index = buildCodeContextIndex(opts);
|
|
@@ -28877,7 +29388,24 @@ function registerCodeContext(server2) {
|
|
|
28877
29388
|
indexedAt: index.indexedAt,
|
|
28878
29389
|
files: Object.keys(index.files).length,
|
|
28879
29390
|
symbols: Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0),
|
|
28880
|
-
imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0)
|
|
29391
|
+
imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0),
|
|
29392
|
+
note: "Structural index only. Use action=index_embed to also generate vector embeddings for semantic search."
|
|
29393
|
+
});
|
|
29394
|
+
}
|
|
29395
|
+
if (action === "index_embed") {
|
|
29396
|
+
const { index, vectorCount, newEmbeddings } = await buildCodeContextIndexWithEmbeddings(opts);
|
|
29397
|
+
const totalSymbols = Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0);
|
|
29398
|
+
return jsonResult({
|
|
29399
|
+
projectRoot: index.projectRoot,
|
|
29400
|
+
branch: index.branch,
|
|
29401
|
+
indexedAt: index.indexedAt,
|
|
29402
|
+
files: Object.keys(index.files).length,
|
|
29403
|
+
symbols: totalSymbols,
|
|
29404
|
+
imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0),
|
|
29405
|
+
vectorCount,
|
|
29406
|
+
newEmbeddings,
|
|
29407
|
+
embeddingCoverage: totalSymbols > 0 ? `${(vectorCount / totalSymbols * 100).toFixed(1)}%` : "0%",
|
|
29408
|
+
note: "Structural index + vector embeddings. Semantic search is now available."
|
|
28881
29409
|
});
|
|
28882
29410
|
}
|
|
28883
29411
|
if (action === "stats") {
|
|
@@ -28885,14 +29413,28 @@ function registerCodeContext(server2) {
|
|
|
28885
29413
|
}
|
|
28886
29414
|
if (action === "search") {
|
|
28887
29415
|
if (!query) return errorResult10('code_context action "search" requires query');
|
|
29416
|
+
const searchOpts = { ...opts, limit, offset, refreshIndex: refresh_index, languages, paths };
|
|
29417
|
+
if (lexical_only) {
|
|
29418
|
+
return jsonResult({
|
|
29419
|
+
query,
|
|
29420
|
+
mode: "lexical",
|
|
29421
|
+
limit: limit ?? 20,
|
|
29422
|
+
offset: offset ?? 0,
|
|
29423
|
+
languages: languages ?? [],
|
|
29424
|
+
paths: paths ?? [],
|
|
29425
|
+
results: searchCodeContext(query, searchOpts)
|
|
29426
|
+
});
|
|
29427
|
+
}
|
|
29428
|
+
const results = await searchCodeContextSemantic(query, searchOpts);
|
|
29429
|
+
const hasSemantic = results.some((r) => r.matches.some((m) => m.startsWith("semantic=")));
|
|
28888
29430
|
return jsonResult({
|
|
28889
29431
|
query,
|
|
29432
|
+
mode: hasSemantic ? "semantic+lexical" : "lexical-fallback",
|
|
28890
29433
|
limit: limit ?? 20,
|
|
28891
29434
|
offset: offset ?? 0,
|
|
28892
|
-
refresh_index: refresh_index ?? false,
|
|
28893
29435
|
languages: languages ?? [],
|
|
28894
29436
|
paths: paths ?? [],
|
|
28895
|
-
results
|
|
29437
|
+
results
|
|
28896
29438
|
});
|
|
28897
29439
|
}
|
|
28898
29440
|
if (action === "trace") {
|
|
@@ -28910,9 +29452,9 @@ function registerCodeContext(server2) {
|
|
|
28910
29452
|
}
|
|
28911
29453
|
|
|
28912
29454
|
// src/mcp/tools/support-inbox.ts
|
|
28913
|
-
import { z as
|
|
29455
|
+
import { z as z94 } from "zod";
|
|
28914
29456
|
var DEFAULT_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
|
|
28915
|
-
var STATUS =
|
|
29457
|
+
var STATUS = z94.enum(["open", "triaged", "fixed", "closed", "wontfix"]);
|
|
28916
29458
|
function adminToken() {
|
|
28917
29459
|
return process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN;
|
|
28918
29460
|
}
|
|
@@ -28951,9 +29493,9 @@ function registerListBugReports(server2) {
|
|
|
28951
29493
|
title: "List Bug Reports",
|
|
28952
29494
|
description: "AskExe-internal only: list incoming customer bug reports from the support inbox.",
|
|
28953
29495
|
inputSchema: {
|
|
28954
|
-
status:
|
|
28955
|
-
severity:
|
|
28956
|
-
limit:
|
|
29496
|
+
status: z94.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("open"),
|
|
29497
|
+
severity: z94.enum(["p0", "p1", "p2", "p3"]).optional(),
|
|
29498
|
+
limit: z94.number().int().min(1).max(100).default(25)
|
|
28957
29499
|
}
|
|
28958
29500
|
},
|
|
28959
29501
|
async ({ status: status2, severity, limit }) => {
|
|
@@ -28972,7 +29514,7 @@ function registerGetBugReport(server2) {
|
|
|
28972
29514
|
{
|
|
28973
29515
|
title: "Get Bug Report",
|
|
28974
29516
|
description: "AskExe-internal only: fetch one customer bug report with full markdown payload.",
|
|
28975
|
-
inputSchema: { id:
|
|
29517
|
+
inputSchema: { id: z94.string().min(8) }
|
|
28976
29518
|
},
|
|
28977
29519
|
async ({ id }) => {
|
|
28978
29520
|
const data = await requestJson(`${endpoint()}/${encodeURIComponent(id)}`);
|
|
@@ -28987,12 +29529,12 @@ function registerTriageBugReport(server2) {
|
|
|
28987
29529
|
title: "Triage Bug Report",
|
|
28988
29530
|
description: "AskExe-internal only: update bug report status and link task/commit/release metadata.",
|
|
28989
29531
|
inputSchema: {
|
|
28990
|
-
id:
|
|
29532
|
+
id: z94.string().min(8),
|
|
28991
29533
|
status: STATUS.optional(),
|
|
28992
|
-
triage_notes:
|
|
28993
|
-
linked_task_id:
|
|
28994
|
-
linked_commit:
|
|
28995
|
-
fixed_version:
|
|
29534
|
+
triage_notes: z94.string().optional(),
|
|
29535
|
+
linked_task_id: z94.string().optional(),
|
|
29536
|
+
linked_commit: z94.string().optional(),
|
|
29537
|
+
fixed_version: z94.string().optional()
|
|
28996
29538
|
}
|
|
28997
29539
|
},
|
|
28998
29540
|
async ({ id, status: status2, triage_notes, linked_task_id, linked_commit, fixed_version }) => {
|
|
@@ -29004,6 +29546,69 @@ function registerTriageBugReport(server2) {
|
|
|
29004
29546
|
}
|
|
29005
29547
|
);
|
|
29006
29548
|
}
|
|
29549
|
+
var FEATURE_STATUS = z94.enum(["open", "planned", "in_progress", "shipped", "closed", "wontdo"]);
|
|
29550
|
+
function featureEndpoint() {
|
|
29551
|
+
return endpoint().replace(/\/bug-reports\/?$/, "/feature-requests");
|
|
29552
|
+
}
|
|
29553
|
+
function registerListFeatureRequests(server2) {
|
|
29554
|
+
server2.registerTool(
|
|
29555
|
+
"list_feature_requests",
|
|
29556
|
+
{
|
|
29557
|
+
title: "List Feature Requests",
|
|
29558
|
+
description: "AskExe-internal only: list incoming customer feature requests from the support inbox.",
|
|
29559
|
+
inputSchema: {
|
|
29560
|
+
status: z94.enum(["all", "open", "planned", "in_progress", "shipped", "closed", "wontdo"]).default("open"),
|
|
29561
|
+
priority: z94.enum(["p0", "p1", "p2", "p3"]).optional(),
|
|
29562
|
+
limit: z94.number().int().min(1).max(100).default(25)
|
|
29563
|
+
}
|
|
29564
|
+
},
|
|
29565
|
+
async ({ status: status2, priority, limit }) => {
|
|
29566
|
+
const url = new URL(featureEndpoint());
|
|
29567
|
+
url.searchParams.set("status", status2);
|
|
29568
|
+
url.searchParams.set("limit", String(limit));
|
|
29569
|
+
if (priority) url.searchParams.set("priority", priority);
|
|
29570
|
+
const data = await requestJson(url.toString());
|
|
29571
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
29572
|
+
}
|
|
29573
|
+
);
|
|
29574
|
+
}
|
|
29575
|
+
function registerGetFeatureRequest(server2) {
|
|
29576
|
+
server2.registerTool(
|
|
29577
|
+
"get_feature_request",
|
|
29578
|
+
{
|
|
29579
|
+
title: "Get Feature Request",
|
|
29580
|
+
description: "AskExe-internal only: fetch one customer feature request with full payload.",
|
|
29581
|
+
inputSchema: { id: z94.string().min(8) }
|
|
29582
|
+
},
|
|
29583
|
+
async ({ id }) => {
|
|
29584
|
+
const data = await requestJson(`${featureEndpoint()}/${encodeURIComponent(id)}`);
|
|
29585
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
29586
|
+
}
|
|
29587
|
+
);
|
|
29588
|
+
}
|
|
29589
|
+
function registerTriageFeatureRequest(server2) {
|
|
29590
|
+
server2.registerTool(
|
|
29591
|
+
"triage_feature_request",
|
|
29592
|
+
{
|
|
29593
|
+
title: "Triage Feature Request",
|
|
29594
|
+
description: "AskExe-internal only: update feature request status, response notes, and version metadata.",
|
|
29595
|
+
inputSchema: {
|
|
29596
|
+
id: z94.string().min(8),
|
|
29597
|
+
status: FEATURE_STATUS.optional(),
|
|
29598
|
+
response_notes: z94.string().optional(),
|
|
29599
|
+
target_version: z94.string().optional(),
|
|
29600
|
+
shipped_version: z94.string().optional()
|
|
29601
|
+
}
|
|
29602
|
+
},
|
|
29603
|
+
async ({ id, status: status2, response_notes, target_version, shipped_version }) => {
|
|
29604
|
+
const data = await requestJson(`${featureEndpoint()}/${encodeURIComponent(id)}`, {
|
|
29605
|
+
method: "PATCH",
|
|
29606
|
+
body: JSON.stringify({ status: status2, response_notes, target_version, shipped_version })
|
|
29607
|
+
});
|
|
29608
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
29609
|
+
}
|
|
29610
|
+
);
|
|
29611
|
+
}
|
|
29007
29612
|
|
|
29008
29613
|
// src/mcp/tool-gates.ts
|
|
29009
29614
|
var TOOL_GATES = {
|
|
@@ -29327,6 +29932,7 @@ function registerAllTools(server2) {
|
|
|
29327
29932
|
gate("registerStoreDecision", registerStoreDecision);
|
|
29328
29933
|
gate("registerGetDecision", registerGetDecision);
|
|
29329
29934
|
gate("registerCreateBugReport", registerCreateBugReport);
|
|
29935
|
+
gate("registerCreateFeatureRequest", registerCreateFeatureRequest);
|
|
29330
29936
|
gate("registerSupportTools", registerSupportTools);
|
|
29331
29937
|
gate("registerCliParityTools", registerCliParityTools);
|
|
29332
29938
|
gate("registerGetSessionEvents", registerGetSessionEvents);
|
|
@@ -29336,6 +29942,9 @@ function registerAllTools(server2) {
|
|
|
29336
29942
|
gate("registerListBugReports", registerListBugReports);
|
|
29337
29943
|
gate("registerGetBugReport", registerGetBugReport);
|
|
29338
29944
|
gate("registerTriageBugReport", registerTriageBugReport);
|
|
29945
|
+
gate("registerListFeatureRequests", registerListFeatureRequests);
|
|
29946
|
+
gate("registerGetFeatureRequest", registerGetFeatureRequest);
|
|
29947
|
+
gate("registerTriageFeatureRequest", registerTriageFeatureRequest);
|
|
29339
29948
|
}
|
|
29340
29949
|
if (exposeLegacyConfig) {
|
|
29341
29950
|
gate("registerGetAgentSpend", registerGetAgentSpend);
|
|
@@ -29503,7 +30112,7 @@ try {
|
|
|
29503
30112
|
}
|
|
29504
30113
|
}, 3e4);
|
|
29505
30114
|
_ppidWatchdog.unref();
|
|
29506
|
-
const MCP_VERSION_PATH =
|
|
30115
|
+
const MCP_VERSION_PATH = path55.join(os20.homedir(), ".exe-os", "mcp-version");
|
|
29507
30116
|
let _currentMcpVersion = null;
|
|
29508
30117
|
try {
|
|
29509
30118
|
_currentMcpVersion = existsSync41(MCP_VERSION_PATH) ? readFileSync36(MCP_VERSION_PATH, "utf8").trim() : null;
|
|
@@ -29545,14 +30154,14 @@ try {
|
|
|
29545
30154
|
`
|
|
29546
30155
|
);
|
|
29547
30156
|
const thisFile = fileURLToPath7(import.meta.url);
|
|
29548
|
-
const backfillPath =
|
|
29549
|
-
|
|
30157
|
+
const backfillPath = path55.resolve(
|
|
30158
|
+
path55.dirname(thisFile),
|
|
29550
30159
|
"../bin/backfill-vectors.js"
|
|
29551
30160
|
);
|
|
29552
30161
|
if (existsSync41(backfillPath)) {
|
|
29553
30162
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
29554
|
-
const logPath =
|
|
29555
|
-
mkdirSync23(
|
|
30163
|
+
const logPath = path55.join(exeDir, "workers.log");
|
|
30164
|
+
mkdirSync23(path55.dirname(logPath), { recursive: true });
|
|
29556
30165
|
let logFd = "ignore";
|
|
29557
30166
|
try {
|
|
29558
30167
|
logFd = openSync4(logPath, "a");
|