@askexenow/exe-os 0.9.30 → 0.9.32

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.
Files changed (64) hide show
  1. package/dist/bin/backfill-conversations.js +135 -7
  2. package/dist/bin/backfill-responses.js +135 -7
  3. package/dist/bin/backfill-vectors.js +135 -7
  4. package/dist/bin/cleanup-stale-review-tasks.js +139 -11
  5. package/dist/bin/cli.js +812 -486
  6. package/dist/bin/exe-assign.js +135 -7
  7. package/dist/bin/exe-boot.js +422 -113
  8. package/dist/bin/exe-cloud.js +160 -9
  9. package/dist/bin/exe-dispatch.js +136 -8
  10. package/dist/bin/exe-doctor.js +255 -13
  11. package/dist/bin/exe-export-behaviors.js +136 -8
  12. package/dist/bin/exe-forget.js +136 -8
  13. package/dist/bin/exe-gateway.js +171 -24
  14. package/dist/bin/exe-heartbeat.js +141 -13
  15. package/dist/bin/exe-kill.js +140 -12
  16. package/dist/bin/exe-launch-agent.js +143 -15
  17. package/dist/bin/exe-link.js +357 -48
  18. package/dist/bin/exe-pending-messages.js +136 -8
  19. package/dist/bin/exe-pending-notifications.js +136 -8
  20. package/dist/bin/exe-pending-reviews.js +138 -10
  21. package/dist/bin/exe-review.js +136 -8
  22. package/dist/bin/exe-search.js +155 -20
  23. package/dist/bin/exe-session-cleanup.js +166 -38
  24. package/dist/bin/exe-start-codex.js +142 -14
  25. package/dist/bin/exe-start-opencode.js +140 -12
  26. package/dist/bin/exe-status.js +148 -20
  27. package/dist/bin/exe-team.js +136 -8
  28. package/dist/bin/git-sweep.js +138 -10
  29. package/dist/bin/graph-backfill.js +135 -7
  30. package/dist/bin/graph-export.js +136 -8
  31. package/dist/bin/intercom-check.js +153 -25
  32. package/dist/bin/scan-tasks.js +138 -10
  33. package/dist/bin/setup.js +447 -121
  34. package/dist/bin/shard-migrate.js +135 -7
  35. package/dist/gateway/index.js +151 -23
  36. package/dist/hooks/bug-report-worker.js +151 -23
  37. package/dist/hooks/codex-stop-task-finalizer.js +145 -17
  38. package/dist/hooks/commit-complete.js +138 -10
  39. package/dist/hooks/error-recall.js +159 -24
  40. package/dist/hooks/ingest.js +142 -14
  41. package/dist/hooks/instructions-loaded.js +136 -8
  42. package/dist/hooks/notification.js +136 -8
  43. package/dist/hooks/post-compact.js +136 -8
  44. package/dist/hooks/post-tool-combined.js +159 -24
  45. package/dist/hooks/pre-compact.js +136 -8
  46. package/dist/hooks/pre-tool-use.js +144 -16
  47. package/dist/hooks/prompt-submit.js +195 -55
  48. package/dist/hooks/session-end.js +141 -13
  49. package/dist/hooks/session-start.js +165 -30
  50. package/dist/hooks/stop.js +136 -8
  51. package/dist/hooks/subagent-stop.js +136 -8
  52. package/dist/hooks/summary-worker.js +374 -65
  53. package/dist/index.js +136 -8
  54. package/dist/lib/cloud-sync.js +355 -46
  55. package/dist/lib/consolidation.js +1 -0
  56. package/dist/lib/exe-daemon.js +469 -127
  57. package/dist/lib/hybrid-search.js +155 -20
  58. package/dist/lib/keychain.js +191 -7
  59. package/dist/lib/schedules.js +138 -10
  60. package/dist/lib/store.js +135 -7
  61. package/dist/mcp/server.js +706 -213
  62. package/dist/runtime/index.js +136 -8
  63. package/dist/tui/App.js +208 -31
  64. package/package.json +1 -1
