@xopcai/xopc 0.0.111 → 0.0.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (443) hide show
  1. package/dist/browser-ext/icons/icon-128.png +0 -0
  2. package/dist/browser-ext/icons/icon-16.png +0 -0
  3. package/dist/browser-ext/icons/icon-32.png +0 -0
  4. package/dist/browser-ext/icons/icon-48.png +0 -0
  5. package/dist/browser-ext/manifest.json +1 -1
  6. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  7. package/dist/extensions/feishu/src/workflow-progress.js +1 -1
  8. package/dist/extensions/telegram/src/adapters/streaming-adapter.js +1 -1
  9. package/dist/extensions/telegram/src/inbound-processor.js +1 -1
  10. package/dist/extensions/telegram/src/plugin.js +1 -1
  11. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  12. package/dist/extensions/telegram/src/update-offset-store.js +1 -1
  13. package/dist/extensions/telegram/src/workflow-progress.js +1 -1
  14. package/dist/extensions/telegram/xopc.extension.json +1 -1
  15. package/dist/extensions/weixin/src/api/api.js +2 -2
  16. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  17. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  18. package/dist/extensions/weixin/src/delivery-to.d.ts +3 -3
  19. package/dist/extensions/weixin/src/delivery-to.js.map +1 -1
  20. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  21. package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
  22. package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
  23. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  24. package/dist/extensions/weixin/src/plugin.js +1 -1
  25. package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
  26. package/dist/extensions/weixin/src/workflow-progress.js +1 -1
  27. package/dist/gateway/static/root/apple-touch-icon.png +0 -0
  28. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-zlx9l5Yp.js → agent-avatar-dicebear-CBlklyNr.js} +1 -1
  29. package/dist/gateway/static/root/assets/agent-browser-settings-page-rGbKc2Hh.js +6 -0
  30. package/dist/gateway/static/root/assets/{agent-defaults-tabbed-page-Bz7JWjRM.js → agent-defaults-tabbed-page-CtwVbjOA.js} +1 -1
  31. package/dist/gateway/static/root/assets/agents-CCBmGcwc.js +71 -0
  32. package/dist/gateway/static/root/assets/{app-management-settings-panel-DaQwdeCq.js → app-management-settings-panel-28E_WfMe.js} +1 -1
  33. package/dist/gateway/static/root/assets/{appearance-settings-D2zv6r3P.js → appearance-settings-Do1z0XgJ.js} +1 -1
  34. package/dist/gateway/static/root/assets/{apps-page-BIppe_jj.js → apps-page-xwCsJS15.js} +1 -1
  35. package/dist/gateway/static/root/assets/{archive-plugin-DEmu8eH7.js → archive-plugin-BAFlsov_.js} +1 -1
  36. package/dist/gateway/static/root/assets/{attachment-load-B98lJoN5.js → attachment-load-V3qGPC3x.js} +1 -1
  37. package/dist/gateway/static/root/assets/{binary-plugins-CL9QKmyo.js → binary-plugins-DT8NvA6W.js} +1 -1
  38. package/dist/gateway/static/root/assets/{block-editor-BOAhMAhT.js → block-editor-CdLMEcHd.js} +1 -1
  39. package/dist/gateway/static/root/assets/{channels-settings-Br8iRnXp.js → channels-settings-C044DcYk.js} +1 -1
  40. package/dist/gateway/static/root/assets/{config-api-0o1WYdz2.js → config-api-Bf5mjpu7.js} +1 -1
  41. package/dist/gateway/static/root/assets/config-reload-event-Bjh2zoKp.js +1 -0
  42. package/dist/gateway/static/root/assets/connectors-settings-8VEWUOTj.js +2 -0
  43. package/dist/gateway/static/root/assets/cron-api--pAB_rps.js +1 -0
  44. package/dist/gateway/static/root/assets/cron-page-DqmNGNOy.js +3 -0
  45. package/dist/gateway/static/root/assets/dreaming-settings-6UZE1FSK.js +1 -0
  46. package/dist/gateway/static/root/assets/{extension-debug-page-Cjx6pZVz.js → extension-debug-page-BGhE7msg.js} +1 -1
  47. package/dist/gateway/static/root/assets/{extension-page-BSPNL90V.js → extension-page-CvpI45WG.js} +1 -1
  48. package/dist/gateway/static/root/assets/extension-settings-page-DAmR5FUe.js +1 -0
  49. package/dist/gateway/static/root/assets/{fetch-Bs7in_4F.js → fetch-NZmjSXLl.js} +1 -1
  50. package/dist/gateway/static/root/assets/format-cron-label-BsotZDO5.js +2 -0
  51. package/dist/gateway/static/root/assets/{format-interval-label-CftJ5uXu.js → format-interval-label-qhth3BMV.js} +1 -1
  52. package/dist/gateway/static/root/assets/{gateway-config-api-Clfck-hW.js → gateway-config-api-CaXBt-9R.js} +1 -1
  53. package/dist/gateway/static/root/assets/gateway-settings-DphpbLBc.js +1 -0
  54. package/dist/gateway/static/root/assets/{gateway-startup-retry-BkjVJc38.js → gateway-startup-retry-Be1omjh3.js} +1 -1
  55. package/dist/gateway/static/root/assets/{goal-detail-page-kqJue0sE.js → goal-detail-page-bv0VRDaf.js} +1 -1
  56. package/dist/gateway/static/root/assets/goals-page-1Ywnt4Cb.js +2 -0
  57. package/dist/gateway/static/root/assets/{goals-settings-CJnzNSDo.js → goals-settings-ceEBjnfc.js} +1 -1
  58. package/dist/gateway/static/root/assets/{heartbeat-config-api-Cp7CPuIn.js → heartbeat-config-api-BzO6G4fD.js} +1 -1
  59. package/dist/gateway/static/root/assets/{heartbeat-settings-ifSI1Wbh.js → heartbeat-settings-DCST9-du.js} +1 -1
  60. package/dist/gateway/static/root/assets/index-Cxm6xsps.js +122 -0
  61. package/dist/gateway/static/root/assets/index-b_R4tKqt.css +1 -0
  62. package/dist/gateway/static/root/assets/interval-presets-DJmshV8n.js +1 -0
  63. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings--MI1rYl7.js → keyboard-shortcuts-settings-9AUUlQ5Q.js} +1 -1
  64. package/dist/gateway/static/root/assets/{logs-page-BwrVNlyW.js → logs-page-B_wrxhee.js} +1 -1
  65. package/dist/gateway/static/root/assets/{markdown-split-B_oc9lJe.js → markdown-split-CYYELqAr.js} +1 -1
  66. package/dist/gateway/static/root/assets/{media-plugins-DLPvB5H3.js → media-plugins-BEcMR2M8.js} +1 -1
  67. package/dist/gateway/static/root/assets/messages-CkT-lcgP.js +3 -0
  68. package/dist/gateway/static/root/assets/models-hub-panel-D0JDSbNu.js +2 -0
  69. package/dist/gateway/static/root/assets/note-detail-page-DpFV-EO6.js +2 -0
  70. package/dist/gateway/static/root/assets/{note-image-lightbox-rSqAgOi9.js → note-image-lightbox-DNAoPcmm.js} +1 -1
  71. package/dist/gateway/static/root/assets/{notes-page-C9ACGEty.js → notes-page-CdkjTch8.js} +1 -1
  72. package/dist/gateway/static/root/assets/{preview-open-alternatives-Bw_N2oZi.js → preview-open-alternatives-BPLz0AIv.js} +1 -1
  73. package/dist/gateway/static/root/assets/{remote-access-hub-BhcWIKuM.js → remote-access-hub-DSYVAi96.js} +1 -1
  74. package/dist/gateway/static/root/assets/{save-bar-controls-CNP2jmw0.js → save-bar-controls-CrWwWRVK.js} +1 -1
  75. package/dist/gateway/static/root/assets/schedule-field-CF2sadvN.js +1 -0
  76. package/dist/gateway/static/root/assets/{schema-form-CDPzua9q.js → schema-form-Di5cKjYE.js} +1 -1
  77. package/dist/gateway/static/root/assets/{sessions-page-DzkpSDZr.js → sessions-page-CenkcVjF.js} +1 -1
  78. package/dist/gateway/static/root/assets/settings-advanced-gate-D9sva86V.js +1 -0
  79. package/dist/gateway/static/root/assets/{settings-collapsible-section-C9bff85B.js → settings-collapsible-section-Chh282jW.js} +1 -1
  80. package/dist/gateway/static/root/assets/settings-form-section-Dq-83cQd.js +1 -0
  81. package/dist/gateway/static/root/assets/{settings-page-DWK4Zo7s.js → settings-page-DL35U0x0.js} +1 -1
  82. package/dist/gateway/static/root/assets/setup-status-panel-Ctwbp9Er.js +1 -0
  83. package/dist/gateway/static/root/assets/{share-preview-page-DnutoY_S.js → share-preview-page-0FXYnSUA.js} +1 -1
  84. package/dist/gateway/static/root/assets/{shares-settings-6oRgJMpn.js → shares-settings-DIM3JNsr.js} +1 -1
  85. package/dist/gateway/static/root/assets/skill-reload-api-DAlNaNzL.js +1 -0
  86. package/dist/gateway/static/root/assets/skills-page-CLrHVzAt.js +2 -0
  87. package/dist/gateway/static/root/assets/{system-settings-panel-qP1JDtDd.js → system-settings-panel-W1pTcojB.js} +1 -1
  88. package/dist/gateway/static/root/assets/use-agent-defaults-form-BVRvkK7m.js +1 -0
  89. package/dist/gateway/static/root/assets/{use-channel-catalog-C4mg1hjD.js → use-channel-catalog-CgJibfMM.js} +1 -1
  90. package/dist/gateway/static/root/assets/{use-settings-tab-guard-F6C0mpJt.js → use-settings-tab-guard-Bw_qkRQE.js} +1 -1
  91. package/dist/gateway/static/root/assets/{voice-api-key-field-BnDDb6qO.js → voice-api-key-field-vYAO71Gn.js} +1 -1
  92. package/dist/gateway/static/root/assets/{voice-player-store-B2EwIhtY.js → voice-player-store-C7lziaAx.js} +1 -1
  93. package/dist/gateway/static/root/assets/{workflow-page.utils-CRB7WX9l.js → workflow-page.utils-sgHjOXOm.js} +1 -1
  94. package/dist/gateway/static/root/assets/{workflows-page-2Fptr0je.js → workflows-page-DYyOTmFx.js} +1 -1
  95. package/dist/gateway/static/root/favicon-16x16.png +0 -0
  96. package/dist/gateway/static/root/favicon-32x32.png +0 -0
  97. package/dist/gateway/static/root/favicon-icon.svg +1 -0
  98. package/dist/gateway/static/root/favicon.ico +0 -0
  99. package/dist/gateway/static/root/favicon.png +0 -0
  100. package/dist/gateway/static/root/favicon.svg +24 -15
  101. package/dist/gateway/static/root/index.html +11 -10
  102. package/dist/gateway/static/root/logo-dark.svg +23 -15
  103. package/dist/gateway/static/root/logo.svg +23 -16
  104. package/dist/package.js +1 -1
  105. package/dist/src/agent/agent-manager.d.ts +28 -2
  106. package/dist/src/agent/agent-manager.js +82 -11
  107. package/dist/src/agent/agent-manager.js.map +1 -1
  108. package/dist/src/agent/agent-scope.js +1 -1
  109. package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
  110. package/dist/src/agent/context/workspace-seed.js +2 -2
  111. package/dist/src/agent/context/workspace-state.js +1 -1
  112. package/dist/src/agent/context/workspace.js +1 -1
  113. package/dist/src/agent/goals/goal-locale.js +2 -2
  114. package/dist/src/agent/goals/persistent-goal-service.js +1 -1
  115. package/dist/src/agent/goals/post-turn.js +3 -3
  116. package/dist/src/agent/image/load-image-media.js +2 -2
  117. package/dist/src/agent/ipc/inbox.js +3 -3
  118. package/dist/src/agent/ipc/socket.js +1 -1
  119. package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
  120. package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
  121. package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
  122. package/dist/src/agent/mcp/mcp-transport.js +1 -1
  123. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  124. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  125. package/dist/src/agent/memory/dreaming/events.js +1 -1
  126. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  127. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  128. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  129. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  130. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  131. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  132. package/dist/src/agent/memory/memory-flush.js +1 -1
  133. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  134. package/dist/src/agent/messaging/command-handler.d.ts +3 -0
  135. package/dist/src/agent/messaging/command-handler.js +5 -0
  136. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  137. package/dist/src/agent/models/manager.js +1 -1
  138. package/dist/src/agent/prompt/memory/index.js +1 -1
  139. package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
  140. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  141. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  142. package/dist/src/agent/sandbox/path-policy.js +2 -2
  143. package/dist/src/agent/service/process-direct-streaming.js +9 -4
  144. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  145. package/dist/src/agent/service/reconcile-dreaming-cron.js +8 -5
  146. package/dist/src/agent/service/reconcile-dreaming-cron.js.map +1 -1
  147. package/dist/src/agent/service.d.ts +2 -1
  148. package/dist/src/agent/service.js +10 -5
  149. package/dist/src/agent/service.js.map +1 -1
  150. package/dist/src/agent/service.types.d.ts +4 -0
  151. package/dist/src/agent/session/session-config-service.d.ts +8 -0
  152. package/dist/src/agent/session/session-config-service.js +20 -0
  153. package/dist/src/agent/session/session-config-service.js.map +1 -1
  154. package/dist/src/agent/session/session-inspector.js +1 -1
  155. package/dist/src/agent/skills/config.js +2 -2
  156. package/dist/src/agent/skills/filesystem-watcher.d.ts +19 -0
  157. package/dist/src/agent/skills/filesystem-watcher.js +182 -0
  158. package/dist/src/agent/skills/filesystem-watcher.js.map +1 -0
  159. package/dist/src/agent/skills/hub-hash.js +2 -2
  160. package/dist/src/agent/skills/hub-lock.js +1 -1
  161. package/dist/src/agent/skills/hub-pull.js +30 -36
  162. package/dist/src/agent/skills/hub-pull.js.map +1 -1
  163. package/dist/src/agent/skills/index.js +82 -38
  164. package/dist/src/agent/skills/index.js.map +1 -1
  165. package/dist/src/agent/skills/installer.js +2 -2
  166. package/dist/src/agent/skills/managed-store.d.ts +19 -0
  167. package/dist/src/agent/skills/managed-store.js +97 -38
  168. package/dist/src/agent/skills/managed-store.js.map +1 -1
  169. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  170. package/dist/src/agent/skills/skill-manager.d.ts +16 -6
  171. package/dist/src/agent/skills/skill-manager.js +79 -24
  172. package/dist/src/agent/skills/skill-manager.js.map +1 -1
  173. package/dist/src/agent/skills/skill-view-path.js +1 -1
  174. package/dist/src/agent/skills/test-framework.js +1 -1
  175. package/dist/src/agent/skills/types.d.ts +11 -0
  176. package/dist/src/agent/skills/watcher.js +1 -1
  177. package/dist/src/agent/starter-agents.js +1 -1
  178. package/dist/src/agent/tools/cronjob-tool.d.ts +6 -3
  179. package/dist/src/agent/tools/cronjob-tool.js +58 -23
  180. package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
  181. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  182. package/dist/src/agent/tools/factory.js +1 -1
  183. package/dist/src/agent/tools/find.js +1 -1
  184. package/dist/src/agent/tools/grep.js +1 -1
  185. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  186. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  187. package/dist/src/agent/tools/workflow-tool.js +1 -1
  188. package/dist/src/agent/workflow/catalog.js +1 -1
  189. package/dist/src/agent/workspace-runtime/registry.d.ts +5 -0
  190. package/dist/src/agent/workspace-runtime/registry.js +7 -0
  191. package/dist/src/agent/workspace-runtime/registry.js.map +1 -1
  192. package/dist/src/ai-assist/text-assist.d.ts +42 -0
  193. package/dist/src/ai-assist/text-assist.js +252 -0
  194. package/dist/src/ai-assist/text-assist.js.map +1 -0
  195. package/dist/src/auth/credentials.js +2 -2
  196. package/dist/src/auth/profiles/store.js +2 -2
  197. package/dist/src/auth/sync-provider-auth.js +1 -1
  198. package/dist/src/browser/cache-dir-policy.js +1 -1
  199. package/dist/src/browser/cdp-local-launcher.js +2 -2
  200. package/dist/src/browser/providers/browser-ext-install.js +4 -4
  201. package/dist/src/browser/providers/cloakbrowser.js +5 -5
  202. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  203. package/dist/src/browser/stealth.js +1 -1
  204. package/dist/src/channels/outbound/persist-store.js +1 -1
  205. package/dist/src/channels/pairing/allow-from-file.js +1 -1
  206. package/dist/src/channels/pairing/pairing-store.js +2 -2
  207. package/dist/src/channels/plugins/types.adapters.d.ts +2 -2
  208. package/dist/src/chat-commands/agent-edit.js +2 -2
  209. package/dist/src/chat-commands/builtins/config.js +3 -3
  210. package/dist/src/chat-commands/builtins/system.js +11 -4
  211. package/dist/src/chat-commands/builtins/system.js.map +1 -1
  212. package/dist/src/chat-commands/context.d.ts +3 -0
  213. package/dist/src/chat-commands/context.js +7 -0
  214. package/dist/src/chat-commands/context.js.map +1 -1
  215. package/dist/src/chat-commands/types.d.ts +2 -0
  216. package/dist/src/cli/commands/config.js +1 -1
  217. package/dist/src/cli/commands/cron.js +55 -16
  218. package/dist/src/cli/commands/cron.js.map +1 -1
  219. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  220. package/dist/src/cli/commands/doctor/checks/cron-health.js +2 -83
  221. package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -1
  222. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  223. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  224. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  225. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  226. package/dist/src/cli/commands/extension-create.js +1 -1
  227. package/dist/src/cli/commands/extension-dev.js +2 -2
  228. package/dist/src/cli/commands/extension-marketplace.js +2 -2
  229. package/dist/src/cli/commands/extension-pack.js +2 -2
  230. package/dist/src/cli/commands/extension.js +3 -3
  231. package/dist/src/cli/commands/gateway/logs.js +1 -1
  232. package/dist/src/cli/commands/image.js +1 -1
  233. package/dist/src/cli/commands/init.js +2 -2
  234. package/dist/src/cli/commands/models.js +1 -1
  235. package/dist/src/cli/commands/skills-test.js +1 -1
  236. package/dist/src/cli/templates.js +1 -1
  237. package/dist/src/cli/utils/gateway-client.js +1 -1
  238. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  239. package/dist/src/config/agent-profile.js +1 -1
  240. package/dist/src/config/agent-typed-models.js +1 -1
  241. package/dist/src/config/gateway-bind.js +1 -1
  242. package/dist/src/config/index.js +7 -7
  243. package/dist/src/config/loader.js +4 -4
  244. package/dist/src/config/models-json.js +2 -2
  245. package/dist/src/config/paths-state.js +1 -1
  246. package/dist/src/config/paths.js +1 -1
  247. package/dist/src/config/profile.js +1 -1
  248. package/dist/src/config/reload.js +1 -1
  249. package/dist/src/config/schema.d.ts +0 -2
  250. package/dist/src/config/schema.js +0 -3
  251. package/dist/src/config/schema.js.map +1 -1
  252. package/dist/src/config/web-patch.d.ts +0 -1
  253. package/dist/src/config/web-patch.js +0 -2
  254. package/dist/src/config/web-patch.js.map +1 -1
  255. package/dist/src/config/workspace-path.js +1 -1
  256. package/dist/src/cron/executor.js +102 -71
  257. package/dist/src/cron/executor.js.map +1 -1
  258. package/dist/src/cron/index.d.ts +1 -1
  259. package/dist/src/cron/index.js +3 -3
  260. package/dist/src/cron/run-log-store.d.ts +1 -0
  261. package/dist/src/cron/run-log-store.js +4 -2
  262. package/dist/src/cron/run-log-store.js.map +1 -1
  263. package/dist/src/cron/schedule.d.ts +10 -0
  264. package/dist/src/cron/schedule.js +85 -0
  265. package/dist/src/cron/schedule.js.map +1 -0
  266. package/dist/src/cron/service.d.ts +34 -77
  267. package/dist/src/cron/service.js +327 -264
  268. package/dist/src/cron/service.js.map +1 -1
  269. package/dist/src/cron/types.d.ts +80 -50
  270. package/dist/src/cron/validation.d.ts +358 -90
  271. package/dist/src/cron/validation.js +152 -132
  272. package/dist/src/cron/validation.js.map +1 -1
  273. package/dist/src/cron/workflow-run-completion.js +1 -1
  274. package/dist/src/daemon/constants.js +1 -1
  275. package/dist/src/daemon/install-plan.js +2 -2
  276. package/dist/src/daemon/launchd.js +2 -2
  277. package/dist/src/daemon/schtasks.js +2 -2
  278. package/dist/src/daemon/systemd.js +2 -2
  279. package/dist/src/extensions/bundle-mcp.js +1 -1
  280. package/dist/src/extensions/discover-extensions.js +2 -2
  281. package/dist/src/extensions/health.js +1 -1
  282. package/dist/src/extensions/install.js +2 -2
  283. package/dist/src/extensions/loader.js +2 -2
  284. package/dist/src/extensions/lockfile.js +3 -3
  285. package/dist/src/extensions/security.js +1 -1
  286. package/dist/src/extensions/update.js +2 -2
  287. package/dist/src/gateway/agents-admin.js +2 -2
  288. package/dist/src/gateway/chat-stream/mapper.d.ts +1 -0
  289. package/dist/src/gateway/chat-stream/mapper.js +6 -2
  290. package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
  291. package/dist/src/gateway/chat-stream/protocol.d.ts +2 -0
  292. package/dist/src/gateway/file-path-classifier.js +2 -2
  293. package/dist/src/gateway/hono/lib/config-payload.d.ts +0 -1
  294. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  295. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  296. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  297. package/dist/src/gateway/hono/oauth-async.js +1 -1
  298. package/dist/src/gateway/hono/routes/agents.js +1 -1
  299. package/dist/src/gateway/hono/routes/ai-assist.d.ts +3 -0
  300. package/dist/src/gateway/hono/routes/ai-assist.js +66 -0
  301. package/dist/src/gateway/hono/routes/ai-assist.js.map +1 -0
  302. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  303. package/dist/src/gateway/hono/routes/commands-skills.js +7 -0
  304. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  305. package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
  306. package/dist/src/gateway/hono/routes/cron.js +24 -5
  307. package/dist/src/gateway/hono/routes/cron.js.map +1 -1
  308. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  309. package/dist/src/gateway/hono/routes/home.js +5 -3
  310. package/dist/src/gateway/hono/routes/home.js.map +1 -1
  311. package/dist/src/gateway/hono/routes/host-fs.js +2 -2
  312. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  313. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  314. package/dist/src/gateway/hono/routes/models.js +1 -1
  315. package/dist/src/gateway/hono/routes/shares.js +1 -1
  316. package/dist/src/gateway/hono/routes/workflows.js +2 -2
  317. package/dist/src/gateway/hono/routes/workspace.js +2 -2
  318. package/dist/src/gateway/lock.js +3 -3
  319. package/dist/src/gateway/ports.js +1 -1
  320. package/dist/src/gateway/service/agent-runner.js +2 -2
  321. package/dist/src/gateway/service/config-coordinator.js +1 -1
  322. package/dist/src/gateway/service/marketplace-service.d.ts +22 -10
  323. package/dist/src/gateway/service/marketplace-service.js +46 -5
  324. package/dist/src/gateway/service/marketplace-service.js.map +1 -1
  325. package/dist/src/gateway/service.js +11 -5
  326. package/dist/src/gateway/service.js.map +1 -1
  327. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  328. package/dist/src/goals/goal-notifications.js +1 -1
  329. package/dist/src/goals/goal-workflow-judge.js +3 -3
  330. package/dist/src/i18n/goals-bundle.js +1 -1
  331. package/dist/src/i18n/index.js +1 -1
  332. package/dist/src/i18n/share-tool-bundle.js +2 -2
  333. package/dist/src/infra/brew.js +1 -1
  334. package/dist/src/infra/package-json.js +1 -1
  335. package/dist/src/infra/package-update-steps.js +1 -1
  336. package/dist/src/infra/path-env.js +2 -2
  337. package/dist/src/infra/restart.js +1 -1
  338. package/dist/src/infra/stable-node-path.js +1 -1
  339. package/dist/src/infra/update-check.js +1 -1
  340. package/dist/src/infra/update-global.js +1 -1
  341. package/dist/src/infra/update-lock.js +2 -2
  342. package/dist/src/infra/update-restart.js +1 -1
  343. package/dist/src/infra/update-runner.js +1 -1
  344. package/dist/src/infra/update-startup.js +1 -1
  345. package/dist/src/infra/write-file-atomic.js +2 -2
  346. package/dist/src/media/store.js +1 -1
  347. package/dist/src/notes/agent-context.js +1 -1
  348. package/dist/src/notes/service.js +1 -1
  349. package/dist/src/notes/store.js +2 -2
  350. package/dist/src/project-trust/trust-store.js +2 -2
  351. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  352. package/dist/src/providers/index.js +1 -1
  353. package/dist/src/providers/model-registry.js +4 -4
  354. package/dist/src/session/init-session-turn.js +2 -2
  355. package/dist/src/session/resolve-session.js +3 -3
  356. package/dist/src/session/session-title.js +2 -2
  357. package/dist/src/session/store-reader.d.ts +5 -0
  358. package/dist/src/session/store-reader.js +1 -0
  359. package/dist/src/session/store.js +1 -1
  360. package/dist/src/share/share-auto.js +2 -2
  361. package/dist/src/share/share-store.js +3 -3
  362. package/dist/src/share/share-thumbnail.js +2 -2
  363. package/dist/src/share/share-zip.js +1 -1
  364. package/dist/src/share/site-share-store.js +3 -3
  365. package/dist/src/share/site-static-serve.js +1 -1
  366. package/dist/src/storage/sqlite/connection.js +1 -1
  367. package/dist/src/storage/sqlite/cron-job-repository.d.ts +6 -0
  368. package/dist/src/storage/sqlite/cron-job-repository.js +70 -0
  369. package/dist/src/storage/sqlite/cron-job-repository.js.map +1 -0
  370. package/dist/src/storage/sqlite/cron-run-repository.js +0 -1
  371. package/dist/src/storage/sqlite/cron-run-repository.js.map +1 -1
  372. package/dist/src/storage/sqlite/index.d.ts +1 -0
  373. package/dist/src/storage/sqlite/index.js +2 -1
  374. package/dist/src/storage/sqlite/memory-index-repository.js +2 -2
  375. package/dist/src/storage/sqlite/migrations/009_cron_jobs.sql +24 -0
  376. package/dist/src/storage/sqlite/migrations/discover.js +1 -1
  377. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  378. package/dist/src/storage/sqlite/migrations/runner.js +4 -4
  379. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  380. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  381. package/dist/src/storage/sqlite/schema.js +2 -2
  382. package/dist/src/storage/sqlite/schema.sql +25 -0
  383. package/dist/src/tui/clipboard-image.js +3 -3
  384. package/dist/src/tui/extension-host/load-extensions.js +1 -1
  385. package/dist/src/tui/theme-manager.js +1 -1
  386. package/dist/src/tui/tui-command-formatters.js +4 -4
  387. package/dist/src/tui/tui-commands.js +2 -2
  388. package/dist/src/tui/tui-keybindings-file.js +1 -1
  389. package/dist/src/tui/tui-scoped-models.js +2 -2
  390. package/dist/src/tui/tui-settings.js +1 -1
  391. package/dist/src/tui/tui-startup-resources.js +1 -1
  392. package/dist/src/tui/tui.js +6 -6
  393. package/dist/src/tunnel/frpc-binary.js +4 -4
  394. package/dist/src/tunnel/frpc-config.js +1 -1
  395. package/dist/src/tunnel/frpc-extract.js +1 -1
  396. package/dist/src/tunnel/tunnel-state.js +1 -1
  397. package/dist/src/utils/logger/audit.js +1 -1
  398. package/dist/src/utils/logger/log-store.js +1 -1
  399. package/dist/src/utils/logger/rotation.js +1 -1
  400. package/dist/src/utils/logger/streams.js +1 -1
  401. package/dist/src/voice/tts/audio.js +1 -1
  402. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  403. package/dist/src/workflows/draft/workflow-draft-service.js +1 -1
  404. package/dist/src/workflows/engine/projector.js +23 -14
  405. package/dist/src/workflows/engine/projector.js.map +1 -1
  406. package/dist/src/workflows/engine/workflow-engine.d.ts +1 -0
  407. package/dist/src/workflows/engine/workflow-engine.js +36 -0
  408. package/dist/src/workflows/engine/workflow-engine.js.map +1 -1
  409. package/dist/src/workflows/index.js +1 -1
  410. package/dist/src/workflows/service/workflow-run-service.js +2 -2
  411. package/dist/src/workflows/service/workflow-session-bridge.js +2 -2
  412. package/dist/src/workflows/store/event-store.js +1 -1
  413. package/dist/src/workflows/store/run-store.js +1 -1
  414. package/package.json +2 -3
  415. package/dist/gateway/static/root/assets/agent-browser-settings-page-B-0zGjt8.js +0 -6
  416. package/dist/gateway/static/root/assets/agents-DwDnJlXT.js +0 -71
  417. package/dist/gateway/static/root/assets/connectors-settings-DDvHo9in.js +0 -2
  418. package/dist/gateway/static/root/assets/cron-api-Dj_UZKST.js +0 -1
  419. package/dist/gateway/static/root/assets/cron-page-Dv7GII9G.js +0 -1
  420. package/dist/gateway/static/root/assets/cron-schedule-picker-DH10KgD6.js +0 -1
  421. package/dist/gateway/static/root/assets/dreaming-settings-L2rgZDQl.js +0 -1
  422. package/dist/gateway/static/root/assets/extension-settings-page-m_UQYkVc.js +0 -1
  423. package/dist/gateway/static/root/assets/gateway-settings-CS177xni.js +0 -1
  424. package/dist/gateway/static/root/assets/goals-page-C0FujUB5.js +0 -2
  425. package/dist/gateway/static/root/assets/index-COkF54xv.js +0 -121
  426. package/dist/gateway/static/root/assets/index-DIaSNGEO.css +0 -1
  427. package/dist/gateway/static/root/assets/interval-presets-B29Ss_2y.js +0 -2
  428. package/dist/gateway/static/root/assets/messages-Csef1aW7.js +0 -3
  429. package/dist/gateway/static/root/assets/models-hub-panel-DHGiTIvf.js +0 -2
  430. package/dist/gateway/static/root/assets/note-detail-page-BM7zlDiT.js +0 -2
  431. package/dist/gateway/static/root/assets/schedule-field-BPAWui3P.js +0 -1
  432. package/dist/gateway/static/root/assets/schedule-summary-B-hdSRdR.js +0 -1
  433. package/dist/gateway/static/root/assets/settings-advanced-gate-BYS-rpzn.js +0 -1
  434. package/dist/gateway/static/root/assets/settings-form-section-BsrK7TBF.js +0 -1
  435. package/dist/gateway/static/root/assets/setup-status-panel-DJiksNu4.js +0 -1
  436. package/dist/gateway/static/root/assets/skill-reload-api-CU9i0nJV.js +0 -1
  437. package/dist/gateway/static/root/assets/skills-page-ERbdWENm.js +0 -2
  438. package/dist/gateway/static/root/assets/use-agent-defaults-form-D3FGVLZb.js +0 -1
  439. package/dist/gateway/static/root/favicon-16.png +0 -0
  440. package/dist/gateway/static/root/favicon-32.png +0 -0
  441. package/dist/src/cron/persistence.d.ts +0 -62
  442. package/dist/src/cron/persistence.js +0 -188
  443. package/dist/src/cron/persistence.js.map +0 -1
