@askexenow/exe-os 0.9.294 → 0.9.296

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 (279) hide show
  1. package/deploy/compose/cloudflared/config.yml.example +14 -9
  2. package/deploy/compose/docker-compose.yml +86 -8
  3. package/deploy/compose/sso-edge/default.conf.template +87 -0
  4. package/deploy/compose/sso-edge/entrypoint.sh +23 -0
  5. package/deploy/compose/sso-edge/sso-redirect.conf +63 -0
  6. package/deploy/stack-manifests/v0.9.json +2 -2
  7. package/dist/active-agent-AFX2FODG.js +28 -0
  8. package/dist/active-agent-E2IJA7YX.js +27 -0
  9. package/dist/agentic-ontology-A2YUZK5O.js +25 -0
  10. package/dist/assets/com.askexe.exed.plist +4 -1
  11. package/dist/backfill-metadata-OC7EOD5U.js +600 -0
  12. package/dist/behaviors-H5ZOVHDH.js +46 -0
  13. package/dist/bin/agentic-ontology-backfill.js +5 -5
  14. package/dist/bin/agentic-reflection-backfill.js +6 -6
  15. package/dist/bin/agentic-semantic-label.js +5 -5
  16. package/dist/bin/backfill-conversations.js +6 -6
  17. package/dist/bin/backfill-responses.js +6 -6
  18. package/dist/bin/backfill-vectors.js +8 -8
  19. package/dist/bin/bulk-sync-postgres.js +7 -7
  20. package/dist/bin/cc-doctor.js +4 -4
  21. package/dist/bin/cleanup-stale-review-tasks.js +11 -11
  22. package/dist/bin/cli.js +16 -16
  23. package/dist/bin/deferred-daemon-restart.js +1 -1
  24. package/dist/bin/exe-agent-config.js +2 -2
  25. package/dist/bin/exe-agent.js +4 -4
  26. package/dist/bin/exe-assign.js +8 -8
  27. package/dist/bin/exe-boot.js +21 -18
  28. package/dist/bin/exe-call.js +4 -4
  29. package/dist/bin/exe-cloud.js +7 -7
  30. package/dist/bin/exe-dispatch.js +11 -11
  31. package/dist/bin/exe-doctor.js +3 -2
  32. package/dist/bin/exe-export-behaviors.js +7 -7
  33. package/dist/bin/exe-forget.js +6 -6
  34. package/dist/bin/exe-gateway.js +7 -7
  35. package/dist/bin/exe-healthcheck.js +6 -4
  36. package/dist/bin/exe-heartbeat.js +11 -11
  37. package/dist/bin/exe-kill.js +14 -14
  38. package/dist/bin/exe-launch-agent.js +18 -18
  39. package/dist/bin/exe-new-employee.js +6 -6
  40. package/dist/bin/exe-pending-messages.js +12 -12
  41. package/dist/bin/exe-pending-notifications.js +11 -11
  42. package/dist/bin/exe-pending-reviews.js +11 -11
  43. package/dist/bin/exe-rename.js +4 -4
  44. package/dist/bin/exe-review.js +13 -13
  45. package/dist/bin/exe-search.js +5 -5
  46. package/dist/bin/exe-session-cleanup.js +16 -16
  47. package/dist/bin/exe-settings.js +39 -9
  48. package/dist/bin/exe-start-codex.js +11 -11
  49. package/dist/bin/exe-start-opencode.js +8 -8
  50. package/dist/bin/exe-status.js +12 -12
  51. package/dist/bin/exe-team.js +3 -3
  52. package/dist/bin/git-sweep.js +12 -12
  53. package/dist/bin/graph-backfill.js +4 -4
  54. package/dist/bin/graph-export.js +5 -5
  55. package/dist/bin/import-history.js +7 -7
  56. package/dist/bin/install-launchd.js +13 -6
  57. package/dist/bin/install.js +26 -14
  58. package/dist/bin/intercom-check.js +4 -4
  59. package/dist/bin/mcp-sessions.js +2 -2
  60. package/dist/bin/orchestration-metrics.js +4 -4
  61. package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
  62. package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
  63. package/dist/bin/scan-tasks.js +11 -11
  64. package/dist/bin/setup.js +1 -1
  65. package/dist/bin/shard-migrate.js +4 -4
  66. package/dist/bin/stack-update.js +2 -2
  67. package/dist/bin/vps-health-gate.js +1 -1
  68. package/dist/capability-cards-4USI7CUW.js +89 -0
  69. package/dist/capacity-monitor-WLCBTEYR.js +51 -0
  70. package/dist/catchup-brief-ZR3NX6LZ.js +175 -0
  71. package/dist/chunk-22TVSRQQ.js +226 -0
  72. package/dist/chunk-2E43UXRH.js +395 -0
  73. package/dist/chunk-2PIGT6UJ.js +460 -0
  74. package/dist/chunk-3XTMW2MZ.js +535 -0
  75. package/dist/chunk-465PQFTH.js +262 -0
  76. package/dist/chunk-5CCXU2AW.js +129 -0
  77. package/dist/chunk-5D6MPWR7.js +1094 -0
  78. package/dist/chunk-5Q4MR6SL.js +123 -0
  79. package/dist/chunk-6327RBWR.js +345 -0
  80. package/dist/chunk-6MZZREZY.js +199 -0
  81. package/dist/chunk-7DI2Q4O5.js +1186 -0
  82. package/dist/chunk-7PW5VNIY.js +122 -0
  83. package/dist/chunk-7T7Y56HW.js +43 -0
  84. package/dist/chunk-7UHCWCLT.js +128 -0
  85. package/dist/chunk-A2ZUMF6L.js +1350 -0
  86. package/dist/chunk-AKV44JEH.js +185 -0
  87. package/dist/chunk-ANHWGX5N.js +735 -0
  88. package/dist/chunk-BQ3P4TKD.js +97 -0
  89. package/dist/chunk-BUZMT3KZ.js +604 -0
  90. package/dist/chunk-C2SBESBO.js +210 -0
  91. package/dist/chunk-CLSXZUZW.js +51 -0
  92. package/dist/chunk-CONHLVAR.js +1079 -0
  93. package/dist/chunk-D3WTZPFX.js +456 -0
  94. package/dist/chunk-DE6SOIYL.js +197 -0
  95. package/dist/chunk-EIVNMA3Q.js +284 -0
  96. package/dist/chunk-EJIF4FNT.js +12 -0
  97. package/dist/chunk-FDFOW564.js +171 -0
  98. package/dist/chunk-GZUBJ5EC.js +127 -0
  99. package/dist/chunk-HGZITN22.js +105 -0
  100. package/dist/chunk-HSRKDU6X.js +362 -0
  101. package/dist/chunk-IIEN2PHV.js +85 -0
  102. package/dist/chunk-JQ56VLMM.js +567 -0
  103. package/dist/chunk-JVHHXRFY.js +280 -0
  104. package/dist/chunk-JXCXGZ3S.js +55 -0
  105. package/dist/chunk-K5ZO532Q.js +4388 -0
  106. package/dist/chunk-K6CAAMXF.js +97 -0
  107. package/dist/chunk-KA26YTNU.js +81 -0
  108. package/dist/chunk-KMUW5C3R.js +381 -0
  109. package/dist/chunk-KOO3J5PV.js +20 -0
  110. package/dist/chunk-LSV7OFIH.js +290 -0
  111. package/dist/chunk-LSVFDVNY.js +1158 -0
  112. package/dist/chunk-LXDQTW32.js +230 -0
  113. package/dist/chunk-MEP7OUVZ.js +181 -0
  114. package/dist/chunk-MN2B2LKS.js +240 -0
  115. package/dist/chunk-N2EAYPYQ.js +1352 -0
  116. package/dist/chunk-N7I2A667.js +70 -0
  117. package/dist/chunk-NLZHVIOP.js +630 -0
  118. package/dist/chunk-NUH5TRZL.js +227 -0
  119. package/dist/chunk-OAHEIH3G.js +167 -0
  120. package/dist/chunk-OBHRQGCK.js +58 -0
  121. package/dist/chunk-ODFA7B2V.js +54 -0
  122. package/dist/chunk-OSNUP45F.js +731 -0
  123. package/dist/chunk-OTPRHBTO.js +33 -0
  124. package/dist/chunk-P6MUA4QU.js +157 -0
  125. package/dist/chunk-PGIOFKSK.js +2093 -0
  126. package/dist/chunk-PSE7VHWK.js +50 -0
  127. package/dist/chunk-QIFUVZFW.js +331 -0
  128. package/dist/chunk-RDPXKTVK.js +221 -0
  129. package/dist/chunk-RKYTYJGB.js +76 -0
  130. package/dist/chunk-RXLR6EFM.js +348 -0
  131. package/dist/chunk-SDB67PQJ.js +159 -0
  132. package/dist/chunk-SF2T7MP3.js +402 -0
  133. package/dist/chunk-SLU3FRFQ.js +2133 -0
  134. package/dist/chunk-SNDZJ5IV.js +214 -0
  135. package/dist/chunk-STEEAABW.js +448 -0
  136. package/dist/chunk-TUTWNHIQ.js +244 -0
  137. package/dist/chunk-UDP35QBR.js +30 -0
  138. package/dist/chunk-UKFHNJBI.js +85 -0
  139. package/dist/chunk-VC2DTK2X.js +382 -0
  140. package/dist/chunk-VRRAE5JX.js +836 -0
  141. package/dist/chunk-VVJTBQPR.js +38 -0
  142. package/dist/chunk-W3EQ362K.js +581 -0
  143. package/dist/chunk-WHIXIFHC.js +2242 -0
  144. package/dist/chunk-WRNGJJNR.js +377 -0
  145. package/dist/chunk-WUKHLCBE.js +3313 -0
  146. package/dist/chunk-WVPLHGDG.js +150 -0
  147. package/dist/chunk-XJZBSTL5.js +204 -0
  148. package/dist/chunk-Y3PMNUM5.js +304 -0
  149. package/dist/chunk-YHVS4QOV.js +14597 -0
  150. package/dist/chunk-YJ2OYAOC.js +668 -0
  151. package/dist/chunk-YYAD2GXX.js +128 -0
  152. package/dist/chunk-ZQML7EWE.js +333 -0
  153. package/dist/co-activation-XJLH46OX.js +74 -0
  154. package/dist/co-occurrence-GNN2X526.js +95 -0
  155. package/dist/code-context-index-OCPRLFG5.js +30 -0
  156. package/dist/core-memory-J4W2IYOF.js +110 -0
  157. package/dist/crdt-sync-QCBTSHIH.js +33 -0
  158. package/dist/crm-webhook-EM442VUW.js +10 -0
  159. package/dist/cto-delegation-gate-MLJMVHBK.js +280 -0
  160. package/dist/daemon-orchestration-2VNLZVTW.js +139 -0
  161. package/dist/db-backup-VUGFTPJ4.js +43 -0
  162. package/dist/doc-graph-extractor-PNRSFPSS.js +133 -0
  163. package/dist/dreaming-SK5VEQRF.js +34 -0
  164. package/dist/entity-boost-TQWWJUC2.js +375 -0
  165. package/dist/exe-drift-N34UPO7S.js +70 -0
  166. package/dist/exe-export-KACBKGVV.js +77 -0
  167. package/dist/exe-import-GXGDWACG.js +80 -0
  168. package/dist/exe-key-XPDOZBWW.js +673 -0
  169. package/dist/exe-snapshot-32GQKGQ5.js +338 -0
  170. package/dist/fast-db-init-F3TDD5VV.js +7 -0
  171. package/dist/gateway/index.js +8 -8
  172. package/dist/git-staleness-J45WNYRF.js +112 -0
  173. package/dist/git-task-sweep-BTGVQPFB.js +42 -0
  174. package/dist/global-procedures-6JCQWU4D.js +22 -0
  175. package/dist/graph-auto-extract-3ZQNXTPC.js +183 -0
  176. package/dist/hooks/bug-report-worker.js +13 -13
  177. package/dist/hooks/codex-stop-task-finalizer.js +13 -13
  178. package/dist/hooks/commit-complete.js +13 -13
  179. package/dist/hooks/error-recall.js +6 -6
  180. package/dist/hooks/exe-heartbeat-hook.js +3 -3
  181. package/dist/hooks/ingest-worker.js +3 -3
  182. package/dist/hooks/ingest.js +6 -6
  183. package/dist/hooks/instructions-loaded.js +4 -4
  184. package/dist/hooks/manifest.json +20 -20
  185. package/dist/hooks/notification.js +4 -4
  186. package/dist/hooks/post-compact.js +12 -12
  187. package/dist/hooks/post-tool-combined.js +6 -6
  188. package/dist/hooks/pre-compact.js +16 -16
  189. package/dist/hooks/pre-tool-use.js +16 -16
  190. package/dist/hooks/prompt-submit.js +24 -24
  191. package/dist/hooks/session-end.js +21 -21
  192. package/dist/hooks/session-start.js +12 -12
  193. package/dist/hooks/stop.js +19 -19
  194. package/dist/hooks/subagent-stop.js +12 -12
  195. package/dist/hooks/summary-worker.js +19 -19
  196. package/dist/index.js +19 -19
  197. package/dist/installer-5VPFY7SB.js +298 -0
  198. package/dist/installer-OENFPMA2.js +344 -0
  199. package/dist/installer-OIX4QOG5.js +40 -0
  200. package/dist/lib/cloud-sync.js +7 -7
  201. package/dist/lib/consolidation.js +6 -5
  202. package/dist/lib/database.js +2 -2
  203. package/dist/lib/db-daemon-client.js +2 -2
  204. package/dist/lib/db.js +2 -2
  205. package/dist/lib/embed-worker.js +1 -0
  206. package/dist/lib/embedder.js +7 -3
  207. package/dist/lib/employee-templates.js +4 -4
  208. package/dist/lib/employees.js +2 -2
  209. package/dist/lib/exe-daemon-client.js +2 -2
  210. package/dist/lib/exe-daemon.js +160 -79
  211. package/dist/lib/hybrid-search.js +5 -5
  212. package/dist/lib/identity.js +2 -2
  213. package/dist/lib/messaging.js +11 -11
  214. package/dist/lib/reminders.js +3 -3
  215. package/dist/lib/schedules.js +5 -5
  216. package/dist/lib/session-registry.js +4 -4
  217. package/dist/lib/skill-learning.js +6 -6
  218. package/dist/lib/store.js +4 -4
  219. package/dist/lib/task-router.js +3 -3
  220. package/dist/lib/tasks.js +12 -12
  221. package/dist/lib/tmux-routing.js +12 -10
  222. package/dist/lib/tmux-transport.js +1 -1
  223. package/dist/lib/token-spend.js +3 -3
  224. package/dist/lib/transport.js +2 -2
  225. package/dist/mcp/register-tools.js +62 -61
  226. package/dist/mcp/server.js +63 -62
  227. package/dist/mcp/tools/complete-reminder.js +4 -4
  228. package/dist/mcp/tools/create-reminder.js +4 -4
  229. package/dist/mcp/tools/create-task.js +14 -14
  230. package/dist/mcp/tools/deactivate-behavior.js +7 -7
  231. package/dist/mcp/tools/list-reminders.js +4 -4
  232. package/dist/mcp/tools/list-tasks.js +14 -14
  233. package/dist/mcp/tools/send-message.js +13 -13
  234. package/dist/mcp/tools/update-task.js +13 -13
  235. package/dist/mcp-http-config-PQTOLCTP.js +29 -0
  236. package/dist/memory-cards-4RVDZIY2.js +180 -0
  237. package/dist/memory-graph-extractor-L6YC7G4M.js +22 -0
  238. package/dist/memory-poisoning-defense-4YVJYH4G.js +224 -0
  239. package/dist/memory-queue-client-MVAUOZNJ.js +16 -0
  240. package/dist/memory-reflection-SHHDQNOH.js +244 -0
  241. package/dist/message-queue-client-DCKZT6X2.js +92 -0
  242. package/dist/notifications-JFR3G42W.js +47 -0
  243. package/dist/orchestration-events-MGCGPTDN.js +27 -0
  244. package/dist/orchestrator-DAFL2YZB.js +35 -0
  245. package/dist/pipeline-router-WWSZVPCH.js +15 -0
  246. package/dist/plan-limits-C7XCSDZC.js +28 -0
  247. package/dist/project-boot-N3NTBVLE.js +299 -0
  248. package/dist/projection-worker-MTPAPCWX.js +1084 -0
  249. package/dist/prospective-memory-BTIVUJSB.js +232 -0
  250. package/dist/reranker-UA6WVESJ.js +19 -0
  251. package/dist/retrieval-health-7XNZJEBF.js +12 -0
  252. package/dist/review-polling-4ALGMXC3.js +126 -0
  253. package/dist/runtime/index.js +13 -13
  254. package/dist/self-query-router-MROFQLQB.js +192 -0
  255. package/dist/session-events-CK44XOU4.js +38 -0
  256. package/dist/session-kill-telemetry-MT6ITDOG.js +31 -0
  257. package/dist/session-scope-3XDBWV65.js +88 -0
  258. package/dist/setup-wizard-X6DOD7MC.js +12 -0
  259. package/dist/skill-refinement-G2CCY3GM.js +159 -0
  260. package/dist/stack-update-JF7F56AS.js +84 -0
  261. package/dist/steward-gate-YF2CYXE7.js +15 -0
  262. package/dist/task-enforcement-YN6HK7NE.js +506 -0
  263. package/dist/task-scope-CVK6ISCZ.js +37 -0
  264. package/dist/tasks-crud-NTNET4JE.js +79 -0
  265. package/dist/tasks-notify-4LJVFPCV.js +40 -0
  266. package/dist/tasks-review-3V4WOIRG.js +49 -0
  267. package/dist/telemetry-upload-5PNUKGTM.js +741 -0
  268. package/dist/token-budget-E46G7ZAQ.js +86 -0
  269. package/dist/tool-capability-index-JDSMKJER.js +10 -0
  270. package/dist/tool-telemetry-J3NLS3LJ.js +17 -0
  271. package/dist/tui/App.js +18 -18
  272. package/dist/tui-data-6DOMUUCM.js +260 -0
  273. package/dist/wiki-acl-5UK37LKF.js +111 -0
  274. package/dist/worker-gate-FM7AEC7G.js +21 -0
  275. package/dist/workflow-engine-2EDUHUIY.js +28 -0
  276. package/dist/worktree-7YKKJIYR.js +28 -0
  277. package/dist/worktree-sweep-C3ELFGDN.js +21 -0
  278. package/package.json +1 -1
  279. package/release-notes.json +88 -88
