@askexenow/exe-os 0.9.280 → 0.9.282
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/deploy/compose/.env.customer.example +1 -1
- package/deploy/compose/.env.default +2 -1
- package/deploy/compose/.env.example +1 -1
- package/deploy/compose/docker-compose.yml +74 -13
- package/deploy/compose/erp-nginx/nginx.conf +52 -0
- package/deploy/compose/generate-env.ts +1 -1
- package/deploy/compose/init-db.sql +13 -2
- package/deploy/stack-manifests/v0.9.json +11 -31
- package/dist/active-agent-6ZBHGHXF.js +26 -0
- package/dist/active-agent-E23SCYER.js +27 -0
- package/dist/active-agent-KMZT44S4.js +26 -0
- package/dist/active-agent-LFFTVROM.js +27 -0
- package/dist/agentic-ontology-PCZB5HV5.js +25 -0
- package/dist/agentic-ontology-PGGJN2ES.js +25 -0
- package/dist/assets/tmux.conf +2 -0
- package/dist/backfill-metadata-KQ4FEVUR.js +599 -0
- package/dist/backfill-metadata-Y3YWCHKJ.js +599 -0
- package/dist/behaviors-H4DZECKL.js +39 -0
- package/dist/behaviors-WIUTIJF6.js +39 -0
- package/dist/bin/agentic-ontology-backfill.js +5 -5
- package/dist/bin/agentic-reflection-backfill.js +6 -6
- package/dist/bin/agentic-semantic-label.js +5 -5
- package/dist/bin/backfill-conversations.js +4 -4
- package/dist/bin/backfill-responses.js +4 -4
- package/dist/bin/backfill-vectors.js +5 -5
- package/dist/bin/bulk-sync-postgres.js +7 -7
- package/dist/bin/cc-doctor.js +4 -4
- package/dist/bin/cleanup-stale-review-tasks.js +10 -10
- package/dist/bin/cli.js +15 -15
- package/dist/bin/exe-agent-config.js +2 -2
- package/dist/bin/exe-agent.js +4 -4
- package/dist/bin/exe-assign.js +5 -5
- package/dist/bin/exe-boot.js +17 -17
- package/dist/bin/exe-call.js +4 -4
- package/dist/bin/exe-cloud.js +5 -5
- package/dist/bin/exe-dispatch.js +10 -10
- package/dist/bin/exe-doctor.js +1 -1
- package/dist/bin/exe-export-behaviors.js +7 -7
- package/dist/bin/exe-forget.js +6 -6
- package/dist/bin/exe-gateway.js +7 -7
- package/dist/bin/exe-healthcheck.js +4 -4
- package/dist/bin/exe-heartbeat.js +10 -10
- package/dist/bin/exe-kill.js +13 -13
- package/dist/bin/exe-launch-agent.js +17 -17
- package/dist/bin/exe-new-employee.js +7 -7
- package/dist/bin/exe-pending-messages.js +11 -11
- package/dist/bin/exe-pending-notifications.js +10 -10
- package/dist/bin/exe-pending-reviews.js +10 -10
- package/dist/bin/exe-rename.js +4 -4
- package/dist/bin/exe-review.js +12 -12
- package/dist/bin/exe-search.js +5 -5
- package/dist/bin/exe-session-cleanup.js +15 -15
- package/dist/bin/exe-settings.js +5 -5
- package/dist/bin/exe-start-codex.js +11 -11
- package/dist/bin/exe-start-opencode.js +8 -8
- package/dist/bin/exe-status.js +11 -11
- package/dist/bin/exe-support.js +2 -2
- package/dist/bin/exe-team.js +3 -3
- package/dist/bin/exe-watchdog.js +17 -2
- package/dist/bin/git-sweep.js +11 -11
- package/dist/bin/graph-backfill.js +4 -4
- package/dist/bin/graph-export.js +5 -5
- package/dist/bin/import-history.js +7 -7
- package/dist/bin/install.js +8 -7
- package/dist/bin/intercom-check.js +4 -4
- package/dist/bin/mcp-sessions.js +2 -2
- package/dist/bin/orchestration-metrics.js +4 -4
- package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
- package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
- package/dist/bin/scan-tasks.js +10 -10
- package/dist/bin/setup.js +1 -1
- package/dist/bin/shard-migrate.js +4 -4
- package/dist/bin/stack-update.js +3 -3
- package/dist/bin/vps-health-gate.js +1 -1
- package/dist/capability-cards-F22XWGLB.js +88 -0
- package/dist/capability-cards-KCKITXXZ.js +88 -0
- package/dist/capacity-monitor-CHXGKVEO.js +50 -0
- package/dist/capacity-monitor-L3M5KB6T.js +50 -0
- package/dist/catchup-brief-BVZANLRZ.js +154 -0
- package/dist/catchup-brief-LDUGMC7S.js +154 -0
- package/dist/catchup-brief-NZDLL7SA.js +154 -0
- package/dist/catchup-brief-OOIXVGBA.js +154 -0
- package/dist/catchup-brief-UPXHDYTB.js +154 -0
- package/dist/chunk-2BI3FQKL.js +1876 -0
- package/dist/chunk-2CJUQGHH.js +362 -0
- package/dist/chunk-2EVYBMBJ.js +128 -0
- package/dist/chunk-2NQOZOXG.js +2113 -0
- package/dist/chunk-2QK5E3LB.js +128 -0
- package/dist/chunk-2VTCG4ZU.js +1352 -0
- package/dist/chunk-2YGI36DV.js +1119 -0
- package/dist/chunk-32BQN2QN.js +185 -0
- package/dist/chunk-33JMO4UV.js +157 -0
- package/dist/chunk-3DZAYLXY.js +377 -0
- package/dist/chunk-3MGBE7GR.js +76 -0
- package/dist/chunk-3MTV4FJL.js +271 -0
- package/dist/chunk-3NN7VQ27.js +1352 -0
- package/dist/chunk-4533HNOG.js +70 -0
- package/dist/chunk-456UW3MT.js +731 -0
- package/dist/chunk-4AU56XV2.js +58 -0
- package/dist/chunk-4MRP6EBR.js +280 -0
- package/dist/chunk-4MRWW52U.js +14219 -0
- package/dist/chunk-4WWECNAY.js +50 -0
- package/dist/chunk-4ZJDDR6L.js +171 -0
- package/dist/chunk-522EOPM6.js +382 -0
- package/dist/chunk-52UDAVZE.js +3208 -0
- package/dist/chunk-5FAMUB4X.js +204 -0
- package/dist/chunk-5PGZJQUI.js +58 -0
- package/dist/chunk-67TZJXNZ.js +262 -0
- package/dist/chunk-6FEZ7GN2.js +123 -0
- package/dist/chunk-6MOGND7S.js +14219 -0
- package/dist/chunk-6TRSVY7L.js +181 -0
- package/dist/chunk-6WCS7ZNK.js +85 -0
- package/dist/chunk-6WRYDREW.js +539 -0
- package/dist/chunk-72UA2FB3.js +181 -0
- package/dist/chunk-77DMFEOL.js +30 -0
- package/dist/chunk-7COXVQ5W.js +214 -0
- package/dist/chunk-7HLWBYH7.js +60 -0
- package/dist/chunk-ALSTZCWT.js +204 -0
- package/dist/chunk-ARUTDXZX.js +280 -0
- package/dist/chunk-AU47B4QY.js +129 -0
- package/dist/chunk-AXOREWVL.js +836 -0
- package/dist/chunk-B5MNC54V.js +127 -0
- package/dist/chunk-B74VSMKX.js +1350 -0
- package/dist/chunk-BHWLH44J.js +362 -0
- package/dist/chunk-BUPZ3HD2.js +85 -0
- package/dist/chunk-BWVLMA53.js +2113 -0
- package/dist/chunk-C5WLBKMJ.js +50 -0
- package/dist/chunk-C62T6R2A.js +97 -0
- package/dist/chunk-CV5KBAIK.js +33 -0
- package/dist/chunk-DAYSDWXA.js +1068 -0
- package/dist/chunk-DI4URIUB.js +227 -0
- package/dist/chunk-DNTCYFJ6.js +76 -0
- package/dist/chunk-DPDRRS7T.js +103 -0
- package/dist/chunk-DT3EV6CW.js +103 -0
- package/dist/chunk-DX45HDWY.js +1076 -0
- package/dist/chunk-E72BD6MG.js +284 -0
- package/dist/chunk-EAPUSVKS.js +375 -0
- package/dist/chunk-EFUANRRT.js +85 -0
- package/dist/chunk-EG2SCT5R.js +1352 -0
- package/dist/chunk-EJD2JU77.js +58 -0
- package/dist/chunk-EMXYUAVP.js +81 -0
- package/dist/chunk-ENM2TAAM.js +14219 -0
- package/dist/chunk-EPDRTPVP.js +1876 -0
- package/dist/chunk-EW6XDHID.js +221 -0
- package/dist/chunk-EYEGSAWZ.js +1094 -0
- package/dist/chunk-F6L33PAQ.js +231 -0
- package/dist/chunk-FC2SCTVE.js +38 -0
- package/dist/chunk-FSRKIZGZ.js +630 -0
- package/dist/chunk-FTG7I5CB.js +81 -0
- package/dist/chunk-GVFRLWX7.js +30 -0
- package/dist/chunk-H3XMZOWW.js +1119 -0
- package/dist/chunk-HAKXE6LN.js +123 -0
- package/dist/chunk-HLP3ZDTW.js +448 -0
- package/dist/chunk-HOYWKQAA.js +510 -0
- package/dist/chunk-HWDD64IW.js +712 -0
- package/dist/chunk-HWMCULHY.js +127 -0
- package/dist/chunk-HZZHRZPK.js +210 -0
- package/dist/chunk-ICRWTYNW.js +103 -0
- package/dist/chunk-J5MWPC33.js +167 -0
- package/dist/chunk-JBXANNNB.js +70 -0
- package/dist/chunk-JPBMIYWF.js +1352 -0
- package/dist/chunk-JY6EXBFI.js +373 -0
- package/dist/chunk-JZLIBXI7.js +14219 -0
- package/dist/chunk-JZPTKXJ6.js +668 -0
- package/dist/chunk-KEYMA4ZP.js +510 -0
- package/dist/chunk-KHGNN6GL.js +2078 -0
- package/dist/chunk-KVLB2PD6.js +97 -0
- package/dist/chunk-L3YBRBKL.js +1076 -0
- package/dist/chunk-L7VZ32NA.js +89 -0
- package/dist/chunk-LEHLADW4.js +221 -0
- package/dist/chunk-LPD4HILQ.js +262 -0
- package/dist/chunk-LSJ3ADDI.js +51 -0
- package/dist/chunk-LUMS2MAS.js +58 -0
- package/dist/chunk-LV4SEC6C.js +394 -0
- package/dist/chunk-LXXBEI4A.js +284 -0
- package/dist/chunk-M4NPCAIH.js +456 -0
- package/dist/chunk-MFJ62LQ5.js +157 -0
- package/dist/chunk-MGEZNKOD.js +836 -0
- package/dist/chunk-MJ7X5IBW.js +227 -0
- package/dist/chunk-MLGRWCY4.js +54 -0
- package/dist/chunk-MQZX57IY.js +348 -0
- package/dist/chunk-MY7MFF6J.js +103 -0
- package/dist/chunk-MYA2X5OY.js +185 -0
- package/dist/chunk-MYEABW5Z.js +630 -0
- package/dist/chunk-MZ2TDCAL.js +402 -0
- package/dist/chunk-NF3FRB7Z.js +271 -0
- package/dist/chunk-NXYIFEPV.js +539 -0
- package/dist/chunk-O2GVE5B5.js +58 -0
- package/dist/chunk-O5GUCDR2.js +456 -0
- package/dist/chunk-OBZNRECA.js +128 -0
- package/dist/chunk-OMZ2RLJG.js +214 -0
- package/dist/chunk-ORDHJRWN.js +299 -0
- package/dist/chunk-OSPIJMCD.js +210 -0
- package/dist/chunk-OUGWEH4J.js +240 -0
- package/dist/chunk-OV5MJQGC.js +1876 -0
- package/dist/chunk-PC635OAG.js +4318 -0
- package/dist/chunk-PH46R4J6.js +348 -0
- package/dist/chunk-PJP2EP7P.js +394 -0
- package/dist/chunk-PODFWH3V.js +333 -0
- package/dist/chunk-PPWH3SHR.js +1068 -0
- package/dist/chunk-PWPJK7KB.js +4318 -0
- package/dist/chunk-PXONZVG4.js +377 -0
- package/dist/chunk-QAOGJRZD.js +369 -0
- package/dist/chunk-QC3LAEI7.js +197 -0
- package/dist/chunk-QDWQDUWI.js +668 -0
- package/dist/chunk-QKJFD6BH.js +1350 -0
- package/dist/chunk-QNYVJGFM.js +345 -0
- package/dist/chunk-QQF3XGQ5.js +14219 -0
- package/dist/chunk-QRPFQNI3.js +150 -0
- package/dist/chunk-QSSU5XWD.js +731 -0
- package/dist/chunk-QXZAGVAV.js +2078 -0
- package/dist/chunk-RA54MW64.js +244 -0
- package/dist/chunk-RF7PUWXI.js +197 -0
- package/dist/chunk-S3INDYSO.js +244 -0
- package/dist/chunk-SLQVTHH5.js +369 -0
- package/dist/chunk-SY2B74KL.js +345 -0
- package/dist/chunk-T6QPXXXW.js +712 -0
- package/dist/chunk-TO5M5YCT.js +41 -0
- package/dist/chunk-TQ4VXUAF.js +129 -0
- package/dist/chunk-UAB7RQC4.js +41 -0
- package/dist/chunk-UMEIBDYW.js +97 -0
- package/dist/chunk-UXW5TB7Y.js +240 -0
- package/dist/chunk-VB2N5WOX.js +150 -0
- package/dist/chunk-VLZEMRG3.js +167 -0
- package/dist/chunk-VM3V6VK7.js +230 -0
- package/dist/chunk-VMCGKBHB.js +1352 -0
- package/dist/chunk-VNIYZAR5.js +128 -0
- package/dist/chunk-VYV4KOD2.js +85 -0
- package/dist/chunk-W4SRJBAT.js +171 -0
- package/dist/chunk-W5W3LZ3Q.js +54 -0
- package/dist/chunk-WDNZEOM3.js +38 -0
- package/dist/chunk-WUKEXVOR.js +3208 -0
- package/dist/chunk-X3Z35Q6L.js +373 -0
- package/dist/chunk-X4VOU6BQ.js +382 -0
- package/dist/chunk-X6EEVSVG.js +290 -0
- package/dist/chunk-XFHGWGNB.js +1094 -0
- package/dist/chunk-XKOLRWYA.js +33 -0
- package/dist/chunk-XMMIL3UD.js +402 -0
- package/dist/chunk-XWILC6VA.js +290 -0
- package/dist/chunk-Y4OQCX4C.js +97 -0
- package/dist/chunk-Y67VYYOA.js +231 -0
- package/dist/chunk-YGQCQTQH.js +230 -0
- package/dist/chunk-YGWFBN5A.js +299 -0
- package/dist/chunk-YMKUXZIG.js +379 -0
- package/dist/chunk-YOMLMT7E.js +230 -0
- package/dist/chunk-YPESIZOB.js +14219 -0
- package/dist/chunk-Z2CGCIU2.js +89 -0
- package/dist/chunk-ZLAWNHQR.js +448 -0
- package/dist/chunk-ZME5UQSN.js +333 -0
- package/dist/co-activation-NUEQYXE5.js +73 -0
- package/dist/co-activation-ZG5HLBCZ.js +73 -0
- package/dist/co-occurrence-7S5KWQB2.js +94 -0
- package/dist/co-occurrence-X5SWDXT2.js +94 -0
- package/dist/core-memory-GOPBRGGZ.js +110 -0
- package/dist/core-memory-XLCU6L5M.js +110 -0
- package/dist/crdt-sync-EPKHPGRZ.js +33 -0
- package/dist/crdt-sync-UIQJ5U7T.js +33 -0
- package/dist/crm-webhook-MKN23JNU.js +10 -0
- package/dist/crm-webhook-SM63BPXO.js +10 -0
- package/dist/cto-delegation-gate-PQY5TOVZ.js +279 -0
- package/dist/cto-delegation-gate-V5VVUR3G.js +279 -0
- package/dist/daemon-orchestration-C7AAS67Q.js +138 -0
- package/dist/daemon-orchestration-OBCAJB2H.js +138 -0
- package/dist/db-backup-F7VP4QRH.js +33 -0
- package/dist/db-backup-KVYC57W7.js +33 -0
- package/dist/doc-graph-extractor-H2ETEINP.js +132 -0
- package/dist/doc-graph-extractor-PCUZEYCH.js +132 -0
- package/dist/dreaming-4OZXSLE3.js +33 -0
- package/dist/dreaming-Z2RYEYNT.js +33 -0
- package/dist/exe-drift-GEWNIK7A.js +69 -0
- package/dist/exe-drift-XCGH7AFO.js +69 -0
- package/dist/exe-export-7DKAU5IP.js +75 -0
- package/dist/exe-export-BCHH6OE6.js +75 -0
- package/dist/exe-import-PDRIZVYF.js +78 -0
- package/dist/exe-import-ZCKUDFKL.js +78 -0
- package/dist/exe-key-FUWLLI3U.js +580 -0
- package/dist/exe-key-RKKNVUMP.js +580 -0
- package/dist/exe-snapshot-G4I5FQMK.js +337 -0
- package/dist/exe-snapshot-GWU7QTZK.js +337 -0
- package/dist/fast-db-init-6QG6YQNT.js +7 -0
- package/dist/fast-db-init-X2QDQUA4.js +7 -0
- package/dist/founder-context-TOMNUBGJ.js +96 -0
- package/dist/founder-context-UU3V6MAS.js +96 -0
- package/dist/gateway/index.js +8 -8
- package/dist/git-staleness-FEPFMZKF.js +111 -0
- package/dist/git-staleness-HYVYLCW3.js +111 -0
- package/dist/git-task-sweep-IRV52JIM.js +41 -0
- package/dist/git-task-sweep-T6BSM3GS.js +41 -0
- package/dist/global-procedures-3AURRMKO.js +21 -0
- package/dist/global-procedures-JPCYBZYC.js +21 -0
- package/dist/graph-auto-extract-OC3AOSMW.js +182 -0
- package/dist/graph-auto-extract-PVDYEJBY.js +182 -0
- package/dist/hooks/bug-report-worker.js +12 -12
- package/dist/hooks/codex-stop-task-finalizer.js +12 -12
- package/dist/hooks/commit-complete.js +12 -12
- package/dist/hooks/error-recall.js +6 -6
- package/dist/hooks/exe-heartbeat-hook.js +3 -3
- package/dist/hooks/ingest.js +6 -6
- package/dist/hooks/instructions-loaded.js +4 -4
- package/dist/hooks/manifest.json +19 -19
- package/dist/hooks/notification.js +4 -4
- package/dist/hooks/post-compact.js +11 -11
- package/dist/hooks/post-tool-combined.js +5 -5
- package/dist/hooks/pre-compact.js +12 -12
- package/dist/hooks/pre-tool-use.js +15 -15
- package/dist/hooks/prompt-submit.js +22 -22
- package/dist/hooks/session-end.js +16 -16
- package/dist/hooks/session-start.js +10 -10
- package/dist/hooks/stop.js +15 -15
- package/dist/hooks/subagent-stop.js +11 -11
- package/dist/hooks/summary-worker.js +15 -15
- package/dist/index.js +18 -18
- package/dist/installer-72XXLBRP.js +39 -0
- package/dist/installer-HDXG2BZN.js +343 -0
- package/dist/installer-JALMKPCS.js +297 -0
- package/dist/installer-Q46SNNLU.js +39 -0
- package/dist/installer-W7PIPRCX.js +343 -0
- package/dist/installer-Z7WQEOS7.js +297 -0
- package/dist/lib/cloud-sync.js +5 -5
- package/dist/lib/consolidation.js +5 -5
- package/dist/lib/database.js +2 -2
- package/dist/lib/db.js +2 -2
- package/dist/lib/employee-templates.js +4 -4
- package/dist/lib/employees.js +2 -2
- package/dist/lib/exe-daemon.js +45 -41
- package/dist/lib/hybrid-search.js +5 -5
- package/dist/lib/identity.js +2 -2
- package/dist/lib/license.js +1 -1
- package/dist/lib/messaging.js +10 -10
- package/dist/lib/reminders.js +3 -3
- package/dist/lib/schedules.js +5 -5
- package/dist/lib/session-registry.js +4 -4
- package/dist/lib/skill-learning.js +6 -6
- package/dist/lib/store.js +4 -4
- package/dist/lib/task-router.js +3 -3
- package/dist/lib/tasks.js +11 -11
- package/dist/lib/tmux-routing.js +9 -9
- package/dist/lib/token-spend.js +3 -3
- package/dist/license-gate-7JZCHOAG.js +14 -0
- package/dist/license-gate-OP4SKL4P.js +14 -0
- package/dist/mcp/register-tools.js +58 -58
- package/dist/mcp/server.js +59 -59
- package/dist/mcp/tools/complete-reminder.js +4 -4
- package/dist/mcp/tools/create-reminder.js +4 -4
- package/dist/mcp/tools/create-task.js +13 -13
- package/dist/mcp/tools/deactivate-behavior.js +7 -7
- package/dist/mcp/tools/list-reminders.js +4 -4
- package/dist/mcp/tools/list-tasks.js +13 -13
- package/dist/mcp/tools/send-message.js +12 -12
- package/dist/mcp/tools/update-task.js +12 -12
- package/dist/mcp-health-VULNT722.js +17 -0
- package/dist/mcp-health-WDOB6XUB.js +19 -0
- package/dist/mcp-http-config-2OZ7N74D.js +28 -0
- package/dist/mcp-http-config-4VXA5K73.js +28 -0
- package/dist/memory-cards-2K6QRZU6.js +179 -0
- package/dist/memory-cards-KSJF5OH2.js +179 -0
- package/dist/memory-graph-extractor-IJD5HWYT.js +21 -0
- package/dist/memory-graph-extractor-O4GAXOK5.js +21 -0
- package/dist/memory-poisoning-defense-2JRPWT5V.js +223 -0
- package/dist/memory-poisoning-defense-DH4A25NU.js +223 -0
- package/dist/memory-reflection-ISY2BBDB.js +243 -0
- package/dist/memory-reflection-Z5AQRR6H.js +243 -0
- package/dist/notifications-2VSWK2UJ.js +46 -0
- package/dist/notifications-4S253VQM.js +46 -0
- package/dist/oauth-server-D7D4574D.js +437 -0
- package/dist/oauth-server-MACN54SJ.js +437 -0
- package/dist/orchestration-events-BGP5RYQI.js +26 -0
- package/dist/orchestration-events-MDXUEVRZ.js +26 -0
- package/dist/orchestrator-DHK7RSSH.js +34 -0
- package/dist/orchestrator-R75WHQVA.js +34 -0
- package/dist/pipeline-router-4WUKQQEC.js +14 -0
- package/dist/pipeline-router-GWB2XK2Q.js +14 -0
- package/dist/plan-limits-NNJRAESF.js +27 -0
- package/dist/plan-limits-YTQW4UR4.js +27 -0
- package/dist/project-boot-46GZJTEX.js +299 -0
- package/dist/project-boot-PPHBBGIF.js +299 -0
- package/dist/projection-worker-UPAWXI7P.js +1034 -0
- package/dist/projection-worker-ZIKDYBW5.js +1034 -0
- package/dist/reranker-5ZBP2RRN.js +19 -0
- package/dist/reranker-E2MQIMJL.js +19 -0
- package/dist/reranker-GLSDJT3V.js +19 -0
- package/dist/reranker-LBBXWNOD.js +19 -0
- package/dist/reranker-XZ2EF4OH.js +19 -0
- package/dist/retrieval-health-JYRKPSII.js +7 -0
- package/dist/retrieval-health-OUV25J6S.js +7 -0
- package/dist/retrieval-health-U73JUAZL.js +7 -0
- package/dist/retrieval-health-WSZ7TYFF.js +7 -0
- package/dist/review-polling-62JV55ZT.js +125 -0
- package/dist/review-polling-CJXLWFWK.js +125 -0
- package/dist/runtime/index.js +12 -12
- package/dist/session-events-2ADD54VI.js +37 -0
- package/dist/session-events-QIJVBSKS.js +37 -0
- package/dist/session-kill-telemetry-HS6HD2YE.js +30 -0
- package/dist/session-kill-telemetry-MRT5FVSM.js +30 -0
- package/dist/session-scope-7ICYPC33.js +87 -0
- package/dist/session-scope-KMXD6EE6.js +87 -0
- package/dist/setup-wizard-B6GIT7YC.js +12 -0
- package/dist/setup-wizard-JUIJ4UZO.js +12 -0
- package/dist/skill-refinement-HIOX4VMC.js +158 -0
- package/dist/skill-refinement-T7JXRYUW.js +158 -0
- package/dist/stack-update-5KE6BZKQ.js +74 -0
- package/dist/stack-update-OP2RHP7N.js +74 -0
- package/dist/stack-update-VGCWDJEE.js +74 -0
- package/dist/steward-gate-L22WE3SY.js +14 -0
- package/dist/steward-gate-YKD2LUWN.js +14 -0
- package/dist/task-enforcement-5AOKXTY4.js +439 -0
- package/dist/task-enforcement-VO3YEGIO.js +439 -0
- package/dist/task-scope-YV2WPKRD.js +36 -0
- package/dist/task-scope-ZSXDZBRE.js +36 -0
- package/dist/tasks-crud-C6KADACT.js +78 -0
- package/dist/tasks-crud-NV6JEWGL.js +78 -0
- package/dist/tasks-notify-E22HSN6O.js +39 -0
- package/dist/tasks-notify-RPSEQ4WV.js +39 -0
- package/dist/tasks-review-V4ZLXOAZ.js +48 -0
- package/dist/tasks-review-ZVRI73JE.js +48 -0
- package/dist/telemetry-upload-LXUH7SKI.js +740 -0
- package/dist/telemetry-upload-TCDAZTUQ.js +740 -0
- package/dist/token-budget-OFBEZJTA.js +85 -0
- package/dist/token-budget-WAN57V6S.js +85 -0
- package/dist/tool-telemetry-UA3N32PK.js +17 -0
- package/dist/tool-telemetry-XXZJ35RR.js +17 -0
- package/dist/tui/App.js +17 -17
- package/dist/tui-data-46QLCJUE.js +259 -0
- package/dist/tui-data-ZDB7BLP2.js +259 -0
- package/dist/wiki-acl-HHSIBPF3.js +111 -0
- package/dist/wiki-acl-O65GZ2ZF.js +111 -0
- package/dist/worker-gate-27I4GAEZ.js +21 -0
- package/dist/worker-gate-DXU4HEPY.js +21 -0
- package/dist/workflow-engine-63EOEJ5Q.js +28 -0
- package/dist/workflow-engine-C6F2RMPN.js +28 -0
- package/dist/worktree-SFKKOMFD.js +27 -0
- package/dist/worktree-SVCE3S7X.js +27 -0
- package/dist/worktree-sweep-S3JHJTVP.js +20 -0
- package/dist/worktree-sweep-U3TIQ7WL.js +20 -0
- package/package.json +1 -1
- package/release-notes.json +175 -57
|
@@ -0,0 +1,1068 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadPreferences
|
|
3
|
+
} from "./chunk-GNHN5HRQ.js";
|
|
4
|
+
import {
|
|
5
|
+
EXE_HOOKS,
|
|
6
|
+
isLegacyHomeDirHookCommand,
|
|
7
|
+
isLegacySplitPostToolCommand
|
|
8
|
+
} from "./chunk-L3TB7CC3.js";
|
|
9
|
+
import {
|
|
10
|
+
buildClaudeHttpMcpEntry
|
|
11
|
+
} from "./chunk-C62T6R2A.js";
|
|
12
|
+
import {
|
|
13
|
+
ensureAllAgentSymlinks
|
|
14
|
+
} from "./chunk-4533HNOG.js";
|
|
15
|
+
import {
|
|
16
|
+
MCP_LEGACY_KEY,
|
|
17
|
+
MCP_PRIMARY_KEY
|
|
18
|
+
} from "./chunk-HYZV25LY.js";
|
|
19
|
+
|
|
20
|
+
// src/adapters/claude/installer.ts
|
|
21
|
+
import { readFile, writeFile, mkdir, readdir, rm } from "fs/promises";
|
|
22
|
+
import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync } from "fs";
|
|
23
|
+
import { createHash } from "crypto";
|
|
24
|
+
import path from "path";
|
|
25
|
+
import os from "os";
|
|
26
|
+
import { execSync } from "child_process";
|
|
27
|
+
import { fileURLToPath } from "url";
|
|
28
|
+
function resolvePackageRoot() {
|
|
29
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
30
|
+
let dir = path.dirname(thisFile);
|
|
31
|
+
const root = path.parse(dir).root;
|
|
32
|
+
while (dir !== root) {
|
|
33
|
+
const pkgPath = path.join(dir, "package.json");
|
|
34
|
+
if (existsSync(pkgPath)) {
|
|
35
|
+
try {
|
|
36
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
37
|
+
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
dir = path.dirname(dir);
|
|
42
|
+
}
|
|
43
|
+
return path.resolve(path.dirname(thisFile), "..", "..", "..");
|
|
44
|
+
}
|
|
45
|
+
async function copySlashCommands(packageRoot, homeDir = os.homedir()) {
|
|
46
|
+
let copied = 0;
|
|
47
|
+
let skipped = 0;
|
|
48
|
+
const skillsBase = path.join(homeDir, ".claude", "skills");
|
|
49
|
+
const deprecatedExeLink = path.join(skillsBase, "exe-link");
|
|
50
|
+
if (existsSync(deprecatedExeLink)) {
|
|
51
|
+
await rm(deprecatedExeLink, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
const exeDir = path.join(packageRoot, "src", "commands", "exe");
|
|
54
|
+
if (existsSync(exeDir)) {
|
|
55
|
+
const entries = await readdir(exeDir);
|
|
56
|
+
const mdFiles = entries.filter((f) => f.endsWith(".md"));
|
|
57
|
+
for (const file of mdFiles) {
|
|
58
|
+
const name = file.replace(".md", "");
|
|
59
|
+
const destDir = path.join(skillsBase, `exe-${name}`);
|
|
60
|
+
await mkdir(destDir, { recursive: true });
|
|
61
|
+
const srcPath = path.join(exeDir, file);
|
|
62
|
+
const destPath = path.join(destDir, "SKILL.md");
|
|
63
|
+
const result = await copyAsSkill(srcPath, destPath, `exe-${name}`);
|
|
64
|
+
if (result) copied++;
|
|
65
|
+
else skipped++;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const topLevelSrc = path.join(packageRoot, "src", "commands", "exe.md");
|
|
69
|
+
if (existsSync(topLevelSrc)) {
|
|
70
|
+
const destDir = path.join(skillsBase, "exe");
|
|
71
|
+
await mkdir(destDir, { recursive: true });
|
|
72
|
+
const destPath = path.join(destDir, "SKILL.md");
|
|
73
|
+
const result = await copyAsSkill(topLevelSrc, destPath, "exe");
|
|
74
|
+
if (result) copied++;
|
|
75
|
+
else skipped++;
|
|
76
|
+
}
|
|
77
|
+
return { copied, skipped };
|
|
78
|
+
}
|
|
79
|
+
async function copyAsSkill(srcPath, destPath, skillName) {
|
|
80
|
+
let content = await readFile(srcPath, "utf-8");
|
|
81
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
82
|
+
if (fmMatch?.[1]) {
|
|
83
|
+
const fm = fmMatch[1];
|
|
84
|
+
if (fm.includes("name:")) {
|
|
85
|
+
content = content.replace(
|
|
86
|
+
/^(---\n[\s\S]*?)name:\s*[^\n]+/,
|
|
87
|
+
`$1name: ${skillName}`
|
|
88
|
+
);
|
|
89
|
+
} else {
|
|
90
|
+
content = content.replace(/^---\n/, `---
|
|
91
|
+
name: ${skillName}
|
|
92
|
+
`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (existsSync(destPath)) {
|
|
96
|
+
const existing = await readFile(destPath, "utf-8");
|
|
97
|
+
if (existing === content) return false;
|
|
98
|
+
}
|
|
99
|
+
await writeFile(destPath, content);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
function readJsonFile(filePath) {
|
|
103
|
+
try {
|
|
104
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function findAncestorMcpJsons(startDir, homeDir) {
|
|
110
|
+
const files = [];
|
|
111
|
+
let dir = path.resolve(startDir);
|
|
112
|
+
const root = path.parse(dir).root;
|
|
113
|
+
const stop = path.resolve(homeDir);
|
|
114
|
+
while (dir !== root) {
|
|
115
|
+
const candidate = path.join(dir, ".mcp.json");
|
|
116
|
+
if (existsSync(candidate)) files.push(candidate);
|
|
117
|
+
if (dir === stop) break;
|
|
118
|
+
dir = path.dirname(dir);
|
|
119
|
+
}
|
|
120
|
+
return files;
|
|
121
|
+
}
|
|
122
|
+
function pathApplies(projectPath, cwd) {
|
|
123
|
+
const project = path.resolve(projectPath);
|
|
124
|
+
const current = path.resolve(cwd);
|
|
125
|
+
return current === project || current.startsWith(project + path.sep);
|
|
126
|
+
}
|
|
127
|
+
function detectMcpNameCollisions(homeDir = os.homedir(), cwd = process.cwd()) {
|
|
128
|
+
const claudeJsonPath = path.join(homeDir, ".claude.json");
|
|
129
|
+
if (!existsSync(claudeJsonPath)) return [];
|
|
130
|
+
const claudeJson = readJsonFile(claudeJsonPath);
|
|
131
|
+
if (!claudeJson?.projects) return [];
|
|
132
|
+
const collisions = [];
|
|
133
|
+
const mcpJsons = findAncestorMcpJsons(cwd, homeDir);
|
|
134
|
+
if (mcpJsons.length === 0) return [];
|
|
135
|
+
for (const [projectPath, projectConfig] of Object.entries(claudeJson.projects)) {
|
|
136
|
+
if (!pathApplies(projectPath, cwd)) continue;
|
|
137
|
+
const projectServerNames = new Set(Object.keys(projectConfig.mcpServers ?? {}));
|
|
138
|
+
if (projectServerNames.size === 0) continue;
|
|
139
|
+
const enabled = new Set(projectConfig.enabledMcpjsonServers ?? []);
|
|
140
|
+
for (const mcpJsonPath of mcpJsons) {
|
|
141
|
+
const mcpJson = readJsonFile(mcpJsonPath);
|
|
142
|
+
const mcpServerNames = Object.keys(mcpJson?.mcpServers ?? {});
|
|
143
|
+
for (const serverName of mcpServerNames) {
|
|
144
|
+
if (!projectServerNames.has(serverName)) continue;
|
|
145
|
+
collisions.push({
|
|
146
|
+
mcpJsonPath,
|
|
147
|
+
projectPath,
|
|
148
|
+
serverName,
|
|
149
|
+
disabledInMcpJson: !enabled.has(serverName)
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return collisions;
|
|
155
|
+
}
|
|
156
|
+
function buildStdioMcpEntry(packageRoot) {
|
|
157
|
+
return {
|
|
158
|
+
type: "stdio",
|
|
159
|
+
command: "node",
|
|
160
|
+
args: [path.join(packageRoot, "dist", "mcp", "server.js")],
|
|
161
|
+
env: {}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function mcpTransportMode() {
|
|
165
|
+
const value = process.env.EXE_OS_MCP_TRANSPORT?.trim().toLowerCase();
|
|
166
|
+
if (value === "stdio") return "stdio";
|
|
167
|
+
if (value === "http") return "http";
|
|
168
|
+
const totalGB = os.totalmem() / (1024 * 1024 * 1024);
|
|
169
|
+
if (totalGB <= 8) return "stdio";
|
|
170
|
+
return "http";
|
|
171
|
+
}
|
|
172
|
+
async function registerMcpServer(packageRoot, homeDir = os.homedir()) {
|
|
173
|
+
const claudeJsonPath = path.join(homeDir, ".claude.json");
|
|
174
|
+
let claudeJson = {};
|
|
175
|
+
if (existsSync(claudeJsonPath)) {
|
|
176
|
+
try {
|
|
177
|
+
claudeJson = JSON.parse(await readFile(claudeJsonPath, "utf-8"));
|
|
178
|
+
} catch {
|
|
179
|
+
claudeJson = {};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (!claudeJson.mcpServers) {
|
|
183
|
+
claudeJson.mcpServers = {};
|
|
184
|
+
}
|
|
185
|
+
const newEntry = mcpTransportMode() === "stdio" ? buildStdioMcpEntry(packageRoot) : buildClaudeHttpMcpEntry(homeDir);
|
|
186
|
+
if (newEntry && typeof newEntry === "object" && "headers" in newEntry) {
|
|
187
|
+
const headers = newEntry.headers;
|
|
188
|
+
if (headers["X-Exe-Session"]) {
|
|
189
|
+
delete headers["X-Exe-Session"];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
for (const key of [MCP_PRIMARY_KEY, MCP_LEGACY_KEY]) {
|
|
193
|
+
const existing = claudeJson.mcpServers?.[key];
|
|
194
|
+
if (existing?.headers) {
|
|
195
|
+
const h = existing.headers;
|
|
196
|
+
if (h["X-Exe-Session"]) {
|
|
197
|
+
delete h["X-Exe-Session"];
|
|
198
|
+
process.stderr.write(`exe-os: stripped stale X-Exe-Session from global ${key} config
|
|
199
|
+
`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (claudeJson.mcpServers[MCP_LEGACY_KEY]) {
|
|
204
|
+
delete claudeJson.mcpServers[MCP_LEGACY_KEY];
|
|
205
|
+
process.stderr.write("exe-os: migrated MCP server key exe-mem \u2192 exe-os\n");
|
|
206
|
+
}
|
|
207
|
+
if (claudeJson.projects) {
|
|
208
|
+
for (const [projectPath, projectConfig] of Object.entries(claudeJson.projects)) {
|
|
209
|
+
if (projectConfig.mcpServers?.[MCP_LEGACY_KEY]) {
|
|
210
|
+
delete projectConfig.mcpServers[MCP_LEGACY_KEY];
|
|
211
|
+
process.stderr.write(`exe-os: removed stale project-level exe-mem from ${projectPath}
|
|
212
|
+
`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const currentOs = claudeJson.mcpServers[MCP_PRIMARY_KEY];
|
|
217
|
+
const osMatches = currentOs && JSON.stringify(currentOs) === JSON.stringify(newEntry);
|
|
218
|
+
if (osMatches) {
|
|
219
|
+
await cleanSettingsJsonMcp(path.join(homeDir, ".claude", "settings.json"));
|
|
220
|
+
await migratePermissionsToExeOs(path.join(homeDir, ".claude", "settings.json"));
|
|
221
|
+
const collisions2 = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
|
|
222
|
+
for (const c of collisions2) {
|
|
223
|
+
process.stderr.write(
|
|
224
|
+
`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.
|
|
225
|
+
`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
await registerExeRustMcpConfig(packageRoot, homeDir);
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
|
|
232
|
+
await writeFile(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
233
|
+
await cleanSettingsJsonMcp(path.join(homeDir, ".claude", "settings.json"));
|
|
234
|
+
await migratePermissionsToExeOs(path.join(homeDir, ".claude", "settings.json"));
|
|
235
|
+
const collisions = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
|
|
236
|
+
for (const c of collisions) {
|
|
237
|
+
process.stderr.write(
|
|
238
|
+
`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.
|
|
239
|
+
`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
await registerExeRustMcpConfig(packageRoot, homeDir);
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
async function registerExeRustMcpConfig(packageRoot, homeDir) {
|
|
246
|
+
const exeDir = path.join(homeDir, ".exe");
|
|
247
|
+
const exeMcpPath = path.join(exeDir, "mcp.json");
|
|
248
|
+
await mkdir(exeDir, { recursive: true });
|
|
249
|
+
let config = { servers: {} };
|
|
250
|
+
if (existsSync(exeMcpPath)) {
|
|
251
|
+
try {
|
|
252
|
+
config = JSON.parse(await readFile(exeMcpPath, "utf-8"));
|
|
253
|
+
if (!config.servers) config.servers = {};
|
|
254
|
+
} catch {
|
|
255
|
+
config = { servers: {} };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const stdioEntry = {
|
|
259
|
+
command: "node",
|
|
260
|
+
args: [path.join(packageRoot, "dist", "mcp", "server.js")],
|
|
261
|
+
env: {},
|
|
262
|
+
shared: true
|
|
263
|
+
};
|
|
264
|
+
const existing = config.servers[MCP_PRIMARY_KEY];
|
|
265
|
+
if (existing && JSON.stringify(existing) === JSON.stringify(stdioEntry)) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (config.servers[MCP_LEGACY_KEY]) {
|
|
269
|
+
delete config.servers[MCP_LEGACY_KEY];
|
|
270
|
+
}
|
|
271
|
+
config.servers[MCP_PRIMARY_KEY] = stdioEntry;
|
|
272
|
+
await writeFile(exeMcpPath, JSON.stringify(config, null, 2) + "\n");
|
|
273
|
+
process.stderr.write("exe-os: registered MCP server in ~/.exe/mcp.json (Rust runtime)\n");
|
|
274
|
+
}
|
|
275
|
+
async function cleanSettingsJsonMcp(settingsPath) {
|
|
276
|
+
if (!existsSync(settingsPath)) return;
|
|
277
|
+
try {
|
|
278
|
+
const settings = JSON.parse(await readFile(settingsPath, "utf-8"));
|
|
279
|
+
const servers = settings.mcpServers;
|
|
280
|
+
if (!servers) return;
|
|
281
|
+
let changed = false;
|
|
282
|
+
if (servers[MCP_PRIMARY_KEY]) {
|
|
283
|
+
delete servers[MCP_PRIMARY_KEY];
|
|
284
|
+
changed = true;
|
|
285
|
+
}
|
|
286
|
+
if (servers[MCP_LEGACY_KEY]) {
|
|
287
|
+
delete servers[MCP_LEGACY_KEY];
|
|
288
|
+
changed = true;
|
|
289
|
+
}
|
|
290
|
+
if (changed) {
|
|
291
|
+
await writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
292
|
+
}
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async function migratePermissionsToExeOs(settingsPath) {
|
|
297
|
+
if (!existsSync(settingsPath)) return;
|
|
298
|
+
try {
|
|
299
|
+
const settings = JSON.parse(await readFile(settingsPath, "utf-8"));
|
|
300
|
+
const permissions = settings.permissions;
|
|
301
|
+
if (!permissions || !Array.isArray(permissions.allow)) return;
|
|
302
|
+
const allow = permissions.allow;
|
|
303
|
+
let migrated = 0;
|
|
304
|
+
for (let i = 0; i < allow.length; i++) {
|
|
305
|
+
if (allow[i].startsWith("mcp__exe-mem__")) {
|
|
306
|
+
const newName = allow[i].replace("mcp__exe-mem__", "mcp__exe-os__");
|
|
307
|
+
if (!allow.includes(newName)) {
|
|
308
|
+
allow[i] = newName;
|
|
309
|
+
} else {
|
|
310
|
+
allow[i] = "__REMOVE__";
|
|
311
|
+
}
|
|
312
|
+
migrated++;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (migrated > 0) {
|
|
316
|
+
permissions.allow = allow.filter((e) => e !== "__REMOVE__");
|
|
317
|
+
permissions.allow = [...new Set(permissions.allow)];
|
|
318
|
+
await writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
319
|
+
process.stderr.write(`exe-os: migrated ${migrated} permission(s) from exe-mem \u2192 exe-os
|
|
320
|
+
`);
|
|
321
|
+
}
|
|
322
|
+
} catch {
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async function mergeHooks(packageRoot, homeDir = os.homedir()) {
|
|
326
|
+
const settingsPath = path.join(homeDir, ".claude", "settings.json");
|
|
327
|
+
const logsDir = path.join(homeDir, ".exe-os", "logs");
|
|
328
|
+
const hookLogPath = path.join(logsDir, "hooks.log");
|
|
329
|
+
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
330
|
+
await mkdir(logsDir, { recursive: true });
|
|
331
|
+
let settings = {};
|
|
332
|
+
if (existsSync(settingsPath)) {
|
|
333
|
+
try {
|
|
334
|
+
settings = JSON.parse(await readFile(settingsPath, "utf-8"));
|
|
335
|
+
} catch {
|
|
336
|
+
settings = {};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (!settings.hooks) {
|
|
340
|
+
settings.hooks = {};
|
|
341
|
+
}
|
|
342
|
+
if (settings.hooks && typeof settings.hooks !== "object") {
|
|
343
|
+
console.warn("[exe-os] Unexpected hooks schema in settings.json \u2014 skipping hook installation");
|
|
344
|
+
return { added: 0, skipped: 0 };
|
|
345
|
+
}
|
|
346
|
+
const hooksToRegister = [
|
|
347
|
+
{
|
|
348
|
+
event: "PostToolUse",
|
|
349
|
+
group: {
|
|
350
|
+
matcher: "Bash|Edit|Write|Read|Glob|Grep|Agent|mcp__.*",
|
|
351
|
+
hooks: [
|
|
352
|
+
{
|
|
353
|
+
type: "command",
|
|
354
|
+
command: `node "${path.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
|
|
355
|
+
}
|
|
356
|
+
]
|
|
357
|
+
},
|
|
358
|
+
marker: EXE_HOOKS.postToolCombined
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
event: "SessionStart",
|
|
362
|
+
group: {
|
|
363
|
+
hooks: [
|
|
364
|
+
{
|
|
365
|
+
type: "command",
|
|
366
|
+
command: `node "${path.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
367
|
+
timeout: 1e4
|
|
368
|
+
}
|
|
369
|
+
]
|
|
370
|
+
},
|
|
371
|
+
marker: EXE_HOOKS.sessionStart
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
event: "UserPromptSubmit",
|
|
375
|
+
group: {
|
|
376
|
+
hooks: [
|
|
377
|
+
{
|
|
378
|
+
type: "command",
|
|
379
|
+
command: `node "${path.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
380
|
+
}
|
|
381
|
+
]
|
|
382
|
+
},
|
|
383
|
+
marker: EXE_HOOKS.promptSubmit
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
event: "UserPromptSubmit",
|
|
387
|
+
group: {
|
|
388
|
+
hooks: [
|
|
389
|
+
{
|
|
390
|
+
type: "command",
|
|
391
|
+
command: `node "${path.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
|
|
392
|
+
timeout: 5e3
|
|
393
|
+
}
|
|
394
|
+
]
|
|
395
|
+
},
|
|
396
|
+
marker: EXE_HOOKS.heartbeat
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
event: "Stop",
|
|
400
|
+
group: {
|
|
401
|
+
hooks: [
|
|
402
|
+
{
|
|
403
|
+
type: "command",
|
|
404
|
+
command: `node "${path.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
405
|
+
}
|
|
406
|
+
]
|
|
407
|
+
},
|
|
408
|
+
marker: EXE_HOOKS.stop
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
event: "PreToolUse",
|
|
412
|
+
group: {
|
|
413
|
+
matcher: "Write|Edit|Read|Bash",
|
|
414
|
+
hooks: [
|
|
415
|
+
{
|
|
416
|
+
type: "command",
|
|
417
|
+
command: `node "${path.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
418
|
+
}
|
|
419
|
+
]
|
|
420
|
+
},
|
|
421
|
+
marker: EXE_HOOKS.preToolUse
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
event: "SubagentStop",
|
|
425
|
+
group: {
|
|
426
|
+
hooks: [
|
|
427
|
+
{
|
|
428
|
+
type: "command",
|
|
429
|
+
command: `node "${path.join(packageRoot, "dist", "hooks", "subagent-stop.js")}"${logSuffix}`
|
|
430
|
+
}
|
|
431
|
+
]
|
|
432
|
+
},
|
|
433
|
+
marker: EXE_HOOKS.subagentStop
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
event: "PreCompact",
|
|
437
|
+
group: {
|
|
438
|
+
hooks: [
|
|
439
|
+
{
|
|
440
|
+
type: "command",
|
|
441
|
+
command: `node "${path.join(packageRoot, "dist", "hooks", "pre-compact.js")}"${logSuffix}`,
|
|
442
|
+
timeout: 1e4
|
|
443
|
+
}
|
|
444
|
+
]
|
|
445
|
+
},
|
|
446
|
+
marker: EXE_HOOKS.preCompact
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
event: "SessionEnd",
|
|
450
|
+
group: {
|
|
451
|
+
hooks: [
|
|
452
|
+
{
|
|
453
|
+
type: "command",
|
|
454
|
+
command: `node "${path.join(packageRoot, "dist", "hooks", "session-end.js")}"${logSuffix}`
|
|
455
|
+
}
|
|
456
|
+
]
|
|
457
|
+
},
|
|
458
|
+
marker: EXE_HOOKS.sessionEnd
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
event: "Notification",
|
|
462
|
+
group: {
|
|
463
|
+
hooks: [
|
|
464
|
+
{
|
|
465
|
+
type: "command",
|
|
466
|
+
command: `node "${path.join(packageRoot, "dist", "hooks", "notification.js")}"${logSuffix}`
|
|
467
|
+
}
|
|
468
|
+
]
|
|
469
|
+
},
|
|
470
|
+
marker: EXE_HOOKS.notification
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
event: "PostCompact",
|
|
474
|
+
group: {
|
|
475
|
+
hooks: [
|
|
476
|
+
{
|
|
477
|
+
type: "command",
|
|
478
|
+
command: `node "${path.join(packageRoot, "dist", "hooks", "post-compact.js")}"${logSuffix}`,
|
|
479
|
+
timeout: 1e4
|
|
480
|
+
}
|
|
481
|
+
]
|
|
482
|
+
},
|
|
483
|
+
marker: EXE_HOOKS.postCompact
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
event: "InstructionsLoaded",
|
|
487
|
+
group: {
|
|
488
|
+
hooks: [
|
|
489
|
+
{
|
|
490
|
+
type: "command",
|
|
491
|
+
command: `node "${path.join(packageRoot, "dist", "hooks", "instructions-loaded.js")}"${logSuffix}`
|
|
492
|
+
}
|
|
493
|
+
]
|
|
494
|
+
},
|
|
495
|
+
marker: EXE_HOOKS.instructionsLoaded
|
|
496
|
+
}
|
|
497
|
+
];
|
|
498
|
+
let added = 0;
|
|
499
|
+
let skipped = 0;
|
|
500
|
+
for (const eventKey of Object.keys(settings.hooks)) {
|
|
501
|
+
const groups = settings.hooks[eventKey];
|
|
502
|
+
if (!Array.isArray(groups)) continue;
|
|
503
|
+
settings.hooks[eventKey] = groups.map((g) => ({
|
|
504
|
+
...g,
|
|
505
|
+
hooks: g.hooks.filter((h) => !isLegacyHomeDirHookCommand(h.command))
|
|
506
|
+
})).filter((g) => g.hooks.length > 0);
|
|
507
|
+
}
|
|
508
|
+
const postToolGroups = settings.hooks["PostToolUse"];
|
|
509
|
+
if (Array.isArray(postToolGroups)) {
|
|
510
|
+
settings.hooks["PostToolUse"] = postToolGroups.map((g) => ({
|
|
511
|
+
...g,
|
|
512
|
+
hooks: g.hooks.filter((h) => !isLegacySplitPostToolCommand(h.command))
|
|
513
|
+
})).filter((g) => g.hooks.length > 0);
|
|
514
|
+
}
|
|
515
|
+
for (const { event, group, marker } of hooksToRegister) {
|
|
516
|
+
if (!settings.hooks[event]) {
|
|
517
|
+
settings.hooks[event] = [];
|
|
518
|
+
}
|
|
519
|
+
if (!Array.isArray(settings.hooks[event])) {
|
|
520
|
+
console.warn(`[exe-os] Hook event "${event}" has unexpected structure \u2014 skipping`);
|
|
521
|
+
skipped++;
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
const existing = settings.hooks[event];
|
|
525
|
+
const correctCommand = group.hooks[0]?.command ?? "";
|
|
526
|
+
const alreadyCorrect = existing.some(
|
|
527
|
+
(g) => g.hooks.some((h) => h.command === correctCommand)
|
|
528
|
+
);
|
|
529
|
+
if (alreadyCorrect) {
|
|
530
|
+
skipped++;
|
|
531
|
+
} else {
|
|
532
|
+
settings.hooks[event] = existing.filter(
|
|
533
|
+
(g) => !g.hooks.some((h) => h.command.includes(marker))
|
|
534
|
+
);
|
|
535
|
+
settings.hooks[event].push(group);
|
|
536
|
+
added++;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const perms = settings.permissions;
|
|
540
|
+
if (!perms) {
|
|
541
|
+
settings.permissions = {};
|
|
542
|
+
}
|
|
543
|
+
const permissions = settings.permissions;
|
|
544
|
+
if (!Array.isArray(permissions.allow)) {
|
|
545
|
+
permissions.allow = [];
|
|
546
|
+
}
|
|
547
|
+
const toolNames = [
|
|
548
|
+
// Core memory
|
|
549
|
+
"store_memory",
|
|
550
|
+
"recall_my_memory",
|
|
551
|
+
"commit_to_long_term_memory",
|
|
552
|
+
"consolidate_memories",
|
|
553
|
+
"ask_team_memory",
|
|
554
|
+
"get_session_context",
|
|
555
|
+
// Tasks
|
|
556
|
+
"create_task",
|
|
557
|
+
"list_tasks",
|
|
558
|
+
"get_task",
|
|
559
|
+
"update_task",
|
|
560
|
+
"close_task",
|
|
561
|
+
"checkpoint_task",
|
|
562
|
+
// Behaviors
|
|
563
|
+
"store_behavior",
|
|
564
|
+
"deactivate_behavior",
|
|
565
|
+
"list_behaviors",
|
|
566
|
+
// Identity
|
|
567
|
+
"get_identity",
|
|
568
|
+
"update_identity",
|
|
569
|
+
// Messaging
|
|
570
|
+
"send_message",
|
|
571
|
+
"acknowledge_messages",
|
|
572
|
+
"send_whatsapp",
|
|
573
|
+
"query_conversations",
|
|
574
|
+
// Reminders + triggers
|
|
575
|
+
"create_reminder",
|
|
576
|
+
"complete_reminder",
|
|
577
|
+
"list_reminders",
|
|
578
|
+
"create_trigger",
|
|
579
|
+
"list_triggers",
|
|
580
|
+
// GraphRAG
|
|
581
|
+
"query_relationships",
|
|
582
|
+
"merge_entities",
|
|
583
|
+
// Documents + wiki
|
|
584
|
+
"ingest_document",
|
|
585
|
+
"list_documents",
|
|
586
|
+
"purge_document",
|
|
587
|
+
"rerank_documents",
|
|
588
|
+
"set_document_importance",
|
|
589
|
+
"get_wiki_page",
|
|
590
|
+
"list_wiki_pages",
|
|
591
|
+
// System
|
|
592
|
+
"load_skill",
|
|
593
|
+
"apply_starter_pack",
|
|
594
|
+
"resume_employee",
|
|
595
|
+
"deploy_client"
|
|
596
|
+
];
|
|
597
|
+
const allowList = permissions.allow;
|
|
598
|
+
for (const name of toolNames) {
|
|
599
|
+
const fullName = `mcp__${MCP_PRIMARY_KEY}__${name}`;
|
|
600
|
+
if (!allowList.includes(fullName)) {
|
|
601
|
+
allowList.push(fullName);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
await mkdir(path.dirname(settingsPath), { recursive: true });
|
|
605
|
+
await writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
606
|
+
return { added, skipped };
|
|
607
|
+
}
|
|
608
|
+
async function cleanOldShellFunctions(homeDir = os.homedir()) {
|
|
609
|
+
const rosterPath = path.join(homeDir, ".exe-os", "exe-employees.json");
|
|
610
|
+
if (!existsSync(rosterPath)) return 0;
|
|
611
|
+
let employees;
|
|
612
|
+
try {
|
|
613
|
+
employees = JSON.parse(await readFile(rosterPath, "utf-8"));
|
|
614
|
+
} catch {
|
|
615
|
+
return 0;
|
|
616
|
+
}
|
|
617
|
+
if (employees.length === 0) return 0;
|
|
618
|
+
const names = employees.map((e) => e.name);
|
|
619
|
+
const funcPatterns = names.map((n) => {
|
|
620
|
+
const funcDef = new RegExp(`^\\s*(function\\s+)?${escapeRegExp(n)}\\d+\\s*\\(\\)`, "m");
|
|
621
|
+
const forLoop = new RegExp(`${escapeRegExp(n)}\\$\\{?[a-zA-Z_][a-zA-Z0-9_]*\\}?`, "m");
|
|
622
|
+
return { name: n, funcDef, forLoop };
|
|
623
|
+
});
|
|
624
|
+
const rcFiles = [
|
|
625
|
+
path.join(homeDir, ".zshrc"),
|
|
626
|
+
path.join(homeDir, ".bashrc")
|
|
627
|
+
];
|
|
628
|
+
const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
|
|
629
|
+
let totalRemoved = 0;
|
|
630
|
+
for (const rcPath of rcFiles) {
|
|
631
|
+
if (!existsSync(rcPath)) continue;
|
|
632
|
+
let content;
|
|
633
|
+
try {
|
|
634
|
+
content = await readFile(rcPath, "utf-8");
|
|
635
|
+
} catch {
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
if (content.includes(REMOVED_MARKER)) continue;
|
|
639
|
+
const lines = content.split("\n");
|
|
640
|
+
let changed = false;
|
|
641
|
+
let inForLoop = false;
|
|
642
|
+
let forLoopMatchesEmployee = false;
|
|
643
|
+
for (let i = 0; i < lines.length; i++) {
|
|
644
|
+
const line = lines[i];
|
|
645
|
+
const trimmed = line.trim();
|
|
646
|
+
if (trimmed.startsWith("#")) continue;
|
|
647
|
+
if (/^\s*for\s+/.test(trimmed)) {
|
|
648
|
+
inForLoop = true;
|
|
649
|
+
forLoopMatchesEmployee = false;
|
|
650
|
+
for (const { forLoop } of funcPatterns) {
|
|
651
|
+
if (forLoop.test(trimmed)) {
|
|
652
|
+
forLoopMatchesEmployee = true;
|
|
653
|
+
break;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (forLoopMatchesEmployee) {
|
|
657
|
+
lines[i] = `${REMOVED_MARKER}
|
|
658
|
+
# ${line}`;
|
|
659
|
+
changed = true;
|
|
660
|
+
totalRemoved++;
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (inForLoop) {
|
|
665
|
+
if (!forLoopMatchesEmployee) {
|
|
666
|
+
for (const { forLoop } of funcPatterns) {
|
|
667
|
+
if (forLoop.test(trimmed)) {
|
|
668
|
+
forLoopMatchesEmployee = true;
|
|
669
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
670
|
+
if (/^\s*for\s+/.test(lines[j].replace(/^#\s*/, ""))) {
|
|
671
|
+
if (!lines[j].startsWith("#")) {
|
|
672
|
+
lines[j] = `${REMOVED_MARKER}
|
|
673
|
+
# ${lines[j]}`;
|
|
674
|
+
totalRemoved++;
|
|
675
|
+
}
|
|
676
|
+
break;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
break;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
if (forLoopMatchesEmployee && !trimmed.startsWith("#")) {
|
|
684
|
+
lines[i] = `# ${line}`;
|
|
685
|
+
changed = true;
|
|
686
|
+
totalRemoved++;
|
|
687
|
+
}
|
|
688
|
+
if (trimmed === "done" || trimmed.startsWith("done;") || trimmed.startsWith("done ")) {
|
|
689
|
+
inForLoop = false;
|
|
690
|
+
forLoopMatchesEmployee = false;
|
|
691
|
+
}
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
for (const { funcDef } of funcPatterns) {
|
|
695
|
+
if (funcDef.test(trimmed)) {
|
|
696
|
+
lines[i] = `${REMOVED_MARKER}
|
|
697
|
+
# ${line}`;
|
|
698
|
+
changed = true;
|
|
699
|
+
totalRemoved++;
|
|
700
|
+
let braceDepth = 0;
|
|
701
|
+
const hasBrace = trimmed.includes("{");
|
|
702
|
+
if (hasBrace) braceDepth = 1;
|
|
703
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
704
|
+
const bodyLine = lines[j].trim();
|
|
705
|
+
if (!hasBrace && braceDepth === 0) {
|
|
706
|
+
if (bodyLine === "{") {
|
|
707
|
+
braceDepth = 1;
|
|
708
|
+
lines[j] = `# ${lines[j]}`;
|
|
709
|
+
totalRemoved++;
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (braceDepth > 0) {
|
|
714
|
+
braceDepth += (bodyLine.match(/{/g) ?? []).length;
|
|
715
|
+
braceDepth -= (bodyLine.match(/}/g) ?? []).length;
|
|
716
|
+
lines[j] = `# ${lines[j]}`;
|
|
717
|
+
totalRemoved++;
|
|
718
|
+
if (braceDepth <= 0) break;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
if (changed) {
|
|
726
|
+
const backupPath = `${rcPath}.exe-os-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
727
|
+
await writeFile(backupPath, content);
|
|
728
|
+
await writeFile(rcPath, lines.join("\n"));
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return totalRemoved;
|
|
732
|
+
}
|
|
733
|
+
function escapeRegExp(s) {
|
|
734
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
735
|
+
}
|
|
736
|
+
var EXE_SECTION_START = "<!-- exe-os:orchestration-start -->";
|
|
737
|
+
var EXE_SECTION_END = "<!-- exe-os:orchestration-end -->";
|
|
738
|
+
var ORCHESTRATION_RULES = `${EXE_SECTION_START}
|
|
739
|
+
## exe-os Multi-Agent Orchestration (auto-managed \u2014 do not edit)
|
|
740
|
+
|
|
741
|
+
These rules are injected by exe-os and override default behavior for multi-agent coordination.
|
|
742
|
+
|
|
743
|
+
- **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.
|
|
744
|
+
- **Always use create_task to assign work.** Never use messages, tmux send-keys, or ad-hoc instructions as a substitute for tasks.
|
|
745
|
+
- **Never modify another agent's in-progress task.** Create a new task instead. Modifying active tasks causes race conditions.
|
|
746
|
+
- **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.
|
|
747
|
+
- **Verify dispatch.** After every create_task, confirm the employee received and started the task via tmux capture-pane.
|
|
748
|
+
${EXE_SECTION_END}`;
|
|
749
|
+
async function injectOrchestrationRules(homeDir) {
|
|
750
|
+
const claudeDir = path.join(homeDir, ".claude");
|
|
751
|
+
const claudeMdPath = path.join(claudeDir, "CLAUDE.md");
|
|
752
|
+
await mkdir(claudeDir, { recursive: true });
|
|
753
|
+
let existing = "";
|
|
754
|
+
try {
|
|
755
|
+
existing = await readFile(claudeMdPath, "utf-8");
|
|
756
|
+
} catch {
|
|
757
|
+
}
|
|
758
|
+
const startIdx = existing.indexOf(EXE_SECTION_START);
|
|
759
|
+
const endIdx = existing.indexOf(EXE_SECTION_END);
|
|
760
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
761
|
+
const currentSection = existing.slice(startIdx, endIdx + EXE_SECTION_END.length);
|
|
762
|
+
if (currentSection === ORCHESTRATION_RULES) {
|
|
763
|
+
return "unchanged";
|
|
764
|
+
}
|
|
765
|
+
const updated = existing.slice(0, startIdx) + ORCHESTRATION_RULES + existing.slice(endIdx + EXE_SECTION_END.length);
|
|
766
|
+
await writeFile(claudeMdPath, updated, "utf-8");
|
|
767
|
+
return "updated";
|
|
768
|
+
}
|
|
769
|
+
const separator = existing.length > 0 && !existing.endsWith("\n\n") ? "\n\n" : "";
|
|
770
|
+
await writeFile(claudeMdPath, existing + separator + ORCHESTRATION_RULES + "\n", "utf-8");
|
|
771
|
+
return "injected";
|
|
772
|
+
}
|
|
773
|
+
async function installStatusLine(packageRoot, homeDir = os.homedir()) {
|
|
774
|
+
const prefs = loadPreferences(homeDir);
|
|
775
|
+
if (prefs.ccStatusLine === false) return "opted-out";
|
|
776
|
+
const claudeDir = path.join(homeDir, ".claude");
|
|
777
|
+
await mkdir(claudeDir, { recursive: true });
|
|
778
|
+
const assetPath = path.join(packageRoot, "dist", "assets", "statusline-command.sh");
|
|
779
|
+
if (!existsSync(assetPath)) return "asset-missing";
|
|
780
|
+
const destScript = path.join(claudeDir, "statusline-command.sh");
|
|
781
|
+
const assetContent = await readFile(assetPath, "utf-8");
|
|
782
|
+
await writeFile(destScript, assetContent, { mode: 493 });
|
|
783
|
+
const settingsPath = path.join(claudeDir, "settings.json");
|
|
784
|
+
let settings = {};
|
|
785
|
+
if (existsSync(settingsPath)) {
|
|
786
|
+
try {
|
|
787
|
+
settings = JSON.parse(await readFile(settingsPath, "utf-8"));
|
|
788
|
+
} catch {
|
|
789
|
+
settings = {};
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
if (settings.statusLine) return "already-configured";
|
|
793
|
+
settings.statusLine = {
|
|
794
|
+
type: "command",
|
|
795
|
+
command: `bash ${destScript}`
|
|
796
|
+
};
|
|
797
|
+
await writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
798
|
+
return "installed";
|
|
799
|
+
}
|
|
800
|
+
async function runInstaller(homeDir) {
|
|
801
|
+
const packageRoot = resolvePackageRoot();
|
|
802
|
+
process.stderr.write(`exe-os installer v1.3.0
|
|
803
|
+
`);
|
|
804
|
+
process.stderr.write(`Package root: ${packageRoot}
|
|
805
|
+
|
|
806
|
+
`);
|
|
807
|
+
const cmdResult = await copySlashCommands(packageRoot, homeDir);
|
|
808
|
+
process.stderr.write(
|
|
809
|
+
`Slash commands: ${cmdResult.copied} copied, ${cmdResult.skipped} unchanged
|
|
810
|
+
`
|
|
811
|
+
);
|
|
812
|
+
const mcpChanged = await registerMcpServer(packageRoot, homeDir);
|
|
813
|
+
process.stderr.write(
|
|
814
|
+
`MCP server: ${mcpChanged ? "registered" : "already registered"}
|
|
815
|
+
`
|
|
816
|
+
);
|
|
817
|
+
const hookResult = await mergeHooks(packageRoot, homeDir);
|
|
818
|
+
process.stderr.write(
|
|
819
|
+
`Hooks: ${hookResult.added} added, ${hookResult.skipped} unchanged
|
|
820
|
+
`
|
|
821
|
+
);
|
|
822
|
+
const resolvedHome = homeDir ?? os.homedir();
|
|
823
|
+
const exeWorkspace = path.join(resolvedHome, "exe");
|
|
824
|
+
if (!existsSync(exeWorkspace)) {
|
|
825
|
+
try {
|
|
826
|
+
await mkdir(path.join(exeWorkspace, "content"), { recursive: true });
|
|
827
|
+
await mkdir(path.join(exeWorkspace, "operations"), { recursive: true });
|
|
828
|
+
await mkdir(path.join(exeWorkspace, "output"), { recursive: true });
|
|
829
|
+
process.stderr.write(
|
|
830
|
+
`Created ~/exe/ \u2014 your automation workspace for non-code projects
|
|
831
|
+
`
|
|
832
|
+
);
|
|
833
|
+
} catch {
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
const claudeMdResult = await injectOrchestrationRules(resolvedHome);
|
|
837
|
+
process.stderr.write(
|
|
838
|
+
`CLAUDE.md orchestration: ${claudeMdResult}
|
|
839
|
+
`
|
|
840
|
+
);
|
|
841
|
+
const symlinkResults = await ensureAllAgentSymlinks(resolvedHome);
|
|
842
|
+
process.stderr.write(
|
|
843
|
+
`Agent symlinks: ${summarizeSymlinkResults(symlinkResults)}
|
|
844
|
+
`
|
|
845
|
+
);
|
|
846
|
+
const statusLineResult = await installStatusLine(packageRoot, resolvedHome);
|
|
847
|
+
process.stderr.write(
|
|
848
|
+
`Status line: ${statusLineResult}
|
|
849
|
+
`
|
|
850
|
+
);
|
|
851
|
+
const shellFuncsCleaned = await cleanOldShellFunctions(resolvedHome);
|
|
852
|
+
if (shellFuncsCleaned > 0) {
|
|
853
|
+
process.stderr.write(
|
|
854
|
+
`Shell cleanup: removed ${shellFuncsCleaned} old shell function(s) that were shadowing exe-os wrappers
|
|
855
|
+
`
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
process.stderr.write(`
|
|
859
|
+
exe-os installed successfully.
|
|
860
|
+
`);
|
|
861
|
+
}
|
|
862
|
+
function setupTmux(home) {
|
|
863
|
+
const homeDir = home ?? os.homedir();
|
|
864
|
+
const prefs = loadPreferences(homeDir);
|
|
865
|
+
if (prefs.tmux === false) {
|
|
866
|
+
process.stderr.write("exe-os: tmux config skipped (user preference)\n");
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
const exeDir = path.join(homeDir, ".exe-os");
|
|
870
|
+
const exeTmuxConf = path.join(exeDir, "tmux.conf");
|
|
871
|
+
const userTmuxConf = path.join(homeDir, ".tmux.conf");
|
|
872
|
+
const sourceLine = "source-file ~/.exe-os/tmux.conf";
|
|
873
|
+
const shellQuote = (value) => `'${value.replace(/'/g, "'\\''")}'`;
|
|
874
|
+
const pkgRoot = resolvePackageRoot();
|
|
875
|
+
const assetPath = path.join(pkgRoot, "dist", "assets", "tmux.conf");
|
|
876
|
+
if (!existsSync(assetPath)) {
|
|
877
|
+
process.stderr.write(`exe-os: tmux.conf asset not found at ${assetPath} \u2014 skipping tmux setup
|
|
878
|
+
`);
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
mkdirSync(exeDir, { recursive: true });
|
|
882
|
+
if (existsSync(exeTmuxConf)) {
|
|
883
|
+
const currentContent = readFileSync(exeTmuxConf, "utf8");
|
|
884
|
+
const newContent = readFileSync(assetPath, "utf8");
|
|
885
|
+
const currentHash = createHash("sha256").update(currentContent).digest("hex");
|
|
886
|
+
const newHash = createHash("sha256").update(newContent).digest("hex");
|
|
887
|
+
if (currentHash !== newHash) {
|
|
888
|
+
const shippedPath = path.join(exeDir, ".tmux.conf.shipped-hash");
|
|
889
|
+
const lastShippedHash = existsSync(shippedPath) ? readFileSync(shippedPath, "utf8").trim() : "";
|
|
890
|
+
if (lastShippedHash && currentHash !== lastShippedHash) {
|
|
891
|
+
process.stderr.write("exe-os: tmux config has user customizations \u2014 skipping overwrite\n");
|
|
892
|
+
} else {
|
|
893
|
+
copyFileSync(assetPath, exeTmuxConf);
|
|
894
|
+
process.stderr.write("exe-os: tmux config updated\n");
|
|
895
|
+
}
|
|
896
|
+
writeFileSync(shippedPath, newHash, "utf8");
|
|
897
|
+
} else {
|
|
898
|
+
process.stderr.write("exe-os: tmux config already up to date\n");
|
|
899
|
+
}
|
|
900
|
+
} else {
|
|
901
|
+
copyFileSync(assetPath, exeTmuxConf);
|
|
902
|
+
const newContent = readFileSync(assetPath, "utf8");
|
|
903
|
+
const newHash = createHash("sha256").update(newContent).digest("hex");
|
|
904
|
+
writeFileSync(path.join(exeDir, ".tmux.conf.shipped-hash"), newHash, "utf8");
|
|
905
|
+
}
|
|
906
|
+
if (existsSync(userTmuxConf)) {
|
|
907
|
+
const existing = readFileSync(userTmuxConf, "utf8");
|
|
908
|
+
if (!existing.includes(sourceLine)) {
|
|
909
|
+
const backupPath = path.join(homeDir, ".tmux.conf.backup");
|
|
910
|
+
if (!existsSync(backupPath)) {
|
|
911
|
+
copyFileSync(userTmuxConf, backupPath);
|
|
912
|
+
process.stderr.write(`exe-os: backed up existing tmux config to ${backupPath}
|
|
913
|
+
`);
|
|
914
|
+
}
|
|
915
|
+
writeFileSync(userTmuxConf, `${sourceLine}
|
|
916
|
+
${existing}`);
|
|
917
|
+
}
|
|
918
|
+
} else {
|
|
919
|
+
writeFileSync(userTmuxConf, `# Exe OS tmux defaults \u2014 remove this line to use your own config
|
|
920
|
+
${sourceLine}
|
|
921
|
+
`);
|
|
922
|
+
}
|
|
923
|
+
try {
|
|
924
|
+
execSync(`tmux source-file ${shellQuote(userTmuxConf)} 2>/dev/null`);
|
|
925
|
+
} catch {
|
|
926
|
+
}
|
|
927
|
+
process.stderr.write("exe-os: tmux config installed\n");
|
|
928
|
+
}
|
|
929
|
+
function setupGhostty(home) {
|
|
930
|
+
const homeDir = home ?? os.homedir();
|
|
931
|
+
const prefs = loadPreferences(homeDir);
|
|
932
|
+
if (prefs.ghostty === false) {
|
|
933
|
+
process.stderr.write("exe-os: Ghostty config skipped (user preference)\n");
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
const xdgConfig = path.join(homeDir, ".config", "ghostty");
|
|
937
|
+
const macConfig = path.join(homeDir, "Library", "Application Support", "com.mitchellh.ghostty");
|
|
938
|
+
const ghosttyInstalled = existsSync(xdgConfig) || existsSync(macConfig) || (() => {
|
|
939
|
+
try {
|
|
940
|
+
execSync("which ghostty 2>/dev/null");
|
|
941
|
+
return true;
|
|
942
|
+
} catch {
|
|
943
|
+
return false;
|
|
944
|
+
}
|
|
945
|
+
})();
|
|
946
|
+
if (!ghosttyInstalled) {
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
const pkgRoot = resolvePackageRoot();
|
|
950
|
+
const assetPath = path.join(pkgRoot, "dist", "assets", "ghostty.conf");
|
|
951
|
+
if (!existsSync(assetPath)) {
|
|
952
|
+
process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
const configDir = xdgConfig;
|
|
956
|
+
const configPath = path.join(configDir, "config");
|
|
957
|
+
const backupPath = path.join(configDir, "config.backup");
|
|
958
|
+
mkdirSync(configDir, { recursive: true });
|
|
959
|
+
const START_MARKER = "# \u2500\u2500 exe-os:ghostty-start \u2500\u2500";
|
|
960
|
+
const END_MARKER = "# \u2500\u2500 exe-os:ghostty-end \u2500\u2500";
|
|
961
|
+
const assetContent = readFileSync(assetPath, "utf8").trim();
|
|
962
|
+
const markedSection = `${START_MARKER}
|
|
963
|
+
${assetContent}
|
|
964
|
+
${END_MARKER}`;
|
|
965
|
+
if (existsSync(configPath)) {
|
|
966
|
+
const existing = readFileSync(configPath, "utf8");
|
|
967
|
+
if (existing.includes(START_MARKER) && existing.includes(END_MARKER)) {
|
|
968
|
+
process.stderr.write("exe-os: Ghostty config already installed \u2014 preserving local settings\n");
|
|
969
|
+
return;
|
|
970
|
+
} else if (existing.includes("Exe OS")) {
|
|
971
|
+
if (!existsSync(backupPath)) {
|
|
972
|
+
copyFileSync(configPath, backupPath);
|
|
973
|
+
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
974
|
+
`);
|
|
975
|
+
}
|
|
976
|
+
writeFileSync(configPath, `${START_MARKER}
|
|
977
|
+
${existing.trim()}
|
|
978
|
+
${END_MARKER}
|
|
979
|
+
`);
|
|
980
|
+
} else {
|
|
981
|
+
if (!existsSync(backupPath)) {
|
|
982
|
+
copyFileSync(configPath, backupPath);
|
|
983
|
+
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
984
|
+
`);
|
|
985
|
+
}
|
|
986
|
+
writeFileSync(configPath, `${markedSection}
|
|
987
|
+
|
|
988
|
+
${existing}`);
|
|
989
|
+
}
|
|
990
|
+
} else {
|
|
991
|
+
writeFileSync(configPath, `${markedSection}
|
|
992
|
+
`);
|
|
993
|
+
}
|
|
994
|
+
process.stderr.write("exe-os: Ghostty config installed\n");
|
|
995
|
+
}
|
|
996
|
+
function setupWezterm(home) {
|
|
997
|
+
const homeDir = home ?? os.homedir();
|
|
998
|
+
const prefs = loadPreferences(homeDir);
|
|
999
|
+
if (prefs.wezterm === false) {
|
|
1000
|
+
process.stderr.write("exe-os: WezTerm config skipped (user preference)\n");
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
const weztermLua = path.join(homeDir, ".wezterm.lua");
|
|
1004
|
+
const weztermDir = path.join(homeDir, ".config", "wezterm");
|
|
1005
|
+
const weztermInstalled = existsSync(weztermLua) || existsSync(weztermDir) || (() => {
|
|
1006
|
+
try {
|
|
1007
|
+
execSync("which wezterm 2>/dev/null", { encoding: "utf8", timeout: 5e3 });
|
|
1008
|
+
return true;
|
|
1009
|
+
} catch {
|
|
1010
|
+
return false;
|
|
1011
|
+
}
|
|
1012
|
+
})();
|
|
1013
|
+
if (!weztermInstalled) {
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
const pkgRoot = resolvePackageRoot();
|
|
1017
|
+
const assetPath = path.join(pkgRoot, "dist", "assets", "wezterm.lua");
|
|
1018
|
+
if (!existsSync(assetPath)) {
|
|
1019
|
+
process.stderr.write("exe-os: wezterm.lua asset not found \u2014 skipping WezTerm setup\n");
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
const configPath = weztermLua;
|
|
1023
|
+
const START_MARKER = "-- \u2500\u2500 exe-os:wezterm-start \u2500\u2500";
|
|
1024
|
+
const END_MARKER = "-- \u2500\u2500 exe-os:wezterm-end \u2500\u2500";
|
|
1025
|
+
const assetContent = readFileSync(assetPath, "utf8").trim();
|
|
1026
|
+
if (existsSync(configPath)) {
|
|
1027
|
+
const existing = readFileSync(configPath, "utf8");
|
|
1028
|
+
if (existing.includes(START_MARKER) && existing.includes(END_MARKER)) {
|
|
1029
|
+
process.stderr.write("exe-os: WezTerm config already installed \u2014 preserving local settings\n");
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
process.stderr.write(
|
|
1033
|
+
"exe-os: existing WezTerm config found at ~/.wezterm.lua \u2014 skipping (manual integration needed)\nexe-os: see ~/.exe-os/assets/wezterm.lua for exe-os defaults you can merge\n"
|
|
1034
|
+
);
|
|
1035
|
+
const refDir = path.join(homeDir, ".exe-os", "assets");
|
|
1036
|
+
mkdirSync(refDir, { recursive: true });
|
|
1037
|
+
copyFileSync(assetPath, path.join(refDir, "wezterm.lua"));
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
writeFileSync(configPath, assetContent + "\n");
|
|
1041
|
+
process.stderr.write("exe-os: WezTerm config installed\n");
|
|
1042
|
+
}
|
|
1043
|
+
function summarizeSymlinkResults(results) {
|
|
1044
|
+
if (results.length === 0) return "no employees in roster";
|
|
1045
|
+
const created = results.filter((r) => r.action === "created").length;
|
|
1046
|
+
const already = results.filter((r) => r.action === "already_correct").length;
|
|
1047
|
+
const conflicts = results.filter((r) => r.action === "conflict");
|
|
1048
|
+
let summary = `${created} created, ${already} already linked`;
|
|
1049
|
+
if (conflicts.length > 0) {
|
|
1050
|
+
const details = conflicts.map((r) => `${r.agentId} (${r.conflict ?? "unknown"})`).join(", ");
|
|
1051
|
+
summary += `, ${conflicts.length} conflict(s): ${details}`;
|
|
1052
|
+
}
|
|
1053
|
+
return summary;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
export {
|
|
1057
|
+
resolvePackageRoot,
|
|
1058
|
+
copySlashCommands,
|
|
1059
|
+
detectMcpNameCollisions,
|
|
1060
|
+
registerMcpServer,
|
|
1061
|
+
mergeHooks,
|
|
1062
|
+
cleanOldShellFunctions,
|
|
1063
|
+
installStatusLine,
|
|
1064
|
+
runInstaller,
|
|
1065
|
+
setupTmux,
|
|
1066
|
+
setupGhostty,
|
|
1067
|
+
setupWezterm
|
|
1068
|
+
};
|