@@ -0,0 +1,182 @@
1
+ import { resolveStateDir } from "../../config/paths-state.js";
2
+ import { init_paths, resolveSkillsDir } from "../../config/paths.js";
3
+ import { createLogger } from "../../utils/logger/index.js";
4
+ import { init_logger } from "../../utils/logger.js";
5
+ import { createSkillConfigManager } from "./config.js";
6
+ import { isManagedSkillTransientDirName } from "./managed-store.js";
7
+ import path from "node:path";
8
+ import chokidar from "chokidar";
9
+ //#region src/agent/skills/filesystem-watcher.ts
10
+ init_paths();
11
+ init_logger();
12
+ const log = createLogger("SkillFilesystemWatcher");
13
+ const DEFAULT_DEBOUNCE_MS = 1e3;
14
+ const WATCH_DEPTH = 8;
15
+ const IGNORED_SEGMENTS = new Set([
16
+ ".git",
17
+ "node_modules",
18
+ "dist",
19
+ "build",
20
+ ".next",
21
+ ".nuxt",
22
+ ".venv",
23
+ "venv",
24
+ "__pycache__",
25
+ ".cache",
26
+ ".mypy_cache",
27
+ ".pytest_cache"
28
+ ]);
29
+ function normalizePath(p) {
30
+ return path.resolve(p);
31
+ }
32
+ function pathSegments(p) {
33
+ return p.split(/[\\/]+/).filter(Boolean);
34
+ }
35
+ function hasIgnoredSegment(p) {
36
+ return pathSegments(p).some((segment) => IGNORED_SEGMENTS.has(segment) || isManagedSkillTransientDirName(segment));
37
+ }
38
+ function isSameOrInside(parent, child) {
39
+ const rel = path.relative(parent, child);
40
+ return rel === "" || !!rel && !rel.startsWith("..") && !path.isAbsolute(rel);
41
+ }
42
+ function isWorkspaceSkillPath(workspaceRoot, candidate) {
43
+ const skillsRoot = path.join(workspaceRoot, "skills");
44
+ return candidate === workspaceRoot || isSameOrInside(skillsRoot, candidate);
45
+ }
46
+ function isWorkspaceSkillsRoot(workspaceRoot, candidate) {
47
+ return candidate === path.join(workspaceRoot, "skills");
48
+ }
49
+ function isSkillMdPath(candidate) {
50
+ return path.basename(candidate).toLowerCase() === "skill.md";
51
+ }
52
+ function readWatcherConfig() {
53
+ const cfg = createSkillConfigManager(resolveStateDir()).load();
54
+ const rawDebounce = cfg.load?.watchDebounceMs;
55
+ return {
56
+ enabled: cfg.load?.watch !== false,
57
+ debounceMs: typeof rawDebounce === "number" && Number.isFinite(rawDebounce) ? Math.max(0, rawDebounce) : DEFAULT_DEBOUNCE_MS
58
+ };
59
+ }
60
+ var SkillFilesystemWatcher = class {
61
+ targets = /* @__PURE__ */ new Map();
62
+ onChange;
63
+ disposed = false;
64
+ constructor(options) {
65
+ this.onChange = options.onChange;
66
+ this.watchGlobalSkillsDir();
67
+ }
68
+ watchGlobalSkillsDir() {
69
+ this.watchRoot(resolveSkillsDir(), "global");
70
+ }
71
+ watchWorkspace(workspaceDir) {
72
+ const resolved = normalizePath(workspaceDir);
73
+ if (!resolved) return;
74
+ this.watchRoot(resolved, "workspace");
75
+ }
76
+ refreshPrimaryWorkspace(workspaceDir) {
77
+ this.watchGlobalSkillsDir();
78
+ this.watchWorkspace(workspaceDir);
79
+ }
80
+ dispose() {
81
+ this.disposed = true;
82
+ for (const target of this.targets.values()) {
83
+ if (target.timer) clearTimeout(target.timer);
84
+ target.watcher.close().catch((err) => {
85
+ log.warn({
86
+ err,
87
+ root: target.root
88
+ }, "Skill watcher close failed");
89
+ });
90
+ }
91
+ this.targets.clear();
92
+ }
93
+ watchRoot(root, kind) {
94
+ if (this.disposed) return;
95
+ const { enabled, debounceMs } = readWatcherConfig();
96
+ const resolvedRoot = normalizePath(root);
97
+ const key = `${kind}:${resolvedRoot}`;
98
+ if (!enabled) {
99
+ this.unwatch(key);
100
+ return;
101
+ }
102
+ if (this.targets.get(key)) return;
103
+ const ignored = (watchPath, stats) => {
104
+ const candidate = normalizePath(watchPath);
105
+ if (candidate === resolvedRoot) return false;
106
+ if (hasIgnoredSegment(candidate)) return true;
107
+ if (kind === "workspace" && !isWorkspaceSkillPath(resolvedRoot, candidate)) return true;
108
+ if (kind === "workspace" && isWorkspaceSkillsRoot(resolvedRoot, candidate)) return false;
109
+ if (!stats) return false;
110
+ if (stats?.isDirectory?.()) return false;
111
+ return !isSkillMdPath(candidate);
112
+ };
113
+ const watcher = chokidar.watch(resolvedRoot, {
114
+ ignoreInitial: true,
115
+ followSymlinks: false,
116
+ depth: WATCH_DEPTH,
117
+ awaitWriteFinish: {
118
+ stabilityThreshold: debounceMs,
119
+ pollInterval: 100
120
+ },
121
+ ignored
122
+ });
123
+ const target = {
124
+ key,
125
+ root: resolvedRoot,
126
+ kind,
127
+ watcher
128
+ };
129
+ const schedule = (changedPath) => this.scheduleRefresh(target, changedPath);
130
+ watcher.on("addDir", schedule);
131
+ watcher.on("add", schedule);
132
+ watcher.on("change", schedule);
133
+ watcher.on("unlink", schedule);
134
+ watcher.on("unlinkDir", schedule);
135
+ watcher.on("error", (err) => {
136
+ log.warn({
137
+ err,
138
+ root: resolvedRoot,
139
+ kind
140
+ }, "Skill watcher error");
141
+ });
142
+ this.targets.set(key, target);
143
+ log.info({
144
+ root: resolvedRoot,
145
+ kind
146
+ }, "Watching skill filesystem");
147
+ }
148
+ unwatch(key) {
149
+ const target = this.targets.get(key);
150
+ if (!target) return;
151
+ if (target.timer) clearTimeout(target.timer);
152
+ target.watcher.close().catch((err) => {
153
+ log.warn({
154
+ err,
155
+ root: target.root
156
+ }, "Skill watcher close failed");
157
+ });
158
+ this.targets.delete(key);
159
+ }
160
+ scheduleRefresh(target, changedPath) {
161
+ const { enabled, debounceMs } = readWatcherConfig();
162
+ if (!enabled) {
163
+ this.unwatch(target.key);
164
+ return;
165
+ }
166
+ target.pendingPath = changedPath ?? target.pendingPath;
167
+ if (target.timer) clearTimeout(target.timer);
168
+ target.timer = setTimeout(() => {
169
+ target.timer = void 0;
170
+ const pendingPath = target.pendingPath;
171
+ target.pendingPath = void 0;
172
+ this.onChange({
173
+ reason: "watch",
174
+ changedPath: pendingPath
175
+ });
176
+ }, debounceMs);
177
+ }
178
+ };
179
+ //#endregion
180
+ export { SkillFilesystemWatcher };
181
+
182
+ //# sourceMappingURL=filesystem-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem-watcher.js","names":[],"sources":["../../../../src/agent/skills/filesystem-watcher.ts"],"sourcesContent":["import path from 'node:path';\n\nimport chokidar, { type FSWatcher } from 'chokidar';\n\nimport { resolveSkillsDir, resolveStateDir } from '../../config/paths.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { createSkillConfigManager } from './config.js';\nimport { isManagedSkillTransientDirName } from './managed-store.js';\n\nconst log = createLogger('SkillFilesystemWatcher');\n\nexport interface SkillFilesystemWatcherOptions {\n onChange: (event: { reason: 'watch'; changedPath?: string }) => void;\n}\n\ntype WatchTarget = {\n key: string;\n root: string;\n kind: 'global' | 'workspace';\n watcher: FSWatcher;\n timer?: NodeJS.Timeout;\n pendingPath?: string;\n};\n\nconst DEFAULT_DEBOUNCE_MS = 1000;\nconst WATCH_DEPTH = 8;\nconst IGNORED_SEGMENTS = new Set([\n '.git',\n 'node_modules',\n 'dist',\n 'build',\n '.next',\n '.nuxt',\n '.venv',\n 'venv',\n '__pycache__',\n '.cache',\n '.mypy_cache',\n '.pytest_cache',\n]);\n\nfunction normalizePath(p: string): string {\n return path.resolve(p);\n}\n\nfunction pathSegments(p: string): string[] {\n return p.split(/[\\\\/]+/).filter(Boolean);\n}\n\nfunction hasIgnoredSegment(p: string): boolean {\n return pathSegments(p).some(\n (segment) => IGNORED_SEGMENTS.has(segment) || isManagedSkillTransientDirName(segment),\n );\n}\n\nfunction isSameOrInside(parent: string, child: string): boolean {\n const rel = path.relative(parent, child);\n return rel === '' || (!!rel && !rel.startsWith('..') && !path.isAbsolute(rel));\n}\n\nfunction isWorkspaceSkillPath(workspaceRoot: string, candidate: string): boolean {\n const skillsRoot = path.join(workspaceRoot, 'skills');\n return candidate === workspaceRoot || isSameOrInside(skillsRoot, candidate);\n}\n\nfunction isWorkspaceSkillsRoot(workspaceRoot: string, candidate: string): boolean {\n return candidate === path.join(workspaceRoot, 'skills');\n}\n\nfunction isSkillMdPath(candidate: string): boolean {\n return path.basename(candidate).toLowerCase() === 'skill.md';\n}\n\nfunction readWatcherConfig(): { enabled: boolean; debounceMs: number } {\n const cfg = createSkillConfigManager(resolveStateDir()).load();\n const rawDebounce = cfg.load?.watchDebounceMs;\n return {\n enabled: cfg.load?.watch !== false,\n debounceMs:\n typeof rawDebounce === 'number' && Number.isFinite(rawDebounce)\n ? Math.max(0, rawDebounce)\n : DEFAULT_DEBOUNCE_MS,\n };\n}\n\nexport class SkillFilesystemWatcher {\n private readonly targets = new Map<string, WatchTarget>();\n private readonly onChange: SkillFilesystemWatcherOptions['onChange'];\n private disposed = false;\n\n constructor(options: SkillFilesystemWatcherOptions) {\n this.onChange = options.onChange;\n this.watchGlobalSkillsDir();\n }\n\n watchGlobalSkillsDir(): void {\n this.watchRoot(resolveSkillsDir(), 'global');\n }\n\n watchWorkspace(workspaceDir: string): void {\n const resolved = normalizePath(workspaceDir);\n if (!resolved) return;\n this.watchRoot(resolved, 'workspace');\n }\n\n refreshPrimaryWorkspace(workspaceDir: string): void {\n this.watchGlobalSkillsDir();\n this.watchWorkspace(workspaceDir);\n }\n\n dispose(): void {\n this.disposed = true;\n for (const target of this.targets.values()) {\n if (target.timer) clearTimeout(target.timer);\n void target.watcher.close().catch((err) => {\n log.warn({ err, root: target.root }, 'Skill watcher close failed');\n });\n }\n this.targets.clear();\n }\n\n private watchRoot(root: string, kind: WatchTarget['kind']): void {\n if (this.disposed) return;\n const { enabled, debounceMs } = readWatcherConfig();\n const resolvedRoot = normalizePath(root);\n const key = `${kind}:${resolvedRoot}`;\n\n if (!enabled) {\n this.unwatch(key);\n return;\n }\n\n const existing = this.targets.get(key);\n if (existing) return;\n\n const ignored = (watchPath: string, stats?: { isDirectory?: () => boolean }) => {\n const candidate = normalizePath(watchPath);\n if (candidate === resolvedRoot) return false;\n if (hasIgnoredSegment(candidate)) return true;\n\n if (kind === 'workspace' && !isWorkspaceSkillPath(resolvedRoot, candidate)) {\n return true;\n }\n\n if (kind === 'workspace' && isWorkspaceSkillsRoot(resolvedRoot, candidate)) return false;\n if (!stats) return false;\n if (stats?.isDirectory?.()) return false;\n return !isSkillMdPath(candidate);\n };\n\n const watcher = chokidar.watch(resolvedRoot, {\n ignoreInitial: true,\n followSymlinks: false,\n depth: WATCH_DEPTH,\n awaitWriteFinish: {\n stabilityThreshold: debounceMs,\n pollInterval: 100,\n },\n ignored,\n });\n\n const target: WatchTarget = { key, root: resolvedRoot, kind, watcher };\n const schedule = (changedPath?: string) => this.scheduleRefresh(target, changedPath);\n\n watcher.on('addDir', schedule);\n watcher.on('add', schedule);\n watcher.on('change', schedule);\n watcher.on('unlink', schedule);\n watcher.on('unlinkDir', schedule);\n watcher.on('error', (err) => {\n log.warn({ err, root: resolvedRoot, kind }, 'Skill watcher error');\n });\n\n this.targets.set(key, target);\n log.info({ root: resolvedRoot, kind }, 'Watching skill filesystem');\n }\n\n private unwatch(key: string): void {\n const target = this.targets.get(key);\n if (!target) return;\n if (target.timer) clearTimeout(target.timer);\n void target.watcher.close().catch((err) => {\n log.warn({ err, root: target.root }, 'Skill watcher close failed');\n });\n this.targets.delete(key);\n }\n\n private scheduleRefresh(target: WatchTarget, changedPath?: string): void {\n const { enabled, debounceMs } = readWatcherConfig();\n if (!enabled) {\n this.unwatch(target.key);\n return;\n }\n\n target.pendingPath = changedPath ?? target.pendingPath;\n if (target.timer) clearTimeout(target.timer);\n target.timer = setTimeout(() => {\n target.timer = undefined;\n const pendingPath = target.pendingPath;\n target.pendingPath = undefined;\n this.onChange({ reason: 'watch', changedPath: pendingPath });\n }, debounceMs);\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { join, relative } from "node:path";
2
- import { createReadStream, existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
1
  import { createHash } from "node:crypto";
2
+ import { createReadStream, existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
+ import { join, relative } from "node:path";
4
4
  //#region src/agent/skills/hub-hash.ts
5
5
  /**
6
6
  * Deterministic content hash for a skill directory (for hub lock / drift detection).
@@ -1,5 +1,5 @@
1
- import { init_paths, resolveSkillsLockPath } from "../../config/paths.js";
2
1
  import { init_write_file_atomic, writeTextAtomicSync } from "../../infra/write-file-atomic.js";
2
+ import { init_paths, resolveSkillsLockPath } from "../../config/paths.js";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  //#region src/agent/skills/hub-lock.ts
5
5
  /**
@@ -1,12 +1,11 @@
1
- import { init_paths, resolveSkillsDir } from "../../config/paths.js";
2
1
  import { parseFrontmatter } from "../../markdown/frontmatter.js";
3
2
  import { formatScanSummary, scanSkillDirectory } from "./scanner.js";
4
3
  import { getSkillsLockEntry, recordSkillsHubInstall } from "./hub-lock.js";
5
- import { installSkillFromZip, isValidSkillId } from "./managed-store.js";
4
+ import { installSkillFromZip, isValidSkillId, prepareManagedSkillTempDir, promoteManagedSkillTempDir } from "./managed-store.js";
6
5
  import { computeSkillTreeHashSync } from "./hub-hash.js";
7
- import { tmpdir } from "node:os";
8
- import { basename, join, normalize, resolve, sep } from "node:path";
9
6
  import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
7
+ import { basename, join, normalize, resolve } from "node:path";
8
+ import { tmpdir } from "node:os";
10
9
  import { fileURLToPath } from "node:url";
11
10
  import { execFileSync } from "node:child_process";
12
11
  import { fetch } from "undici";
@@ -14,7 +13,6 @@ import { fetch } from "undici";
14
13
  /**
15
14
  * CLI / hub: install or update skills from git or archive (zip / tar.gz) into ~/.xopc/skills.
16
15
  */
17
- init_paths();
18
16
  function classifyHubSource(raw) {
19
17
  const s = raw.trim();
20
18
  const lower = s.toLowerCase();
@@ -70,42 +68,38 @@ async function copySkillTreeToManaged(skillRoot, ctx) {
70
68
  const targetId = ctx.skillId?.trim() || inferSkillIdFromSkillMd(skillRoot);
71
69
  if (!targetId) throw new Error("Could not determine skill id: pass --id <name> (must match managed id rules)");
72
70
  if (!isValidSkillId(targetId)) throw new Error(`Invalid skill id "${targetId}" (letters, digits, ._-; max 63 chars after first)`);
73
- const rootDir = resolveSkillsDir();
74
- mkdirSync(rootDir, { recursive: true });
75
- const dest = join(rootDir, targetId);
76
- const destResolved = resolve(dest);
77
- const rootResolved = resolve(rootDir);
78
- if (!destResolved.startsWith(rootResolved + sep) && destResolved !== rootResolved) throw new Error("Invalid destination path");
79
- if (existsSync(dest)) {
80
- if (!ctx.force) throw new Error(`Skill "${targetId}" already exists. Use --force to replace.`);
81
- rmSync(dest, {
82
- recursive: true,
83
- force: true
71
+ const { destDir, tempDir } = prepareManagedSkillTempDir(targetId);
72
+ try {
73
+ if (existsSync(destDir) && !ctx.force) throw new Error(`Skill "${targetId}" already exists. Use --force to replace.`);
74
+ cpSync(skillRoot, tempDir, { recursive: true });
75
+ if (!existsSync(join(tempDir, "SKILL.md"))) throw new Error("Installed tree is missing SKILL.md");
76
+ await runScan(tempDir, ctx.strictScan);
77
+ const promoted = promoteManagedSkillTempDir({
78
+ skillId: targetId,
79
+ tempDir,
80
+ overwrite: ctx.force
84
81
  });
85
- }
86
- cpSync(skillRoot, dest, { recursive: true });
87
- if (!existsSync(join(dest, "SKILL.md"))) {
88
- rmSync(dest, {
82
+ const hash = computeSkillTreeHashSync(promoted.path);
83
+ recordSkillsHubInstall(targetId, {
84
+ kind: ctx.kind,
85
+ source: ctx.source,
86
+ ref: ctx.kind === "git" ? ctx.ref : void 0,
87
+ subpath: ctx.kind === "git" ? ctx.subpath : void 0
88
+ }, hash);
89
+ return {
90
+ skillId: targetId,
91
+ path: promoted.path,
92
+ contentHash: hash,
93
+ kind: ctx.kind,
94
+ source: ctx.source
95
+ };
96
+ } catch (err) {
97
+ rmSync(tempDir, {
89
98
  recursive: true,
90
99
  force: true
91
100
  });
92
- throw new Error("Installed tree is missing SKILL.md");
101
+ throw err;
93
102
  }
94
- const hash = computeSkillTreeHashSync(dest);
95
- recordSkillsHubInstall(targetId, {
96
- kind: ctx.kind,
97
- source: ctx.source,
98
- ref: ctx.kind === "git" ? ctx.ref : void 0,
99
- subpath: ctx.kind === "git" ? ctx.subpath : void 0
100
- }, hash);
101
- await runScan(dest, ctx.strictScan);
102
- return {
103
- skillId: targetId,
104
- path: dest,
105
- contentHash: hash,
106
- kind: ctx.kind,
107
- source: ctx.source
108
- };
109
103
  }
110
104
  async function pullSkillFromZipBuffer(buf, sourceLabel, options) {
111
105
  const r = installSkillFromZip(buf, {
@@ -1 +1 @@
1
- {"version":3,"file":"hub-pull.js","names":[],"sources":["../../../../src/agent/skills/hub-pull.ts"],"sourcesContent":["/**\n * CLI / hub: install or update skills from git or archive (zip / tar.gz) into ~/.xopc/skills.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n cpSync,\n existsSync,\n mkdtempSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n rmSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport { basename, join, normalize, resolve, sep } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { fileURLToPath } from 'node:url';\n\nimport { fetch } from 'undici';\n\nimport { parseFrontmatter } from '../../markdown/frontmatter.js';\nimport { resolveSkillsDir } from '../../config/paths.js';\nimport { computeSkillTreeHashSync } from './hub-hash.js';\nimport { getSkillsLockEntry, recordSkillsHubInstall } from './hub-lock.js';\nimport type { SkillHubKind } from './hub-lock.js';\nimport { installSkillFromZip, isValidSkillId } from './managed-store.js';\nimport { formatScanSummary, scanSkillDirectory } from './scanner.js';\n\nexport interface HubPullOptions {\n skillId?: string;\n ref?: string;\n subpath?: string;\n force?: boolean;\n /** When true, critical scanner findings fail the install. */\n strictScan?: boolean;\n}\n\nexport interface HubPullResult {\n skillId: string;\n path: string;\n contentHash: string;\n kind: SkillHubKind;\n source: string;\n}\n\nexport function classifyHubSource(raw: string): 'git' | 'archive' {\n const s = raw.trim();\n const lower = s.toLowerCase();\n\n try {\n if (s.startsWith('file:')) {\n const p = fileURLToPath(s);\n const pl = p.toLowerCase();\n if (pl.endsWith('.zip')) return 'archive';\n if (pl.endsWith('.tar.gz') || pl.endsWith('.tgz')) return 'archive';\n return 'git';\n }\n } catch {\n // ignore invalid file URL\n }\n\n if (/^[a-zA-Z]:[\\\\/]/.test(s) || (s.startsWith('/') && !s.startsWith('//'))) {\n if (lower.endsWith('.zip')) return 'archive';\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) return 'archive';\n return 'git';\n }\n\n try {\n const u = new URL(s);\n const path = u.pathname.toLowerCase();\n if (path.endsWith('.zip')) return 'archive';\n if (path.endsWith('.tar.gz') || path.endsWith('.tgz')) return 'archive';\n } catch {\n // not a URL\n }\n\n if (lower.endsWith('.zip')) return 'archive';\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) return 'archive';\n return 'git';\n}\n\nexport function findSkillRoot(repoRoot: string, subpath?: string): string {\n const sub = subpath?.trim()\n ? normalize(subpath.trim()).replace(/^[/\\\\]+/, '')\n : '';\n const base = sub ? resolve(repoRoot, sub) : resolve(repoRoot);\n if (!existsSync(base)) {\n throw new Error(`Path not found in source: ${sub || '.'}`);\n }\n if (existsSync(join(base, 'SKILL.md'))) {\n return base;\n }\n const entries = readdirSync(base, { withFileTypes: true }).filter((e) => e.isDirectory());\n if (entries.length === 1) {\n const one = join(base, entries[0].name);\n if (existsSync(join(one, 'SKILL.md'))) {\n return one;\n }\n }\n throw new Error(\n 'Could not locate SKILL.md (expected at root, under --path, or in a single child folder)',\n );\n}\n\nfunction inferSkillIdFromSkillMd(skillRoot: string): string {\n const raw = readFileSync(join(skillRoot, 'SKILL.md'), 'utf-8');\n const { frontmatter } = parseFrontmatter(raw);\n const n = String(frontmatter.name || '').trim();\n if (n && isValidSkillId(n)) {\n return n;\n }\n const dirName = basename(skillRoot);\n if (isValidSkillId(dirName)) {\n return dirName;\n }\n return '';\n}\n\nasync function runScan(dir: string, strict: boolean | undefined): Promise<void> {\n const summary = await scanSkillDirectory(dir);\n const label = basename(dir);\n if (summary.critical > 0 && strict) {\n throw new Error(\n `Security scan reported ${summary.critical} critical finding(s). ${formatScanSummary(summary, label)}`,\n );\n }\n if (summary.critical > 0 || summary.warn > 0) {\n console.warn(formatScanSummary(summary, label));\n }\n}\n\nasync function copySkillTreeToManaged(\n skillRoot: string,\n ctx: HubPullOptions & { kind: SkillHubKind; source: string },\n): Promise<HubPullResult> {\n const targetId = ctx.skillId?.trim() || inferSkillIdFromSkillMd(skillRoot);\n if (!targetId) {\n throw new Error('Could not determine skill id: pass --id <name> (must match managed id rules)');\n }\n if (!isValidSkillId(targetId)) {\n throw new Error(`Invalid skill id \"${targetId}\" (letters, digits, ._-; max 63 chars after first)`);\n }\n\n const rootDir = resolveSkillsDir();\n mkdirSync(rootDir, { recursive: true });\n const dest = join(rootDir, targetId);\n const destResolved = resolve(dest);\n const rootResolved = resolve(rootDir);\n if (!destResolved.startsWith(rootResolved + sep) && destResolved !== rootResolved) {\n throw new Error('Invalid destination path');\n }\n\n if (existsSync(dest)) {\n if (!ctx.force) {\n throw new Error(`Skill \"${targetId}\" already exists. Use --force to replace.`);\n }\n rmSync(dest, { recursive: true, force: true });\n }\n\n cpSync(skillRoot, dest, { recursive: true });\n\n if (!existsSync(join(dest, 'SKILL.md'))) {\n rmSync(dest, { recursive: true, force: true });\n throw new Error('Installed tree is missing SKILL.md');\n }\n\n const hash = computeSkillTreeHashSync(dest);\n recordSkillsHubInstall(\n targetId,\n {\n kind: ctx.kind,\n source: ctx.source,\n ref: ctx.kind === 'git' ? ctx.ref : undefined,\n subpath: ctx.kind === 'git' ? ctx.subpath : undefined,\n },\n hash,\n );\n await runScan(dest, ctx.strictScan);\n\n return {\n skillId: targetId,\n path: dest,\n contentHash: hash,\n kind: ctx.kind,\n source: ctx.source,\n };\n}\n\nasync function pullSkillFromZipBuffer(\n buf: Buffer,\n sourceLabel: string,\n options: HubPullOptions,\n): Promise<HubPullResult> {\n const r = installSkillFromZip(buf, {\n skillId: options.skillId,\n overwrite: options.force ?? false,\n });\n const hash = computeSkillTreeHashSync(r.path);\n recordSkillsHubInstall(r.skillId, { kind: 'archive', source: sourceLabel }, hash);\n await runScan(r.path, options.strictScan);\n return {\n skillId: r.skillId,\n path: r.path,\n contentHash: hash,\n kind: 'archive',\n source: sourceLabel,\n };\n}\n\nasync function pullSkillFromTarFile(\n archivePath: string,\n sourceLabel: string,\n options: HubPullOptions,\n): Promise<HubPullResult> {\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-tar-'));\n try {\n execFileSync('tar', ['-xzf', archivePath, '-C', tmpRoot], {\n stdio: 'pipe',\n encoding: 'utf-8',\n });\n const skillRoot = findSkillRoot(tmpRoot);\n return await copySkillTreeToManaged(skillRoot, { ...options, kind: 'archive', source: sourceLabel });\n } finally {\n rmSync(tmpRoot, { recursive: true, force: true });\n }\n}\n\nasync function pullSkillFromLocalPath(absPath: string, options: HubPullOptions): Promise<HubPullResult> {\n const resolved = resolve(absPath);\n if (!existsSync(resolved)) {\n throw new Error(`Not found: ${resolved}`);\n }\n const st = statSync(resolved);\n if (!st.isFile()) {\n throw new Error('Expected a path to a .zip or .tar.gz file (directories are not supported here)');\n }\n const lower = resolved.toLowerCase();\n if (lower.endsWith('.zip')) {\n const buf = readFileSync(resolved);\n return pullSkillFromZipBuffer(buf, resolved, options);\n }\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) {\n return pullSkillFromTarFile(resolved, resolved, options);\n }\n throw new Error('Unsupported file type (expected .zip, .tar.gz, or .tgz)');\n}\n\nasync function pullSkillFromGit(url: string, options: HubPullOptions): Promise<HubPullResult> {\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-hub-'));\n const cloneDir = join(tmpRoot, 'clone');\n try {\n const args = ['clone', '--depth', '1'];\n if (options.ref?.trim()) {\n args.push('--branch', options.ref.trim());\n }\n args.push(url, cloneDir);\n try {\n execFileSync('git', args, { stdio: 'pipe', encoding: 'utf-8' });\n } catch {\n if (options.ref?.trim()) {\n execFileSync('git', ['clone', '--depth', '1', url, cloneDir], {\n stdio: 'pipe',\n encoding: 'utf-8',\n });\n } else {\n throw new Error(`git clone failed for ${url}`);\n }\n }\n const skillRoot = findSkillRoot(cloneDir, options.subpath);\n return await copySkillTreeToManaged(skillRoot, { ...options, kind: 'git', source: url });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (msg.includes('ENOENT') || msg.includes('git clone failed')) {\n throw new Error(\n `Git operation failed (${msg}). Ensure git is installed and the repository URL is valid.`,\n );\n }\n throw e;\n } finally {\n rmSync(tmpRoot, { recursive: true, force: true });\n }\n}\n\n/**\n * Install or replace a skill from a git URL, http(s) archive URL, file:// URL, or local archive path.\n */\nexport async function pullSkillFromSource(\n source: string,\n options: HubPullOptions = {},\n): Promise<HubPullResult> {\n const trimmed = source.trim();\n if (!trimmed) {\n throw new Error('Missing source URL or path');\n }\n\n if (trimmed.startsWith('file:')) {\n return pullSkillFromLocalPath(fileURLToPath(trimmed), options);\n }\n\n if (!/^[a-zA-Z][a-zA-Z+.-]*:\\/\\//.test(trimmed) && existsSync(trimmed)) {\n return pullSkillFromLocalPath(resolve(trimmed), options);\n }\n\n const kind = classifyHubSource(trimmed);\n if (kind === 'archive') {\n const res = await fetch(trimmed);\n if (!res.ok) {\n throw new Error(`HTTP ${res.status} while fetching archive`);\n }\n const buf = Buffer.from(await res.arrayBuffer());\n const lower = trimmed.toLowerCase();\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) {\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-fetch-tar-'));\n const f = join(tmpRoot, 'archive.tgz');\n try {\n mkdirSync(tmpRoot, { recursive: true });\n writeFileSync(f, buf);\n return await pullSkillFromTarFile(f, trimmed, options);\n } finally {\n rmSync(tmpRoot, { recursive: true, force: true });\n }\n }\n return pullSkillFromZipBuffer(buf, trimmed, options);\n }\n\n return pullSkillFromGit(trimmed, options);\n}\n\n/** Re-install from the source recorded in ~/.xopc/skills-lock.json for this skill id. */\nexport async function updateSkillFromLock(\n skillId: string,\n options: Pick<HubPullOptions, 'strictScan'> = {},\n): Promise<HubPullResult> {\n const e = getSkillsLockEntry(skillId);\n if (!e) {\n throw new Error(\n `No hub lock entry for \"${skillId}\". Install with: xopc skills pull <git-or-archive-url>`,\n );\n }\n return pullSkillFromSource(e.source, {\n skillId,\n ref: e.ref,\n subpath: e.subpath,\n force: true,\n strictScan: options.strictScan,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"hub-pull.js","names":[],"sources":["../../../../src/agent/skills/hub-pull.ts"],"sourcesContent":["/**\n * CLI / hub: install or update skills from git or archive (zip / tar.gz) into ~/.xopc/skills.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n cpSync,\n existsSync,\n mkdtempSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n rmSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport { basename, join, normalize, resolve } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { fileURLToPath } from 'node:url';\n\nimport { fetch } from 'undici';\n\nimport { parseFrontmatter } from '../../markdown/frontmatter.js';\nimport { computeSkillTreeHashSync } from './hub-hash.js';\nimport { getSkillsLockEntry, recordSkillsHubInstall } from './hub-lock.js';\nimport type { SkillHubKind } from './hub-lock.js';\nimport {\n installSkillFromZip,\n isValidSkillId,\n prepareManagedSkillTempDir,\n promoteManagedSkillTempDir,\n} from './managed-store.js';\nimport { formatScanSummary, scanSkillDirectory } from './scanner.js';\n\nexport interface HubPullOptions {\n skillId?: string;\n ref?: string;\n subpath?: string;\n force?: boolean;\n /** When true, critical scanner findings fail the install. */\n strictScan?: boolean;\n}\n\nexport interface HubPullResult {\n skillId: string;\n path: string;\n contentHash: string;\n kind: SkillHubKind;\n source: string;\n}\n\nexport function classifyHubSource(raw: string): 'git' | 'archive' {\n const s = raw.trim();\n const lower = s.toLowerCase();\n\n try {\n if (s.startsWith('file:')) {\n const p = fileURLToPath(s);\n const pl = p.toLowerCase();\n if (pl.endsWith('.zip')) return 'archive';\n if (pl.endsWith('.tar.gz') || pl.endsWith('.tgz')) return 'archive';\n return 'git';\n }\n } catch {\n // ignore invalid file URL\n }\n\n if (/^[a-zA-Z]:[\\\\/]/.test(s) || (s.startsWith('/') && !s.startsWith('//'))) {\n if (lower.endsWith('.zip')) return 'archive';\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) return 'archive';\n return 'git';\n }\n\n try {\n const u = new URL(s);\n const path = u.pathname.toLowerCase();\n if (path.endsWith('.zip')) return 'archive';\n if (path.endsWith('.tar.gz') || path.endsWith('.tgz')) return 'archive';\n } catch {\n // not a URL\n }\n\n if (lower.endsWith('.zip')) return 'archive';\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) return 'archive';\n return 'git';\n}\n\nexport function findSkillRoot(repoRoot: string, subpath?: string): string {\n const sub = subpath?.trim()\n ? normalize(subpath.trim()).replace(/^[/\\\\]+/, '')\n : '';\n const base = sub ? resolve(repoRoot, sub) : resolve(repoRoot);\n if (!existsSync(base)) {\n throw new Error(`Path not found in source: ${sub || '.'}`);\n }\n if (existsSync(join(base, 'SKILL.md'))) {\n return base;\n }\n const entries = readdirSync(base, { withFileTypes: true }).filter((e) => e.isDirectory());\n if (entries.length === 1) {\n const one = join(base, entries[0].name);\n if (existsSync(join(one, 'SKILL.md'))) {\n return one;\n }\n }\n throw new Error(\n 'Could not locate SKILL.md (expected at root, under --path, or in a single child folder)',\n );\n}\n\nfunction inferSkillIdFromSkillMd(skillRoot: string): string {\n const raw = readFileSync(join(skillRoot, 'SKILL.md'), 'utf-8');\n const { frontmatter } = parseFrontmatter(raw);\n const n = String(frontmatter.name || '').trim();\n if (n && isValidSkillId(n)) {\n return n;\n }\n const dirName = basename(skillRoot);\n if (isValidSkillId(dirName)) {\n return dirName;\n }\n return '';\n}\n\nasync function runScan(dir: string, strict: boolean | undefined): Promise<void> {\n const summary = await scanSkillDirectory(dir);\n const label = basename(dir);\n if (summary.critical > 0 && strict) {\n throw new Error(\n `Security scan reported ${summary.critical} critical finding(s). ${formatScanSummary(summary, label)}`,\n );\n }\n if (summary.critical > 0 || summary.warn > 0) {\n console.warn(formatScanSummary(summary, label));\n }\n}\n\nasync function copySkillTreeToManaged(\n skillRoot: string,\n ctx: HubPullOptions & { kind: SkillHubKind; source: string },\n): Promise<HubPullResult> {\n const targetId = ctx.skillId?.trim() || inferSkillIdFromSkillMd(skillRoot);\n if (!targetId) {\n throw new Error('Could not determine skill id: pass --id <name> (must match managed id rules)');\n }\n if (!isValidSkillId(targetId)) {\n throw new Error(`Invalid skill id \"${targetId}\" (letters, digits, ._-; max 63 chars after first)`);\n }\n\n const { destDir, tempDir } = prepareManagedSkillTempDir(targetId);\n try {\n if (existsSync(destDir) && !ctx.force) {\n throw new Error(`Skill \"${targetId}\" already exists. Use --force to replace.`);\n }\n\n cpSync(skillRoot, tempDir, { recursive: true });\n\n if (!existsSync(join(tempDir, 'SKILL.md'))) {\n throw new Error('Installed tree is missing SKILL.md');\n }\n\n await runScan(tempDir, ctx.strictScan);\n const promoted = promoteManagedSkillTempDir({\n skillId: targetId,\n tempDir,\n overwrite: ctx.force,\n });\n const hash = computeSkillTreeHashSync(promoted.path);\n recordSkillsHubInstall(\n targetId,\n {\n kind: ctx.kind,\n source: ctx.source,\n ref: ctx.kind === 'git' ? ctx.ref : undefined,\n subpath: ctx.kind === 'git' ? ctx.subpath : undefined,\n },\n hash,\n );\n\n return {\n skillId: targetId,\n path: promoted.path,\n contentHash: hash,\n kind: ctx.kind,\n source: ctx.source,\n };\n } catch (err) {\n rmSync(tempDir, { recursive: true, force: true });\n throw err;\n }\n}\n\nasync function pullSkillFromZipBuffer(\n buf: Buffer,\n sourceLabel: string,\n options: HubPullOptions,\n): Promise<HubPullResult> {\n const r = installSkillFromZip(buf, {\n skillId: options.skillId,\n overwrite: options.force ?? false,\n });\n const hash = computeSkillTreeHashSync(r.path);\n recordSkillsHubInstall(r.skillId, { kind: 'archive', source: sourceLabel }, hash);\n await runScan(r.path, options.strictScan);\n return {\n skillId: r.skillId,\n path: r.path,\n contentHash: hash,\n kind: 'archive',\n source: sourceLabel,\n };\n}\n\nasync function pullSkillFromTarFile(\n archivePath: string,\n sourceLabel: string,\n options: HubPullOptions,\n): Promise<HubPullResult> {\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-tar-'));\n try {\n execFileSync('tar', ['-xzf', archivePath, '-C', tmpRoot], {\n stdio: 'pipe',\n encoding: 'utf-8',\n });\n const skillRoot = findSkillRoot(tmpRoot);\n return await copySkillTreeToManaged(skillRoot, { ...options, kind: 'archive', source: sourceLabel });\n } finally {\n rmSync(tmpRoot, { recursive: true, force: true });\n }\n}\n\nasync function pullSkillFromLocalPath(absPath: string, options: HubPullOptions): Promise<HubPullResult> {\n const resolved = resolve(absPath);\n if (!existsSync(resolved)) {\n throw new Error(`Not found: ${resolved}`);\n }\n const st = statSync(resolved);\n if (!st.isFile()) {\n throw new Error('Expected a path to a .zip or .tar.gz file (directories are not supported here)');\n }\n const lower = resolved.toLowerCase();\n if (lower.endsWith('.zip')) {\n const buf = readFileSync(resolved);\n return pullSkillFromZipBuffer(buf, resolved, options);\n }\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) {\n return pullSkillFromTarFile(resolved, resolved, options);\n }\n throw new Error('Unsupported file type (expected .zip, .tar.gz, or .tgz)');\n}\n\nasync function pullSkillFromGit(url: string, options: HubPullOptions): Promise<HubPullResult> {\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-hub-'));\n const cloneDir = join(tmpRoot, 'clone');\n try {\n const args = ['clone', '--depth', '1'];\n if (options.ref?.trim()) {\n args.push('--branch', options.ref.trim());\n }\n args.push(url, cloneDir);\n try {\n execFileSync('git', args, { stdio: 'pipe', encoding: 'utf-8' });\n } catch {\n if (options.ref?.trim()) {\n execFileSync('git', ['clone', '--depth', '1', url, cloneDir], {\n stdio: 'pipe',\n encoding: 'utf-8',\n });\n } else {\n throw new Error(`git clone failed for ${url}`);\n }\n }\n const skillRoot = findSkillRoot(cloneDir, options.subpath);\n return await copySkillTreeToManaged(skillRoot, { ...options, kind: 'git', source: url });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (msg.includes('ENOENT') || msg.includes('git clone failed')) {\n throw new Error(\n `Git operation failed (${msg}). Ensure git is installed and the repository URL is valid.`,\n );\n }\n throw e;\n } finally {\n rmSync(tmpRoot, { recursive: true, force: true });\n }\n}\n\n/**\n * Install or replace a skill from a git URL, http(s) archive URL, file:// URL, or local archive path.\n */\nexport async function pullSkillFromSource(\n source: string,\n options: HubPullOptions = {},\n): Promise<HubPullResult> {\n const trimmed = source.trim();\n if (!trimmed) {\n throw new Error('Missing source URL or path');\n }\n\n if (trimmed.startsWith('file:')) {\n return pullSkillFromLocalPath(fileURLToPath(trimmed), options);\n }\n\n if (!/^[a-zA-Z][a-zA-Z+.-]*:\\/\\//.test(trimmed) && existsSync(trimmed)) {\n return pullSkillFromLocalPath(resolve(trimmed), options);\n }\n\n const kind = classifyHubSource(trimmed);\n if (kind === 'archive') {\n const res = await fetch(trimmed);\n if (!res.ok) {\n throw new Error(`HTTP ${res.status} while fetching archive`);\n }\n const buf = Buffer.from(await res.arrayBuffer());\n const lower = trimmed.toLowerCase();\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) {\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-fetch-tar-'));\n const f = join(tmpRoot, 'archive.tgz');\n try {\n mkdirSync(tmpRoot, { recursive: true });\n writeFileSync(f, buf);\n return await pullSkillFromTarFile(f, trimmed, options);\n } finally {\n rmSync(tmpRoot, { recursive: true, force: true });\n }\n }\n return pullSkillFromZipBuffer(buf, trimmed, options);\n }\n\n return pullSkillFromGit(trimmed, options);\n}\n\n/** Re-install from the source recorded in ~/.xopc/skills-lock.json for this skill id. */\nexport async function updateSkillFromLock(\n skillId: string,\n options: Pick<HubPullOptions, 'strictScan'> = {},\n): Promise<HubPullResult> {\n const e = getSkillsLockEntry(skillId);\n if (!e) {\n throw new Error(\n `No hub lock entry for \"${skillId}\". Install with: xopc skills pull <git-or-archive-url>`,\n );\n }\n return pullSkillFromSource(e.source, {\n skillId,\n ref: e.ref,\n subpath: e.subpath,\n force: true,\n strictScan: options.strictScan,\n });\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { resolveStateDir } from "../../config/paths-state.js";
2
+ import { init_paths } from "../../config/paths.js";
2
3
  import { createLogger } from "../../utils/logger/index.js";
3
4
  import { init_logger } from "../../utils/logger.js";
4
- import { init_paths } from "../../config/paths.js";
5
5
  import { parseFrontmatter } from "../../markdown/frontmatter.js";
6
6
  import { findInstallSpec, getDefaultInstallerPreferences, hasBinary, installSkill } from "./installer.js";
7
7
  import { applySkillEnvOverrides, createSkillConfigManager, getSkillEnvironment, isSkillEnabled, resolveSkillConfig, validateSkillConfig } from "./config.js";
@@ -11,8 +11,8 @@ import { parseRequiredEnvVarNames } from "./required-env-vars.js";
11
11
  import { formatSkillsForPrompt } from "./format-skills-prompt.js";
12
12
  import { parseSkillMetadata } from "./parse-skill-metadata.js";
13
13
  import { SkillTestFramework, SkillTestRunner, formatTestResults, formatTestResultsJson, formatTestResultsTap } from "./test-framework.js";
14
- import { basename, dirname, join, relative, sep } from "path";
15
14
  import { existsSync, readFileSync, readdirSync } from "fs";
15
+ import { basename, dirname, join, relative, sep } from "path";
16
16
  //#region src/agent/skills/index.ts
17
17
  init_paths();
18
18
  init_logger();
@@ -68,7 +68,11 @@ function shouldIgnore(path, ignoredPaths) {
68
68
  }
69
69
  function discoverSkills(dir, source) {
70
70
  const skills = [];
71
- if (!existsSync(dir)) return skills;
71
+ const diagnostics = [];
72
+ if (!existsSync(dir)) return {
73
+ skills,
74
+ diagnostics
75
+ };
72
76
  function scan(currentDir, currentIgnoredPaths) {
73
77
  try {
74
78
  const entries = readdirSync(currentDir, { withFileTypes: true });
@@ -86,16 +90,27 @@ function discoverSkills(dir, source) {
86
90
  for (const rule of subRules) subIgnoredPaths.add(`${skillRelPath}${rule}`);
87
91
  }
88
92
  if (existsSync(skillMdPath) && !shouldIgnore(skillRelPath, currentIgnoredPaths)) {
89
- const skill = loadSkillFromFile(skillMdPath, source, dir);
93
+ const { skill, diagnostic } = loadSkillFromFile(skillMdPath, source, dir);
94
+ if (diagnostic) diagnostics.push(diagnostic);
90
95
  if (skill) skills.push(skill);
91
96
  }
92
97
  scan(fullPath, subIgnoredPaths);
93
98
  }
94
99
  }
95
- } catch {}
100
+ } catch (err) {
101
+ const message = err instanceof Error ? err.message : String(err);
102
+ diagnostics.push({
103
+ type: "error",
104
+ message: `Failed to scan skills directory: ${message}`,
105
+ path: currentDir
106
+ });
107
+ }
96
108
  }
97
109
  scan(dir, loadIgnoreRules(dir, dir));
98
- return skills;
110
+ return {
111
+ skills,
112
+ diagnostics
113
+ };
99
114
  }
100
115
  function deriveDescriptionFromMarkdown(content) {
101
116
  let fallbackHeading = "";
@@ -118,7 +133,15 @@ function loadSkillFromFile(filePath, source, rootDir) {
118
133
  const parentDirName = basename(skillDir);
119
134
  const name = frontmatter.name || parentDirName;
120
135
  const description = frontmatter.description?.trim() || deriveDescriptionFromMarkdown(content);
121
- if (!description?.trim()) return null;
136
+ if (!description?.trim()) return {
137
+ skill: null,
138
+ diagnostic: {
139
+ type: "error",
140
+ skillName: name,
141
+ message: `Skill "${name}" is missing a description`,
142
+ path: filePath
143
+ }
144
+ };
122
145
  let category;
123
146
  if (rootDir) {
124
147
  const segments = toPosixPath(relative(rootDir, skillDir)).split("/");
@@ -129,7 +152,7 @@ function loadSkillFromFile(filePath, source, rootDir) {
129
152
  metadata.description ||= description.trim();
130
153
  const toolConditions = parseSkillToolConditions(frontmatter);
131
154
  const requiredEnvVarNames = parseRequiredEnvVarNames(frontmatter);
132
- return {
155
+ return { skill: {
133
156
  name,
134
157
  description: description.trim(),
135
158
  category,
@@ -141,38 +164,55 @@ function loadSkillFromFile(filePath, source, rootDir) {
141
164
  toolConditions,
142
165
  requiredEnvVarNames: requiredEnvVarNames.length > 0 ? requiredEnvVarNames : void 0,
143
166
  content
167
+ } };
168
+ } catch (err) {
169
+ return {
170
+ skill: null,
171
+ diagnostic: {
172
+ type: "error",
173
+ message: `Failed to load skill file: ${err instanceof Error ? err.message : String(err)}`,
174
+ path: filePath
175
+ }
144
176
  };
145
- } catch {
146
- return null;
147
177
  }
148
178
  }
149
179
  function loadSkills(options) {
150
180
  const { workspaceDir, builtinDir, extraDirs = [] } = options;
151
181
  const skillMap = /* @__PURE__ */ new Map();
152
182
  const diagnostics = [];
153
- if (builtinDir) for (const skill of discoverSkills(builtinDir, "builtin")) {
154
- const existing = skillMap.get(skill.name);
155
- if (existing) diagnostics.push({
156
- type: "collision",
157
- skillName: skill.name,
158
- message: `Skill "${skill.name}" collision: ${existing.source} overrides ${skill.source}`,
159
- path: skill.filePath
160
- });
161
- else skillMap.set(skill.name, skill);
183
+ if (builtinDir) {
184
+ const discovered = discoverSkills(builtinDir, "builtin");
185
+ diagnostics.push(...discovered.diagnostics);
186
+ for (const skill of discovered.skills) {
187
+ const existing = skillMap.get(skill.name);
188
+ if (existing) diagnostics.push({
189
+ type: "collision",
190
+ skillName: skill.name,
191
+ message: `Skill "${skill.name}" collision: ${existing.source} overrides ${skill.source}`,
192
+ path: skill.filePath
193
+ });
194
+ else skillMap.set(skill.name, skill);
195
+ }
162
196
  }
163
197
  const globalDirs = [options.globalDir, join(resolveStateDir(), "skills")].filter((d) => !!d && existsSync(d));
164
- for (const dir of globalDirs) for (const skill of discoverSkills(dir, "global")) {
165
- const existing = skillMap.get(skill.name);
166
- if (existing) diagnostics.push({
167
- type: "collision",
168
- skillName: skill.name,
169
- message: `Skill "${skill.name}" collision: ${skill.source} overrides ${existing.source}`,
170
- path: skill.filePath
171
- });
172
- skillMap.set(skill.name, skill);
198
+ for (const dir of globalDirs) {
199
+ const discovered = discoverSkills(dir, "global");
200
+ diagnostics.push(...discovered.diagnostics);
201
+ for (const skill of discovered.skills) {
202
+ const existing = skillMap.get(skill.name);
203
+ if (existing) diagnostics.push({
204
+ type: "collision",
205
+ skillName: skill.name,
206
+ message: `Skill "${skill.name}" collision: ${skill.source} overrides ${existing.source}`,
207
+ path: skill.filePath
208
+ });
209
+ skillMap.set(skill.name, skill);
210
+ }
173
211
  }
174
212
  if (workspaceDir) {
175
- const workspaceSkills = discoverSkills(join(workspaceDir, "skills"), "workspace");
213
+ const discovered = discoverSkills(join(workspaceDir, "skills"), "workspace");
214
+ diagnostics.push(...discovered.diagnostics);
215
+ const workspaceSkills = discovered.skills;
176
216
  for (const skill of workspaceSkills) {
177
217
  const existing = skillMap.get(skill.name);
178
218
  if (existing) diagnostics.push({
@@ -184,15 +224,19 @@ function loadSkills(options) {
184
224
  skillMap.set(skill.name, skill);
185
225
  }
186
226
  }
187
- for (const extraDir of extraDirs) if (existsSync(extraDir)) for (const skill of discoverSkills(extraDir, "global")) {
188
- const existing = skillMap.get(skill.name);
189
- if (existing) diagnostics.push({
190
- type: "collision",
191
- skillName: skill.name,
192
- message: `Skill "${skill.name}" collision: ${existing.source} overrides ${skill.source}`,
193
- path: skill.filePath
194
- });
195
- else skillMap.set(skill.name, skill);
227
+ for (const extraDir of extraDirs) if (existsSync(extraDir)) {
228
+ const discovered = discoverSkills(extraDir, "global");
229
+ diagnostics.push(...discovered.diagnostics);
230
+ for (const skill of discovered.skills) {
231
+ const existing = skillMap.get(skill.name);
232
+ if (existing) diagnostics.push({
233
+ type: "collision",
234
+ skillName: skill.name,
235
+ message: `Skill "${skill.name}" collision: ${existing.source} overrides ${skill.source}`,
236
+ path: skill.filePath
237
+ });
238
+ else skillMap.set(skill.name, skill);
239
+ }
196
240
  }
197
241
  const skillsConfig = createSkillConfigManager(resolveStateDir()).load();
198
242
  const merged = Array.from(skillMap.values());