@@ -0,0 +1,97 @@
1
+ import {
2
+ listBehaviors
3
+ } from "./chunk-QIFUVZFW.js";
4
+ import {
5
+ loadConfigSync
6
+ } from "./chunk-R36FAN53.js";
7
+
8
+ // src/lib/behaviors-export.ts
9
+ import os from "os";
10
+ import path from "path";
11
+ import {
12
+ existsSync,
13
+ mkdirSync,
14
+ readdirSync,
15
+ statSync,
16
+ unlinkSync,
17
+ writeFileSync
18
+ } from "fs";
19
+ var BEHAVIORS_EXPORT_DIR = path.join(
20
+ os.homedir(),
21
+ ".exe-os",
22
+ "behaviors-export"
23
+ );
24
+ var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
25
+ var DEFAULT_BEHAVIOR_LIMIT = 30;
26
+ function getBehaviorLimit() {
27
+ try {
28
+ const cfg = loadConfigSync();
29
+ return cfg.behaviorExportLimit ?? DEFAULT_BEHAVIOR_LIMIT;
30
+ } catch {
31
+ return DEFAULT_BEHAVIOR_LIMIT;
32
+ }
33
+ }
34
+ function sweepStaleBehaviorExports(now = Date.now()) {
35
+ if (!existsSync(BEHAVIORS_EXPORT_DIR)) return;
36
+ let entries;
37
+ try {
38
+ entries = readdirSync(BEHAVIORS_EXPORT_DIR);
39
+ } catch {
40
+ return;
41
+ }
42
+ for (const entry of entries) {
43
+ const filePath = path.join(BEHAVIORS_EXPORT_DIR, entry);
44
+ try {
45
+ const stat = statSync(filePath);
46
+ if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
47
+ unlinkSync(filePath);
48
+ }
49
+ } catch {
50
+ }
51
+ }
52
+ }
53
+ function renderBehaviorLine(row) {
54
+ const tag = row.domain ? `[${row.domain}] ` : "";
55
+ const scope = row.project_name ? ` (${row.project_name})` : "";
56
+ return `- ${tag}${row.content}${scope}`;
57
+ }
58
+ function renderBehaviorExport(behaviors) {
59
+ if (behaviors.length === 0) {
60
+ return "";
61
+ }
62
+ const lines = behaviors.map(renderBehaviorLine);
63
+ return [
64
+ "<system-reminder>",
65
+ "## Your Behavioral Memory",
66
+ "Validated patterns and corrections for this session. Follow them.",
67
+ "Sourced from exe-os Layer 2 (Expertise). Changes apply on next spawn.",
68
+ "",
69
+ ...lines,
70
+ "</system-reminder>",
71
+ ""
72
+ ].join("\n");
73
+ }
74
+ function exportFilePath(agentId, projectName, sessionKey) {
75
+ if (!sessionKey) {
76
+ return path.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
77
+ }
78
+ const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
79
+ return path.join(
80
+ BEHAVIORS_EXPORT_DIR,
81
+ `${agentId}-${safeProject}-${sessionKey}.md`
82
+ );
83
+ }
84
+ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
85
+ mkdirSync(BEHAVIORS_EXPORT_DIR, { recursive: true });
86
+ sweepStaleBehaviorExports();
87
+ const behaviors = await listBehaviors(agentId, projectName, getBehaviorLimit());
88
+ if (behaviors.length === 0) return null;
89
+ const body = renderBehaviorExport(behaviors);
90
+ const target = exportFilePath(agentId, projectName, sessionKey);
91
+ writeFileSync(target, body, "utf-8");
92
+ return target;
93
+ }
94
+
95
+ export {
96
+ exportBehaviorsForAgent
97
+ };
@@ -0,0 +1,604 @@
1
+ import {
2
+ chunkSourceFile,
3
+ isChunkable,
4
+ languageForFile,
5
+ summarizeChunk
6
+ } from "./chunk-I7AW4237.js";
7
+ import {
8
+ connectEmbedDaemon,
9
+ embedBatchViaClient
10
+ } from "./chunk-W3EQ362K.js";
11
+ import {
12
+ EXE_AI_DIR
13
+ } from "./chunk-R36FAN53.js";
14
+
15
+ // src/lib/code-context-index.ts
16
+ import crypto from "crypto";
17
+ import path from "path";
18
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
19
+ import { spawnSync } from "child_process";
20
+ var VECTOR_STORE_VERSION = 1;
21
+ var EMBED_BATCH_SIZE = 64;
22
+ function vectorStorePath(projectRoot) {
23
+ const rootHash = hashText(projectRoot).slice(0, 16);
24
+ return path.join(indexDir(), `${rootHash}.vectors.json`);
25
+ }
26
+ function loadVectorStore(projectRoot) {
27
+ const file = vectorStorePath(projectRoot);
28
+ if (!existsSync(file)) return null;
29
+ try {
30
+ const parsed = JSON.parse(readFileSync(file, "utf8"));
31
+ if (parsed.version !== VECTOR_STORE_VERSION) return null;
32
+ return parsed;
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+ function saveVectorStore(projectRoot, store) {
38
+ writeFileSync(vectorStorePath(projectRoot), JSON.stringify(store));
39
+ }
40
+ function cosineSimilarity(a, b) {
41
+ let dot = 0, normA = 0, normB = 0;
42
+ for (let i = 0; i < a.length; i++) {
43
+ dot += a[i] * b[i];
44
+ normA += a[i] * a[i];
45
+ normB += b[i] * b[i];
46
+ }
47
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
48
+ return denom === 0 ? 0 : dot / denom;
49
+ }
50
+ async function embedSymbols(index) {
51
+ const rootHash = hashText(index.projectRoot).slice(0, 16);
52
+ const existing = loadVectorStore(index.projectRoot);
53
+ const store = {
54
+ version: VECTOR_STORE_VERSION,
55
+ projectRootHash: rootHash,
56
+ vectors: {}
57
+ };
58
+ const allSymbols = [];
59
+ for (const file of Object.values(index.files)) {
60
+ for (const symbol of file.symbols) {
61
+ if (existing?.vectors[symbol.id]) {
62
+ store.vectors[symbol.id] = existing.vectors[symbol.id];
63
+ } else {
64
+ allSymbols.push({ id: symbol.id, text: symbol.summary || `${symbol.kind} ${symbol.name} in ${symbol.filePath}` });
65
+ }
66
+ }
67
+ }
68
+ if (allSymbols.length === 0) {
69
+ saveVectorStore(index.projectRoot, store);
70
+ return store;
71
+ }
72
+ const connected = await connectEmbedDaemon().catch(() => false);
73
+ if (!connected) {
74
+ saveVectorStore(index.projectRoot, store);
75
+ return store;
76
+ }
77
+ for (let i = 0; i < allSymbols.length; i += EMBED_BATCH_SIZE) {
78
+ const batch = allSymbols.slice(i, i + EMBED_BATCH_SIZE);
79
+ const texts = batch.map((s) => s.text);
80
+ try {
81
+ const vectors = await embedBatchViaClient(texts, "low");
82
+ if (vectors && vectors.length === batch.length) {
83
+ for (let j = 0; j < batch.length; j++) {
84
+ store.vectors[batch[j].id] = vectors[j];
85
+ }
86
+ }
87
+ } catch {
88
+ }
89
+ }
90
+ saveVectorStore(index.projectRoot, store);
91
+ return store;
92
+ }
93
+ var INDEX_VERSION = 2;
94
+ var DEFAULT_MAX_FILES = 5e3;
95
+ var IGNORE_SEGMENTS = /* @__PURE__ */ new Set([
96
+ "node_modules",
97
+ "dist",
98
+ ".git",
99
+ "coverage",
100
+ ".worktrees",
101
+ ".next",
102
+ "build",
103
+ "target",
104
+ "vendor",
105
+ // macOS/user-home/system folders. These are never project source and some
106
+ // are permission-protected (for example ~/.Trash), so descending into them
107
+ // can make stats/index fail before returning any useful code context.
108
+ ".Trash",
109
+ "Library",
110
+ "Applications",
111
+ "Desktop",
112
+ "Documents",
113
+ "Downloads",
114
+ "Movies",
115
+ "Music",
116
+ "Pictures",
117
+ "Public"
118
+ ]);
119
+ function normalizeProjectRoot(projectRoot) {
120
+ return path.resolve(projectRoot || process.cwd());
121
+ }
122
+ function hashText(text) {
123
+ return crypto.createHash("sha256").update(text).digest("hex");
124
+ }
125
+ function indexDir() {
126
+ const dir = path.join(EXE_AI_DIR, "code-context");
127
+ mkdirSync(dir, { recursive: true });
128
+ return dir;
129
+ }
130
+ function getCodeContextIndexPath(projectRoot) {
131
+ const root = normalizeProjectRoot(projectRoot);
132
+ const rootHash = hashText(root).slice(0, 16);
133
+ return path.join(indexDir(), `${rootHash}.json`);
134
+ }
135
+ function currentBranch(projectRoot) {
136
+ const result = spawnSync("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
137
+ const branch = result.status === 0 ? result.stdout.trim() : "";
138
+ return branch || "detached-or-unknown";
139
+ }
140
+ function shouldIgnore(relPath) {
141
+ const parts = relPath.split(/[\\/]/);
142
+ return parts.some((part) => IGNORE_SEGMENTS.has(part));
143
+ }
144
+ function listRecursive(projectRoot, dir = projectRoot, out = []) {
145
+ let entries;
146
+ try {
147
+ entries = readdirSync(dir, { withFileTypes: true });
148
+ } catch {
149
+ return out;
150
+ }
151
+ for (const entry of entries) {
152
+ const abs = path.join(dir, entry.name);
153
+ const rel = path.relative(projectRoot, abs).replaceAll(path.sep, "/");
154
+ if (shouldIgnore(rel)) continue;
155
+ if (entry.isDirectory()) listRecursive(projectRoot, abs, out);
156
+ else if (entry.isFile()) out.push(rel);
157
+ }
158
+ return out;
159
+ }
160
+ function listCodeFiles(projectRoot, maxFiles) {
161
+ const git = spawnSync("git", ["ls-files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
162
+ let files = [];
163
+ if (git.status === 0 && git.stdout.trim()) {
164
+ files = git.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
165
+ } else {
166
+ const rg = spawnSync("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
167
+ files = rg.status === 0 && rg.stdout.trim() ? rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean) : listRecursive(projectRoot);
168
+ }
169
+ return files.map((file) => file.replaceAll(path.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
170
+ }
171
+ function parseImportPaths(importText) {
172
+ const paths = [];
173
+ const patterns = [/from\s+["']([^"']+)["']/g, /^import\s+["']([^"']+)["']/gm, /require\(["']([^"']+)["']\)/g, /use\s+([A-Za-z0-9_:]+)::/g, /#include\s+[<"]([^>"]+)[>"]/g];
174
+ for (const regex of patterns) {
175
+ let match;
176
+ while ((match = regex.exec(importText)) !== null) if (!paths.includes(match[1])) paths.push(match[1]);
177
+ }
178
+ return paths;
179
+ }
180
+ function resolveImport(fromFile, importPath, allFiles) {
181
+ if (!importPath.startsWith(".")) return null;
182
+ const base = path.posix.normalize(path.posix.join(path.posix.dirname(fromFile.replaceAll(path.sep, "/")), importPath));
183
+ const withoutKnownExt = base.replace(/\.(?:[a-z0-9]+)$/i, "");
184
+ const candidates = [base];
185
+ 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"]) {
186
+ candidates.push(`${withoutKnownExt}.${ext}`, `${base}.${ext}`);
187
+ }
188
+ for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(path.posix.join(base, indexName));
189
+ return candidates.find((candidate) => allFiles.has(candidate)) ?? null;
190
+ }
191
+ function symbolId(filePath, chunk) {
192
+ return hashText(`${filePath}:${chunk.kind}:${chunk.name}:${chunk.startLine}:${chunk.endLine}`).slice(0, 24);
193
+ }
194
+ function loadIndex(projectRoot) {
195
+ const file = getCodeContextIndexPath(projectRoot);
196
+ if (!existsSync(file)) return null;
197
+ try {
198
+ const parsed = JSON.parse(readFileSync(file, "utf8"));
199
+ if (parsed.version !== INDEX_VERSION || parsed.projectRoot !== projectRoot) return null;
200
+ return parsed;
201
+ } catch {
202
+ return null;
203
+ }
204
+ }
205
+ function saveIndex(index) {
206
+ writeFileSync(getCodeContextIndexPath(index.projectRoot), JSON.stringify(index, null, 2));
207
+ }
208
+ function buildFileRecord(projectRoot, relPath, allFiles, previous) {
209
+ const absPath = path.join(projectRoot, relPath);
210
+ let stat;
211
+ try {
212
+ stat = statSync(absPath);
213
+ } catch {
214
+ return { record: null, reused: false };
215
+ }
216
+ if (!stat.isFile()) return { record: null, reused: false };
217
+ const language = languageForFile(relPath);
218
+ if (!language || !isChunkable(relPath)) return { record: null, reused: false };
219
+ const source = readFileSync(absPath, "utf8");
220
+ const hash = hashText(source);
221
+ if (previous && previous.hash === hash && previous.mtimeMs === stat.mtimeMs && previous.size === stat.size && previous.language === language) {
222
+ return { record: previous, reused: true };
223
+ }
224
+ const chunks = chunkSourceFile(source, relPath);
225
+ const imports = chunks.filter((chunk) => chunk.kind === "import").flatMap((chunk) => parseImportPaths(chunk.text));
226
+ const resolvedImports = imports.map((importPath) => resolveImport(relPath, importPath, allFiles)).filter((file) => Boolean(file));
227
+ const symbols = chunks.filter((chunk) => chunk.name !== "(unknown)").map((chunk) => ({
228
+ id: symbolId(relPath, chunk),
229
+ name: chunk.name,
230
+ kind: chunk.kind,
231
+ filePath: relPath,
232
+ language,
233
+ startLine: chunk.startLine,
234
+ endLine: chunk.endLine,
235
+ summary: summarizeChunk(chunk, relPath),
236
+ text: chunk.text.slice(0, 8e3),
237
+ comment: chunk.comment
238
+ }));
239
+ return { record: { path: relPath, absPath, language, hash, mtimeMs: stat.mtimeMs, size: stat.size, imports, resolvedImports, symbols }, reused: false };
240
+ }
241
+ function buildCodeContextIndex(options = {}) {
242
+ const projectRoot = normalizeProjectRoot(options.projectRoot);
243
+ const maxFiles = options.maxFiles ?? DEFAULT_MAX_FILES;
244
+ const branch = currentBranch(projectRoot);
245
+ const previous = options.force ? null : loadIndex(projectRoot);
246
+ const files = listCodeFiles(projectRoot, maxFiles);
247
+ const allFiles = new Set(files.map((file) => file.replaceAll(path.sep, "/")));
248
+ const fileRecords = {};
249
+ let rebuiltFiles = 0;
250
+ let reusedFiles = 0;
251
+ let skippedFiles = 0;
252
+ for (const rel of files) {
253
+ const normalized = rel.replaceAll(path.sep, "/");
254
+ const { record, reused } = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
255
+ if (record) {
256
+ fileRecords[normalized] = record;
257
+ if (reused) reusedFiles++;
258
+ else rebuiltFiles++;
259
+ } else skippedFiles++;
260
+ }
261
+ const languageBreakdown = {};
262
+ for (const file of Object.values(fileRecords)) languageBreakdown[file.language] = (languageBreakdown[file.language] ?? 0) + 1;
263
+ const index = {
264
+ version: INDEX_VERSION,
265
+ projectRoot,
266
+ rootHash: hashText(projectRoot).slice(0, 16),
267
+ branch,
268
+ indexedAt: (/* @__PURE__ */ new Date()).toISOString(),
269
+ stats: { filesSeen: files.length, filesIndexed: Object.keys(fileRecords).length, rebuiltFiles, reusedFiles, skippedFiles, languageBreakdown },
270
+ files: fileRecords
271
+ };
272
+ saveIndex(index);
273
+ return index;
274
+ }
275
+ function loadOrBuildCodeContextIndex(options = {}) {
276
+ const projectRoot = normalizeProjectRoot(options.projectRoot);
277
+ if (!options.force) {
278
+ const loaded = loadIndex(projectRoot);
279
+ if (loaded) {
280
+ const currentFiles = listCodeFiles(projectRoot, options.maxFiles ?? DEFAULT_MAX_FILES);
281
+ const unchanged = currentFiles.every((rel) => {
282
+ const normalized = rel.replaceAll(path.sep, "/");
283
+ const existing = loaded.files[normalized];
284
+ if (!existing) return false;
285
+ try {
286
+ const stat = statSync(path.join(projectRoot, normalized));
287
+ return stat.mtimeMs === existing.mtimeMs && stat.size === existing.size;
288
+ } catch {
289
+ return false;
290
+ }
291
+ });
292
+ if (unchanged && Object.keys(loaded.files).length === currentFiles.length) return loaded;
293
+ }
294
+ }
295
+ return buildCodeContextIndex(options);
296
+ }
297
+ function normalizeLanguage(language) {
298
+ return language.toLowerCase().replace(/^c\+\+$/, "cpp").replace(/^c#$/, "csharp").replace(/^js$/, "javascript").replace(/^ts$/, "typescript");
299
+ }
300
+ function tokenize(query) {
301
+ const raw = query.toLowerCase().replace(/([a-z])([A-Z])/g, "$1 $2").split(/[^a-z0-9_.$/-]+/).map((s) => s.trim()).filter((s) => s.length >= 2);
302
+ const expanded = /* @__PURE__ */ new Set();
303
+ for (const term of raw) {
304
+ expanded.add(term);
305
+ for (const part of term.split(/[_.$/-]+/)) if (part.length >= 2) expanded.add(part);
306
+ const dashed = term.replace(/[_.$/]+/g, "-");
307
+ if (dashed.length >= 2) expanded.add(dashed);
308
+ if (!term.includes("-")) expanded.add(`${term}s`);
309
+ if (term.endsWith("s") && term.length > 3) expanded.add(term.slice(0, -1));
310
+ }
311
+ return [...expanded];
312
+ }
313
+ function ngrams(terms) {
314
+ const grams = [...terms];
315
+ for (let i = 0; i < terms.length - 1; i++) grams.push(`${terms[i]} ${terms[i + 1]}`);
316
+ return grams;
317
+ }
318
+ function globToRegex(pattern) {
319
+ let out = "";
320
+ for (let i = 0; i < pattern.length; i++) {
321
+ const ch = pattern[i];
322
+ const next = pattern[i + 1];
323
+ if (ch === "*" && next === "*") {
324
+ out += ".*";
325
+ i++;
326
+ continue;
327
+ }
328
+ if (ch === "*") {
329
+ out += "[^/]*";
330
+ continue;
331
+ }
332
+ if (".+^${}()|[]\\".includes(ch)) out += `\\${ch}`;
333
+ else out += ch;
334
+ }
335
+ return new RegExp(`^${out}$`);
336
+ }
337
+ function matchesPath(filePath, patterns) {
338
+ if (!patterns || patterns.length === 0) return true;
339
+ const normalized = filePath.replaceAll(path.sep, "/");
340
+ return patterns.some((pattern) => {
341
+ const p = pattern.replaceAll(path.sep, "/").replace(/^\.\//, "");
342
+ return normalized === p || normalized.startsWith(`${p}/`) || normalized.endsWith(p) || globToRegex(p).test(normalized);
343
+ });
344
+ }
345
+ function scoreSymbol(symbol, terms) {
346
+ const grams = ngrams(terms);
347
+ const haystacks = {
348
+ name: symbol.name.toLowerCase(),
349
+ path: symbol.filePath.toLowerCase(),
350
+ language: symbol.language.toLowerCase(),
351
+ summary: symbol.summary.toLowerCase(),
352
+ text: symbol.text.toLowerCase()
353
+ };
354
+ let score = 0;
355
+ const matches = [];
356
+ for (const term of terms) {
357
+ if (haystacks.name === term) {
358
+ score += 100;
359
+ matches.push(`name=${term}`);
360
+ continue;
361
+ }
362
+ if (haystacks.name.includes(term)) {
363
+ score += 45;
364
+ matches.push(`name~${term}`);
365
+ }
366
+ if (haystacks.path.includes(term)) {
367
+ score += 18;
368
+ matches.push(`path~${term}`);
369
+ }
370
+ if (haystacks.language === term) {
371
+ score += 18;
372
+ matches.push(`language=${term}`);
373
+ }
374
+ if (haystacks.summary.includes(term)) {
375
+ score += 16;
376
+ matches.push(`summary~${term}`);
377
+ }
378
+ if (haystacks.text.includes(term)) {
379
+ score += 5;
380
+ matches.push(`text~${term}`);
381
+ }
382
+ }
383
+ for (const gram of grams.filter((g) => g.includes(" "))) {
384
+ if (haystacks.text.includes(gram) || haystacks.summary.includes(gram)) {
385
+ score += 20;
386
+ matches.push(`phrase~${gram}`);
387
+ }
388
+ }
389
+ const uniqueMatches = new Set(matches.map((m) => m.replace(/^[^=~]+[=~]/, ""))).size;
390
+ score += uniqueMatches * 3;
391
+ return { score, matches };
392
+ }
393
+ function filteredFiles(index, options = {}) {
394
+ const languages = options.languages?.map(normalizeLanguage).filter(Boolean);
395
+ return Object.values(index.files).filter((file) => {
396
+ if (languages && languages.length > 0 && !languages.includes(normalizeLanguage(file.language))) return false;
397
+ return matchesPath(file.path, options.paths);
398
+ });
399
+ }
400
+ function lexicalSearch(query, options = {}) {
401
+ const terms = tokenize(query);
402
+ if (terms.length === 0) return [];
403
+ const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
404
+ const results = [];
405
+ for (const file of filteredFiles(index, options)) {
406
+ for (const symbol of file.symbols) {
407
+ const scored = scoreSymbol(symbol, terms);
408
+ if (scored.score > 0) {
409
+ results.push({
410
+ symbol,
411
+ score: scored.score,
412
+ matches: scored.matches,
413
+ filePath: symbol.filePath,
414
+ language: symbol.language,
415
+ content: symbol.text,
416
+ startLine: symbol.startLine,
417
+ endLine: symbol.endLine
418
+ });
419
+ }
420
+ }
421
+ }
422
+ const offset = Math.max(0, options.offset ?? 0);
423
+ const limit = options.limit ?? 20;
424
+ return results.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
425
+ }
426
+ function searchCodeContext(query, options = {}) {
427
+ return lexicalSearch(query, options);
428
+ }
429
+ var RRF_K = 60;
430
+ async function searchCodeContextSemantic(query, options = {}) {
431
+ const terms = tokenize(query);
432
+ if (terms.length === 0) return [];
433
+ const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
434
+ const projectRoot = normalizeProjectRoot(options.projectRoot);
435
+ const vectorStore = loadVectorStore(projectRoot);
436
+ if (!vectorStore || Object.keys(vectorStore.vectors).length === 0) {
437
+ return lexicalSearch(query, options);
438
+ }
439
+ let queryVector = null;
440
+ try {
441
+ const connected = await connectEmbedDaemon().catch(() => false);
442
+ if (connected) {
443
+ const result = await embedBatchViaClient([query], "high");
444
+ if (result && result.length === 1) queryVector = result[0];
445
+ }
446
+ } catch {
447
+ }
448
+ if (!queryVector) return lexicalSearch(query, options);
449
+ const files = filteredFiles(index, options);
450
+ const candidates = [];
451
+ for (const file of files) {
452
+ for (const symbol of file.symbols) {
453
+ const lexical = scoreSymbol(symbol, terms);
454
+ const vec = vectorStore.vectors[symbol.id];
455
+ const vectorScore = vec ? cosineSimilarity(queryVector, vec) : 0;
456
+ if (lexical.score > 0 || vectorScore > 0.3) {
457
+ candidates.push({
458
+ symbol,
459
+ lexicalScore: lexical.score,
460
+ vectorScore,
461
+ matches: lexical.matches
462
+ });
463
+ }
464
+ }
465
+ }
466
+ const byLexical = [...candidates].sort((a, b) => b.lexicalScore - a.lexicalScore);
467
+ const byVector = [...candidates].sort((a, b) => b.vectorScore - a.vectorScore);
468
+ const lexicalRank = /* @__PURE__ */ new Map();
469
+ const vectorRank = /* @__PURE__ */ new Map();
470
+ byLexical.forEach((c, i) => lexicalRank.set(c.symbol.id, i + 1));
471
+ byVector.forEach((c, i) => vectorRank.set(c.symbol.id, i + 1));
472
+ const VECTOR_WEIGHT = 0.6;
473
+ const LEXICAL_WEIGHT = 0.4;
474
+ const fused = candidates.map((c) => {
475
+ const lRank = lexicalRank.get(c.symbol.id) ?? candidates.length + 1;
476
+ const vRank = vectorRank.get(c.symbol.id) ?? candidates.length + 1;
477
+ const rrfScore = VECTOR_WEIGHT * (1 / (RRF_K + vRank)) + LEXICAL_WEIGHT * (1 / (RRF_K + lRank));
478
+ return {
479
+ symbol: c.symbol,
480
+ score: rrfScore,
481
+ matches: c.vectorScore > 0.3 ? [...c.matches, `semantic=${c.vectorScore.toFixed(3)}`] : c.matches,
482
+ filePath: c.symbol.filePath,
483
+ language: c.symbol.language,
484
+ content: c.symbol.text,
485
+ startLine: c.symbol.startLine,
486
+ endLine: c.symbol.endLine
487
+ };
488
+ });
489
+ const offset = Math.max(0, options.offset ?? 0);
490
+ const limit = options.limit ?? 20;
491
+ return fused.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
492
+ }
493
+ async function buildCodeContextIndexWithEmbeddings(options = {}) {
494
+ const index = buildCodeContextIndex(options);
495
+ const existingStore = loadVectorStore(index.projectRoot);
496
+ const existingCount = existingStore ? Object.keys(existingStore.vectors).length : 0;
497
+ const store = await embedSymbols(index);
498
+ const vectorCount = Object.keys(store.vectors).length;
499
+ const newEmbeddings = vectorCount - Math.min(existingCount, vectorCount);
500
+ return { index, vectorCount, newEmbeddings };
501
+ }
502
+ function dependentsMap(index) {
503
+ const map = /* @__PURE__ */ new Map();
504
+ for (const file of Object.values(index.files)) {
505
+ for (const dep of file.resolvedImports) {
506
+ if (!map.has(dep)) map.set(dep, /* @__PURE__ */ new Set());
507
+ map.get(dep).add(file.path);
508
+ }
509
+ }
510
+ return map;
511
+ }
512
+ function findSymbols(index, symbolName, limit = 20) {
513
+ const q = symbolName.toLowerCase();
514
+ const exact = [];
515
+ const fuzzy = [];
516
+ for (const file of Object.values(index.files)) {
517
+ for (const symbol of file.symbols) {
518
+ if (symbol.name.toLowerCase() === q) exact.push(symbol);
519
+ else if (symbol.name.toLowerCase().includes(q) || symbol.summary.toLowerCase().includes(q)) fuzzy.push(symbol);
520
+ }
521
+ }
522
+ return [...exact, ...fuzzy].slice(0, limit);
523
+ }
524
+ function traceCodeSymbol(symbolName, options = {}) {
525
+ const index = loadOrBuildCodeContextIndex(options);
526
+ const matches = findSymbols(index, symbolName, options.limit ?? 20);
527
+ const dependents = dependentsMap(index);
528
+ return { query: symbolName, matches, definitions: matches.map((symbol) => {
529
+ const file = index.files[symbol.filePath];
530
+ return { symbol, imports: file.resolvedImports, dependents: [...dependents.get(symbol.filePath) ?? /* @__PURE__ */ new Set()].sort(), relatedSymbols: file.symbols.filter((s) => s.id !== symbol.id).slice(0, 20) };
531
+ }) };
532
+ }
533
+ function resolveTargetFile(index, input) {
534
+ if (input.filePath) {
535
+ const normalized = input.filePath.replaceAll(path.sep, "/").replace(/^\.\//, "");
536
+ if (index.files[normalized]) return { filePath: normalized, target: normalized };
537
+ const suffix = Object.keys(index.files).find((file) => file.endsWith(normalized));
538
+ if (suffix) return { filePath: suffix, target: input.filePath };
539
+ }
540
+ if (input.symbol) {
541
+ const match = findSymbols(index, input.symbol, 1)[0];
542
+ if (match) return { filePath: match.filePath, target: input.symbol };
543
+ }
544
+ return null;
545
+ }
546
+ function analyzeBlastRadius(input) {
547
+ const index = loadOrBuildCodeContextIndex({ projectRoot: input.projectRoot, force: input.force });
548
+ const target = resolveTargetFile(index, { filePath: input.filePath, symbol: input.symbol });
549
+ if (!target) return null;
550
+ const dependents = dependentsMap(index);
551
+ const maxDepth = input.depth ?? 2;
552
+ const impacted = /* @__PURE__ */ new Map();
553
+ const queue = [{ filePath: target.filePath, distance: 0 }];
554
+ impacted.set(target.filePath, { distance: 0, reason: "target" });
555
+ while (queue.length > 0) {
556
+ const item = queue.shift();
557
+ if (item.distance >= maxDepth) continue;
558
+ for (const dep of dependents.get(item.filePath) ?? []) {
559
+ if (!impacted.has(dep)) {
560
+ impacted.set(dep, { distance: item.distance + 1, reason: `imports ${item.filePath}` });
561
+ queue.push({ filePath: dep, distance: item.distance + 1 });
562
+ }
563
+ }
564
+ }
565
+ const targetBase = path.basename(target.filePath).replace(/\.[^.]+$/, "").toLowerCase();
566
+ const symbolLower = input.symbol?.toLowerCase();
567
+ const tests = Object.keys(index.files).filter((file) => {
568
+ const lower = file.toLowerCase();
569
+ return (lower.includes("test") || lower.includes("spec")) && (lower.includes(targetBase) || (symbolLower ? index.files[file].symbols.some((s) => s.text.toLowerCase().includes(symbolLower)) : false));
570
+ });
571
+ for (const test of tests) if (!impacted.has(test)) impacted.set(test, { distance: 1, reason: "related test/spec" });
572
+ const impactedFiles = [...impacted.entries()].map(([filePath, value]) => ({ filePath, distance: value.distance, reason: value.reason })).sort((a, b) => a.distance - b.distance || a.filePath.localeCompare(b.filePath));
573
+ const nonTestImpacted = impactedFiles.filter((f) => !f.filePath.match(/(test|spec)\./i)).length;
574
+ return { target: target.target, targetFile: target.filePath, impactedFiles, tests, symbolsInTarget: index.files[target.filePath]?.symbols ?? [], riskLevel: nonTestImpacted >= 8 ? "high" : nonTestImpacted >= 4 ? "medium" : "low" };
575
+ }
576
+ function getCodeContextStats(options = {}) {
577
+ const index = loadOrBuildCodeContextIndex(options);
578
+ return {
579
+ projectRoot: index.projectRoot,
580
+ branch: index.branch,
581
+ indexedAt: index.indexedAt,
582
+ indexAgeMs: Math.max(0, Date.now() - Date.parse(index.indexedAt)),
583
+ files: Object.keys(index.files).length,
584
+ symbols: Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0),
585
+ imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0),
586
+ languageBreakdown: index.stats.languageBreakdown,
587
+ rebuiltFiles: index.stats.rebuiltFiles,
588
+ reusedFiles: index.stats.reusedFiles,
589
+ skippedFiles: index.stats.skippedFiles,
590
+ indexPath: getCodeContextIndexPath(index.projectRoot)
591
+ };
592
+ }
593
+
594
+ export {
595
+ getCodeContextIndexPath,
596
+ buildCodeContextIndex,
597
+ loadOrBuildCodeContextIndex,
598
+ searchCodeContext,
599
+ searchCodeContextSemantic,
600
+ buildCodeContextIndexWithEmbeddings,
601
+ traceCodeSymbol,
602
+ analyzeBlastRadius,
603
+ getCodeContextStats
604
+ };