@askexenow/exe-os 0.9.127 → 0.9.129
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/active-agent-3FDJYREJ.js +22 -0
- package/dist/active-agent-WC7YK3L5.js +23 -0
- package/dist/agent-context-CDWUZEXY.js +11 -0
- package/dist/agent-loop-G4VUR5ME.js +10 -0
- package/dist/agentic-ontology-2VYNTMNO.js +25 -0
- package/dist/backfill-metadata-OJWOCLSZ.js +601 -0
- package/dist/background-jobs-LGRDXGXC.js +25 -0
- package/dist/bash-Y5PEW5UQ.js +8 -0
- package/dist/behaviors-CSB2YLDT.js +22 -0
- package/dist/bin/age-ontology-load.js +10 -258
- package/dist/bin/agentic-ontology-backfill.js +23 -5296
- package/dist/bin/agentic-reflection-backfill.js +19 -4739
- package/dist/bin/agentic-semantic-label.js +28 -4723
- package/dist/bin/backfill-conversations.js +42 -5543
- package/dist/bin/backfill-responses.js +41 -5542
- package/dist/bin/backfill-vectors.js +43 -4795
- package/dist/bin/bulk-sync-postgres.js +22 -5313
- package/dist/bin/cc-doctor.js +10 -602
- package/dist/bin/cleanup-stale-review-tasks.js +32 -6803
- package/dist/bin/cli.js +169 -38993
- package/dist/bin/exe-agent-config.js +21 -253
- package/dist/bin/exe-agent.js +63 -2429
- package/dist/bin/exe-assign.js +34 -5810
- package/dist/bin/exe-boot.js +180 -12726
- package/dist/bin/exe-call.js +48 -1409
- package/dist/bin/exe-cloud.js +64 -8516
- package/dist/bin/exe-dispatch.js +35 -10818
- package/dist/bin/exe-doctor.js +24 -7537
- package/dist/bin/exe-export-behaviors.js +18 -6122
- package/dist/bin/exe-forget.js +22 -6274
- package/dist/bin/exe-gateway.js +62 -15286
- package/dist/bin/exe-healthcheck.js +9 -602
- package/dist/bin/exe-heartbeat.js +51 -6898
- package/dist/bin/exe-kill.js +30 -5961
- package/dist/bin/exe-launch-agent.js +101 -6788
- package/dist/bin/exe-new-employee.js +42 -3774
- package/dist/bin/exe-pending-messages.js +28 -6833
- package/dist/bin/exe-pending-notifications.js +31 -6869
- package/dist/bin/exe-pending-reviews.js +38 -6910
- package/dist/bin/exe-rename.js +57 -5766
- package/dist/bin/exe-review.js +29 -5948
- package/dist/bin/exe-search.js +22 -7692
- package/dist/bin/exe-session-cleanup.js +52 -11147
- package/dist/bin/exe-settings.js +35 -524
- package/dist/bin/exe-start-codex.js +70 -6641
- package/dist/bin/exe-start-opencode.js +54 -6584
- package/dist/bin/exe-status.js +26 -7081
- package/dist/bin/exe-support.js +10 -546
- package/dist/bin/exe-team.js +18 -5942
- package/dist/bin/git-sweep.js +26 -10909
- package/dist/bin/graph-backfill.js +24 -5979
- package/dist/bin/graph-export.js +31 -6128
- package/dist/bin/graph-layer-benchmark.js +4 -8
- package/dist/bin/install.js +47 -2322
- package/dist/bin/intercom-check.js +25 -11501
- package/dist/bin/list-providers.js +4 -18
- package/dist/bin/postgres-agentic-reflection-backfill.js +14 -357
- package/dist/bin/postgres-agentic-semantic-backfill.js +14 -404
- package/dist/bin/pre-publish.js +4 -298
- package/dist/bin/registry-proxy.js +8 -172
- package/dist/bin/scan-tasks.js +47 -10881
- package/dist/bin/setup.js +6 -9492
- package/dist/bin/shard-migrate.js +30 -5149
- package/dist/bin/stack-update.js +75 -203
- package/dist/bin/update.js +20 -654
- package/dist/capacity-monitor-3GZIHLB4.js +46 -0
- package/dist/catchup-brief-67QDCINS.js +148 -0
- package/dist/chunk-2QD7KBJN.js +664 -0
- package/dist/chunk-2WGSIVI4.js +149 -0
- package/dist/chunk-35TVNLJI.js +57 -0
- package/dist/chunk-3SYV36U5.js +47 -0
- package/dist/chunk-46SQTBQW.js +207 -0
- package/dist/chunk-4CMVLKEI.js +171 -0
- package/dist/chunk-4FLUOZTR.js +116 -0
- package/dist/chunk-4NYQAS33.js +17 -0
- package/dist/chunk-4QNPADWL.js +902 -0
- package/dist/chunk-4Y6QZBEO.js +58 -0
- package/dist/chunk-557C2IGL.js +0 -0
- package/dist/chunk-55S56YDO.js +305 -0
- package/dist/chunk-5BS5ELMC.js +85 -0
- package/dist/chunk-5DHM77JE.js +256 -0
- package/dist/chunk-5QTWQKNF.js +137 -0
- package/dist/chunk-6A7P74QV.js +53 -0
- package/dist/chunk-6BFIFI5F.js +45 -0
- package/dist/chunk-6KFICEAT.js +20 -0
- package/dist/chunk-6LKDJ5WX.js +317 -0
- package/dist/chunk-6M5II4EV.js +148 -0
- package/dist/chunk-6QEFN3DV.js +302 -0
- package/dist/chunk-6WTMJAG3.js +171 -0
- package/dist/chunk-6Y4B3QF6.js +19 -0
- package/dist/chunk-7RK3AYJE.js +163 -0
- package/dist/chunk-7TCP2XTP.js +390 -0
- package/dist/chunk-A2NZP64U.js +2307 -0
- package/dist/chunk-AFYIYM2K.js +322 -0
- package/dist/chunk-AJWFPKQS.js +573 -0
- package/dist/chunk-AVE2B4DQ.js +140 -0
- package/dist/chunk-B4TT2QQA.js +87 -0
- package/dist/chunk-BCMOO4OQ.js +1066 -0
- package/dist/chunk-BU3ID2WT.js +432 -0
- package/dist/chunk-CFIM43JH.js +862 -0
- package/dist/chunk-CURJUF3W.js +66 -0
- package/dist/chunk-DC4ZD76Y.js +288 -0
- package/dist/chunk-DL52JPGF.js +706 -0
- package/dist/chunk-DRSC6ZRA.js +36 -0
- package/dist/chunk-E2Q4Y22A.js +86 -0
- package/dist/chunk-ECGTESAP.js +236 -0
- package/dist/chunk-EHOJUXYG.js +365 -0
- package/dist/chunk-EIT6J37V.js +119 -0
- package/dist/chunk-ELE2C7L4.js +31 -0
- package/dist/chunk-EQSQ4HBW.js +9 -0
- package/dist/chunk-FWFFZGSC.js +94 -0
- package/dist/chunk-GCNWCYJI.js +249 -0
- package/dist/chunk-GGTUUI7J.js +6 -0
- package/dist/chunk-GU56L7OY.js +223 -0
- package/dist/chunk-HF25XPZE.js +83 -0
- package/dist/chunk-HOKOOBDL.js +52 -0
- package/dist/chunk-HP43GLZ6.js +89 -0
- package/dist/chunk-HSRM6PNM.js +682 -0
- package/dist/chunk-HVRTW3OP.js +164 -0
- package/dist/chunk-IHDHL5ML.js +125 -0
- package/dist/chunk-IHF5RXJM.js +168 -0
- package/dist/chunk-IJ73YE73.js +409 -0
- package/dist/chunk-INO4ZD5Y.js +162 -0
- package/dist/chunk-IRQTM7DY.js +475 -0
- package/dist/chunk-JD4FAPYG.js +1208 -0
- package/dist/chunk-JN44LQ53.js +316 -0
- package/dist/chunk-JQ6TLNIV.js +133 -0
- package/dist/chunk-JT76KRZQ.js +35 -0
- package/dist/chunk-K6OEWZTB.js +262 -0
- package/dist/chunk-KCUYRZSX.js +72 -0
- package/dist/chunk-KFQGP6VL.js +33 -0
- package/dist/chunk-KRLOFIPI.js +83 -0
- package/dist/chunk-KVPG5UT6.js +305 -0
- package/dist/chunk-L3TB7CC3.js +170 -0
- package/dist/chunk-L4X5EUHR.js +283 -0
- package/dist/chunk-LAN26C3W.js +655 -0
- package/dist/chunk-LPGBKR4H.js +54 -0
- package/dist/chunk-LVNMOVGD.js +132 -0
- package/dist/chunk-LX5WEEZ7.js +157 -0
- package/dist/chunk-MBH42QNH.js +295 -0
- package/dist/chunk-MFO7MNSD.js +102 -0
- package/dist/chunk-MILTNDZH.js +736 -0
- package/dist/chunk-MJAE4NB7.js +1090 -0
- package/dist/chunk-MLCUPTP7.js +132 -0
- package/dist/chunk-MPURH6JI.js +445 -0
- package/dist/chunk-MVBWW2EO.js +89 -0
- package/dist/chunk-MZIGUUDQ.js +42 -0
- package/dist/chunk-N3DTSVKE.js +157 -0
- package/dist/chunk-N5RRQOAC.js +134 -0
- package/dist/chunk-N7L33TED.js +1664 -0
- package/dist/chunk-NDYSNVI5.js +122 -0
- package/dist/chunk-NEAHEC5G.js +155 -0
- package/dist/chunk-NGVOA6ZQ.js +84 -0
- package/dist/chunk-NHK5KW6Y.js +193 -0
- package/dist/chunk-NMQKGFEP.js +458 -0
- package/dist/chunk-NPT4EHZK.js +352 -0
- package/dist/chunk-NXFJHTED.js +55 -0
- package/dist/chunk-NZLCDI6Y.js +216 -0
- package/dist/chunk-O377P7GM.js +100 -0
- package/dist/chunk-O4VFZRA5.js +33 -0
- package/dist/chunk-O7YD7JYE.js +237 -0
- package/dist/chunk-OFK72LTZ.js +81 -0
- package/dist/chunk-ORCCI2VV.js +140 -0
- package/dist/chunk-OVQR4KMC.js +579 -0
- package/dist/chunk-P3CVUMEH.js +54 -0
- package/dist/chunk-P4HIF72O.js +30 -0
- package/dist/chunk-PEDLSJBL.js +1039 -0
- package/dist/chunk-PFICKZZ5.js +219 -0
- package/dist/chunk-PGW7TIV6.js +142 -0
- package/dist/chunk-PJHHFJAN.js +63 -0
- package/dist/chunk-PP4SNOJ5.js +471 -0
- package/dist/chunk-PRKVT4KN.js +90 -0
- package/dist/chunk-Q2O4VJHH.js +305 -0
- package/dist/chunk-Q36CMZIX.js +38 -0
- package/dist/chunk-Q3V7K4ME.js +149 -0
- package/dist/chunk-QGYPJGHB.js +81 -0
- package/dist/chunk-R23EMFJV.js +198 -0
- package/dist/chunk-R4M6XCMU.js +39 -0
- package/dist/chunk-R4WMF32C.js +81 -0
- package/dist/chunk-RJT7H2KR.js +349 -0
- package/dist/chunk-RJZMRZNV.js +280 -0
- package/dist/chunk-RMFDPCA2.js +72 -0
- package/dist/chunk-RUG3N6P4.js +292 -0
- package/dist/chunk-RWO327FJ.js +84 -0
- package/dist/chunk-SG7EFHQ7.js +108 -0
- package/dist/chunk-SH45SJQW.js +0 -0
- package/dist/chunk-SPKE5ANC.js +42 -0
- package/dist/chunk-SVMFVPBF.js +104 -0
- package/dist/chunk-SVXDCELZ.js +163 -0
- package/dist/chunk-TMEE7AT7.js +72 -0
- package/dist/chunk-TWYEE4FB.js +125 -0
- package/dist/chunk-TYLF4LSW.js +110 -0
- package/dist/chunk-V4TZI6EO.js +60 -0
- package/dist/chunk-VFZTXKQI.js +372 -0
- package/dist/chunk-VQ4K2MLM.js +199 -0
- package/dist/chunk-VU447UAO.js +173 -0
- package/dist/chunk-WMC3TXJF.js +50 -0
- package/dist/chunk-WPOA7XAE.js +84 -0
- package/dist/chunk-WZQ4CPRG.js +64 -0
- package/dist/chunk-X2IMCCM5.js +49 -0
- package/dist/chunk-X2Z5GT3V.js +118 -0
- package/dist/chunk-XEKO37NM.js +43 -0
- package/dist/chunk-XUHFQHGZ.js +505 -0
- package/dist/chunk-YGAAZN3E.js +47 -0
- package/dist/chunk-YKOW7KMK.js +558 -0
- package/dist/chunk-YOQKMB6N.js +145 -0
- package/dist/chunk-YZFZDJWZ.js +107 -0
- package/dist/chunk-ZBY4AUD4.js +10342 -0
- package/dist/chunk-ZOJYPNCM.js +127 -0
- package/dist/chunk-ZS7MGQSD.js +1494 -0
- package/dist/chunk-ZTZQ5A6B.js +191 -0
- package/dist/code-context-index-HXGC4IJL.js +28 -0
- package/dist/content-extractor-UVZXRIPC.js +8 -0
- package/dist/conversation-entity-extractor-HKSNDF4L.js +113 -0
- package/dist/conversation-wiki-populator-NKDAQEZ5.js +105 -0
- package/dist/crdt-sync-T7MHVQCS.js +33 -0
- package/dist/crm-bridge-Q5JBVJ6P.js +19 -0
- package/dist/crm-webhook-OAFTLJYA.js +10 -0
- package/dist/cto-delegation-gate-XWHZCUJH.js +208 -0
- package/dist/daemon-auth-GWUXTUYO.js +15 -0
- package/dist/daemon-orchestration-2C3TVOHO.js +91 -0
- package/dist/daemon-protocol-RLH2XM2K.js +19 -0
- package/dist/db-backup-CH2KG3JY.js +23 -0
- package/dist/devtools-PLJCAU53.js +8 -0
- package/dist/discord-G4BQJ2VX.js +7 -0
- package/dist/email-XMRNMFXR.js +137 -0
- package/dist/entity-boost-IOER4VRR.js +249 -0
- package/dist/exe-drift-2IJFQV43.js +68 -0
- package/dist/exe-export-Y2RFXGNB.js +71 -0
- package/dist/exe-import-LN6PE2YT.js +74 -0
- package/dist/exe-key-DHKBWLIT.js +571 -0
- package/dist/exe-org-F7LZRHFB.js +73 -0
- package/dist/factory-YTD23AW7.js +67 -0
- package/dist/fast-db-init-IIP6EGWT.js +7 -0
- package/dist/file-edit-FJVEO7XZ.js +8 -0
- package/dist/file-read-Q2NH3J35.js +8 -0
- package/dist/file-write-UNSFXNH4.js +8 -0
- package/dist/gateway/index.js +87 -16563
- package/dist/gateway-client-APTYJNCL.js +11 -0
- package/dist/git-staleness-I2BUEDE2.js +111 -0
- package/dist/git-task-sweep-UEOTXRW4.js +35 -0
- package/dist/glob-GRMXLDMB.js +7 -0
- package/dist/global-procedures-MFE5LGRF.js +19 -0
- package/dist/graph-query-657O5JYJ.js +25 -0
- package/dist/graph-rag-ZE5N7C65.js +28 -0
- package/dist/grep-NBV6SKYZ.js +7 -0
- package/dist/hooks/bug-report-worker.js +41 -10823
- package/dist/hooks/codex-stop-task-finalizer.js +38 -9055
- package/dist/hooks/commit-complete.js +32 -10914
- package/dist/hooks/error-recall.js +35 -7955
- package/dist/hooks/exe-heartbeat-hook.js +17 -327
- package/dist/hooks/ingest-worker.js +16 -565
- package/dist/hooks/ingest.js +98 -11436
- package/dist/hooks/instructions-loaded.js +25 -6209
- package/dist/hooks/notification.js +16 -6098
- package/dist/hooks/post-compact.js +34 -7041
- package/dist/hooks/post-tool-combined.js +27 -9751
- package/dist/hooks/pre-compact.js +38 -11130
- package/dist/hooks/pre-tool-use.js +61 -7233
- package/dist/hooks/prompt-submit.js +113 -13358
- package/dist/hooks/session-end.js +57 -11503
- package/dist/hooks/session-start.js +46 -9423
- package/dist/hooks/stop.js +70 -7210
- package/dist/hooks/subagent-stop.js +33 -6897
- package/dist/hooks/summary-worker.js +86 -8629
- package/dist/hooks-KD3N2D7P.js +9 -0
- package/dist/imessage-L7WKA2WD.js +7 -0
- package/dist/index.js +49 -19391
- package/dist/installer-5N24GXFB.js +32 -0
- package/dist/installer-QSD76YRY.js +291 -0
- package/dist/installer-ZPQUKHU5.js +331 -0
- package/dist/intercom-queue-PEEGAJ33.js +19 -0
- package/dist/key-backup-status-57UB74DU.js +41 -0
- package/dist/lib/agent-config.js +19 -224
- package/dist/lib/cloud-sync.js +44 -5398
- package/dist/lib/cloudflare-dns.js +7 -111
- package/dist/lib/config.js +18 -278
- package/dist/lib/consolidation.js +21 -1047
- package/dist/lib/crypto.js +7 -45
- package/dist/lib/database.js +20 -2932
- package/dist/lib/db-daemon-client.js +24 -460
- package/dist/lib/db.js +22 -2932
- package/dist/lib/device-registry.js +20 -2992
- package/dist/lib/embedder.js +14 -822
- package/dist/lib/employee-templates.js +21 -1102
- package/dist/lib/employees.js +30 -497
- package/dist/lib/error-detector.js +9 -149
- package/dist/lib/exe-daemon-client.js +16 -570
- package/dist/lib/exe-daemon.js +509 -18398
- package/dist/lib/file-grep.js +2 -0
- package/dist/lib/hybrid-search.js +18 -7674
- package/dist/lib/identity-templates.js +2 -0
- package/dist/lib/identity.js +13 -309
- package/dist/lib/keychain.js +9 -499
- package/dist/lib/license.js +21 -549
- package/dist/lib/messaging.js +35 -1631
- package/dist/lib/post-tool-memory.js +8 -370
- package/dist/lib/registry-proxy.js +8 -155
- package/dist/lib/reminders.js +11 -231
- package/dist/lib/runtime-table.js +7 -20
- package/dist/lib/schedules.js +16 -4614
- package/dist/lib/session-registry.js +9 -57
- package/dist/lib/session-wrappers.js +3 -127
- package/dist/lib/skill-learning.js +20 -2101
- package/dist/lib/status-brief.js +4 -368
- package/dist/lib/store.js +25 -5593
- package/dist/lib/task-router.js +12 -254
- package/dist/lib/tasks.js +44 -5801
- package/dist/lib/tmux-routing.js +45 -5804
- package/dist/lib/tmux-status.js +16 -248
- package/dist/lib/tmux-transport.js +6 -90
- package/dist/lib/token-spend.js +9 -337
- package/dist/lib/transport.js +8 -134
- package/dist/lib/ws-auth.js +6 -14
- package/dist/lib/ws-client.js +4 -22
- package/dist/license-gate-NXXDOOAQ.js +14 -0
- package/dist/mcp/register-tools.js +90 -32454
- package/dist/mcp/server.js +139 -32625
- package/dist/mcp/tools/complete-reminder.js +10 -237
- package/dist/mcp/tools/create-reminder.js +10 -222
- package/dist/mcp/tools/create-task.js +28 -6730
- package/dist/mcp/tools/deactivate-behavior.js +13 -476
- package/dist/mcp/tools/list-reminders.js +10 -232
- package/dist/mcp/tools/list-tasks.js +28 -1571
- package/dist/mcp/tools/send-message.js +25 -1672
- package/dist/mcp/tools/update-task.js +27 -6123
- package/dist/mcp-diagnostics-OOZ3TDPY.js +200 -0
- package/dist/memory-cards-RIL5BZHK.js +175 -0
- package/dist/memory-queue-JYIJ7TMQ.js +13 -0
- package/dist/memory-queue-client-PE7757HU.js +14 -0
- package/dist/notifications-CJFULBMN.js +42 -0
- package/dist/orchestration-phase-Y2TYNW5H.js +23 -0
- package/dist/orchestrator-RXIPT6PZ.js +29 -0
- package/dist/permission-presets-WI524OSE.js +25 -0
- package/dist/permissions-TYT6YMNZ.js +14 -0
- package/dist/pg-ssl-KFHNJPGO.js +7 -0
- package/dist/plan-limits-5WY5YK6X.js +27 -0
- package/dist/preferences-MW3BYTQ5.js +10 -0
- package/dist/project-name-PL2ZDWNU.js +11 -0
- package/dist/projection-worker-OY4WYZW6.js +661 -0
- package/dist/reranker-LFJ73RRS.js +19 -0
- package/dist/review-gate-E6K7QQE5.js +120 -0
- package/dist/runtime/index.js +82 -13408
- package/dist/self-query-router-IZANO632.js +83 -0
- package/dist/session-events-TQ6S5KQN.js +32 -0
- package/dist/session-kill-telemetry-46YIJWE2.js +30 -0
- package/dist/session-scope-3MYK3YRV.js +34 -0
- package/dist/setup-wizard-ZJ7UCRBP.js +12 -0
- package/dist/shard-manager-RCFZ6RXY.js +29 -0
- package/dist/signal-XN2C7PIG.js +7 -0
- package/dist/slack-Z77TFJCK.js +7 -0
- package/dist/task-enforcement-337DZI3I.js +252 -0
- package/dist/task-scanner-IMIR3T7T.js +11 -0
- package/dist/task-scope-MUU4KXE6.js +32 -0
- package/dist/tasks-crud-PMLK3MM4.js +53 -0
- package/dist/tasks-review-56LVKKNL.js +44 -0
- package/dist/telegram-CRPZO7JT.js +7 -0
- package/dist/telemetry-SPVGPKU7.js +9 -0
- package/dist/tool-capability-index-XNBSDCH3.js +10 -0
- package/dist/tool-gates-LGH6CSY4.js +14 -0
- package/dist/tool-gates-W7SZCYTX.js +13 -0
- package/dist/tool-registry-436LVVLY.js +11 -0
- package/dist/tool-telemetry-L77WNAYT.js +15 -0
- package/dist/tui/App.js +253 -14668
- package/dist/tui-data-SB45REYU.js +161 -0
- package/dist/update-check-LZGZISRJ.js +11 -0
- package/dist/webhook-VVP7KQVC.js +96 -0
- package/dist/whatsapp-J6QNDBGY.js +9 -0
- package/dist/whatsapp-accounts-RTJF3LM6.js +15 -0
- package/dist/wiki-client-EKCIKCVC.js +136 -0
- package/dist/worker-gate-UYNN62GJ.js +19 -0
- package/dist/worktree-4T4BPWPN.js +205 -0
- package/package.json +1 -1
package/dist/bin/install.js
CHANGED
|
@@ -1,2311 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __esm = (fn, res) => function __init() {
|
|
7
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
-
};
|
|
9
|
-
var __export = (target, all) => {
|
|
10
|
-
for (var name in all)
|
|
11
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
-
};
|
|
13
|
-
var __copyProps = (to, from, except, desc) => {
|
|
14
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
-
for (let key of __getOwnPropNames(from))
|
|
16
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
-
}
|
|
19
|
-
return to;
|
|
20
|
-
};
|
|
21
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
-
|
|
23
|
-
// src/lib/secure-files.ts
|
|
24
|
-
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
25
|
-
import { chmod, mkdir } from "fs/promises";
|
|
26
|
-
function ensurePrivateDirSync(dirPath) {
|
|
27
|
-
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
28
|
-
try {
|
|
29
|
-
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
30
|
-
} catch {
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function enforcePrivateFileSync(filePath) {
|
|
34
|
-
try {
|
|
35
|
-
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
36
|
-
} catch {
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
40
|
-
var init_secure_files = __esm({
|
|
41
|
-
"src/lib/secure-files.ts"() {
|
|
42
|
-
"use strict";
|
|
43
|
-
PRIVATE_DIR_MODE = 448;
|
|
44
|
-
PRIVATE_FILE_MODE = 384;
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// src/lib/config.ts
|
|
49
|
-
import { readFile, writeFile } from "fs/promises";
|
|
50
|
-
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
51
|
-
import path from "path";
|
|
52
|
-
import os from "os";
|
|
53
|
-
function resolveDataDir() {
|
|
54
|
-
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
55
|
-
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
56
|
-
const newDir = path.join(os.homedir(), ".exe-os");
|
|
57
|
-
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
58
|
-
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
59
|
-
try {
|
|
60
|
-
renameSync(legacyDir, newDir);
|
|
61
|
-
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
62
|
-
`);
|
|
63
|
-
} catch {
|
|
64
|
-
return legacyDir;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return newDir;
|
|
68
|
-
}
|
|
69
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG;
|
|
70
|
-
var init_config = __esm({
|
|
71
|
-
"src/lib/config.ts"() {
|
|
72
|
-
"use strict";
|
|
73
|
-
init_secure_files();
|
|
74
|
-
EXE_AI_DIR = resolveDataDir();
|
|
75
|
-
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
76
|
-
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
77
|
-
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
78
|
-
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
79
|
-
CURRENT_CONFIG_VERSION = 1;
|
|
80
|
-
DEFAULT_CONFIG = {
|
|
81
|
-
config_version: CURRENT_CONFIG_VERSION,
|
|
82
|
-
dbPath: DB_PATH,
|
|
83
|
-
modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
|
|
84
|
-
embeddingDim: 1024,
|
|
85
|
-
batchSize: 20,
|
|
86
|
-
flushIntervalMs: 1e4,
|
|
87
|
-
autoIngestion: true,
|
|
88
|
-
autoRetrieval: true,
|
|
89
|
-
searchMode: "hybrid",
|
|
90
|
-
hookSearchMode: "hybrid",
|
|
91
|
-
fileGrepEnabled: true,
|
|
92
|
-
splashEffect: true,
|
|
93
|
-
consolidationEnabled: true,
|
|
94
|
-
consolidationIntervalMs: 6 * 60 * 60 * 1e3,
|
|
95
|
-
consolidationModel: "claude-haiku-4-5-20251001",
|
|
96
|
-
consolidationMaxCallsPerRun: 20,
|
|
97
|
-
selfQueryRouter: true,
|
|
98
|
-
selfQueryModel: "claude-haiku-4-5-20251001",
|
|
99
|
-
rerankerEnabled: true,
|
|
100
|
-
scalingRoadmap: {
|
|
101
|
-
rerankerAutoTrigger: {
|
|
102
|
-
enabled: true,
|
|
103
|
-
broadQueryMinCardinality: 5e4,
|
|
104
|
-
fetchTopK: 150,
|
|
105
|
-
returnTopK: 5
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
graphRagEnabled: true,
|
|
109
|
-
wikiEnabled: false,
|
|
110
|
-
wikiUrl: "",
|
|
111
|
-
wikiApiKey: "",
|
|
112
|
-
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
113
|
-
wikiWorkspaceMapping: {},
|
|
114
|
-
wikiAutoUpdate: true,
|
|
115
|
-
wikiAutoUpdateThreshold: 0.5,
|
|
116
|
-
wikiAutoUpdateCreateNew: true,
|
|
117
|
-
skillLearning: true,
|
|
118
|
-
skillThreshold: 3,
|
|
119
|
-
skillModel: "claude-haiku-4-5-20251001",
|
|
120
|
-
exeHeartbeat: {
|
|
121
|
-
enabled: true,
|
|
122
|
-
intervalSeconds: 60,
|
|
123
|
-
staleInProgressThresholdHours: 2
|
|
124
|
-
},
|
|
125
|
-
sessionLifecycle: {
|
|
126
|
-
idleKillEnabled: true,
|
|
127
|
-
idleKillTicksRequired: 3,
|
|
128
|
-
idleKillIntercomAckWindowMs: 1e4,
|
|
129
|
-
maxAutoInstances: 10
|
|
130
|
-
},
|
|
131
|
-
autoUpdate: {
|
|
132
|
-
checkOnBoot: true,
|
|
133
|
-
autoInstall: false,
|
|
134
|
-
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
135
|
-
},
|
|
136
|
-
support: {
|
|
137
|
-
bugReportEndpoint: "https://askexe.com/v1/support/bug-reports"
|
|
138
|
-
},
|
|
139
|
-
orchestration: {
|
|
140
|
-
phase: "phase_1_coo",
|
|
141
|
-
phaseSetBy: "default"
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// src/lib/runtime-table.ts
|
|
148
|
-
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
149
|
-
var init_runtime_table = __esm({
|
|
150
|
-
"src/lib/runtime-table.ts"() {
|
|
151
|
-
"use strict";
|
|
152
|
-
RUNTIME_TABLE = {
|
|
153
|
-
codex: {
|
|
154
|
-
binary: "codex",
|
|
155
|
-
launchMode: "interactive",
|
|
156
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
157
|
-
inlineFlag: "--no-alt-screen",
|
|
158
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
159
|
-
defaultModel: "gpt-5.5"
|
|
160
|
-
},
|
|
161
|
-
opencode: {
|
|
162
|
-
binary: "opencode",
|
|
163
|
-
launchMode: "exec",
|
|
164
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
165
|
-
inlineFlag: "",
|
|
166
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
167
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
DEFAULT_RUNTIME = "claude";
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// src/lib/agent-config.ts
|
|
175
|
-
var agent_config_exports = {};
|
|
176
|
-
__export(agent_config_exports, {
|
|
177
|
-
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
178
|
-
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
179
|
-
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
180
|
-
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
181
|
-
clearAgentRuntime: () => clearAgentRuntime,
|
|
182
|
-
getAgentRuntime: () => getAgentRuntime,
|
|
183
|
-
loadAgentConfig: () => loadAgentConfig,
|
|
184
|
-
normalizeCcModelName: () => normalizeCcModelName,
|
|
185
|
-
saveAgentConfig: () => saveAgentConfig,
|
|
186
|
-
setAgentMcps: () => setAgentMcps,
|
|
187
|
-
setAgentRuntime: () => setAgentRuntime
|
|
188
|
-
});
|
|
189
|
-
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
190
|
-
import path2 from "path";
|
|
191
|
-
function loadAgentConfig() {
|
|
192
|
-
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
193
|
-
try {
|
|
194
|
-
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
195
|
-
} catch {
|
|
196
|
-
return {};
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
function saveAgentConfig(config) {
|
|
200
|
-
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
201
|
-
ensurePrivateDirSync(dir);
|
|
202
|
-
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
203
|
-
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
204
|
-
}
|
|
205
|
-
function getAgentRuntime(agentId) {
|
|
206
|
-
const config = loadAgentConfig();
|
|
207
|
-
const entry = config[agentId];
|
|
208
|
-
if (entry) return entry;
|
|
209
|
-
const orgDefault = config["default"];
|
|
210
|
-
if (orgDefault) return orgDefault;
|
|
211
|
-
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
212
|
-
}
|
|
213
|
-
function normalizeCcModelName(model) {
|
|
214
|
-
let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
215
|
-
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
216
|
-
ccModel += "[1m]";
|
|
217
|
-
}
|
|
218
|
-
return ccModel;
|
|
219
|
-
}
|
|
220
|
-
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
221
|
-
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
222
|
-
if (!knownModels) {
|
|
223
|
-
return {
|
|
224
|
-
ok: false,
|
|
225
|
-
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
if (!knownModels.includes(model)) {
|
|
229
|
-
return {
|
|
230
|
-
ok: false,
|
|
231
|
-
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
const config = loadAgentConfig();
|
|
235
|
-
const existing = config[agentId];
|
|
236
|
-
const entry = { runtime, model };
|
|
237
|
-
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
238
|
-
if (mcps !== void 0) {
|
|
239
|
-
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
240
|
-
} else if (existing?.mcps) {
|
|
241
|
-
entry.mcps = existing.mcps;
|
|
242
|
-
}
|
|
243
|
-
config[agentId] = entry;
|
|
244
|
-
saveAgentConfig(config);
|
|
245
|
-
return { ok: true };
|
|
246
|
-
}
|
|
247
|
-
function setAgentMcps(agentId, mcps) {
|
|
248
|
-
const config = loadAgentConfig();
|
|
249
|
-
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
250
|
-
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
251
|
-
config[agentId] = existing;
|
|
252
|
-
saveAgentConfig(config);
|
|
253
|
-
return { ok: true };
|
|
254
|
-
}
|
|
255
|
-
function clearAgentRuntime(agentId) {
|
|
256
|
-
const config = loadAgentConfig();
|
|
257
|
-
delete config[agentId];
|
|
258
|
-
saveAgentConfig(config);
|
|
259
|
-
}
|
|
260
|
-
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
261
|
-
var init_agent_config = __esm({
|
|
262
|
-
"src/lib/agent-config.ts"() {
|
|
263
|
-
"use strict";
|
|
264
|
-
init_config();
|
|
265
|
-
init_runtime_table();
|
|
266
|
-
init_secure_files();
|
|
267
|
-
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
268
|
-
KNOWN_RUNTIMES = {
|
|
269
|
-
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
270
|
-
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
271
|
-
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
272
|
-
};
|
|
273
|
-
RUNTIME_LABELS = {
|
|
274
|
-
claude: "Claude Code (Anthropic)",
|
|
275
|
-
codex: "Codex (OpenAI)",
|
|
276
|
-
opencode: "OpenCode (open source)"
|
|
277
|
-
};
|
|
278
|
-
DEFAULT_MODELS = {
|
|
279
|
-
claude: "claude-opus-4.6",
|
|
280
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
281
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
// src/lib/employees.ts
|
|
287
|
-
var employees_exports = {};
|
|
288
|
-
__export(employees_exports, {
|
|
289
|
-
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
290
|
-
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
291
|
-
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
292
|
-
addEmployee: () => addEmployee,
|
|
293
|
-
baseAgentName: () => baseAgentName,
|
|
294
|
-
canCoordinate: () => canCoordinate,
|
|
295
|
-
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
296
|
-
getCoordinatorName: () => getCoordinatorName,
|
|
297
|
-
getEmployee: () => getEmployee,
|
|
298
|
-
getEmployeeByRole: () => getEmployeeByRole,
|
|
299
|
-
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
300
|
-
hasRole: () => hasRole,
|
|
301
|
-
hireEmployee: () => hireEmployee,
|
|
302
|
-
isCoordinatorName: () => isCoordinatorName,
|
|
303
|
-
isCoordinatorRole: () => isCoordinatorRole,
|
|
304
|
-
isMultiInstance: () => isMultiInstance,
|
|
305
|
-
loadEmployees: () => loadEmployees,
|
|
306
|
-
loadEmployeesSync: () => loadEmployeesSync,
|
|
307
|
-
normalizeRole: () => normalizeRole,
|
|
308
|
-
normalizeRosterCase: () => normalizeRosterCase,
|
|
309
|
-
registerBinSymlinks: () => registerBinSymlinks,
|
|
310
|
-
saveEmployees: () => saveEmployees,
|
|
311
|
-
validateEmployeeName: () => validateEmployeeName
|
|
312
|
-
});
|
|
313
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
314
|
-
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
315
|
-
import { execSync } from "child_process";
|
|
316
|
-
import path3 from "path";
|
|
317
|
-
import os2 from "os";
|
|
318
|
-
function normalizeRole(role) {
|
|
319
|
-
return (role ?? "").trim().toLowerCase();
|
|
320
|
-
}
|
|
321
|
-
function isCoordinatorRole(role) {
|
|
322
|
-
return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
|
|
323
|
-
}
|
|
324
|
-
function getCoordinatorEmployee(employees) {
|
|
325
|
-
return employees.find((e) => isCoordinatorRole(e.role));
|
|
326
|
-
}
|
|
327
|
-
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
328
|
-
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
329
|
-
}
|
|
330
|
-
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
331
|
-
if (!agentName) return false;
|
|
332
|
-
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
333
|
-
}
|
|
334
|
-
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
335
|
-
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
336
|
-
}
|
|
337
|
-
function validateEmployeeName(name) {
|
|
338
|
-
if (!name) {
|
|
339
|
-
return { valid: false, error: "Name is required" };
|
|
340
|
-
}
|
|
341
|
-
if (name.length > 32) {
|
|
342
|
-
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
343
|
-
}
|
|
344
|
-
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
345
|
-
return {
|
|
346
|
-
valid: false,
|
|
347
|
-
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
return { valid: true };
|
|
351
|
-
}
|
|
352
|
-
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
353
|
-
if (!existsSync4(employeesPath)) {
|
|
354
|
-
return [];
|
|
355
|
-
}
|
|
356
|
-
const raw = await readFile2(employeesPath, "utf-8");
|
|
357
|
-
try {
|
|
358
|
-
return JSON.parse(raw);
|
|
359
|
-
} catch {
|
|
360
|
-
return [];
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
364
|
-
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
365
|
-
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
366
|
-
}
|
|
367
|
-
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
368
|
-
if (!existsSync4(employeesPath)) return [];
|
|
369
|
-
try {
|
|
370
|
-
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
371
|
-
} catch {
|
|
372
|
-
return [];
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
function getEmployee(employees, name) {
|
|
376
|
-
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
377
|
-
}
|
|
378
|
-
function getEmployeeByRole(employees, role) {
|
|
379
|
-
const lower = role.toLowerCase();
|
|
380
|
-
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
381
|
-
}
|
|
382
|
-
function getEmployeeNamesByRole(employees, role) {
|
|
383
|
-
const lower = role.toLowerCase();
|
|
384
|
-
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
385
|
-
}
|
|
386
|
-
function hasRole(agentName, role) {
|
|
387
|
-
const employees = loadEmployeesSync();
|
|
388
|
-
const emp = getEmployee(employees, agentName);
|
|
389
|
-
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
390
|
-
}
|
|
391
|
-
function baseAgentName(name, employees) {
|
|
392
|
-
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
393
|
-
if (!match) return name;
|
|
394
|
-
const base = match[1];
|
|
395
|
-
const roster = employees ?? loadEmployeesSync();
|
|
396
|
-
if (getEmployee(roster, base)) return base;
|
|
397
|
-
return name;
|
|
398
|
-
}
|
|
399
|
-
function isMultiInstance(agentName, employees) {
|
|
400
|
-
const roster = employees ?? loadEmployeesSync();
|
|
401
|
-
const emp = getEmployee(roster, agentName);
|
|
402
|
-
if (!emp) return false;
|
|
403
|
-
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
404
|
-
}
|
|
405
|
-
function addEmployee(employees, employee) {
|
|
406
|
-
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
407
|
-
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
408
|
-
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
409
|
-
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
410
|
-
}
|
|
411
|
-
return [...employees, normalized];
|
|
412
|
-
}
|
|
413
|
-
function appendToCoordinatorTeam(employee) {
|
|
414
|
-
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
415
|
-
if (!coordinator) return;
|
|
416
|
-
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
417
|
-
if (!existsSync4(idPath)) return;
|
|
418
|
-
const content = readFileSync3(idPath, "utf-8");
|
|
419
|
-
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
420
|
-
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
421
|
-
if (!teamMatch || teamMatch.index === void 0) return;
|
|
422
|
-
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
423
|
-
const nextHeading = afterTeam.match(/\n## /);
|
|
424
|
-
const entry = `
|
|
425
|
-
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
426
|
-
`;
|
|
427
|
-
let updated;
|
|
428
|
-
if (nextHeading && nextHeading.index !== void 0) {
|
|
429
|
-
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
430
|
-
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
431
|
-
} else {
|
|
432
|
-
updated = content.trimEnd() + "\n" + entry;
|
|
433
|
-
}
|
|
434
|
-
writeFileSync2(idPath, updated, "utf-8");
|
|
435
|
-
}
|
|
436
|
-
function capitalize(s) {
|
|
437
|
-
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
438
|
-
}
|
|
439
|
-
async function hireEmployee(employee) {
|
|
440
|
-
const employees = await loadEmployees();
|
|
441
|
-
const updated = addEmployee(employees, employee);
|
|
442
|
-
await saveEmployees(updated);
|
|
443
|
-
try {
|
|
444
|
-
appendToCoordinatorTeam(employee);
|
|
445
|
-
} catch {
|
|
446
|
-
}
|
|
447
|
-
try {
|
|
448
|
-
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
449
|
-
const config = loadAgentConfig2();
|
|
450
|
-
const name = employee.name.toLowerCase();
|
|
451
|
-
if (!config[name] && config["default"]) {
|
|
452
|
-
config[name] = { ...config["default"] };
|
|
453
|
-
saveAgentConfig2(config);
|
|
454
|
-
}
|
|
455
|
-
} catch {
|
|
456
|
-
}
|
|
457
|
-
return updated;
|
|
458
|
-
}
|
|
459
|
-
async function normalizeRosterCase(rosterPath) {
|
|
460
|
-
const employees = await loadEmployees(rosterPath);
|
|
461
|
-
let changed = false;
|
|
462
|
-
for (const emp of employees) {
|
|
463
|
-
if (emp.name !== emp.name.toLowerCase()) {
|
|
464
|
-
const oldName = emp.name;
|
|
465
|
-
emp.name = emp.name.toLowerCase();
|
|
466
|
-
changed = true;
|
|
467
|
-
try {
|
|
468
|
-
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
469
|
-
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
470
|
-
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
471
|
-
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
472
|
-
renameSync2(oldPath, newPath);
|
|
473
|
-
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
474
|
-
const content = readFileSync3(oldPath, "utf-8");
|
|
475
|
-
writeFileSync2(newPath, content, "utf-8");
|
|
476
|
-
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
477
|
-
unlinkSync(oldPath);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
} catch {
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
if (changed) {
|
|
485
|
-
await saveEmployees(employees, rosterPath);
|
|
486
|
-
}
|
|
487
|
-
return changed;
|
|
488
|
-
}
|
|
489
|
-
function findExeBin() {
|
|
490
|
-
try {
|
|
491
|
-
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
492
|
-
} catch {
|
|
493
|
-
return null;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
function registerBinSymlinks(name) {
|
|
497
|
-
const created = [];
|
|
498
|
-
const skipped = [];
|
|
499
|
-
const errors = [];
|
|
500
|
-
const exeBinPath = findExeBin();
|
|
501
|
-
if (!exeBinPath) {
|
|
502
|
-
errors.push("Could not find 'exe-os' in PATH");
|
|
503
|
-
return { created, skipped, errors };
|
|
504
|
-
}
|
|
505
|
-
const binDir = path3.dirname(exeBinPath);
|
|
506
|
-
let target;
|
|
507
|
-
try {
|
|
508
|
-
target = readlinkSync(exeBinPath);
|
|
509
|
-
} catch {
|
|
510
|
-
errors.push("Could not read 'exe' symlink");
|
|
511
|
-
return { created, skipped, errors };
|
|
512
|
-
}
|
|
513
|
-
for (const suffix of ["", "-opencode"]) {
|
|
514
|
-
const linkName = `${name}${suffix}`;
|
|
515
|
-
const linkPath = path3.join(binDir, linkName);
|
|
516
|
-
if (existsSync4(linkPath)) {
|
|
517
|
-
skipped.push(linkName);
|
|
518
|
-
continue;
|
|
519
|
-
}
|
|
520
|
-
try {
|
|
521
|
-
symlinkSync(target, linkPath);
|
|
522
|
-
created.push(linkName);
|
|
523
|
-
} catch (err) {
|
|
524
|
-
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
return { created, skipped, errors };
|
|
528
|
-
}
|
|
529
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
530
|
-
var init_employees = __esm({
|
|
531
|
-
"src/lib/employees.ts"() {
|
|
532
|
-
"use strict";
|
|
533
|
-
init_config();
|
|
534
|
-
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
535
|
-
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
536
|
-
COORDINATOR_ROLE = "COO";
|
|
537
|
-
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
538
|
-
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
539
|
-
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
540
|
-
}
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
// src/lib/agent-symlinks.ts
|
|
544
|
-
import os3 from "os";
|
|
545
|
-
import path4 from "path";
|
|
546
2
|
import {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
} from "
|
|
553
|
-
|
|
554
|
-
return path4.join(homeDir, ".claude", "agents");
|
|
555
|
-
}
|
|
556
|
-
function identitySourcePath(homeDir, agentId) {
|
|
557
|
-
return path4.join(homeDir, ".exe-os", "identity", `${agentId}.md`);
|
|
558
|
-
}
|
|
559
|
-
function claudeAgentLinkPath(homeDir, agentId) {
|
|
560
|
-
return path4.join(claudeAgentsDir(homeDir), `${agentId}.md`);
|
|
561
|
-
}
|
|
562
|
-
function ensureAgentSymlink(agentId, homeDir = os3.homedir()) {
|
|
563
|
-
const target = identitySourcePath(homeDir, agentId);
|
|
564
|
-
const link = claudeAgentLinkPath(homeDir, agentId);
|
|
565
|
-
mkdirSync2(claudeAgentsDir(homeDir), { recursive: true });
|
|
566
|
-
if (existsSync5(link)) {
|
|
567
|
-
let stat;
|
|
568
|
-
try {
|
|
569
|
-
stat = lstatSync(link);
|
|
570
|
-
} catch {
|
|
571
|
-
return { agentId, action: "conflict", target, link, conflict: "stat_failed" };
|
|
572
|
-
}
|
|
573
|
-
if (!stat.isSymbolicLink()) {
|
|
574
|
-
return { agentId, action: "conflict", target, link, conflict: "regular_file" };
|
|
575
|
-
}
|
|
576
|
-
let currentTarget;
|
|
577
|
-
try {
|
|
578
|
-
currentTarget = readlinkSync2(link);
|
|
579
|
-
} catch {
|
|
580
|
-
return { agentId, action: "conflict", target, link, conflict: "readlink_failed" };
|
|
581
|
-
}
|
|
582
|
-
if (currentTarget === target) {
|
|
583
|
-
return { agentId, action: "already_correct", target, link };
|
|
584
|
-
}
|
|
585
|
-
return { agentId, action: "conflict", target, link, conflict: `points_to:${currentTarget}` };
|
|
586
|
-
}
|
|
587
|
-
try {
|
|
588
|
-
symlinkSync2(target, link);
|
|
589
|
-
} catch (err) {
|
|
590
|
-
return {
|
|
591
|
-
agentId,
|
|
592
|
-
action: "conflict",
|
|
593
|
-
target,
|
|
594
|
-
link,
|
|
595
|
-
conflict: err instanceof Error ? err.message : String(err)
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
return { agentId, action: "created", target, link };
|
|
599
|
-
}
|
|
600
|
-
async function ensureAllAgentSymlinks(homeDir = os3.homedir()) {
|
|
601
|
-
const employees = await loadEmployees();
|
|
602
|
-
return employees.map((emp) => ensureAgentSymlink(emp.name, homeDir));
|
|
603
|
-
}
|
|
604
|
-
var init_agent_symlinks = __esm({
|
|
605
|
-
"src/lib/agent-symlinks.ts"() {
|
|
606
|
-
"use strict";
|
|
607
|
-
init_employees();
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
// src/lib/mcp-prefix.ts
|
|
612
|
-
var MCP_PRIMARY_KEY, MCP_LEGACY_KEY, MCP_TOOL_PREFIXES;
|
|
613
|
-
var init_mcp_prefix = __esm({
|
|
614
|
-
"src/lib/mcp-prefix.ts"() {
|
|
615
|
-
"use strict";
|
|
616
|
-
MCP_PRIMARY_KEY = "exe-os";
|
|
617
|
-
MCP_LEGACY_KEY = "exe-mem";
|
|
618
|
-
MCP_TOOL_PREFIXES = [
|
|
619
|
-
`mcp__${MCP_PRIMARY_KEY}__`,
|
|
620
|
-
`mcp__${MCP_LEGACY_KEY}__`
|
|
621
|
-
];
|
|
622
|
-
}
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
// src/lib/preferences.ts
|
|
626
|
-
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
627
|
-
import path5 from "path";
|
|
628
|
-
import os4 from "os";
|
|
629
|
-
function loadPreferences(homeDir = os4.homedir()) {
|
|
630
|
-
const configPath = path5.join(homeDir, ".exe-os", "config.json");
|
|
631
|
-
if (!existsSync6(configPath)) return {};
|
|
632
|
-
try {
|
|
633
|
-
const config = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
634
|
-
return config.preferences ?? {};
|
|
635
|
-
} catch {
|
|
636
|
-
return {};
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
var init_preferences = __esm({
|
|
640
|
-
"src/lib/preferences.ts"() {
|
|
641
|
-
"use strict";
|
|
642
|
-
init_secure_files();
|
|
643
|
-
}
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
// src/adapters/mcp-http-config.ts
|
|
647
|
-
import { chmodSync as chmodSync2, existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
648
|
-
import { randomBytes } from "crypto";
|
|
649
|
-
import path6 from "path";
|
|
650
|
-
import os5 from "os";
|
|
651
|
-
function resolveDefaultAgentId() {
|
|
652
|
-
if (process.env.AGENT_ID && process.env.AGENT_ID !== "default") return process.env.AGENT_ID;
|
|
653
|
-
try {
|
|
654
|
-
const { loadEmployeesSync: loadEmployeesSync2 } = (init_employees(), __toCommonJS(employees_exports));
|
|
655
|
-
const coo = loadEmployeesSync2().find((e) => e.role === "COO");
|
|
656
|
-
if (coo) return coo.name;
|
|
657
|
-
} catch {
|
|
658
|
-
}
|
|
659
|
-
return "exe";
|
|
660
|
-
}
|
|
661
|
-
function mcpHttpPort() {
|
|
662
|
-
return process.env.EXE_MCP_PORT || DEFAULT_MCP_HTTP_PORT;
|
|
663
|
-
}
|
|
664
|
-
function mcpHttpUrl() {
|
|
665
|
-
return `http://127.0.0.1:${mcpHttpPort()}/mcp`;
|
|
666
|
-
}
|
|
667
|
-
function readOrCreateDaemonToken(homeDir = os5.homedir()) {
|
|
668
|
-
const exeDir = path6.join(homeDir, ".exe-os");
|
|
669
|
-
const tokenPath = path6.join(exeDir, "exed.token");
|
|
670
|
-
if (existsSync7(tokenPath)) {
|
|
671
|
-
try {
|
|
672
|
-
const token2 = readFileSync5(tokenPath, "utf-8").trim();
|
|
673
|
-
if (/^[a-f0-9]{64}$/i.test(token2)) return token2;
|
|
674
|
-
} catch {
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
const token = randomBytes(32).toString("hex");
|
|
678
|
-
mkdirSync3(exeDir, { recursive: true });
|
|
679
|
-
writeFileSync4(tokenPath, `${token}
|
|
680
|
-
`, "utf-8");
|
|
681
|
-
try {
|
|
682
|
-
chmodSync2(tokenPath, 384);
|
|
683
|
-
} catch {
|
|
684
|
-
}
|
|
685
|
-
return token;
|
|
686
|
-
}
|
|
687
|
-
function buildMcpHttpHeaders(homeDir = os5.homedir(), opts = {}) {
|
|
688
|
-
const agentId = opts.useShellPlaceholders ? "${AGENT_ID:-exe}" : opts.agentId ?? DEFAULT_MCP_HTTP_AGENT_ID;
|
|
689
|
-
const agentRole = opts.useShellPlaceholders ? "${AGENT_ROLE:-COO}" : opts.agentRole ?? DEFAULT_MCP_HTTP_AGENT_ROLE;
|
|
690
|
-
const sessionName = opts.useShellPlaceholders ? "" : process.env.EXE_SESSION_NAME ?? "";
|
|
691
|
-
const headers = {
|
|
692
|
-
Authorization: `Bearer ${readOrCreateDaemonToken(homeDir)}`,
|
|
693
|
-
"X-Agent-Id": agentId,
|
|
694
|
-
"X-Agent-Role": agentRole
|
|
695
|
-
};
|
|
696
|
-
if (sessionName) headers["X-Exe-Session"] = sessionName;
|
|
697
|
-
return headers;
|
|
698
|
-
}
|
|
699
|
-
function buildClaudeHttpMcpEntry(homeDir = os5.homedir()) {
|
|
700
|
-
return {
|
|
701
|
-
type: "http",
|
|
702
|
-
url: mcpHttpUrl(),
|
|
703
|
-
headers: buildMcpHttpHeaders(homeDir, { useShellPlaceholders: true })
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
var DEFAULT_MCP_HTTP_PORT, DEFAULT_MCP_HTTP_AGENT_ID, DEFAULT_MCP_HTTP_AGENT_ROLE;
|
|
707
|
-
var init_mcp_http_config = __esm({
|
|
708
|
-
"src/adapters/mcp-http-config.ts"() {
|
|
709
|
-
"use strict";
|
|
710
|
-
DEFAULT_MCP_HTTP_PORT = "48739";
|
|
711
|
-
DEFAULT_MCP_HTTP_AGENT_ID = resolveDefaultAgentId();
|
|
712
|
-
DEFAULT_MCP_HTTP_AGENT_ROLE = "COO";
|
|
713
|
-
}
|
|
714
|
-
});
|
|
715
|
-
|
|
716
|
-
// src/adapters/runtime-hook-manifest.ts
|
|
717
|
-
function isLegacyHomeDirHookCommand(command) {
|
|
718
|
-
return LEGACY_HOME_DIR_HOOK_PATTERNS.some((pattern) => command.includes(pattern));
|
|
719
|
-
}
|
|
720
|
-
function commandHasAnyMarker(command, markers) {
|
|
721
|
-
return markers.some((marker) => command.includes(marker));
|
|
722
|
-
}
|
|
723
|
-
function isLegacySplitPostToolCommand(command) {
|
|
724
|
-
return commandHasAnyMarker(command, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS);
|
|
725
|
-
}
|
|
726
|
-
var EXE_HOOKS, EXE_HOOK_MANIFEST, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS, LEGACY_HOME_DIR_HOOK_PATTERNS;
|
|
727
|
-
var init_runtime_hook_manifest = __esm({
|
|
728
|
-
"src/adapters/runtime-hook-manifest.ts"() {
|
|
729
|
-
"use strict";
|
|
730
|
-
EXE_HOOKS = {
|
|
731
|
-
postToolCombined: "dist/hooks/post-tool-combined.js",
|
|
732
|
-
sessionStart: "dist/hooks/session-start.js",
|
|
733
|
-
promptSubmit: "dist/hooks/prompt-submit.js",
|
|
734
|
-
heartbeat: "dist/hooks/exe-heartbeat-hook.js",
|
|
735
|
-
stop: "dist/hooks/stop.js",
|
|
736
|
-
preToolUse: "dist/hooks/pre-tool-use.js",
|
|
737
|
-
subagentStop: "dist/hooks/subagent-stop.js",
|
|
738
|
-
preCompact: "dist/hooks/pre-compact.js",
|
|
739
|
-
postCompact: "dist/hooks/post-compact.js",
|
|
740
|
-
sessionEnd: "dist/hooks/session-end.js",
|
|
741
|
-
notification: "dist/hooks/notification.js",
|
|
742
|
-
instructionsLoaded: "dist/hooks/instructions-loaded.js"
|
|
743
|
-
};
|
|
744
|
-
EXE_HOOK_MANIFEST = [
|
|
745
|
-
{
|
|
746
|
-
key: "postToolCombined",
|
|
747
|
-
event: "PostToolUse",
|
|
748
|
-
commandMarker: EXE_HOOKS.postToolCombined,
|
|
749
|
-
owner: "exe-os",
|
|
750
|
-
purpose: "Single PostToolUse entrypoint for ingestion, error recall, summaries, and bug detection.",
|
|
751
|
-
runtimes: ["claude", "codex", "opencode"],
|
|
752
|
-
checkpointRole: "none"
|
|
753
|
-
},
|
|
754
|
-
{
|
|
755
|
-
key: "sessionStart",
|
|
756
|
-
event: "SessionStart",
|
|
757
|
-
commandMarker: EXE_HOOKS.sessionStart,
|
|
758
|
-
owner: "exe-os",
|
|
759
|
-
purpose: "Loads agent identity, procedures, and boot context.",
|
|
760
|
-
runtimes: ["claude", "codex", "opencode"],
|
|
761
|
-
checkpointRole: "none"
|
|
762
|
-
},
|
|
763
|
-
{
|
|
764
|
-
key: "promptSubmit",
|
|
765
|
-
event: "UserPromptSubmit",
|
|
766
|
-
commandMarker: EXE_HOOKS.promptSubmit,
|
|
767
|
-
owner: "exe-os",
|
|
768
|
-
purpose: "Injects current tasks, pending reviews, and local context before each prompt.",
|
|
769
|
-
runtimes: ["claude", "codex", "opencode"],
|
|
770
|
-
checkpointRole: "none"
|
|
771
|
-
},
|
|
772
|
-
{
|
|
773
|
-
key: "heartbeat",
|
|
774
|
-
event: "UserPromptSubmit",
|
|
775
|
-
commandMarker: EXE_HOOKS.heartbeat,
|
|
776
|
-
owner: "exe-os",
|
|
777
|
-
purpose: "Lightweight heartbeat/status sidecar for Claude Code sessions.",
|
|
778
|
-
runtimes: ["claude"],
|
|
779
|
-
checkpointRole: "none"
|
|
780
|
-
},
|
|
781
|
-
{
|
|
782
|
-
key: "stop",
|
|
783
|
-
event: "Stop",
|
|
784
|
-
commandMarker: EXE_HOOKS.stop,
|
|
785
|
-
owner: "exe-os",
|
|
786
|
-
purpose: "Finalizes task state, capacity signals, and emergency checkpointing.",
|
|
787
|
-
runtimes: ["claude", "codex", "opencode"],
|
|
788
|
-
checkpointRole: "capacity_checkpoint"
|
|
789
|
-
},
|
|
790
|
-
{
|
|
791
|
-
key: "preToolUse",
|
|
792
|
-
event: "PreToolUse",
|
|
793
|
-
commandMarker: EXE_HOOKS.preToolUse,
|
|
794
|
-
owner: "exe-os",
|
|
795
|
-
purpose: "Preflight guardrails before shell/tool execution.",
|
|
796
|
-
runtimes: ["claude", "codex", "opencode"],
|
|
797
|
-
checkpointRole: "none"
|
|
798
|
-
},
|
|
799
|
-
{
|
|
800
|
-
key: "subagentStop",
|
|
801
|
-
event: "SubagentStop",
|
|
802
|
-
commandMarker: EXE_HOOKS.subagentStop,
|
|
803
|
-
owner: "exe-os",
|
|
804
|
-
purpose: "Captures subagent completion context.",
|
|
805
|
-
runtimes: ["claude"],
|
|
806
|
-
checkpointRole: "none"
|
|
807
|
-
},
|
|
808
|
-
{
|
|
809
|
-
key: "preCompact",
|
|
810
|
-
event: "PreCompact",
|
|
811
|
-
commandMarker: EXE_HOOKS.preCompact,
|
|
812
|
-
owner: "exe-os",
|
|
813
|
-
purpose: "Writes active-task snapshot and compaction recovery context.",
|
|
814
|
-
runtimes: ["claude"],
|
|
815
|
-
checkpointRole: "recovery_context"
|
|
816
|
-
},
|
|
817
|
-
{
|
|
818
|
-
key: "postCompact",
|
|
819
|
-
event: "PostCompact",
|
|
820
|
-
commandMarker: EXE_HOOKS.postCompact,
|
|
821
|
-
owner: "exe-os",
|
|
822
|
-
purpose: "Rehydrates recovery context after compaction.",
|
|
823
|
-
runtimes: ["claude"],
|
|
824
|
-
checkpointRole: "recovery_context"
|
|
825
|
-
},
|
|
826
|
-
{
|
|
827
|
-
key: "sessionEnd",
|
|
828
|
-
event: "SessionEnd",
|
|
829
|
-
commandMarker: EXE_HOOKS.sessionEnd,
|
|
830
|
-
owner: "exe-os",
|
|
831
|
-
purpose: "Stores session-end checkpoint and triages orphaned in-progress tasks.",
|
|
832
|
-
runtimes: ["claude"],
|
|
833
|
-
checkpointRole: "session_summary"
|
|
834
|
-
},
|
|
835
|
-
{
|
|
836
|
-
key: "notification",
|
|
837
|
-
event: "Notification",
|
|
838
|
-
commandMarker: EXE_HOOKS.notification,
|
|
839
|
-
owner: "exe-os",
|
|
840
|
-
purpose: "Captures runtime notifications and nudges.",
|
|
841
|
-
runtimes: ["claude"],
|
|
842
|
-
checkpointRole: "none"
|
|
843
|
-
},
|
|
844
|
-
{
|
|
845
|
-
key: "instructionsLoaded",
|
|
846
|
-
event: "InstructionsLoaded",
|
|
847
|
-
commandMarker: EXE_HOOKS.instructionsLoaded,
|
|
848
|
-
owner: "exe-os",
|
|
849
|
-
purpose: "Applies runtime instruction post-processing.",
|
|
850
|
-
runtimes: ["claude"],
|
|
851
|
-
checkpointRole: "none"
|
|
852
|
-
}
|
|
853
|
-
];
|
|
854
|
-
LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS = [
|
|
855
|
-
"dist/hooks/ingest.js",
|
|
856
|
-
"dist/hooks/error-recall.js",
|
|
857
|
-
"dist/hooks/ingest-worker.js"
|
|
858
|
-
];
|
|
859
|
-
LEGACY_HOME_DIR_HOOK_PATTERNS = [
|
|
860
|
-
".exe-os/hooks/",
|
|
861
|
-
// Old hook storage path (pre-npm-package era)
|
|
862
|
-
"log-prefix.js",
|
|
863
|
-
// Removed hook from early versions
|
|
864
|
-
"approval-bridge.log"
|
|
865
|
-
// Removed log redirect from early versions
|
|
866
|
-
];
|
|
867
|
-
}
|
|
868
|
-
});
|
|
869
|
-
|
|
870
|
-
// src/adapters/claude/installer.ts
|
|
871
|
-
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir, rm } from "fs/promises";
|
|
872
|
-
import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync5, copyFileSync, mkdirSync as mkdirSync4 } from "fs";
|
|
873
|
-
import { createHash } from "crypto";
|
|
874
|
-
import path7 from "path";
|
|
875
|
-
import os6 from "os";
|
|
876
|
-
import { execSync as execSync2 } from "child_process";
|
|
877
|
-
import { fileURLToPath } from "url";
|
|
878
|
-
function resolvePackageRoot() {
|
|
879
|
-
const thisFile = fileURLToPath(import.meta.url);
|
|
880
|
-
let dir = path7.dirname(thisFile);
|
|
881
|
-
const root = path7.parse(dir).root;
|
|
882
|
-
while (dir !== root) {
|
|
883
|
-
const pkgPath = path7.join(dir, "package.json");
|
|
884
|
-
if (existsSync8(pkgPath)) {
|
|
885
|
-
try {
|
|
886
|
-
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
887
|
-
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
888
|
-
} catch {
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
dir = path7.dirname(dir);
|
|
892
|
-
}
|
|
893
|
-
return path7.resolve(path7.dirname(thisFile), "..", "..", "..");
|
|
894
|
-
}
|
|
895
|
-
async function copySlashCommands(packageRoot, homeDir = os6.homedir()) {
|
|
896
|
-
let copied = 0;
|
|
897
|
-
let skipped = 0;
|
|
898
|
-
const skillsBase = path7.join(homeDir, ".claude", "skills");
|
|
899
|
-
const deprecatedExeLink = path7.join(skillsBase, "exe-link");
|
|
900
|
-
if (existsSync8(deprecatedExeLink)) {
|
|
901
|
-
await rm(deprecatedExeLink, { recursive: true, force: true });
|
|
902
|
-
}
|
|
903
|
-
const exeDir = path7.join(packageRoot, "src", "commands", "exe");
|
|
904
|
-
if (existsSync8(exeDir)) {
|
|
905
|
-
const entries = await readdir(exeDir);
|
|
906
|
-
const mdFiles = entries.filter((f) => f.endsWith(".md"));
|
|
907
|
-
for (const file of mdFiles) {
|
|
908
|
-
const name = file.replace(".md", "");
|
|
909
|
-
const destDir = path7.join(skillsBase, `exe-${name}`);
|
|
910
|
-
await mkdir3(destDir, { recursive: true });
|
|
911
|
-
const srcPath = path7.join(exeDir, file);
|
|
912
|
-
const destPath = path7.join(destDir, "SKILL.md");
|
|
913
|
-
const result = await copyAsSkill(srcPath, destPath, `exe-${name}`);
|
|
914
|
-
if (result) copied++;
|
|
915
|
-
else skipped++;
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
const topLevelSrc = path7.join(packageRoot, "src", "commands", "exe.md");
|
|
919
|
-
if (existsSync8(topLevelSrc)) {
|
|
920
|
-
const destDir = path7.join(skillsBase, "exe");
|
|
921
|
-
await mkdir3(destDir, { recursive: true });
|
|
922
|
-
const destPath = path7.join(destDir, "SKILL.md");
|
|
923
|
-
const result = await copyAsSkill(topLevelSrc, destPath, "exe");
|
|
924
|
-
if (result) copied++;
|
|
925
|
-
else skipped++;
|
|
926
|
-
}
|
|
927
|
-
return { copied, skipped };
|
|
928
|
-
}
|
|
929
|
-
async function copyAsSkill(srcPath, destPath, skillName) {
|
|
930
|
-
let content = await readFile3(srcPath, "utf-8");
|
|
931
|
-
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
932
|
-
if (fmMatch?.[1]) {
|
|
933
|
-
const fm = fmMatch[1];
|
|
934
|
-
if (fm.includes("name:")) {
|
|
935
|
-
content = content.replace(
|
|
936
|
-
/^(---\n[\s\S]*?)name:\s*[^\n]+/,
|
|
937
|
-
`$1name: ${skillName}`
|
|
938
|
-
);
|
|
939
|
-
} else {
|
|
940
|
-
content = content.replace(/^---\n/, `---
|
|
941
|
-
name: ${skillName}
|
|
942
|
-
`);
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
if (existsSync8(destPath)) {
|
|
946
|
-
const existing = await readFile3(destPath, "utf-8");
|
|
947
|
-
if (existing === content) return false;
|
|
948
|
-
}
|
|
949
|
-
await writeFile3(destPath, content);
|
|
950
|
-
return true;
|
|
951
|
-
}
|
|
952
|
-
function readJsonFile(filePath) {
|
|
953
|
-
try {
|
|
954
|
-
return JSON.parse(readFileSync6(filePath, "utf-8"));
|
|
955
|
-
} catch {
|
|
956
|
-
return null;
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
function findAncestorMcpJsons(startDir, homeDir) {
|
|
960
|
-
const files = [];
|
|
961
|
-
let dir = path7.resolve(startDir);
|
|
962
|
-
const root = path7.parse(dir).root;
|
|
963
|
-
const stop = path7.resolve(homeDir);
|
|
964
|
-
while (dir !== root) {
|
|
965
|
-
const candidate = path7.join(dir, ".mcp.json");
|
|
966
|
-
if (existsSync8(candidate)) files.push(candidate);
|
|
967
|
-
if (dir === stop) break;
|
|
968
|
-
dir = path7.dirname(dir);
|
|
969
|
-
}
|
|
970
|
-
return files;
|
|
971
|
-
}
|
|
972
|
-
function pathApplies(projectPath, cwd) {
|
|
973
|
-
const project = path7.resolve(projectPath);
|
|
974
|
-
const current = path7.resolve(cwd);
|
|
975
|
-
return current === project || current.startsWith(project + path7.sep);
|
|
976
|
-
}
|
|
977
|
-
function detectMcpNameCollisions(homeDir = os6.homedir(), cwd = process.cwd()) {
|
|
978
|
-
const claudeJsonPath = path7.join(homeDir, ".claude.json");
|
|
979
|
-
if (!existsSync8(claudeJsonPath)) return [];
|
|
980
|
-
const claudeJson = readJsonFile(claudeJsonPath);
|
|
981
|
-
if (!claudeJson?.projects) return [];
|
|
982
|
-
const collisions = [];
|
|
983
|
-
const mcpJsons = findAncestorMcpJsons(cwd, homeDir);
|
|
984
|
-
if (mcpJsons.length === 0) return [];
|
|
985
|
-
for (const [projectPath, projectConfig] of Object.entries(claudeJson.projects)) {
|
|
986
|
-
if (!pathApplies(projectPath, cwd)) continue;
|
|
987
|
-
const projectServerNames = new Set(Object.keys(projectConfig.mcpServers ?? {}));
|
|
988
|
-
if (projectServerNames.size === 0) continue;
|
|
989
|
-
const enabled = new Set(projectConfig.enabledMcpjsonServers ?? []);
|
|
990
|
-
for (const mcpJsonPath of mcpJsons) {
|
|
991
|
-
const mcpJson = readJsonFile(mcpJsonPath);
|
|
992
|
-
const mcpServerNames = Object.keys(mcpJson?.mcpServers ?? {});
|
|
993
|
-
for (const serverName of mcpServerNames) {
|
|
994
|
-
if (!projectServerNames.has(serverName)) continue;
|
|
995
|
-
collisions.push({
|
|
996
|
-
mcpJsonPath,
|
|
997
|
-
projectPath,
|
|
998
|
-
serverName,
|
|
999
|
-
disabledInMcpJson: !enabled.has(serverName)
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
return collisions;
|
|
1005
|
-
}
|
|
1006
|
-
function buildStdioMcpEntry(packageRoot) {
|
|
1007
|
-
return {
|
|
1008
|
-
type: "stdio",
|
|
1009
|
-
command: "node",
|
|
1010
|
-
args: [path7.join(packageRoot, "dist", "mcp", "server.js")],
|
|
1011
|
-
env: {}
|
|
1012
|
-
};
|
|
1013
|
-
}
|
|
1014
|
-
function mcpTransportMode() {
|
|
1015
|
-
const value = process.env.EXE_OS_MCP_TRANSPORT?.trim().toLowerCase();
|
|
1016
|
-
if (value === "stdio") return "stdio";
|
|
1017
|
-
if (value === "http") return "http";
|
|
1018
|
-
const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
|
|
1019
|
-
if (totalGB <= 8) return "stdio";
|
|
1020
|
-
return "http";
|
|
1021
|
-
}
|
|
1022
|
-
async function registerMcpServer(packageRoot, homeDir = os6.homedir()) {
|
|
1023
|
-
const claudeJsonPath = path7.join(homeDir, ".claude.json");
|
|
1024
|
-
let claudeJson = {};
|
|
1025
|
-
if (existsSync8(claudeJsonPath)) {
|
|
1026
|
-
try {
|
|
1027
|
-
claudeJson = JSON.parse(await readFile3(claudeJsonPath, "utf-8"));
|
|
1028
|
-
} catch {
|
|
1029
|
-
claudeJson = {};
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
if (!claudeJson.mcpServers) {
|
|
1033
|
-
claudeJson.mcpServers = {};
|
|
1034
|
-
}
|
|
1035
|
-
const newEntry = mcpTransportMode() === "stdio" ? buildStdioMcpEntry(packageRoot) : buildClaudeHttpMcpEntry(homeDir);
|
|
1036
|
-
if (claudeJson.mcpServers[MCP_LEGACY_KEY]) {
|
|
1037
|
-
delete claudeJson.mcpServers[MCP_LEGACY_KEY];
|
|
1038
|
-
process.stderr.write("exe-os: migrated MCP server key exe-mem \u2192 exe-os\n");
|
|
1039
|
-
}
|
|
1040
|
-
if (claudeJson.projects) {
|
|
1041
|
-
for (const [projectPath, projectConfig] of Object.entries(claudeJson.projects)) {
|
|
1042
|
-
if (projectConfig.mcpServers?.[MCP_LEGACY_KEY]) {
|
|
1043
|
-
delete projectConfig.mcpServers[MCP_LEGACY_KEY];
|
|
1044
|
-
process.stderr.write(`exe-os: removed stale project-level exe-mem from ${projectPath}
|
|
1045
|
-
`);
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
const currentOs = claudeJson.mcpServers[MCP_PRIMARY_KEY];
|
|
1050
|
-
const osMatches = currentOs && JSON.stringify(currentOs) === JSON.stringify(newEntry);
|
|
1051
|
-
if (osMatches) {
|
|
1052
|
-
await cleanSettingsJsonMcp(path7.join(homeDir, ".claude", "settings.json"));
|
|
1053
|
-
await migratePermissionsToExeOs(path7.join(homeDir, ".claude", "settings.json"));
|
|
1054
|
-
const collisions2 = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
|
|
1055
|
-
for (const c of collisions2) {
|
|
1056
|
-
process.stderr.write(
|
|
1057
|
-
`exe-os: WARNING Claude Code MCP name collision: ${c.serverName} exists in ${c.mcpJsonPath} and ~/.claude.json project ${c.projectPath}. Remove or rename the .mcp.json entry if tools do not surface.
|
|
1058
|
-
`
|
|
1059
|
-
);
|
|
1060
|
-
}
|
|
1061
|
-
await registerExeRustMcpConfig(packageRoot, homeDir);
|
|
1062
|
-
return false;
|
|
1063
|
-
}
|
|
1064
|
-
claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
|
|
1065
|
-
await writeFile3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
1066
|
-
await cleanSettingsJsonMcp(path7.join(homeDir, ".claude", "settings.json"));
|
|
1067
|
-
await migratePermissionsToExeOs(path7.join(homeDir, ".claude", "settings.json"));
|
|
1068
|
-
const collisions = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
|
|
1069
|
-
for (const c of collisions) {
|
|
1070
|
-
process.stderr.write(
|
|
1071
|
-
`exe-os: WARNING Claude Code MCP name collision: ${c.serverName} exists in ${c.mcpJsonPath} and ~/.claude.json project ${c.projectPath}. Remove or rename the .mcp.json entry if tools do not surface.
|
|
1072
|
-
`
|
|
1073
|
-
);
|
|
1074
|
-
}
|
|
1075
|
-
await registerExeRustMcpConfig(packageRoot, homeDir);
|
|
1076
|
-
return true;
|
|
1077
|
-
}
|
|
1078
|
-
async function registerExeRustMcpConfig(packageRoot, homeDir) {
|
|
1079
|
-
const exeDir = path7.join(homeDir, ".exe");
|
|
1080
|
-
const exeMcpPath = path7.join(exeDir, "mcp.json");
|
|
1081
|
-
await mkdir3(exeDir, { recursive: true });
|
|
1082
|
-
let config = { servers: {} };
|
|
1083
|
-
if (existsSync8(exeMcpPath)) {
|
|
1084
|
-
try {
|
|
1085
|
-
config = JSON.parse(await readFile3(exeMcpPath, "utf-8"));
|
|
1086
|
-
if (!config.servers) config.servers = {};
|
|
1087
|
-
} catch {
|
|
1088
|
-
config = { servers: {} };
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
const stdioEntry = {
|
|
1092
|
-
command: "node",
|
|
1093
|
-
args: [path7.join(packageRoot, "dist", "mcp", "server.js")],
|
|
1094
|
-
env: {},
|
|
1095
|
-
shared: true
|
|
1096
|
-
};
|
|
1097
|
-
const existing = config.servers[MCP_PRIMARY_KEY];
|
|
1098
|
-
if (existing && JSON.stringify(existing) === JSON.stringify(stdioEntry)) {
|
|
1099
|
-
return;
|
|
1100
|
-
}
|
|
1101
|
-
if (config.servers[MCP_LEGACY_KEY]) {
|
|
1102
|
-
delete config.servers[MCP_LEGACY_KEY];
|
|
1103
|
-
}
|
|
1104
|
-
config.servers[MCP_PRIMARY_KEY] = stdioEntry;
|
|
1105
|
-
await writeFile3(exeMcpPath, JSON.stringify(config, null, 2) + "\n");
|
|
1106
|
-
process.stderr.write("exe-os: registered MCP server in ~/.exe/mcp.json (Rust runtime)\n");
|
|
1107
|
-
}
|
|
1108
|
-
async function cleanSettingsJsonMcp(settingsPath) {
|
|
1109
|
-
if (!existsSync8(settingsPath)) return;
|
|
1110
|
-
try {
|
|
1111
|
-
const settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1112
|
-
const servers = settings.mcpServers;
|
|
1113
|
-
if (!servers) return;
|
|
1114
|
-
let changed = false;
|
|
1115
|
-
if (servers[MCP_PRIMARY_KEY]) {
|
|
1116
|
-
delete servers[MCP_PRIMARY_KEY];
|
|
1117
|
-
changed = true;
|
|
1118
|
-
}
|
|
1119
|
-
if (servers[MCP_LEGACY_KEY]) {
|
|
1120
|
-
delete servers[MCP_LEGACY_KEY];
|
|
1121
|
-
changed = true;
|
|
1122
|
-
}
|
|
1123
|
-
if (changed) {
|
|
1124
|
-
await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1125
|
-
}
|
|
1126
|
-
} catch {
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
async function migratePermissionsToExeOs(settingsPath) {
|
|
1130
|
-
if (!existsSync8(settingsPath)) return;
|
|
1131
|
-
try {
|
|
1132
|
-
const settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1133
|
-
const permissions = settings.permissions;
|
|
1134
|
-
if (!permissions || !Array.isArray(permissions.allow)) return;
|
|
1135
|
-
const allow = permissions.allow;
|
|
1136
|
-
let migrated = 0;
|
|
1137
|
-
for (let i = 0; i < allow.length; i++) {
|
|
1138
|
-
if (allow[i].startsWith("mcp__exe-mem__")) {
|
|
1139
|
-
const newName = allow[i].replace("mcp__exe-mem__", "mcp__exe-os__");
|
|
1140
|
-
if (!allow.includes(newName)) {
|
|
1141
|
-
allow[i] = newName;
|
|
1142
|
-
} else {
|
|
1143
|
-
allow[i] = "__REMOVE__";
|
|
1144
|
-
}
|
|
1145
|
-
migrated++;
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
if (migrated > 0) {
|
|
1149
|
-
permissions.allow = allow.filter((e) => e !== "__REMOVE__");
|
|
1150
|
-
permissions.allow = [...new Set(permissions.allow)];
|
|
1151
|
-
await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1152
|
-
process.stderr.write(`exe-os: migrated ${migrated} permission(s) from exe-mem \u2192 exe-os
|
|
1153
|
-
`);
|
|
1154
|
-
}
|
|
1155
|
-
} catch {
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
async function mergeHooks(packageRoot, homeDir = os6.homedir()) {
|
|
1159
|
-
const settingsPath = path7.join(homeDir, ".claude", "settings.json");
|
|
1160
|
-
const logsDir = path7.join(homeDir, ".exe-os", "logs");
|
|
1161
|
-
const hookLogPath = path7.join(logsDir, "hooks.log");
|
|
1162
|
-
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
1163
|
-
await mkdir3(logsDir, { recursive: true });
|
|
1164
|
-
let settings = {};
|
|
1165
|
-
if (existsSync8(settingsPath)) {
|
|
1166
|
-
try {
|
|
1167
|
-
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1168
|
-
} catch {
|
|
1169
|
-
settings = {};
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
if (!settings.hooks) {
|
|
1173
|
-
settings.hooks = {};
|
|
1174
|
-
}
|
|
1175
|
-
if (settings.hooks && typeof settings.hooks !== "object") {
|
|
1176
|
-
console.warn("[exe-os] Unexpected hooks schema in settings.json \u2014 skipping hook installation");
|
|
1177
|
-
return { added: 0, skipped: 0 };
|
|
1178
|
-
}
|
|
1179
|
-
const hooksToRegister = [
|
|
1180
|
-
{
|
|
1181
|
-
event: "PostToolUse",
|
|
1182
|
-
group: {
|
|
1183
|
-
matcher: "Bash|Edit|Write|Read|Glob|Grep|Agent|mcp__.*",
|
|
1184
|
-
hooks: [
|
|
1185
|
-
{
|
|
1186
|
-
type: "command",
|
|
1187
|
-
command: `node "${path7.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
|
|
1188
|
-
}
|
|
1189
|
-
]
|
|
1190
|
-
},
|
|
1191
|
-
marker: EXE_HOOKS.postToolCombined
|
|
1192
|
-
},
|
|
1193
|
-
{
|
|
1194
|
-
event: "SessionStart",
|
|
1195
|
-
group: {
|
|
1196
|
-
hooks: [
|
|
1197
|
-
{
|
|
1198
|
-
type: "command",
|
|
1199
|
-
command: `node "${path7.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
1200
|
-
timeout: 1e4
|
|
1201
|
-
}
|
|
1202
|
-
]
|
|
1203
|
-
},
|
|
1204
|
-
marker: EXE_HOOKS.sessionStart
|
|
1205
|
-
},
|
|
1206
|
-
{
|
|
1207
|
-
event: "UserPromptSubmit",
|
|
1208
|
-
group: {
|
|
1209
|
-
hooks: [
|
|
1210
|
-
{
|
|
1211
|
-
type: "command",
|
|
1212
|
-
command: `node "${path7.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
1213
|
-
}
|
|
1214
|
-
]
|
|
1215
|
-
},
|
|
1216
|
-
marker: EXE_HOOKS.promptSubmit
|
|
1217
|
-
},
|
|
1218
|
-
{
|
|
1219
|
-
event: "UserPromptSubmit",
|
|
1220
|
-
group: {
|
|
1221
|
-
hooks: [
|
|
1222
|
-
{
|
|
1223
|
-
type: "command",
|
|
1224
|
-
command: `node "${path7.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
|
|
1225
|
-
timeout: 5e3
|
|
1226
|
-
}
|
|
1227
|
-
]
|
|
1228
|
-
},
|
|
1229
|
-
marker: EXE_HOOKS.heartbeat
|
|
1230
|
-
},
|
|
1231
|
-
{
|
|
1232
|
-
event: "Stop",
|
|
1233
|
-
group: {
|
|
1234
|
-
hooks: [
|
|
1235
|
-
{
|
|
1236
|
-
type: "command",
|
|
1237
|
-
command: `node "${path7.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
1238
|
-
}
|
|
1239
|
-
]
|
|
1240
|
-
},
|
|
1241
|
-
marker: EXE_HOOKS.stop
|
|
1242
|
-
},
|
|
1243
|
-
{
|
|
1244
|
-
event: "PreToolUse",
|
|
1245
|
-
group: {
|
|
1246
|
-
matcher: "Write|Edit|Read|Bash",
|
|
1247
|
-
hooks: [
|
|
1248
|
-
{
|
|
1249
|
-
type: "command",
|
|
1250
|
-
command: `node "${path7.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
1251
|
-
}
|
|
1252
|
-
]
|
|
1253
|
-
},
|
|
1254
|
-
marker: EXE_HOOKS.preToolUse
|
|
1255
|
-
},
|
|
1256
|
-
{
|
|
1257
|
-
event: "SubagentStop",
|
|
1258
|
-
group: {
|
|
1259
|
-
hooks: [
|
|
1260
|
-
{
|
|
1261
|
-
type: "command",
|
|
1262
|
-
command: `node "${path7.join(packageRoot, "dist", "hooks", "subagent-stop.js")}"${logSuffix}`
|
|
1263
|
-
}
|
|
1264
|
-
]
|
|
1265
|
-
},
|
|
1266
|
-
marker: EXE_HOOKS.subagentStop
|
|
1267
|
-
},
|
|
1268
|
-
{
|
|
1269
|
-
event: "PreCompact",
|
|
1270
|
-
group: {
|
|
1271
|
-
hooks: [
|
|
1272
|
-
{
|
|
1273
|
-
type: "command",
|
|
1274
|
-
command: `node "${path7.join(packageRoot, "dist", "hooks", "pre-compact.js")}"${logSuffix}`,
|
|
1275
|
-
timeout: 1e4
|
|
1276
|
-
}
|
|
1277
|
-
]
|
|
1278
|
-
},
|
|
1279
|
-
marker: EXE_HOOKS.preCompact
|
|
1280
|
-
},
|
|
1281
|
-
{
|
|
1282
|
-
event: "SessionEnd",
|
|
1283
|
-
group: {
|
|
1284
|
-
hooks: [
|
|
1285
|
-
{
|
|
1286
|
-
type: "command",
|
|
1287
|
-
command: `node "${path7.join(packageRoot, "dist", "hooks", "session-end.js")}"${logSuffix}`
|
|
1288
|
-
}
|
|
1289
|
-
]
|
|
1290
|
-
},
|
|
1291
|
-
marker: EXE_HOOKS.sessionEnd
|
|
1292
|
-
},
|
|
1293
|
-
{
|
|
1294
|
-
event: "Notification",
|
|
1295
|
-
group: {
|
|
1296
|
-
hooks: [
|
|
1297
|
-
{
|
|
1298
|
-
type: "command",
|
|
1299
|
-
command: `node "${path7.join(packageRoot, "dist", "hooks", "notification.js")}"${logSuffix}`
|
|
1300
|
-
}
|
|
1301
|
-
]
|
|
1302
|
-
},
|
|
1303
|
-
marker: EXE_HOOKS.notification
|
|
1304
|
-
},
|
|
1305
|
-
{
|
|
1306
|
-
event: "PostCompact",
|
|
1307
|
-
group: {
|
|
1308
|
-
hooks: [
|
|
1309
|
-
{
|
|
1310
|
-
type: "command",
|
|
1311
|
-
command: `node "${path7.join(packageRoot, "dist", "hooks", "post-compact.js")}"${logSuffix}`,
|
|
1312
|
-
timeout: 1e4
|
|
1313
|
-
}
|
|
1314
|
-
]
|
|
1315
|
-
},
|
|
1316
|
-
marker: EXE_HOOKS.postCompact
|
|
1317
|
-
},
|
|
1318
|
-
{
|
|
1319
|
-
event: "InstructionsLoaded",
|
|
1320
|
-
group: {
|
|
1321
|
-
hooks: [
|
|
1322
|
-
{
|
|
1323
|
-
type: "command",
|
|
1324
|
-
command: `node "${path7.join(packageRoot, "dist", "hooks", "instructions-loaded.js")}"${logSuffix}`
|
|
1325
|
-
}
|
|
1326
|
-
]
|
|
1327
|
-
},
|
|
1328
|
-
marker: EXE_HOOKS.instructionsLoaded
|
|
1329
|
-
}
|
|
1330
|
-
];
|
|
1331
|
-
let added = 0;
|
|
1332
|
-
let skipped = 0;
|
|
1333
|
-
for (const eventKey of Object.keys(settings.hooks)) {
|
|
1334
|
-
const groups = settings.hooks[eventKey];
|
|
1335
|
-
if (!Array.isArray(groups)) continue;
|
|
1336
|
-
settings.hooks[eventKey] = groups.map((g) => ({
|
|
1337
|
-
...g,
|
|
1338
|
-
hooks: g.hooks.filter((h) => !isLegacyHomeDirHookCommand(h.command))
|
|
1339
|
-
})).filter((g) => g.hooks.length > 0);
|
|
1340
|
-
}
|
|
1341
|
-
const postToolGroups = settings.hooks["PostToolUse"];
|
|
1342
|
-
if (Array.isArray(postToolGroups)) {
|
|
1343
|
-
settings.hooks["PostToolUse"] = postToolGroups.map((g) => ({
|
|
1344
|
-
...g,
|
|
1345
|
-
hooks: g.hooks.filter((h) => !isLegacySplitPostToolCommand(h.command))
|
|
1346
|
-
})).filter((g) => g.hooks.length > 0);
|
|
1347
|
-
}
|
|
1348
|
-
for (const { event, group, marker } of hooksToRegister) {
|
|
1349
|
-
if (!settings.hooks[event]) {
|
|
1350
|
-
settings.hooks[event] = [];
|
|
1351
|
-
}
|
|
1352
|
-
if (!Array.isArray(settings.hooks[event])) {
|
|
1353
|
-
console.warn(`[exe-os] Hook event "${event}" has unexpected structure \u2014 skipping`);
|
|
1354
|
-
skipped++;
|
|
1355
|
-
continue;
|
|
1356
|
-
}
|
|
1357
|
-
const existing = settings.hooks[event];
|
|
1358
|
-
const correctCommand = group.hooks[0]?.command ?? "";
|
|
1359
|
-
const alreadyCorrect = existing.some(
|
|
1360
|
-
(g) => g.hooks.some((h) => h.command === correctCommand)
|
|
1361
|
-
);
|
|
1362
|
-
if (alreadyCorrect) {
|
|
1363
|
-
skipped++;
|
|
1364
|
-
} else {
|
|
1365
|
-
settings.hooks[event] = existing.filter(
|
|
1366
|
-
(g) => !g.hooks.some((h) => h.command.includes(marker))
|
|
1367
|
-
);
|
|
1368
|
-
settings.hooks[event].push(group);
|
|
1369
|
-
added++;
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
const perms = settings.permissions;
|
|
1373
|
-
if (!perms) {
|
|
1374
|
-
settings.permissions = {};
|
|
1375
|
-
}
|
|
1376
|
-
const permissions = settings.permissions;
|
|
1377
|
-
if (!Array.isArray(permissions.allow)) {
|
|
1378
|
-
permissions.allow = [];
|
|
1379
|
-
}
|
|
1380
|
-
const toolNames = [
|
|
1381
|
-
// Core memory
|
|
1382
|
-
"store_memory",
|
|
1383
|
-
"recall_my_memory",
|
|
1384
|
-
"commit_to_long_term_memory",
|
|
1385
|
-
"consolidate_memories",
|
|
1386
|
-
"ask_team_memory",
|
|
1387
|
-
"get_session_context",
|
|
1388
|
-
// Tasks
|
|
1389
|
-
"create_task",
|
|
1390
|
-
"list_tasks",
|
|
1391
|
-
"get_task",
|
|
1392
|
-
"update_task",
|
|
1393
|
-
"close_task",
|
|
1394
|
-
"checkpoint_task",
|
|
1395
|
-
// Behaviors
|
|
1396
|
-
"store_behavior",
|
|
1397
|
-
"deactivate_behavior",
|
|
1398
|
-
"list_behaviors",
|
|
1399
|
-
// Identity
|
|
1400
|
-
"get_identity",
|
|
1401
|
-
"update_identity",
|
|
1402
|
-
// Messaging
|
|
1403
|
-
"send_message",
|
|
1404
|
-
"acknowledge_messages",
|
|
1405
|
-
"send_whatsapp",
|
|
1406
|
-
"query_conversations",
|
|
1407
|
-
// Reminders + triggers
|
|
1408
|
-
"create_reminder",
|
|
1409
|
-
"complete_reminder",
|
|
1410
|
-
"list_reminders",
|
|
1411
|
-
"create_trigger",
|
|
1412
|
-
"list_triggers",
|
|
1413
|
-
// GraphRAG
|
|
1414
|
-
"query_relationships",
|
|
1415
|
-
"merge_entities",
|
|
1416
|
-
// Documents + wiki
|
|
1417
|
-
"ingest_document",
|
|
1418
|
-
"list_documents",
|
|
1419
|
-
"purge_document",
|
|
1420
|
-
"rerank_documents",
|
|
1421
|
-
"set_document_importance",
|
|
1422
|
-
"get_wiki_page",
|
|
1423
|
-
"list_wiki_pages",
|
|
1424
|
-
// System
|
|
1425
|
-
"load_skill",
|
|
1426
|
-
"apply_starter_pack",
|
|
1427
|
-
"resume_employee",
|
|
1428
|
-
"deploy_client"
|
|
1429
|
-
];
|
|
1430
|
-
const allowList = permissions.allow;
|
|
1431
|
-
for (const name of toolNames) {
|
|
1432
|
-
const fullName = `mcp__${MCP_PRIMARY_KEY}__${name}`;
|
|
1433
|
-
if (!allowList.includes(fullName)) {
|
|
1434
|
-
allowList.push(fullName);
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
await mkdir3(path7.dirname(settingsPath), { recursive: true });
|
|
1438
|
-
await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1439
|
-
return { added, skipped };
|
|
1440
|
-
}
|
|
1441
|
-
async function cleanOldShellFunctions(homeDir = os6.homedir()) {
|
|
1442
|
-
const rosterPath = path7.join(homeDir, ".exe-os", "exe-employees.json");
|
|
1443
|
-
if (!existsSync8(rosterPath)) return 0;
|
|
1444
|
-
let employees;
|
|
1445
|
-
try {
|
|
1446
|
-
employees = JSON.parse(await readFile3(rosterPath, "utf-8"));
|
|
1447
|
-
} catch {
|
|
1448
|
-
return 0;
|
|
1449
|
-
}
|
|
1450
|
-
if (employees.length === 0) return 0;
|
|
1451
|
-
const names = employees.map((e) => e.name);
|
|
1452
|
-
const funcPatterns = names.map((n) => {
|
|
1453
|
-
const funcDef = new RegExp(`^\\s*(function\\s+)?${escapeRegExp(n)}\\d+\\s*\\(\\)`, "m");
|
|
1454
|
-
const forLoop = new RegExp(`${escapeRegExp(n)}\\$\\{?[a-zA-Z_][a-zA-Z0-9_]*\\}?`, "m");
|
|
1455
|
-
return { name: n, funcDef, forLoop };
|
|
1456
|
-
});
|
|
1457
|
-
const rcFiles = [
|
|
1458
|
-
path7.join(homeDir, ".zshrc"),
|
|
1459
|
-
path7.join(homeDir, ".bashrc")
|
|
1460
|
-
];
|
|
1461
|
-
const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
|
|
1462
|
-
let totalRemoved = 0;
|
|
1463
|
-
for (const rcPath of rcFiles) {
|
|
1464
|
-
if (!existsSync8(rcPath)) continue;
|
|
1465
|
-
let content;
|
|
1466
|
-
try {
|
|
1467
|
-
content = await readFile3(rcPath, "utf-8");
|
|
1468
|
-
} catch {
|
|
1469
|
-
continue;
|
|
1470
|
-
}
|
|
1471
|
-
if (content.includes(REMOVED_MARKER)) continue;
|
|
1472
|
-
const lines = content.split("\n");
|
|
1473
|
-
let changed = false;
|
|
1474
|
-
let inForLoop = false;
|
|
1475
|
-
let forLoopMatchesEmployee = false;
|
|
1476
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1477
|
-
const line = lines[i];
|
|
1478
|
-
const trimmed = line.trim();
|
|
1479
|
-
if (trimmed.startsWith("#")) continue;
|
|
1480
|
-
if (/^\s*for\s+/.test(trimmed)) {
|
|
1481
|
-
inForLoop = true;
|
|
1482
|
-
forLoopMatchesEmployee = false;
|
|
1483
|
-
for (const { forLoop } of funcPatterns) {
|
|
1484
|
-
if (forLoop.test(trimmed)) {
|
|
1485
|
-
forLoopMatchesEmployee = true;
|
|
1486
|
-
break;
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
if (forLoopMatchesEmployee) {
|
|
1490
|
-
lines[i] = `${REMOVED_MARKER}
|
|
1491
|
-
# ${line}`;
|
|
1492
|
-
changed = true;
|
|
1493
|
-
totalRemoved++;
|
|
1494
|
-
continue;
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
if (inForLoop) {
|
|
1498
|
-
if (!forLoopMatchesEmployee) {
|
|
1499
|
-
for (const { forLoop } of funcPatterns) {
|
|
1500
|
-
if (forLoop.test(trimmed)) {
|
|
1501
|
-
forLoopMatchesEmployee = true;
|
|
1502
|
-
for (let j = i - 1; j >= 0; j--) {
|
|
1503
|
-
if (/^\s*for\s+/.test(lines[j].replace(/^#\s*/, ""))) {
|
|
1504
|
-
if (!lines[j].startsWith("#")) {
|
|
1505
|
-
lines[j] = `${REMOVED_MARKER}
|
|
1506
|
-
# ${lines[j]}`;
|
|
1507
|
-
totalRemoved++;
|
|
1508
|
-
}
|
|
1509
|
-
break;
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
break;
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
if (forLoopMatchesEmployee && !trimmed.startsWith("#")) {
|
|
1517
|
-
lines[i] = `# ${line}`;
|
|
1518
|
-
changed = true;
|
|
1519
|
-
totalRemoved++;
|
|
1520
|
-
}
|
|
1521
|
-
if (trimmed === "done" || trimmed.startsWith("done;") || trimmed.startsWith("done ")) {
|
|
1522
|
-
inForLoop = false;
|
|
1523
|
-
forLoopMatchesEmployee = false;
|
|
1524
|
-
}
|
|
1525
|
-
continue;
|
|
1526
|
-
}
|
|
1527
|
-
for (const { funcDef } of funcPatterns) {
|
|
1528
|
-
if (funcDef.test(trimmed)) {
|
|
1529
|
-
lines[i] = `${REMOVED_MARKER}
|
|
1530
|
-
# ${line}`;
|
|
1531
|
-
changed = true;
|
|
1532
|
-
totalRemoved++;
|
|
1533
|
-
let braceDepth = 0;
|
|
1534
|
-
const hasBrace = trimmed.includes("{");
|
|
1535
|
-
if (hasBrace) braceDepth = 1;
|
|
1536
|
-
for (let j = i + 1; j < lines.length; j++) {
|
|
1537
|
-
const bodyLine = lines[j].trim();
|
|
1538
|
-
if (!hasBrace && braceDepth === 0) {
|
|
1539
|
-
if (bodyLine === "{") {
|
|
1540
|
-
braceDepth = 1;
|
|
1541
|
-
lines[j] = `# ${lines[j]}`;
|
|
1542
|
-
totalRemoved++;
|
|
1543
|
-
continue;
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
if (braceDepth > 0) {
|
|
1547
|
-
braceDepth += (bodyLine.match(/{/g) ?? []).length;
|
|
1548
|
-
braceDepth -= (bodyLine.match(/}/g) ?? []).length;
|
|
1549
|
-
lines[j] = `# ${lines[j]}`;
|
|
1550
|
-
totalRemoved++;
|
|
1551
|
-
if (braceDepth <= 0) break;
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
break;
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
if (changed) {
|
|
1559
|
-
await writeFile3(rcPath, lines.join("\n"));
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
return totalRemoved;
|
|
1563
|
-
}
|
|
1564
|
-
function escapeRegExp(s) {
|
|
1565
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1566
|
-
}
|
|
1567
|
-
async function injectOrchestrationRules(homeDir) {
|
|
1568
|
-
const claudeDir = path7.join(homeDir, ".claude");
|
|
1569
|
-
const claudeMdPath = path7.join(claudeDir, "CLAUDE.md");
|
|
1570
|
-
await mkdir3(claudeDir, { recursive: true });
|
|
1571
|
-
let existing = "";
|
|
1572
|
-
try {
|
|
1573
|
-
existing = await readFile3(claudeMdPath, "utf-8");
|
|
1574
|
-
} catch {
|
|
1575
|
-
}
|
|
1576
|
-
const startIdx = existing.indexOf(EXE_SECTION_START);
|
|
1577
|
-
const endIdx = existing.indexOf(EXE_SECTION_END);
|
|
1578
|
-
if (startIdx !== -1 && endIdx !== -1) {
|
|
1579
|
-
const currentSection = existing.slice(startIdx, endIdx + EXE_SECTION_END.length);
|
|
1580
|
-
if (currentSection === ORCHESTRATION_RULES) {
|
|
1581
|
-
return "unchanged";
|
|
1582
|
-
}
|
|
1583
|
-
const updated = existing.slice(0, startIdx) + ORCHESTRATION_RULES + existing.slice(endIdx + EXE_SECTION_END.length);
|
|
1584
|
-
await writeFile3(claudeMdPath, updated, "utf-8");
|
|
1585
|
-
return "updated";
|
|
1586
|
-
}
|
|
1587
|
-
const separator = existing.length > 0 && !existing.endsWith("\n\n") ? "\n\n" : "";
|
|
1588
|
-
await writeFile3(claudeMdPath, existing + separator + ORCHESTRATION_RULES + "\n", "utf-8");
|
|
1589
|
-
return "injected";
|
|
1590
|
-
}
|
|
1591
|
-
async function installStatusLine(packageRoot, homeDir = os6.homedir()) {
|
|
1592
|
-
const prefs = loadPreferences(homeDir);
|
|
1593
|
-
if (prefs.ccStatusLine === false) return "opted-out";
|
|
1594
|
-
const claudeDir = path7.join(homeDir, ".claude");
|
|
1595
|
-
await mkdir3(claudeDir, { recursive: true });
|
|
1596
|
-
const assetPath = path7.join(packageRoot, "dist", "assets", "statusline-command.sh");
|
|
1597
|
-
if (!existsSync8(assetPath)) return "asset-missing";
|
|
1598
|
-
const destScript = path7.join(claudeDir, "statusline-command.sh");
|
|
1599
|
-
const assetContent = await readFile3(assetPath, "utf-8");
|
|
1600
|
-
await writeFile3(destScript, assetContent, { mode: 493 });
|
|
1601
|
-
const settingsPath = path7.join(claudeDir, "settings.json");
|
|
1602
|
-
let settings = {};
|
|
1603
|
-
if (existsSync8(settingsPath)) {
|
|
1604
|
-
try {
|
|
1605
|
-
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1606
|
-
} catch {
|
|
1607
|
-
settings = {};
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
if (settings.statusLine) return "already-configured";
|
|
1611
|
-
settings.statusLine = {
|
|
1612
|
-
type: "command",
|
|
1613
|
-
command: `bash ${destScript}`
|
|
1614
|
-
};
|
|
1615
|
-
await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1616
|
-
return "installed";
|
|
1617
|
-
}
|
|
1618
|
-
async function runInstaller(homeDir) {
|
|
1619
|
-
const packageRoot = resolvePackageRoot();
|
|
1620
|
-
process.stderr.write(`exe-os installer v1.3.0
|
|
1621
|
-
`);
|
|
1622
|
-
process.stderr.write(`Package root: ${packageRoot}
|
|
1623
|
-
|
|
1624
|
-
`);
|
|
1625
|
-
const cmdResult = await copySlashCommands(packageRoot, homeDir);
|
|
1626
|
-
process.stderr.write(
|
|
1627
|
-
`Slash commands: ${cmdResult.copied} copied, ${cmdResult.skipped} unchanged
|
|
1628
|
-
`
|
|
1629
|
-
);
|
|
1630
|
-
const mcpChanged = await registerMcpServer(packageRoot, homeDir);
|
|
1631
|
-
process.stderr.write(
|
|
1632
|
-
`MCP server: ${mcpChanged ? "registered" : "already registered"}
|
|
1633
|
-
`
|
|
1634
|
-
);
|
|
1635
|
-
const hookResult = await mergeHooks(packageRoot, homeDir);
|
|
1636
|
-
process.stderr.write(
|
|
1637
|
-
`Hooks: ${hookResult.added} added, ${hookResult.skipped} unchanged
|
|
1638
|
-
`
|
|
1639
|
-
);
|
|
1640
|
-
const resolvedHome = homeDir ?? os6.homedir();
|
|
1641
|
-
const exeWorkspace = path7.join(resolvedHome, "exe");
|
|
1642
|
-
if (!existsSync8(exeWorkspace)) {
|
|
1643
|
-
try {
|
|
1644
|
-
await mkdir3(path7.join(exeWorkspace, "content"), { recursive: true });
|
|
1645
|
-
await mkdir3(path7.join(exeWorkspace, "operations"), { recursive: true });
|
|
1646
|
-
await mkdir3(path7.join(exeWorkspace, "output"), { recursive: true });
|
|
1647
|
-
process.stderr.write(
|
|
1648
|
-
`Created ~/exe/ \u2014 your automation workspace for non-code projects
|
|
1649
|
-
`
|
|
1650
|
-
);
|
|
1651
|
-
} catch {
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
const claudeMdResult = await injectOrchestrationRules(resolvedHome);
|
|
1655
|
-
process.stderr.write(
|
|
1656
|
-
`CLAUDE.md orchestration: ${claudeMdResult}
|
|
1657
|
-
`
|
|
1658
|
-
);
|
|
1659
|
-
const symlinkResults = await ensureAllAgentSymlinks(resolvedHome);
|
|
1660
|
-
process.stderr.write(
|
|
1661
|
-
`Agent symlinks: ${summarizeSymlinkResults(symlinkResults)}
|
|
1662
|
-
`
|
|
1663
|
-
);
|
|
1664
|
-
const statusLineResult = await installStatusLine(packageRoot, resolvedHome);
|
|
1665
|
-
process.stderr.write(
|
|
1666
|
-
`Status line: ${statusLineResult}
|
|
1667
|
-
`
|
|
1668
|
-
);
|
|
1669
|
-
const shellFuncsCleaned = await cleanOldShellFunctions(resolvedHome);
|
|
1670
|
-
if (shellFuncsCleaned > 0) {
|
|
1671
|
-
process.stderr.write(
|
|
1672
|
-
`Shell cleanup: removed ${shellFuncsCleaned} old shell function(s) that were shadowing exe-os wrappers
|
|
1673
|
-
`
|
|
1674
|
-
);
|
|
1675
|
-
}
|
|
1676
|
-
process.stderr.write(`
|
|
1677
|
-
exe-os installed successfully.
|
|
1678
|
-
`);
|
|
1679
|
-
}
|
|
1680
|
-
function setupTmux(home) {
|
|
1681
|
-
const homeDir = home ?? os6.homedir();
|
|
1682
|
-
const exeDir = path7.join(homeDir, ".exe-os");
|
|
1683
|
-
const exeTmuxConf = path7.join(exeDir, "tmux.conf");
|
|
1684
|
-
const userTmuxConf = path7.join(homeDir, ".tmux.conf");
|
|
1685
|
-
const backupPath = path7.join(homeDir, ".tmux.conf.backup");
|
|
1686
|
-
const sourceLine = "source-file ~/.exe-os/tmux.conf";
|
|
1687
|
-
const pkgRoot = resolvePackageRoot();
|
|
1688
|
-
const assetPath = path7.join(pkgRoot, "dist", "assets", "tmux.conf");
|
|
1689
|
-
if (!existsSync8(assetPath)) {
|
|
1690
|
-
process.stderr.write(`exe-os: tmux.conf asset not found at ${assetPath} \u2014 skipping tmux setup
|
|
1691
|
-
`);
|
|
1692
|
-
return;
|
|
1693
|
-
}
|
|
1694
|
-
mkdirSync4(exeDir, { recursive: true });
|
|
1695
|
-
if (existsSync8(exeTmuxConf)) {
|
|
1696
|
-
const currentContent = readFileSync6(exeTmuxConf, "utf8");
|
|
1697
|
-
const newContent = readFileSync6(assetPath, "utf8");
|
|
1698
|
-
const currentHash = createHash("sha256").update(currentContent).digest("hex");
|
|
1699
|
-
const newHash = createHash("sha256").update(newContent).digest("hex");
|
|
1700
|
-
if (currentHash !== newHash) {
|
|
1701
|
-
const shippedPath = path7.join(exeDir, ".tmux.conf.shipped-hash");
|
|
1702
|
-
const lastShippedHash = existsSync8(shippedPath) ? readFileSync6(shippedPath, "utf8").trim() : "";
|
|
1703
|
-
if (lastShippedHash && currentHash !== lastShippedHash) {
|
|
1704
|
-
process.stderr.write("exe-os: tmux config has user customizations \u2014 skipping overwrite\n");
|
|
1705
|
-
} else {
|
|
1706
|
-
copyFileSync(assetPath, exeTmuxConf);
|
|
1707
|
-
process.stderr.write("exe-os: tmux config updated\n");
|
|
1708
|
-
}
|
|
1709
|
-
writeFileSync5(shippedPath, newHash, "utf8");
|
|
1710
|
-
} else {
|
|
1711
|
-
process.stderr.write("exe-os: tmux config already up to date\n");
|
|
1712
|
-
}
|
|
1713
|
-
} else {
|
|
1714
|
-
copyFileSync(assetPath, exeTmuxConf);
|
|
1715
|
-
const newContent = readFileSync6(assetPath, "utf8");
|
|
1716
|
-
const newHash = createHash("sha256").update(newContent).digest("hex");
|
|
1717
|
-
writeFileSync5(path7.join(exeDir, ".tmux.conf.shipped-hash"), newHash, "utf8");
|
|
1718
|
-
}
|
|
1719
|
-
if (existsSync8(userTmuxConf)) {
|
|
1720
|
-
const existing = readFileSync6(userTmuxConf, "utf8");
|
|
1721
|
-
if (!existing.includes(sourceLine)) {
|
|
1722
|
-
if (!existsSync8(backupPath)) {
|
|
1723
|
-
copyFileSync(userTmuxConf, backupPath);
|
|
1724
|
-
process.stderr.write(`exe-os: backed up existing tmux config to ${backupPath}
|
|
1725
|
-
`);
|
|
1726
|
-
}
|
|
1727
|
-
writeFileSync5(userTmuxConf, `${sourceLine}
|
|
1728
|
-
${existing}`);
|
|
1729
|
-
}
|
|
1730
|
-
} else {
|
|
1731
|
-
writeFileSync5(userTmuxConf, `# Exe OS tmux defaults \u2014 remove this line to use your own config
|
|
1732
|
-
${sourceLine}
|
|
1733
|
-
`);
|
|
1734
|
-
}
|
|
1735
|
-
try {
|
|
1736
|
-
execSync2(`tmux source-file ${exeTmuxConf} 2>/dev/null`);
|
|
1737
|
-
} catch {
|
|
1738
|
-
}
|
|
1739
|
-
process.stderr.write("exe-os: tmux config installed\n");
|
|
1740
|
-
}
|
|
1741
|
-
function setupGhostty(home) {
|
|
1742
|
-
const homeDir = home ?? os6.homedir();
|
|
1743
|
-
const xdgConfig = path7.join(homeDir, ".config", "ghostty");
|
|
1744
|
-
const macConfig = path7.join(homeDir, "Library", "Application Support", "com.mitchellh.ghostty");
|
|
1745
|
-
const ghosttyInstalled = existsSync8(xdgConfig) || existsSync8(macConfig) || (() => {
|
|
1746
|
-
try {
|
|
1747
|
-
execSync2("which ghostty 2>/dev/null");
|
|
1748
|
-
return true;
|
|
1749
|
-
} catch {
|
|
1750
|
-
return false;
|
|
1751
|
-
}
|
|
1752
|
-
})();
|
|
1753
|
-
if (!ghosttyInstalled) {
|
|
1754
|
-
return;
|
|
1755
|
-
}
|
|
1756
|
-
const pkgRoot = resolvePackageRoot();
|
|
1757
|
-
const assetPath = path7.join(pkgRoot, "dist", "assets", "ghostty.conf");
|
|
1758
|
-
if (!existsSync8(assetPath)) {
|
|
1759
|
-
process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
|
|
1760
|
-
return;
|
|
1761
|
-
}
|
|
1762
|
-
const configDir = xdgConfig;
|
|
1763
|
-
const configPath = path7.join(configDir, "config");
|
|
1764
|
-
const backupPath = path7.join(configDir, "config.backup");
|
|
1765
|
-
mkdirSync4(configDir, { recursive: true });
|
|
1766
|
-
const START_MARKER = "# \u2500\u2500 exe-os:ghostty-start \u2500\u2500";
|
|
1767
|
-
const END_MARKER = "# \u2500\u2500 exe-os:ghostty-end \u2500\u2500";
|
|
1768
|
-
const assetContent = readFileSync6(assetPath, "utf8").trim();
|
|
1769
|
-
const markedSection = `${START_MARKER}
|
|
1770
|
-
${assetContent}
|
|
1771
|
-
${END_MARKER}`;
|
|
1772
|
-
if (existsSync8(configPath)) {
|
|
1773
|
-
const existing = readFileSync6(configPath, "utf8");
|
|
1774
|
-
if (existing.includes(START_MARKER) && existing.includes(END_MARKER)) {
|
|
1775
|
-
process.stderr.write("exe-os: Ghostty config already installed \u2014 preserving local settings\n");
|
|
1776
|
-
return;
|
|
1777
|
-
} else if (existing.includes("Exe OS")) {
|
|
1778
|
-
if (!existsSync8(backupPath)) {
|
|
1779
|
-
copyFileSync(configPath, backupPath);
|
|
1780
|
-
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1781
|
-
`);
|
|
1782
|
-
}
|
|
1783
|
-
writeFileSync5(configPath, `${START_MARKER}
|
|
1784
|
-
${existing.trim()}
|
|
1785
|
-
${END_MARKER}
|
|
1786
|
-
`);
|
|
1787
|
-
} else {
|
|
1788
|
-
if (!existsSync8(backupPath)) {
|
|
1789
|
-
copyFileSync(configPath, backupPath);
|
|
1790
|
-
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1791
|
-
`);
|
|
1792
|
-
}
|
|
1793
|
-
writeFileSync5(configPath, `${markedSection}
|
|
1794
|
-
|
|
1795
|
-
${existing}`);
|
|
1796
|
-
}
|
|
1797
|
-
} else {
|
|
1798
|
-
writeFileSync5(configPath, `${markedSection}
|
|
1799
|
-
`);
|
|
1800
|
-
}
|
|
1801
|
-
process.stderr.write("exe-os: Ghostty config installed\n");
|
|
1802
|
-
}
|
|
1803
|
-
function summarizeSymlinkResults(results) {
|
|
1804
|
-
if (results.length === 0) return "no employees in roster";
|
|
1805
|
-
const created = results.filter((r) => r.action === "created").length;
|
|
1806
|
-
const already = results.filter((r) => r.action === "already_correct").length;
|
|
1807
|
-
const conflicts = results.filter((r) => r.action === "conflict");
|
|
1808
|
-
let summary = `${created} created, ${already} already linked`;
|
|
1809
|
-
if (conflicts.length > 0) {
|
|
1810
|
-
const details = conflicts.map((r) => `${r.agentId} (${r.conflict ?? "unknown"})`).join(", ");
|
|
1811
|
-
summary += `, ${conflicts.length} conflict(s): ${details}`;
|
|
1812
|
-
}
|
|
1813
|
-
return summary;
|
|
1814
|
-
}
|
|
1815
|
-
var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
|
|
1816
|
-
var init_installer = __esm({
|
|
1817
|
-
"src/adapters/claude/installer.ts"() {
|
|
1818
|
-
"use strict";
|
|
1819
|
-
init_agent_symlinks();
|
|
1820
|
-
init_mcp_prefix();
|
|
1821
|
-
init_preferences();
|
|
1822
|
-
init_mcp_http_config();
|
|
1823
|
-
init_runtime_hook_manifest();
|
|
1824
|
-
EXE_SECTION_START = "<!-- exe-os:orchestration-start -->";
|
|
1825
|
-
EXE_SECTION_END = "<!-- exe-os:orchestration-end -->";
|
|
1826
|
-
ORCHESTRATION_RULES = `${EXE_SECTION_START}
|
|
1827
|
-
## exe-os Multi-Agent Orchestration (auto-managed \u2014 do not edit)
|
|
1828
|
-
|
|
1829
|
-
These rules are injected by exe-os and override default behavior for multi-agent coordination.
|
|
1830
|
-
|
|
1831
|
-
- **Session routing is deterministic.** When working in a coordinator session, ALL employee sessions use that same coordinator-session suffix. NEVER reference, consider, or dispatch to employee sessions from other project sessions. This is not a choice \u2014 it is a hard constraint.
|
|
1832
|
-
- **Always use create_task to assign work.** Never use messages, tmux send-keys, or ad-hoc instructions as a substitute for tasks.
|
|
1833
|
-
- **Never modify another agent's in-progress task.** Create a new task instead. Modifying active tasks causes race conditions.
|
|
1834
|
-
- **Chain of command:** founder -> coordinator/executive agent (internal routing slot may be "COO") -> managers -> specialists. The coordinator does not bypass managers for specialist work. Internal role labels are plumbing, not user-facing titles.
|
|
1835
|
-
- **Verify dispatch.** After every create_task, confirm the employee received and started the task via tmux capture-pane.
|
|
1836
|
-
${EXE_SECTION_END}`;
|
|
1837
|
-
}
|
|
1838
|
-
});
|
|
1839
|
-
|
|
1840
|
-
// src/adapters/codex/installer.ts
|
|
1841
|
-
var installer_exports = {};
|
|
1842
|
-
__export(installer_exports, {
|
|
1843
|
-
ensureCodexHooksFeature: () => ensureCodexHooksFeature,
|
|
1844
|
-
installCodexStatusLine: () => installCodexStatusLine,
|
|
1845
|
-
mergeCodexHooks: () => mergeCodexHooks,
|
|
1846
|
-
registerCodexMcpServer: () => registerCodexMcpServer,
|
|
1847
|
-
runCodexInstaller: () => runCodexInstaller,
|
|
1848
|
-
verifyCodexHooks: () => verifyCodexHooks
|
|
1849
|
-
});
|
|
1850
|
-
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
1851
|
-
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
1852
|
-
import path9 from "path";
|
|
1853
|
-
import os7 from "os";
|
|
1854
|
-
async function mergeCodexHooks(packageRoot, homeDir = os7.homedir()) {
|
|
1855
|
-
const codexDir = path9.join(homeDir, ".codex");
|
|
1856
|
-
const hooksPath = path9.join(codexDir, "hooks.json");
|
|
1857
|
-
const logsDir = path9.join(homeDir, ".exe-os", "logs");
|
|
1858
|
-
const hookLogPath = path9.join(logsDir, "hooks.log");
|
|
1859
|
-
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
1860
|
-
await mkdir4(codexDir, { recursive: true });
|
|
1861
|
-
await mkdir4(logsDir, { recursive: true });
|
|
1862
|
-
let hooksJson = {};
|
|
1863
|
-
if (existsSync10(hooksPath)) {
|
|
1864
|
-
try {
|
|
1865
|
-
hooksJson = JSON.parse(await readFile4(hooksPath, "utf-8"));
|
|
1866
|
-
} catch {
|
|
1867
|
-
hooksJson = {};
|
|
1868
|
-
}
|
|
1869
|
-
}
|
|
1870
|
-
if (!hooksJson.hooks) {
|
|
1871
|
-
hooksJson.hooks = {};
|
|
1872
|
-
}
|
|
1873
|
-
const hooksToRegister = [
|
|
1874
|
-
{
|
|
1875
|
-
event: "PostToolUse",
|
|
1876
|
-
group: {
|
|
1877
|
-
matcher: "Bash|apply_patch|Edit|Write|Read|Glob|Grep|mcp__.*",
|
|
1878
|
-
hooks: [
|
|
1879
|
-
{
|
|
1880
|
-
// Combined hook: runs ingest + error-recall in one Node process.
|
|
1881
|
-
// Eliminates a cold-start cycle per tool call (~3-6s savings on Codex).
|
|
1882
|
-
type: "command",
|
|
1883
|
-
command: `node "${path9.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
|
|
1884
|
-
}
|
|
1885
|
-
]
|
|
1886
|
-
},
|
|
1887
|
-
marker: EXE_HOOKS.postToolCombined
|
|
1888
|
-
},
|
|
1889
|
-
{
|
|
1890
|
-
event: "UserPromptSubmit",
|
|
1891
|
-
group: {
|
|
1892
|
-
hooks: [
|
|
1893
|
-
{
|
|
1894
|
-
// Single hook: prompt-submit handles memory retrieval + entity boost.
|
|
1895
|
-
// exe-heartbeat-hook is CC-specific (intercom) — omitted on Codex.
|
|
1896
|
-
type: "command",
|
|
1897
|
-
command: `node "${path9.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
1898
|
-
}
|
|
1899
|
-
]
|
|
1900
|
-
},
|
|
1901
|
-
marker: EXE_HOOKS.promptSubmit
|
|
1902
|
-
},
|
|
1903
|
-
{
|
|
1904
|
-
event: "Stop",
|
|
1905
|
-
group: {
|
|
1906
|
-
hooks: [
|
|
1907
|
-
{
|
|
1908
|
-
type: "command",
|
|
1909
|
-
command: `node "${path9.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
1910
|
-
}
|
|
1911
|
-
]
|
|
1912
|
-
},
|
|
1913
|
-
marker: EXE_HOOKS.stop
|
|
1914
|
-
},
|
|
1915
|
-
{
|
|
1916
|
-
event: "PreToolUse",
|
|
1917
|
-
group: {
|
|
1918
|
-
matcher: "Bash|apply_patch",
|
|
1919
|
-
hooks: [
|
|
1920
|
-
{
|
|
1921
|
-
type: "command",
|
|
1922
|
-
command: `node "${path9.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
1923
|
-
}
|
|
1924
|
-
]
|
|
1925
|
-
},
|
|
1926
|
-
marker: EXE_HOOKS.preToolUse
|
|
1927
|
-
}
|
|
1928
|
-
];
|
|
1929
|
-
let added = 0;
|
|
1930
|
-
let skipped = 0;
|
|
1931
|
-
const sessionStartGroups = hooksJson.hooks["SessionStart"];
|
|
1932
|
-
if (Array.isArray(sessionStartGroups)) {
|
|
1933
|
-
hooksJson.hooks["SessionStart"] = sessionStartGroups.map((g) => ({
|
|
1934
|
-
...g,
|
|
1935
|
-
hooks: g.hooks.filter((h) => !h.command.includes(EXE_HOOKS.sessionStart))
|
|
1936
|
-
})).filter((g) => g.hooks.length > 0);
|
|
1937
|
-
if (hooksJson.hooks["SessionStart"].length === 0) {
|
|
1938
|
-
delete hooksJson.hooks["SessionStart"];
|
|
1939
|
-
}
|
|
1940
|
-
}
|
|
1941
|
-
const postToolGroups = hooksJson.hooks["PostToolUse"];
|
|
1942
|
-
if (Array.isArray(postToolGroups)) {
|
|
1943
|
-
hooksJson.hooks["PostToolUse"] = postToolGroups.map((g) => ({
|
|
1944
|
-
...g,
|
|
1945
|
-
hooks: g.hooks.filter((h) => !isLegacySplitPostToolCommand(h.command))
|
|
1946
|
-
})).filter((g) => g.hooks.length > 0);
|
|
1947
|
-
}
|
|
1948
|
-
for (const { event, group, marker } of hooksToRegister) {
|
|
1949
|
-
if (!hooksJson.hooks[event]) {
|
|
1950
|
-
hooksJson.hooks[event] = [];
|
|
1951
|
-
}
|
|
1952
|
-
const existing = hooksJson.hooks[event];
|
|
1953
|
-
const correctCommand = group.hooks[0]?.command ?? "";
|
|
1954
|
-
const alreadyCorrect = existing.some(
|
|
1955
|
-
(g) => g.hooks.some((h) => h.command === correctCommand)
|
|
1956
|
-
);
|
|
1957
|
-
if (alreadyCorrect) {
|
|
1958
|
-
skipped++;
|
|
1959
|
-
} else {
|
|
1960
|
-
hooksJson.hooks[event] = existing.filter(
|
|
1961
|
-
(g) => !g.hooks.some((h) => h.command.includes(marker))
|
|
1962
|
-
);
|
|
1963
|
-
hooksJson.hooks[event].push(group);
|
|
1964
|
-
added++;
|
|
1965
|
-
}
|
|
1966
|
-
}
|
|
1967
|
-
await writeFile4(hooksPath, JSON.stringify(hooksJson, null, 2) + "\n");
|
|
1968
|
-
return { added, skipped };
|
|
1969
|
-
}
|
|
1970
|
-
function verifyCodexHooks(homeDir = os7.homedir()) {
|
|
1971
|
-
const hooksPath = path9.join(homeDir, ".codex", "hooks.json");
|
|
1972
|
-
if (!existsSync10(hooksPath)) return false;
|
|
1973
|
-
try {
|
|
1974
|
-
const hooksJson = JSON.parse(readFileSync8(hooksPath, "utf-8"));
|
|
1975
|
-
if (!hooksJson.hooks) return false;
|
|
1976
|
-
const required = ["PostToolUse", "UserPromptSubmit", "Stop", "PreToolUse"];
|
|
1977
|
-
for (const event of required) {
|
|
1978
|
-
const groups = hooksJson.hooks[event];
|
|
1979
|
-
if (!groups || !groups.some(
|
|
1980
|
-
(g) => g.hooks.some((h) => h.command.includes("dist/hooks/"))
|
|
1981
|
-
)) {
|
|
1982
|
-
return false;
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
const postToolCommands = (hooksJson.hooks.PostToolUse ?? []).flatMap((g) => g.hooks.map((h) => h.command));
|
|
1986
|
-
if (!postToolCommands.some((cmd) => cmd.includes(EXE_HOOKS.postToolCombined))) {
|
|
1987
|
-
return false;
|
|
1988
|
-
}
|
|
1989
|
-
if (postToolCommands.some(
|
|
1990
|
-
(cmd) => isLegacySplitPostToolCommand(cmd)
|
|
1991
|
-
)) {
|
|
1992
|
-
return false;
|
|
1993
|
-
}
|
|
1994
|
-
const sessionStartCommands = (hooksJson.hooks.SessionStart ?? []).flatMap((g) => g.hooks.map((h) => h.command));
|
|
1995
|
-
if (sessionStartCommands.some((cmd) => cmd.includes(EXE_HOOKS.sessionStart))) {
|
|
1996
|
-
return false;
|
|
1997
|
-
}
|
|
1998
|
-
return true;
|
|
1999
|
-
} catch {
|
|
2000
|
-
return false;
|
|
2001
|
-
}
|
|
2002
|
-
}
|
|
2003
|
-
async function installCodexStatusLine(homeDir = os7.homedir()) {
|
|
2004
|
-
const prefs = loadPreferences(homeDir);
|
|
2005
|
-
if (prefs.codexStatusLine === false) return "opted-out";
|
|
2006
|
-
const codexDir = path9.join(homeDir, ".codex");
|
|
2007
|
-
const configPath = path9.join(codexDir, "config.toml");
|
|
2008
|
-
await mkdir4(codexDir, { recursive: true });
|
|
2009
|
-
let content = "";
|
|
2010
|
-
if (existsSync10(configPath)) {
|
|
2011
|
-
content = await readFile4(configPath, "utf-8");
|
|
2012
|
-
if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
|
|
2013
|
-
return "already-configured";
|
|
2014
|
-
}
|
|
2015
|
-
}
|
|
2016
|
-
const statusLineToml = `[tui]
|
|
2017
|
-
status_line = [${DEFAULT_CODEX_STATUS_LINE.map((s) => `"${s}"`).join(", ")}]`;
|
|
2018
|
-
if (content.includes("[tui]")) {
|
|
2019
|
-
content = content.replace(/\[tui\]/, `${statusLineToml}`);
|
|
2020
|
-
} else {
|
|
2021
|
-
const separator = content.length > 0 && !content.endsWith("\n") ? "\n\n" : content.length > 0 ? "\n" : "";
|
|
2022
|
-
content = content + separator + statusLineToml + "\n";
|
|
2023
|
-
}
|
|
2024
|
-
await writeFile4(configPath, content);
|
|
2025
|
-
return "installed";
|
|
2026
|
-
}
|
|
2027
|
-
function tomlString(value) {
|
|
2028
|
-
return JSON.stringify(value);
|
|
2029
|
-
}
|
|
2030
|
-
function codexHttpHeadersToml(homeDir) {
|
|
2031
|
-
const headers = buildMcpHttpHeaders(homeDir);
|
|
2032
|
-
return `{ ${Object.entries(headers).map(([key, value]) => `${tomlString(key)} = ${tomlString(value)}`).join(", ")} }`;
|
|
2033
|
-
}
|
|
2034
|
-
function desiredCodexMcpSection(homeDir) {
|
|
2035
|
-
return [
|
|
2036
|
-
"[mcp_servers.exe-os]",
|
|
2037
|
-
`url = ${tomlString(mcpHttpUrl())}`,
|
|
2038
|
-
`http_headers = ${codexHttpHeadersToml(homeDir)}`,
|
|
2039
|
-
`startup_timeout_sec = ${CODEX_EXE_MCP_STARTUP_TIMEOUT_SEC}`,
|
|
2040
|
-
`tool_timeout_sec = ${CODEX_EXE_MCP_TOOL_TIMEOUT_SEC}`,
|
|
2041
|
-
""
|
|
2042
|
-
].join("\n");
|
|
2043
|
-
}
|
|
2044
|
-
function reconcileCodexMcpSection(sectionContent, homeDir) {
|
|
2045
|
-
const desiredLines = {
|
|
2046
|
-
url: `url = ${tomlString(mcpHttpUrl())}`,
|
|
2047
|
-
headers: `http_headers = ${codexHttpHeadersToml(homeDir)}`,
|
|
2048
|
-
startup: `startup_timeout_sec = ${CODEX_EXE_MCP_STARTUP_TIMEOUT_SEC}`,
|
|
2049
|
-
tool: `tool_timeout_sec = ${CODEX_EXE_MCP_TOOL_TIMEOUT_SEC}`
|
|
2050
|
-
};
|
|
2051
|
-
let next = sectionContent;
|
|
2052
|
-
next = next.split("\n").filter((line) => !/^(command|args|env|type|url|http_headers|startup_timeout_sec|tool_timeout_sec)\s*=/.test(line.trim())).join("\n").replace(/\n{3,}/g, "\n\n");
|
|
2053
|
-
for (const line of [desiredLines.url, desiredLines.headers, desiredLines.startup, desiredLines.tool]) {
|
|
2054
|
-
next = next.endsWith("\n") ? `${next}${line}
|
|
2055
|
-
` : `${next}
|
|
2056
|
-
${line}
|
|
2057
|
-
`;
|
|
2058
|
-
}
|
|
2059
|
-
return { content: next, changed: next !== sectionContent };
|
|
2060
|
-
}
|
|
2061
|
-
async function registerCodexMcpServer(packageRoot, homeDir = os7.homedir()) {
|
|
2062
|
-
const codexDir = path9.join(homeDir, ".codex");
|
|
2063
|
-
const configPath = path9.join(codexDir, "config.toml");
|
|
2064
|
-
void packageRoot;
|
|
2065
|
-
await mkdir4(codexDir, { recursive: true });
|
|
2066
|
-
let content = "";
|
|
2067
|
-
if (existsSync10(configPath)) {
|
|
2068
|
-
content = await readFile4(configPath, "utf-8");
|
|
2069
|
-
}
|
|
2070
|
-
const sectionHeader = "[mcp_servers.exe-os]";
|
|
2071
|
-
const headerIndex = content.indexOf(sectionHeader);
|
|
2072
|
-
if (headerIndex !== -1) {
|
|
2073
|
-
const afterHeader = content.slice(headerIndex + sectionHeader.length);
|
|
2074
|
-
const nextSectionMatch = afterHeader.match(/\n\[(?!mcp_servers\.exe-os)/);
|
|
2075
|
-
const sectionEnd = nextSectionMatch ? headerIndex + sectionHeader.length + nextSectionMatch.index : content.length;
|
|
2076
|
-
const sectionContent = content.slice(headerIndex, sectionEnd);
|
|
2077
|
-
const reconciled = reconcileCodexMcpSection(sectionContent, homeDir);
|
|
2078
|
-
if (!reconciled.changed) {
|
|
2079
|
-
return "already-registered";
|
|
2080
|
-
}
|
|
2081
|
-
content = content.slice(0, headerIndex) + reconciled.content + content.slice(sectionEnd);
|
|
2082
|
-
await writeFile4(configPath, content);
|
|
2083
|
-
return "updated";
|
|
2084
|
-
}
|
|
2085
|
-
const newSection = desiredCodexMcpSection(homeDir);
|
|
2086
|
-
const separator = content.length > 0 && !content.endsWith("\n") ? "\n\n" : content.length > 0 ? "\n" : "";
|
|
2087
|
-
content = content + separator + newSection;
|
|
2088
|
-
await writeFile4(configPath, content);
|
|
2089
|
-
return "registered";
|
|
2090
|
-
}
|
|
2091
|
-
async function ensureCodexHooksFeature(homeDir = os7.homedir()) {
|
|
2092
|
-
const configPath = path9.join(homeDir, ".codex", "config.toml");
|
|
2093
|
-
await mkdir4(path9.join(homeDir, ".codex"), { recursive: true });
|
|
2094
|
-
let content = "";
|
|
2095
|
-
if (existsSync10(configPath)) {
|
|
2096
|
-
content = await readFile4(configPath, "utf-8");
|
|
2097
|
-
}
|
|
2098
|
-
if (/\[features\][\s\S]*?codex_hooks\s*=\s*true/.test(content)) {
|
|
2099
|
-
return "already-enabled";
|
|
2100
|
-
}
|
|
2101
|
-
if (content.includes("[features]")) {
|
|
2102
|
-
if (/codex_hooks\s*=\s*false/.test(content)) {
|
|
2103
|
-
content = content.replace(/codex_hooks\s*=\s*false/, "codex_hooks = true");
|
|
2104
|
-
} else {
|
|
2105
|
-
content = content.replace(/\[features\]/, "[features]\ncodex_hooks = true");
|
|
2106
|
-
}
|
|
2107
|
-
} else {
|
|
2108
|
-
const firstSectionMatch = content.match(/\n\[(?!features\])/);
|
|
2109
|
-
if (firstSectionMatch && firstSectionMatch.index != null) {
|
|
2110
|
-
const insertAt = firstSectionMatch.index;
|
|
2111
|
-
content = content.slice(0, insertAt) + "\n\n[features]\ncodex_hooks = true" + content.slice(insertAt);
|
|
2112
|
-
} else {
|
|
2113
|
-
const separator = content.length > 0 && !content.endsWith("\n") ? "\n\n" : "\n";
|
|
2114
|
-
content = content + separator + "[features]\ncodex_hooks = true\n";
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
await writeFile4(configPath, content);
|
|
2118
|
-
return "enabled";
|
|
2119
|
-
}
|
|
2120
|
-
async function runCodexInstaller(homeDir) {
|
|
2121
|
-
const packageRoot = resolvePackageRoot();
|
|
2122
|
-
const result = await mergeCodexHooks(packageRoot, homeDir);
|
|
2123
|
-
process.stderr.write(
|
|
2124
|
-
`[exe-os] Codex hooks: ${result.added} added, ${result.skipped} unchanged
|
|
2125
|
-
`
|
|
2126
|
-
);
|
|
2127
|
-
const featureResult = await ensureCodexHooksFeature(homeDir);
|
|
2128
|
-
process.stderr.write(
|
|
2129
|
-
`[exe-os] Codex hooks feature: ${featureResult}
|
|
2130
|
-
`
|
|
2131
|
-
);
|
|
2132
|
-
const mcpResult = await registerCodexMcpServer(packageRoot, homeDir);
|
|
2133
|
-
process.stderr.write(
|
|
2134
|
-
`[exe-os] Codex MCP server: ${mcpResult}
|
|
2135
|
-
`
|
|
2136
|
-
);
|
|
2137
|
-
const statusResult = await installCodexStatusLine(homeDir);
|
|
2138
|
-
process.stderr.write(
|
|
2139
|
-
`[exe-os] Codex status line: ${statusResult}
|
|
2140
|
-
`
|
|
2141
|
-
);
|
|
2142
|
-
}
|
|
2143
|
-
var DEFAULT_CODEX_STATUS_LINE, CODEX_EXE_MCP_STARTUP_TIMEOUT_SEC, CODEX_EXE_MCP_TOOL_TIMEOUT_SEC;
|
|
2144
|
-
var init_installer2 = __esm({
|
|
2145
|
-
"src/adapters/codex/installer.ts"() {
|
|
2146
|
-
"use strict";
|
|
2147
|
-
init_installer();
|
|
2148
|
-
init_preferences();
|
|
2149
|
-
init_runtime_hook_manifest();
|
|
2150
|
-
init_mcp_http_config();
|
|
2151
|
-
DEFAULT_CODEX_STATUS_LINE = [
|
|
2152
|
-
"model-with-reasoning",
|
|
2153
|
-
"current-dir",
|
|
2154
|
-
"project-name",
|
|
2155
|
-
"git-branch",
|
|
2156
|
-
"run-state",
|
|
2157
|
-
"context-used"
|
|
2158
|
-
];
|
|
2159
|
-
CODEX_EXE_MCP_STARTUP_TIMEOUT_SEC = 30;
|
|
2160
|
-
CODEX_EXE_MCP_TOOL_TIMEOUT_SEC = 120;
|
|
2161
|
-
}
|
|
2162
|
-
});
|
|
2163
|
-
|
|
2164
|
-
// src/bin/install.ts
|
|
2165
|
-
init_installer();
|
|
2166
|
-
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync7, unlinkSync as unlinkSync3, readdirSync as readdirSync2, openSync, closeSync } from "fs";
|
|
2167
|
-
import { spawn, execSync as execSync4 } from "child_process";
|
|
2168
|
-
import path10 from "path";
|
|
2169
|
-
import os8 from "os";
|
|
2170
|
-
|
|
2171
|
-
// src/lib/session-wrappers.ts
|
|
3
|
+
copySlashCommands,
|
|
4
|
+
resolvePackageRoot,
|
|
5
|
+
runInstaller,
|
|
6
|
+
setupGhostty,
|
|
7
|
+
setupTmux
|
|
8
|
+
} from "../chunk-BCMOO4OQ.js";
|
|
9
|
+
import "../chunk-SPKE5ANC.js";
|
|
2172
10
|
import {
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
import
|
|
2182
|
-
import path8 from "path";
|
|
2183
|
-
import { homedir } from "os";
|
|
2184
|
-
var MAX_N = 9;
|
|
2185
|
-
function generateSessionWrappers(packageRoot, homeDir) {
|
|
2186
|
-
const home = homeDir ?? homedir();
|
|
2187
|
-
const binDir = path8.join(home, ".exe-os", "bin");
|
|
2188
|
-
const rosterPath = path8.join(home, ".exe-os", "exe-employees.json");
|
|
2189
|
-
const shouldMirrorToGlobalBin = homeDir === void 0;
|
|
2190
|
-
mkdirSync5(binDir, { recursive: true });
|
|
2191
|
-
const exeStartDst = path8.join(binDir, "exe-start");
|
|
2192
|
-
const candidates = [
|
|
2193
|
-
path8.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
2194
|
-
path8.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
2195
|
-
];
|
|
2196
|
-
for (const src of candidates) {
|
|
2197
|
-
if (existsSync9(src)) {
|
|
2198
|
-
writeFileSync6(exeStartDst, readFileSync7(src));
|
|
2199
|
-
chmodSync3(exeStartDst, 493);
|
|
2200
|
-
break;
|
|
2201
|
-
}
|
|
2202
|
-
}
|
|
2203
|
-
let employees = [];
|
|
2204
|
-
try {
|
|
2205
|
-
employees = JSON.parse(readFileSync7(rosterPath, "utf8"));
|
|
2206
|
-
} catch {
|
|
2207
|
-
return { created: 0, pathConfigured: false };
|
|
2208
|
-
}
|
|
2209
|
-
if (employees.length === 0) {
|
|
2210
|
-
return { created: 0, pathConfigured: false };
|
|
2211
|
-
}
|
|
2212
|
-
try {
|
|
2213
|
-
for (const f of readdirSync(binDir)) {
|
|
2214
|
-
if (f === "exe-start") continue;
|
|
2215
|
-
const fPath = path8.join(binDir, f);
|
|
2216
|
-
try {
|
|
2217
|
-
const content = readFileSync7(fPath, "utf8");
|
|
2218
|
-
if (content.includes("exe-start")) {
|
|
2219
|
-
unlinkSync2(fPath);
|
|
2220
|
-
}
|
|
2221
|
-
} catch {
|
|
2222
|
-
}
|
|
2223
|
-
}
|
|
2224
|
-
} catch {
|
|
2225
|
-
}
|
|
2226
|
-
let created = 0;
|
|
2227
|
-
const wrapperContent = `#!/bin/bash
|
|
2228
|
-
exec "${exeStartDst}" "$0" "$@"
|
|
2229
|
-
`;
|
|
2230
|
-
const globalBinDir = shouldMirrorToGlobalBin ? resolveGlobalBinDir() : null;
|
|
2231
|
-
for (const emp of employees) {
|
|
2232
|
-
for (let n = 1; n <= MAX_N; n++) {
|
|
2233
|
-
writeWrapper(path8.join(binDir, `${emp.name}${n}`), wrapperContent);
|
|
2234
|
-
if (globalBinDir) {
|
|
2235
|
-
writeWrapper(path8.join(globalBinDir, `${emp.name}${n}`), wrapperContent);
|
|
2236
|
-
}
|
|
2237
|
-
created++;
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
const pathConfigured = ensurePath(home, binDir);
|
|
2241
|
-
return { created, pathConfigured };
|
|
2242
|
-
}
|
|
2243
|
-
function writeWrapper(wrapperPath, content) {
|
|
2244
|
-
try {
|
|
2245
|
-
writeFileSync6(wrapperPath, content);
|
|
2246
|
-
chmodSync3(wrapperPath, 493);
|
|
2247
|
-
} catch {
|
|
2248
|
-
}
|
|
2249
|
-
}
|
|
2250
|
-
function resolveGlobalBinDir() {
|
|
2251
|
-
try {
|
|
2252
|
-
const exeOsPath = execSync3("command -v exe-os", { encoding: "utf8", timeout: 3e3 }).trim().split("\n")[0];
|
|
2253
|
-
if (exeOsPath) return path8.dirname(exeOsPath);
|
|
2254
|
-
} catch {
|
|
2255
|
-
}
|
|
2256
|
-
try {
|
|
2257
|
-
const prefix = execSync3("npm prefix -g", { encoding: "utf8", timeout: 3e3 }).trim();
|
|
2258
|
-
if (prefix) return path8.join(prefix, "bin");
|
|
2259
|
-
} catch {
|
|
2260
|
-
}
|
|
2261
|
-
return null;
|
|
2262
|
-
}
|
|
2263
|
-
function ensurePath(home, binDir) {
|
|
2264
|
-
if (process.env.PATH?.split(":").includes(binDir)) {
|
|
2265
|
-
return false;
|
|
2266
|
-
}
|
|
2267
|
-
const exportLine = `
|
|
2268
|
-
# exe-os session commands
|
|
2269
|
-
export PATH="${binDir}:$PATH"
|
|
2270
|
-
`;
|
|
2271
|
-
const shell = process.env.SHELL ?? "/bin/bash";
|
|
2272
|
-
const profilePaths = [];
|
|
2273
|
-
if (shell.includes("zsh")) {
|
|
2274
|
-
profilePaths.push(path8.join(home, ".zshrc"));
|
|
2275
|
-
} else if (shell.includes("bash")) {
|
|
2276
|
-
profilePaths.push(path8.join(home, ".bashrc"));
|
|
2277
|
-
profilePaths.push(path8.join(home, ".bash_profile"));
|
|
2278
|
-
} else {
|
|
2279
|
-
profilePaths.push(path8.join(home, ".profile"));
|
|
2280
|
-
}
|
|
2281
|
-
for (const profilePath of profilePaths) {
|
|
2282
|
-
try {
|
|
2283
|
-
let content = "";
|
|
2284
|
-
try {
|
|
2285
|
-
content = readFileSync7(profilePath, "utf8");
|
|
2286
|
-
} catch {
|
|
2287
|
-
}
|
|
2288
|
-
if (content.includes(".exe-os/bin")) {
|
|
2289
|
-
return false;
|
|
2290
|
-
}
|
|
2291
|
-
writeFileSync6(profilePath, content + exportLine);
|
|
2292
|
-
return true;
|
|
2293
|
-
} catch {
|
|
2294
|
-
continue;
|
|
2295
|
-
}
|
|
2296
|
-
}
|
|
2297
|
-
return false;
|
|
2298
|
-
}
|
|
11
|
+
generateSessionWrappers
|
|
12
|
+
} from "../chunk-MLCUPTP7.js";
|
|
13
|
+
import "../chunk-L3TB7CC3.js";
|
|
14
|
+
import "../chunk-OFK72LTZ.js";
|
|
15
|
+
import "../chunk-6BFIFI5F.js";
|
|
16
|
+
import "../chunk-RUG3N6P4.js";
|
|
17
|
+
import "../chunk-KVPG5UT6.js";
|
|
18
|
+
import "../chunk-X2IMCCM5.js";
|
|
19
|
+
import "../chunk-KFQGP6VL.js";
|
|
2299
20
|
|
|
2300
21
|
// src/bin/install.ts
|
|
2301
|
-
|
|
2302
|
-
|
|
22
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync, readdirSync, openSync, closeSync } from "fs";
|
|
23
|
+
import { spawn, execSync } from "child_process";
|
|
24
|
+
import path from "path";
|
|
25
|
+
import os from "os";
|
|
26
|
+
var homedir = os.homedir;
|
|
27
|
+
var EXE_DIR = path.join(homedir(), ".exe-os");
|
|
2303
28
|
function restartDaemon() {
|
|
2304
|
-
const pidPath =
|
|
2305
|
-
const sockPath =
|
|
29
|
+
const pidPath = path.join(EXE_DIR, "exed.pid");
|
|
30
|
+
const sockPath = path.join(EXE_DIR, "exed.sock");
|
|
2306
31
|
try {
|
|
2307
|
-
if (
|
|
2308
|
-
const pid = parseInt(
|
|
32
|
+
if (existsSync(pidPath)) {
|
|
33
|
+
const pid = parseInt(readFileSync(pidPath, "utf8").trim(), 10);
|
|
2309
34
|
if (!isNaN(pid) && pid > 0) {
|
|
2310
35
|
try {
|
|
2311
36
|
process.kill(pid, "SIGKILL");
|
|
@@ -2313,16 +38,16 @@ function restartDaemon() {
|
|
|
2313
38
|
}
|
|
2314
39
|
}
|
|
2315
40
|
try {
|
|
2316
|
-
|
|
41
|
+
unlinkSync(pidPath);
|
|
2317
42
|
} catch {
|
|
2318
43
|
}
|
|
2319
44
|
try {
|
|
2320
|
-
|
|
45
|
+
unlinkSync(pidPath + ".lock");
|
|
2321
46
|
} catch {
|
|
2322
47
|
}
|
|
2323
48
|
}
|
|
2324
49
|
try {
|
|
2325
|
-
const pids =
|
|
50
|
+
const pids = execSync("pgrep -f 'exe-daemon.js' 2>/dev/null || true", { encoding: "utf8" }).trim().split("\n").filter(Boolean).map(Number).filter((n) => !isNaN(n) && n !== process.pid);
|
|
2326
51
|
for (const pid of pids) {
|
|
2327
52
|
try {
|
|
2328
53
|
process.kill(pid, "SIGKILL");
|
|
@@ -2336,7 +61,7 @@ function restartDaemon() {
|
|
|
2336
61
|
} catch {
|
|
2337
62
|
}
|
|
2338
63
|
try {
|
|
2339
|
-
const pids =
|
|
64
|
+
const pids = execSync("pgrep -f 'backfill-vectors.js' 2>/dev/null || true", { encoding: "utf8" }).trim().split("\n").filter(Boolean).map(Number).filter((n) => !isNaN(n) && n !== process.pid);
|
|
2340
65
|
for (const pid of pids) {
|
|
2341
66
|
try {
|
|
2342
67
|
process.kill(pid, "SIGKILL");
|
|
@@ -2350,18 +75,18 @@ function restartDaemon() {
|
|
|
2350
75
|
} catch {
|
|
2351
76
|
}
|
|
2352
77
|
try {
|
|
2353
|
-
const versionPath =
|
|
2354
|
-
|
|
78
|
+
const versionPath = path.join(EXE_DIR, "mcp-version");
|
|
79
|
+
writeFileSync(versionPath, `deploy-${Date.now()}`);
|
|
2355
80
|
process.stderr.write(`exe-os: MCP version marker updated \u2014 servers will hot-reload within 10s
|
|
2356
81
|
`);
|
|
2357
82
|
} catch {
|
|
2358
83
|
}
|
|
2359
84
|
try {
|
|
2360
|
-
const wpDir =
|
|
2361
|
-
if (
|
|
2362
|
-
for (const f of
|
|
85
|
+
const wpDir = path.join(EXE_DIR, "worker-pids");
|
|
86
|
+
if (existsSync(wpDir)) {
|
|
87
|
+
for (const f of readdirSync(wpDir)) {
|
|
2363
88
|
try {
|
|
2364
|
-
|
|
89
|
+
unlinkSync(path.join(wpDir, f));
|
|
2365
90
|
} catch {
|
|
2366
91
|
}
|
|
2367
92
|
}
|
|
@@ -2369,16 +94,16 @@ function restartDaemon() {
|
|
|
2369
94
|
} catch {
|
|
2370
95
|
}
|
|
2371
96
|
try {
|
|
2372
|
-
|
|
97
|
+
unlinkSync(sockPath);
|
|
2373
98
|
} catch {
|
|
2374
99
|
}
|
|
2375
100
|
try {
|
|
2376
|
-
|
|
101
|
+
execSync("sleep 0.5");
|
|
2377
102
|
} catch {
|
|
2378
103
|
}
|
|
2379
104
|
} catch {
|
|
2380
105
|
}
|
|
2381
|
-
const totalGB =
|
|
106
|
+
const totalGB = os.totalmem() / (1024 * 1024 * 1024);
|
|
2382
107
|
if (totalGB <= 8) {
|
|
2383
108
|
process.stderr.write(
|
|
2384
109
|
`exe-os: ${totalGB.toFixed(0)}GB system \u2014 skipping daemon spawn (keyword search mode)
|
|
@@ -2388,13 +113,13 @@ function restartDaemon() {
|
|
|
2388
113
|
}
|
|
2389
114
|
try {
|
|
2390
115
|
const pkgRoot = resolvePackageRoot();
|
|
2391
|
-
const daemonPath =
|
|
2392
|
-
if (!
|
|
116
|
+
const daemonPath = path.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
117
|
+
if (!existsSync(daemonPath)) {
|
|
2393
118
|
process.stderr.write(`exe-os: daemon not found at ${daemonPath} \u2014 skipping respawn
|
|
2394
119
|
`);
|
|
2395
120
|
return;
|
|
2396
121
|
}
|
|
2397
|
-
const logPath =
|
|
122
|
+
const logPath = path.join(EXE_DIR, "exed.log");
|
|
2398
123
|
let stderrFd = "ignore";
|
|
2399
124
|
try {
|
|
2400
125
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2444,8 +169,8 @@ if (args.includes("--commands-only")) {
|
|
|
2444
169
|
setupTmux();
|
|
2445
170
|
setupGhostty();
|
|
2446
171
|
try {
|
|
2447
|
-
const { normalizeRosterCase
|
|
2448
|
-
const changed = await
|
|
172
|
+
const { normalizeRosterCase } = await import("../lib/employees.js");
|
|
173
|
+
const changed = await normalizeRosterCase();
|
|
2449
174
|
if (changed) {
|
|
2450
175
|
process.stderr.write("exe-os: normalized employee names to lowercase\n");
|
|
2451
176
|
}
|
|
@@ -2467,9 +192,9 @@ if (args.includes("--commands-only")) {
|
|
|
2467
192
|
`);
|
|
2468
193
|
}
|
|
2469
194
|
try {
|
|
2470
|
-
|
|
2471
|
-
const { runCodexInstaller
|
|
2472
|
-
await
|
|
195
|
+
execSync("which codex", { encoding: "utf8", timeout: 5e3 });
|
|
196
|
+
const { runCodexInstaller } = await import("../installer-ZPQUKHU5.js");
|
|
197
|
+
await runCodexInstaller();
|
|
2473
198
|
} catch {
|
|
2474
199
|
}
|
|
2475
200
|
restartDaemon();
|