@@ -1054,8 +1054,8 @@ function findPackageRoot() {
1054
1054
  function getAvailableMemoryGB() {
1055
1055
  if (process.platform === "darwin") {
1056
1056
  try {
1057
- const { execSync: execSync2 } = __require("child_process");
1058
- const vmstat = execSync2("vm_stat", { encoding: "utf8" });
1057
+ const { execSync: execSync3 } = __require("child_process");
1058
+ const vmstat = execSync3("vm_stat", { encoding: "utf8" });
1059
1059
  const pageSize = 16384;
1060
1060
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1061
1061
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -2794,6 +2794,7 @@ var init_database = __esm({
2794
2794
  // src/lib/keychain.ts
2795
2795
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2796
2796
  import { existsSync as existsSync6 } from "fs";
2797
+ import { execSync as execSync2 } from "child_process";
2797
2798
  import path6 from "path";
2798
2799
  import os5 from "os";
2799
2800
  function getKeyDir() {
@@ -2802,6 +2803,59 @@ function getKeyDir() {
2802
2803
  function getKeyPath() {
2803
2804
  return path6.join(getKeyDir(), "master.key");
2804
2805
  }
2806
+ function macKeychainGet() {
2807
+ if (process.platform !== "darwin") return null;
2808
+ try {
2809
+ return execSync2(
2810
+ `security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
2811
+ { encoding: "utf-8", timeout: 5e3 }
2812
+ ).trim();
2813
+ } catch {
2814
+ return null;
2815
+ }
2816
+ }
2817
+ function macKeychainSet(value) {
2818
+ if (process.platform !== "darwin") return false;
2819
+ try {
2820
+ try {
2821
+ execSync2(
2822
+ `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
2823
+ { timeout: 5e3 }
2824
+ );
2825
+ } catch {
2826
+ }
2827
+ execSync2(
2828
+ `security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
2829
+ { timeout: 5e3 }
2830
+ );
2831
+ return true;
2832
+ } catch {
2833
+ return false;
2834
+ }
2835
+ }
2836
+ function linuxSecretGet() {
2837
+ if (process.platform !== "linux") return null;
2838
+ try {
2839
+ return execSync2(
2840
+ `secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
2841
+ { encoding: "utf-8", timeout: 5e3 }
2842
+ ).trim();
2843
+ } catch {
2844
+ return null;
2845
+ }
2846
+ }
2847
+ function linuxSecretSet(value) {
2848
+ if (process.platform !== "linux") return false;
2849
+ try {
2850
+ execSync2(
2851
+ `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
2852
+ { timeout: 5e3 }
2853
+ );
2854
+ return true;
2855
+ } catch {
2856
+ return false;
2857
+ }
2858
+ }
2805
2859
  async function tryKeytar() {
2806
2860
  try {
2807
2861
  return await import("keytar");
@@ -2809,13 +2863,63 @@ async function tryKeytar() {
2809
2863
  return null;
2810
2864
  }
2811
2865
  }
2866
+ function deriveMachineKey() {
2867
+ try {
2868
+ const crypto2 = __require("crypto");
2869
+ const material = [
2870
+ os5.hostname(),
2871
+ os5.userInfo().username,
2872
+ os5.arch(),
2873
+ os5.platform(),
2874
+ // Machine ID on Linux (stable across reboots)
2875
+ process.platform === "linux" ? readMachineId() : ""
2876
+ ].join("|");
2877
+ return crypto2.createHash("sha256").update(material).digest();
2878
+ } catch {
2879
+ return null;
2880
+ }
2881
+ }
2882
+ function readMachineId() {
2883
+ try {
2884
+ const { readFileSync: readFileSync5 } = __require("fs");
2885
+ return readFileSync5("/etc/machine-id", "utf-8").trim();
2886
+ } catch {
2887
+ return "";
2888
+ }
2889
+ }
2890
+ function decryptWithMachineKey(encrypted, machineKey) {
2891
+ if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
2892
+ try {
2893
+ const crypto2 = __require("crypto");
2894
+ const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
2895
+ if (parts.length !== 3) return null;
2896
+ const [ivB64, tagB64, cipherB64] = parts;
2897
+ const iv = Buffer.from(ivB64, "base64");
2898
+ const authTag = Buffer.from(tagB64, "base64");
2899
+ const decipher = crypto2.createDecipheriv("aes-256-gcm", machineKey, iv);
2900
+ decipher.setAuthTag(authTag);
2901
+ let decrypted = decipher.update(cipherB64, "base64", "utf-8");
2902
+ decrypted += decipher.final("utf-8");
2903
+ return decrypted;
2904
+ } catch {
2905
+ return null;
2906
+ }
2907
+ }
2812
2908
  async function getMasterKey() {
2909
+ const nativeValue = macKeychainGet() ?? linuxSecretGet();
2910
+ if (nativeValue) {
2911
+ return Buffer.from(nativeValue, "base64");
2912
+ }
2813
2913
  const keytar = await tryKeytar();
2814
2914
  if (keytar) {
2815
2915
  try {
2816
- const stored = await keytar.getPassword(SERVICE, ACCOUNT);
2817
- if (stored) {
2818
- return Buffer.from(stored, "base64");
2916
+ const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
2917
+ if (keytarValue) {
2918
+ const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
2919
+ if (migrated) {
2920
+ process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
2921
+ }
2922
+ return Buffer.from(keytarValue, "base64");
2819
2923
  }
2820
2924
  } catch {
2821
2925
  }
@@ -2829,8 +2933,31 @@ async function getMasterKey() {
2829
2933
  return null;
2830
2934
  }
2831
2935
  try {
2832
- const content = await readFile3(keyPath, "utf-8");
2833
- return Buffer.from(content.trim(), "base64");
2936
+ const content = (await readFile3(keyPath, "utf-8")).trim();
2937
+ let b64Value;
2938
+ if (content.startsWith(ENCRYPTED_PREFIX)) {
2939
+ const machineKey = deriveMachineKey();
2940
+ if (!machineKey) {
2941
+ process.stderr.write("[keychain] Cannot derive machine key to decrypt stored key.\n");
2942
+ return null;
2943
+ }
2944
+ const decrypted = decryptWithMachineKey(content, machineKey);
2945
+ if (!decrypted) {
2946
+ process.stderr.write(
2947
+ "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
2948
+ );
2949
+ return null;
2950
+ }
2951
+ b64Value = decrypted;
2952
+ } else {
2953
+ b64Value = content;
2954
+ }
2955
+ const key = Buffer.from(b64Value, "base64");
2956
+ const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
2957
+ if (migrated) {
2958
+ process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
2959
+ }
2960
+ return key;
2834
2961
  } catch (err) {
2835
2962
  process.stderr.write(
2836
2963
  `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
@@ -2839,12 +2966,13 @@ async function getMasterKey() {
2839
2966
  return null;
2840
2967
  }
2841
2968
  }
2842
- var SERVICE, ACCOUNT;
2969
+ var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
2843
2970
  var init_keychain = __esm({
2844
2971
  "src/lib/keychain.ts"() {
2845
2972
  "use strict";
2846
2973
  SERVICE = "exe-mem";
2847
2974
  ACCOUNT = "master-key";
2975
+ ENCRYPTED_PREFIX = "enc:";
2848
2976
  }
2849
2977
  });
2850
2978
 
@@ -1084,8 +1084,8 @@ function findPackageRoot() {
1084
1084
  function getAvailableMemoryGB() {
1085
1085
  if (process.platform === "darwin") {
1086
1086
  try {
1087
- const { execSync: execSync4 } = __require("child_process");
1088
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1087
+ const { execSync: execSync5 } = __require("child_process");
1088
+ const vmstat = execSync5("vm_stat", { encoding: "utf8" });
1089
1089
  const pageSize = 16384;
1090
1090
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1091
1091
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -2769,6 +2769,7 @@ var init_database = __esm({
2769
2769
  // src/lib/keychain.ts
2770
2770
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2771
2771
  import { existsSync as existsSync6 } from "fs";
2772
+ import { execSync as execSync2 } from "child_process";
2772
2773
  import path6 from "path";
2773
2774
  import os5 from "os";
2774
2775
  function getKeyDir() {
@@ -2777,6 +2778,59 @@ function getKeyDir() {
2777
2778
  function getKeyPath() {
2778
2779
  return path6.join(getKeyDir(), "master.key");
2779
2780
  }
2781
+ function macKeychainGet() {
2782
+ if (process.platform !== "darwin") return null;
2783
+ try {
2784
+ return execSync2(
2785
+ `security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
2786
+ { encoding: "utf-8", timeout: 5e3 }
2787
+ ).trim();
2788
+ } catch {
2789
+ return null;
2790
+ }
2791
+ }
2792
+ function macKeychainSet(value) {
2793
+ if (process.platform !== "darwin") return false;
2794
+ try {
2795
+ try {
2796
+ execSync2(
2797
+ `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
2798
+ { timeout: 5e3 }
2799
+ );
2800
+ } catch {
2801
+ }
2802
+ execSync2(
2803
+ `security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
2804
+ { timeout: 5e3 }
2805
+ );
2806
+ return true;
2807
+ } catch {
2808
+ return false;
2809
+ }
2810
+ }
2811
+ function linuxSecretGet() {
2812
+ if (process.platform !== "linux") return null;
2813
+ try {
2814
+ return execSync2(
2815
+ `secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
2816
+ { encoding: "utf-8", timeout: 5e3 }
2817
+ ).trim();
2818
+ } catch {
2819
+ return null;
2820
+ }
2821
+ }
2822
+ function linuxSecretSet(value) {
2823
+ if (process.platform !== "linux") return false;
2824
+ try {
2825
+ execSync2(
2826
+ `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
2827
+ { timeout: 5e3 }
2828
+ );
2829
+ return true;
2830
+ } catch {
2831
+ return false;
2832
+ }
2833
+ }
2780
2834
  async function tryKeytar() {
2781
2835
  try {
2782
2836
  return await import("keytar");
@@ -2784,13 +2838,63 @@ async function tryKeytar() {
2784
2838
  return null;
2785
2839
  }
2786
2840
  }
2841
+ function deriveMachineKey() {
2842
+ try {
2843
+ const crypto3 = __require("crypto");
2844
+ const material = [
2845
+ os5.hostname(),
2846
+ os5.userInfo().username,
2847
+ os5.arch(),
2848
+ os5.platform(),
2849
+ // Machine ID on Linux (stable across reboots)
2850
+ process.platform === "linux" ? readMachineId() : ""
2851
+ ].join("|");
2852
+ return crypto3.createHash("sha256").update(material).digest();
2853
+ } catch {
2854
+ return null;
2855
+ }
2856
+ }
2857
+ function readMachineId() {
2858
+ try {
2859
+ const { readFileSync: readFileSync6 } = __require("fs");
2860
+ return readFileSync6("/etc/machine-id", "utf-8").trim();
2861
+ } catch {
2862
+ return "";
2863
+ }
2864
+ }
2865
+ function decryptWithMachineKey(encrypted, machineKey) {
2866
+ if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
2867
+ try {
2868
+ const crypto3 = __require("crypto");
2869
+ const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
2870
+ if (parts.length !== 3) return null;
2871
+ const [ivB64, tagB64, cipherB64] = parts;
2872
+ const iv = Buffer.from(ivB64, "base64");
2873
+ const authTag = Buffer.from(tagB64, "base64");
2874
+ const decipher = crypto3.createDecipheriv("aes-256-gcm", machineKey, iv);
2875
+ decipher.setAuthTag(authTag);
2876
+ let decrypted = decipher.update(cipherB64, "base64", "utf-8");
2877
+ decrypted += decipher.final("utf-8");
2878
+ return decrypted;
2879
+ } catch {
2880
+ return null;
2881
+ }
2882
+ }
2787
2883
  async function getMasterKey() {
2884
+ const nativeValue = macKeychainGet() ?? linuxSecretGet();
2885
+ if (nativeValue) {
2886
+ return Buffer.from(nativeValue, "base64");
2887
+ }
2788
2888
  const keytar = await tryKeytar();
2789
2889
  if (keytar) {
2790
2890
  try {
2791
- const stored = await keytar.getPassword(SERVICE, ACCOUNT);
2792
- if (stored) {
2793
- return Buffer.from(stored, "base64");
2891
+ const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
2892
+ if (keytarValue) {
2893
+ const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
2894
+ if (migrated) {
2895
+ process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
2896
+ }
2897
+ return Buffer.from(keytarValue, "base64");
2794
2898
  }
2795
2899
  } catch {
2796
2900
  }
@@ -2804,8 +2908,31 @@ async function getMasterKey() {
2804
2908
  return null;
2805
2909
  }
2806
2910
  try {
2807
- const content = await readFile3(keyPath, "utf-8");
2808
- return Buffer.from(content.trim(), "base64");
2911
+ const content = (await readFile3(keyPath, "utf-8")).trim();
2912
+ let b64Value;
2913
+ if (content.startsWith(ENCRYPTED_PREFIX)) {
2914
+ const machineKey = deriveMachineKey();
2915
+ if (!machineKey) {
2916
+ process.stderr.write("[keychain] Cannot derive machine key to decrypt stored key.\n");
2917
+ return null;
2918
+ }
2919
+ const decrypted = decryptWithMachineKey(content, machineKey);
2920
+ if (!decrypted) {
2921
+ process.stderr.write(
2922
+ "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
2923
+ );
2924
+ return null;
2925
+ }
2926
+ b64Value = decrypted;
2927
+ } else {
2928
+ b64Value = content;
2929
+ }
2930
+ const key = Buffer.from(b64Value, "base64");
2931
+ const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
2932
+ if (migrated) {
2933
+ process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
2934
+ }
2935
+ return key;
2809
2936
  } catch (err) {
2810
2937
  process.stderr.write(
2811
2938
  `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
@@ -2814,12 +2941,13 @@ async function getMasterKey() {
2814
2941
  return null;
2815
2942
  }
2816
2943
  }
2817
- var SERVICE, ACCOUNT;
2944
+ var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
2818
2945
  var init_keychain = __esm({
2819
2946
  "src/lib/keychain.ts"() {
2820
2947
  "use strict";
2821
2948
  SERVICE = "exe-mem";
2822
2949
  ACCOUNT = "master-key";
2950
+ ENCRYPTED_PREFIX = "enc:";
2823
2951
  }
2824
2952
  });
2825
2953
 
@@ -4342,7 +4470,7 @@ __export(project_name_exports, {
4342
4470
  _resetCache: () => _resetCache,
4343
4471
  getProjectName: () => getProjectName
4344
4472
  });
4345
- import { execSync as execSync2 } from "child_process";
4473
+ import { execSync as execSync3 } from "child_process";
4346
4474
  import path9 from "path";
4347
4475
  function getProjectName(cwd) {
4348
4476
  const dir = cwd ?? process.cwd();
@@ -4350,7 +4478,7 @@ function getProjectName(cwd) {
4350
4478
  try {
4351
4479
  let repoRoot;
4352
4480
  try {
4353
- const gitCommonDir = execSync2("git rev-parse --path-format=absolute --git-common-dir", {
4481
+ const gitCommonDir = execSync3("git rev-parse --path-format=absolute --git-common-dir", {
4354
4482
  cwd: dir,
4355
4483
  encoding: "utf8",
4356
4484
  timeout: 2e3,
@@ -4358,7 +4486,7 @@ function getProjectName(cwd) {
4358
4486
  }).trim();
4359
4487
  repoRoot = path9.dirname(gitCommonDir);
4360
4488
  } catch {
4361
- repoRoot = execSync2("git rev-parse --show-toplevel", {
4489
+ repoRoot = execSync3("git rev-parse --show-toplevel", {
4362
4490
  cwd: dir,
4363
4491
  encoding: "utf8",
4364
4492
  timeout: 2e3,
@@ -4392,14 +4520,14 @@ var file_grep_exports = {};
4392
4520
  __export(file_grep_exports, {
4393
4521
  grepProjectFiles: () => grepProjectFiles
4394
4522
  });
4395
- import { execSync as execSync3 } from "child_process";
4523
+ import { execSync as execSync4 } from "child_process";
4396
4524
  import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync9 } from "fs";
4397
4525
  import path10 from "path";
4398
4526
  import crypto2 from "crypto";
4399
4527
  function hasRipgrep() {
4400
4528
  if (_hasRg === null) {
4401
4529
  try {
4402
- execSync3("rg --version", { stdio: "ignore", timeout: 2e3 });
4530
+ execSync4("rg --version", { stdio: "ignore", timeout: 2e3 });
4403
4531
  _hasRg = true;
4404
4532
  } catch {
4405
4533
  _hasRg = false;
@@ -4465,7 +4593,7 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
4465
4593
  const globs = (patterns ?? DEFAULT_PATTERNS).map((p) => `--glob '${p}'`).join(" ");
4466
4594
  const excludes = EXCLUDE_DIRS.map((d) => `--glob '!${d}'`).join(" ");
4467
4595
  const cmd = `rg -i -c --hidden --no-config --no-ignore '${pattern.replace(/'/g, "\\'")}' . ${globs} ${excludes} --max-filesize ${MAX_FILE_SIZE} 2>/dev/null || true`;
4468
- const output = execSync3(cmd, {
4596
+ const output = execSync4(cmd, {
4469
4597
  cwd: projectRoot,
4470
4598
  encoding: "utf8",
4471
4599
  timeout: 3e3,
@@ -4480,12 +4608,12 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
4480
4608
  const matchCount = parseInt(line.slice(colonIdx + 1));
4481
4609
  if (isNaN(matchCount) || matchCount === 0) continue;
4482
4610
  try {
4483
- const firstMatch = execSync3(
4611
+ const firstMatch = execSync4(
4484
4612
  `rg -i -n --hidden '${pattern.replace(/'/g, "\\'")}' '${filePath}' --max-count 1 2>/dev/null | head -1`,
4485
4613
  { cwd: projectRoot, encoding: "utf8", timeout: 1e3 }
4486
4614
  ).trim();
4487
4615
  const lineNum = parseInt(firstMatch.split(":")[0] ?? "1");
4488
- const totalLines = execSync3(`wc -l < '${filePath}'`, {
4616
+ const totalLines = execSync4(`wc -l < '${filePath}'`, {
4489
4617
  cwd: projectRoot,
4490
4618
  encoding: "utf8",
4491
4619
  timeout: 1e3
@@ -5114,10 +5242,17 @@ async function applyEntityBoost(results, query, client) {
5114
5242
  if (ENTITY_BOOST_WEIGHT === 0 || results.length === 0) {
5115
5243
  return emptyResult;
5116
5244
  }
5117
- console.time("entity-boost");
5245
+ const debugStart = process.env.EXE_DEBUG_HOOKS ? performance.now() : 0;
5246
+ const debugEnd = () => {
5247
+ if (!process.env.EXE_DEBUG_HOOKS) return;
5248
+ process.stderr.write(
5249
+ `[entity-boost] ${(performance.now() - debugStart).toFixed(3)}ms
5250
+ `
5251
+ );
5252
+ };
5118
5253
  const entities = await matchEntities(query, client);
5119
5254
  if (entities.length === 0) {
5120
- console.timeEnd("entity-boost");
5255
+ debugEnd();
5121
5256
  return emptyResult;
5122
5257
  }
5123
5258
  const boostMap = /* @__PURE__ */ new Map();
@@ -5139,7 +5274,7 @@ async function applyEntityBoost(results, query, client) {
5139
5274
  await traverseAndScore(entities, client, boostMap, resultIds, graphContextMap);
5140
5275
  await applyHyperedgeBoost(entities, client, boostMap, resultIds);
5141
5276
  if (boostMap.size === 0) {
5142
- console.timeEnd("entity-boost");
5277
+ debugEnd();
5143
5278
  return emptyResult;
5144
5279
  }
5145
5280
  const scored = results.map((r, i) => ({
@@ -5150,7 +5285,7 @@ async function applyEntityBoost(results, query, client) {
5150
5285
  scored.sort(
5151
5286
  (a, b) => b.baseScore + b.entityBoost - (a.baseScore + a.entityBoost)
5152
5287
  );
5153
- console.timeEnd("entity-boost");
5288
+ debugEnd();
5154
5289
  return {
5155
5290
  results: scored.map((s) => s.record),
5156
5291
  graphContext: graphContextMap