cowork-os 0.3.21

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 (526) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1638 -0
  3. package/bin/cowork.js +42 -0
  4. package/build/entitlements.mac.plist +16 -0
  5. package/build/icon.icns +0 -0
  6. package/build/icon.png +0 -0
  7. package/dist/electron/electron/activity/ActivityRepository.js +190 -0
  8. package/dist/electron/electron/agent/browser/browser-service.js +639 -0
  9. package/dist/electron/electron/agent/context-manager.js +225 -0
  10. package/dist/electron/electron/agent/custom-skill-loader.js +566 -0
  11. package/dist/electron/electron/agent/daemon.js +975 -0
  12. package/dist/electron/electron/agent/executor.js +3561 -0
  13. package/dist/electron/electron/agent/llm/anthropic-provider.js +155 -0
  14. package/dist/electron/electron/agent/llm/bedrock-provider.js +202 -0
  15. package/dist/electron/electron/agent/llm/gemini-provider.js +375 -0
  16. package/dist/electron/electron/agent/llm/index.js +34 -0
  17. package/dist/electron/electron/agent/llm/ollama-provider.js +263 -0
  18. package/dist/electron/electron/agent/llm/openai-oauth.js +101 -0
  19. package/dist/electron/electron/agent/llm/openai-provider.js +657 -0
  20. package/dist/electron/electron/agent/llm/openrouter-provider.js +232 -0
  21. package/dist/electron/electron/agent/llm/pricing.js +160 -0
  22. package/dist/electron/electron/agent/llm/provider-factory.js +880 -0
  23. package/dist/electron/electron/agent/llm/types.js +178 -0
  24. package/dist/electron/electron/agent/queue-manager.js +378 -0
  25. package/dist/electron/electron/agent/sandbox/docker-sandbox.js +402 -0
  26. package/dist/electron/electron/agent/sandbox/macos-sandbox.js +407 -0
  27. package/dist/electron/electron/agent/sandbox/runner.js +410 -0
  28. package/dist/electron/electron/agent/sandbox/sandbox-factory.js +228 -0
  29. package/dist/electron/electron/agent/sandbox/security-utils.js +258 -0
  30. package/dist/electron/electron/agent/search/brave-provider.js +119 -0
  31. package/dist/electron/electron/agent/search/google-provider.js +100 -0
  32. package/dist/electron/electron/agent/search/index.js +28 -0
  33. package/dist/electron/electron/agent/search/provider-factory.js +395 -0
  34. package/dist/electron/electron/agent/search/serpapi-provider.js +112 -0
  35. package/dist/electron/electron/agent/search/tavily-provider.js +90 -0
  36. package/dist/electron/electron/agent/search/types.js +40 -0
  37. package/dist/electron/electron/agent/security/index.js +12 -0
  38. package/dist/electron/electron/agent/security/input-sanitizer.js +303 -0
  39. package/dist/electron/electron/agent/security/output-filter.js +217 -0
  40. package/dist/electron/electron/agent/skill-eligibility.js +281 -0
  41. package/dist/electron/electron/agent/skill-registry.js +396 -0
  42. package/dist/electron/electron/agent/skills/document.js +878 -0
  43. package/dist/electron/electron/agent/skills/image-generator.js +225 -0
  44. package/dist/electron/electron/agent/skills/organizer.js +141 -0
  45. package/dist/electron/electron/agent/skills/presentation.js +367 -0
  46. package/dist/electron/electron/agent/skills/spreadsheet.js +165 -0
  47. package/dist/electron/electron/agent/tools/browser-tools.js +523 -0
  48. package/dist/electron/electron/agent/tools/builtin-settings.js +384 -0
  49. package/dist/electron/electron/agent/tools/canvas-tools.js +530 -0
  50. package/dist/electron/electron/agent/tools/cron-tools.js +577 -0
  51. package/dist/electron/electron/agent/tools/edit-tools.js +194 -0
  52. package/dist/electron/electron/agent/tools/file-tools.js +719 -0
  53. package/dist/electron/electron/agent/tools/glob-tools.js +283 -0
  54. package/dist/electron/electron/agent/tools/grep-tools.js +387 -0
  55. package/dist/electron/electron/agent/tools/image-tools.js +111 -0
  56. package/dist/electron/electron/agent/tools/mention-tools.js +282 -0
  57. package/dist/electron/electron/agent/tools/node-tools.js +476 -0
  58. package/dist/electron/electron/agent/tools/registry.js +2719 -0
  59. package/dist/electron/electron/agent/tools/search-tools.js +91 -0
  60. package/dist/electron/electron/agent/tools/shell-tools.js +574 -0
  61. package/dist/electron/electron/agent/tools/skill-tools.js +274 -0
  62. package/dist/electron/electron/agent/tools/system-tools.js +578 -0
  63. package/dist/electron/electron/agent/tools/web-fetch-tools.js +444 -0
  64. package/dist/electron/electron/agent/tools/x-tools.js +264 -0
  65. package/dist/electron/electron/agents/AgentRoleRepository.js +420 -0
  66. package/dist/electron/electron/agents/HeartbeatService.js +356 -0
  67. package/dist/electron/electron/agents/MentionRepository.js +197 -0
  68. package/dist/electron/electron/agents/TaskSubscriptionRepository.js +168 -0
  69. package/dist/electron/electron/agents/WorkingStateRepository.js +229 -0
  70. package/dist/electron/electron/canvas/canvas-manager.js +714 -0
  71. package/dist/electron/electron/canvas/canvas-preload.js +53 -0
  72. package/dist/electron/electron/canvas/canvas-protocol.js +195 -0
  73. package/dist/electron/electron/canvas/canvas-store.js +174 -0
  74. package/dist/electron/electron/canvas/index.js +13 -0
  75. package/dist/electron/electron/control-plane/client.js +364 -0
  76. package/dist/electron/electron/control-plane/handlers.js +572 -0
  77. package/dist/electron/electron/control-plane/index.js +41 -0
  78. package/dist/electron/electron/control-plane/node-manager.js +264 -0
  79. package/dist/electron/electron/control-plane/protocol.js +194 -0
  80. package/dist/electron/electron/control-plane/remote-client.js +437 -0
  81. package/dist/electron/electron/control-plane/server.js +640 -0
  82. package/dist/electron/electron/control-plane/settings.js +369 -0
  83. package/dist/electron/electron/control-plane/ssh-tunnel.js +549 -0
  84. package/dist/electron/electron/cron/index.js +30 -0
  85. package/dist/electron/electron/cron/schedule.js +190 -0
  86. package/dist/electron/electron/cron/service.js +614 -0
  87. package/dist/electron/electron/cron/store.js +155 -0
  88. package/dist/electron/electron/cron/types.js +82 -0
  89. package/dist/electron/electron/cron/webhook.js +258 -0
  90. package/dist/electron/electron/database/SecureSettingsRepository.js +444 -0
  91. package/dist/electron/electron/database/TaskLabelRepository.js +120 -0
  92. package/dist/electron/electron/database/repositories.js +1781 -0
  93. package/dist/electron/electron/database/schema.js +978 -0
  94. package/dist/electron/electron/extensions/index.js +33 -0
  95. package/dist/electron/electron/extensions/loader.js +313 -0
  96. package/dist/electron/electron/extensions/registry.js +485 -0
  97. package/dist/electron/electron/extensions/types.js +11 -0
  98. package/dist/electron/electron/gateway/channel-registry.js +1102 -0
  99. package/dist/electron/electron/gateway/channels/bluebubbles-client.js +479 -0
  100. package/dist/electron/electron/gateway/channels/bluebubbles.js +432 -0
  101. package/dist/electron/electron/gateway/channels/discord.js +975 -0
  102. package/dist/electron/electron/gateway/channels/email-client.js +593 -0
  103. package/dist/electron/electron/gateway/channels/email.js +443 -0
  104. package/dist/electron/electron/gateway/channels/google-chat.js +631 -0
  105. package/dist/electron/electron/gateway/channels/imessage-client.js +363 -0
  106. package/dist/electron/electron/gateway/channels/imessage.js +465 -0
  107. package/dist/electron/electron/gateway/channels/index.js +36 -0
  108. package/dist/electron/electron/gateway/channels/line-client.js +470 -0
  109. package/dist/electron/electron/gateway/channels/line.js +479 -0
  110. package/dist/electron/electron/gateway/channels/matrix-client.js +432 -0
  111. package/dist/electron/electron/gateway/channels/matrix.js +592 -0
  112. package/dist/electron/electron/gateway/channels/mattermost-client.js +394 -0
  113. package/dist/electron/electron/gateway/channels/mattermost.js +496 -0
  114. package/dist/electron/electron/gateway/channels/signal-client.js +500 -0
  115. package/dist/electron/electron/gateway/channels/signal.js +582 -0
  116. package/dist/electron/electron/gateway/channels/slack.js +415 -0
  117. package/dist/electron/electron/gateway/channels/teams.js +596 -0
  118. package/dist/electron/electron/gateway/channels/telegram.js +1390 -0
  119. package/dist/electron/electron/gateway/channels/twitch-client.js +502 -0
  120. package/dist/electron/electron/gateway/channels/twitch.js +396 -0
  121. package/dist/electron/electron/gateway/channels/types.js +8 -0
  122. package/dist/electron/electron/gateway/channels/whatsapp.js +953 -0
  123. package/dist/electron/electron/gateway/context-policy.js +268 -0
  124. package/dist/electron/electron/gateway/index.js +1063 -0
  125. package/dist/electron/electron/gateway/infrastructure.js +496 -0
  126. package/dist/electron/electron/gateway/router.js +2700 -0
  127. package/dist/electron/electron/gateway/security.js +375 -0
  128. package/dist/electron/electron/gateway/session.js +115 -0
  129. package/dist/electron/electron/gateway/tunnel.js +503 -0
  130. package/dist/electron/electron/guardrails/guardrail-manager.js +348 -0
  131. package/dist/electron/electron/hooks/gmail-watcher.js +300 -0
  132. package/dist/electron/electron/hooks/index.js +46 -0
  133. package/dist/electron/electron/hooks/mappings.js +381 -0
  134. package/dist/electron/electron/hooks/server.js +480 -0
  135. package/dist/electron/electron/hooks/settings.js +447 -0
  136. package/dist/electron/electron/hooks/types.js +41 -0
  137. package/dist/electron/electron/ipc/canvas-handlers.js +158 -0
  138. package/dist/electron/electron/ipc/handlers.js +3138 -0
  139. package/dist/electron/electron/ipc/mission-control-handlers.js +141 -0
  140. package/dist/electron/electron/main.js +448 -0
  141. package/dist/electron/electron/mcp/client/MCPClientManager.js +330 -0
  142. package/dist/electron/electron/mcp/client/MCPServerConnection.js +437 -0
  143. package/dist/electron/electron/mcp/client/transports/SSETransport.js +304 -0
  144. package/dist/electron/electron/mcp/client/transports/StdioTransport.js +307 -0
  145. package/dist/electron/electron/mcp/client/transports/WebSocketTransport.js +329 -0
  146. package/dist/electron/electron/mcp/host/MCPHostServer.js +354 -0
  147. package/dist/electron/electron/mcp/host/ToolAdapter.js +100 -0
  148. package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +497 -0
  149. package/dist/electron/electron/mcp/settings.js +446 -0
  150. package/dist/electron/electron/mcp/types.js +59 -0
  151. package/dist/electron/electron/memory/MemoryService.js +435 -0
  152. package/dist/electron/electron/notifications/index.js +17 -0
  153. package/dist/electron/electron/notifications/service.js +118 -0
  154. package/dist/electron/electron/notifications/store.js +144 -0
  155. package/dist/electron/electron/preload.js +842 -0
  156. package/dist/electron/electron/reports/StandupReportService.js +272 -0
  157. package/dist/electron/electron/security/concurrency.js +293 -0
  158. package/dist/electron/electron/security/index.js +15 -0
  159. package/dist/electron/electron/security/policy-manager.js +435 -0
  160. package/dist/electron/electron/settings/appearance-manager.js +193 -0
  161. package/dist/electron/electron/settings/personality-manager.js +724 -0
  162. package/dist/electron/electron/settings/x-manager.js +58 -0
  163. package/dist/electron/electron/tailscale/exposure.js +188 -0
  164. package/dist/electron/electron/tailscale/index.js +28 -0
  165. package/dist/electron/electron/tailscale/settings.js +205 -0
  166. package/dist/electron/electron/tailscale/tailscale.js +355 -0
  167. package/dist/electron/electron/tray/QuickInputWindow.js +568 -0
  168. package/dist/electron/electron/tray/TrayManager.js +895 -0
  169. package/dist/electron/electron/tray/index.js +9 -0
  170. package/dist/electron/electron/updater/index.js +6 -0
  171. package/dist/electron/electron/updater/update-manager.js +418 -0
  172. package/dist/electron/electron/utils/env-migration.js +209 -0
  173. package/dist/electron/electron/utils/process.js +102 -0
  174. package/dist/electron/electron/utils/rate-limiter.js +104 -0
  175. package/dist/electron/electron/utils/validation.js +419 -0
  176. package/dist/electron/electron/utils/x-cli.js +177 -0
  177. package/dist/electron/electron/voice/VoiceService.js +507 -0
  178. package/dist/electron/electron/voice/index.js +14 -0
  179. package/dist/electron/electron/voice/voice-settings-manager.js +359 -0
  180. package/dist/electron/shared/channelMessages.js +170 -0
  181. package/dist/electron/shared/types.js +1185 -0
  182. package/package.json +159 -0
  183. package/resources/skills/1password.json +10 -0
  184. package/resources/skills/add-documentation.json +31 -0
  185. package/resources/skills/analyze-csv.json +17 -0
  186. package/resources/skills/apple-notes.json +10 -0
  187. package/resources/skills/apple-reminders.json +10 -0
  188. package/resources/skills/auto-commenter.json +10 -0
  189. package/resources/skills/bear-notes.json +10 -0
  190. package/resources/skills/bird.json +35 -0
  191. package/resources/skills/blogwatcher.json +10 -0
  192. package/resources/skills/blucli.json +10 -0
  193. package/resources/skills/bluebubbles.json +10 -0
  194. package/resources/skills/camsnap.json +10 -0
  195. package/resources/skills/clean-imports.json +18 -0
  196. package/resources/skills/code-review.json +18 -0
  197. package/resources/skills/coding-agent.json +10 -0
  198. package/resources/skills/compare-files.json +23 -0
  199. package/resources/skills/convert-code.json +34 -0
  200. package/resources/skills/create-changelog.json +24 -0
  201. package/resources/skills/debug-error.json +17 -0
  202. package/resources/skills/dependency-check.json +10 -0
  203. package/resources/skills/discord.json +10 -0
  204. package/resources/skills/eightctl.json +10 -0
  205. package/resources/skills/explain-code.json +29 -0
  206. package/resources/skills/extract-todos.json +18 -0
  207. package/resources/skills/food-order.json +10 -0
  208. package/resources/skills/gemini.json +10 -0
  209. package/resources/skills/generate-readme.json +10 -0
  210. package/resources/skills/gifgrep.json +10 -0
  211. package/resources/skills/git-commit.json +10 -0
  212. package/resources/skills/github.json +10 -0
  213. package/resources/skills/gog.json +10 -0
  214. package/resources/skills/goplaces.json +10 -0
  215. package/resources/skills/himalaya.json +10 -0
  216. package/resources/skills/imsg.json +10 -0
  217. package/resources/skills/karpathy-guidelines.json +12 -0
  218. package/resources/skills/last30days.json +26 -0
  219. package/resources/skills/local-places.json +10 -0
  220. package/resources/skills/mcporter.json +10 -0
  221. package/resources/skills/model-usage.json +10 -0
  222. package/resources/skills/nano-banana-pro.json +10 -0
  223. package/resources/skills/nano-pdf.json +10 -0
  224. package/resources/skills/notion.json +10 -0
  225. package/resources/skills/obsidian.json +10 -0
  226. package/resources/skills/openai-image-gen.json +10 -0
  227. package/resources/skills/openai-whisper-api.json +10 -0
  228. package/resources/skills/openai-whisper.json +10 -0
  229. package/resources/skills/openhue.json +10 -0
  230. package/resources/skills/oracle.json +10 -0
  231. package/resources/skills/ordercli.json +10 -0
  232. package/resources/skills/peekaboo.json +10 -0
  233. package/resources/skills/project-structure.json +10 -0
  234. package/resources/skills/proofread.json +17 -0
  235. package/resources/skills/refactor-code.json +31 -0
  236. package/resources/skills/rename-symbol.json +23 -0
  237. package/resources/skills/sag.json +10 -0
  238. package/resources/skills/security-audit.json +18 -0
  239. package/resources/skills/session-logs.json +10 -0
  240. package/resources/skills/sherpa-onnx-tts.json +10 -0
  241. package/resources/skills/skill-creator.json +15 -0
  242. package/resources/skills/skill-hub.json +29 -0
  243. package/resources/skills/slack.json +10 -0
  244. package/resources/skills/songsee.json +10 -0
  245. package/resources/skills/sonoscli.json +10 -0
  246. package/resources/skills/spotify-player.json +10 -0
  247. package/resources/skills/startup-cfo.json +55 -0
  248. package/resources/skills/summarize-folder.json +18 -0
  249. package/resources/skills/summarize.json +10 -0
  250. package/resources/skills/things-mac.json +10 -0
  251. package/resources/skills/tmux.json +10 -0
  252. package/resources/skills/translate.json +36 -0
  253. package/resources/skills/trello.json +10 -0
  254. package/resources/skills/video-frames.json +10 -0
  255. package/resources/skills/voice-call.json +10 -0
  256. package/resources/skills/wacli.json +10 -0
  257. package/resources/skills/weather.json +10 -0
  258. package/resources/skills/write-tests.json +31 -0
  259. package/src/electron/activity/ActivityRepository.ts +238 -0
  260. package/src/electron/agent/browser/browser-service.ts +721 -0
  261. package/src/electron/agent/context-manager.ts +257 -0
  262. package/src/electron/agent/custom-skill-loader.ts +634 -0
  263. package/src/electron/agent/daemon.ts +1097 -0
  264. package/src/electron/agent/executor.ts +4017 -0
  265. package/src/electron/agent/llm/anthropic-provider.ts +175 -0
  266. package/src/electron/agent/llm/bedrock-provider.ts +236 -0
  267. package/src/electron/agent/llm/gemini-provider.ts +422 -0
  268. package/src/electron/agent/llm/index.ts +9 -0
  269. package/src/electron/agent/llm/ollama-provider.ts +347 -0
  270. package/src/electron/agent/llm/openai-oauth.ts +127 -0
  271. package/src/electron/agent/llm/openai-provider.ts +686 -0
  272. package/src/electron/agent/llm/openrouter-provider.ts +273 -0
  273. package/src/electron/agent/llm/pricing.ts +180 -0
  274. package/src/electron/agent/llm/provider-factory.ts +971 -0
  275. package/src/electron/agent/llm/types.ts +291 -0
  276. package/src/electron/agent/queue-manager.ts +408 -0
  277. package/src/electron/agent/sandbox/docker-sandbox.ts +453 -0
  278. package/src/electron/agent/sandbox/macos-sandbox.ts +426 -0
  279. package/src/electron/agent/sandbox/runner.ts +453 -0
  280. package/src/electron/agent/sandbox/sandbox-factory.ts +337 -0
  281. package/src/electron/agent/sandbox/security-utils.ts +251 -0
  282. package/src/electron/agent/search/brave-provider.ts +141 -0
  283. package/src/electron/agent/search/google-provider.ts +131 -0
  284. package/src/electron/agent/search/index.ts +6 -0
  285. package/src/electron/agent/search/provider-factory.ts +450 -0
  286. package/src/electron/agent/search/serpapi-provider.ts +138 -0
  287. package/src/electron/agent/search/tavily-provider.ts +108 -0
  288. package/src/electron/agent/search/types.ts +118 -0
  289. package/src/electron/agent/security/index.ts +20 -0
  290. package/src/electron/agent/security/input-sanitizer.ts +380 -0
  291. package/src/electron/agent/security/output-filter.ts +259 -0
  292. package/src/electron/agent/skill-eligibility.ts +334 -0
  293. package/src/electron/agent/skill-registry.ts +457 -0
  294. package/src/electron/agent/skills/document.ts +1070 -0
  295. package/src/electron/agent/skills/image-generator.ts +272 -0
  296. package/src/electron/agent/skills/organizer.ts +131 -0
  297. package/src/electron/agent/skills/presentation.ts +418 -0
  298. package/src/electron/agent/skills/spreadsheet.ts +166 -0
  299. package/src/electron/agent/tools/browser-tools.ts +546 -0
  300. package/src/electron/agent/tools/builtin-settings.ts +422 -0
  301. package/src/electron/agent/tools/canvas-tools.ts +572 -0
  302. package/src/electron/agent/tools/cron-tools.ts +723 -0
  303. package/src/electron/agent/tools/edit-tools.ts +196 -0
  304. package/src/electron/agent/tools/file-tools.ts +811 -0
  305. package/src/electron/agent/tools/glob-tools.ts +303 -0
  306. package/src/electron/agent/tools/grep-tools.ts +432 -0
  307. package/src/electron/agent/tools/image-tools.ts +126 -0
  308. package/src/electron/agent/tools/mention-tools.ts +371 -0
  309. package/src/electron/agent/tools/node-tools.ts +550 -0
  310. package/src/electron/agent/tools/registry.ts +3052 -0
  311. package/src/electron/agent/tools/search-tools.ts +111 -0
  312. package/src/electron/agent/tools/shell-tools.ts +651 -0
  313. package/src/electron/agent/tools/skill-tools.ts +340 -0
  314. package/src/electron/agent/tools/system-tools.ts +665 -0
  315. package/src/electron/agent/tools/web-fetch-tools.ts +528 -0
  316. package/src/electron/agent/tools/x-tools.ts +267 -0
  317. package/src/electron/agents/AgentRoleRepository.ts +557 -0
  318. package/src/electron/agents/HeartbeatService.ts +469 -0
  319. package/src/electron/agents/MentionRepository.ts +242 -0
  320. package/src/electron/agents/TaskSubscriptionRepository.ts +231 -0
  321. package/src/electron/agents/WorkingStateRepository.ts +278 -0
  322. package/src/electron/canvas/canvas-manager.ts +818 -0
  323. package/src/electron/canvas/canvas-preload.ts +102 -0
  324. package/src/electron/canvas/canvas-protocol.ts +174 -0
  325. package/src/electron/canvas/canvas-store.ts +200 -0
  326. package/src/electron/canvas/index.ts +8 -0
  327. package/src/electron/control-plane/client.ts +527 -0
  328. package/src/electron/control-plane/handlers.ts +723 -0
  329. package/src/electron/control-plane/index.ts +51 -0
  330. package/src/electron/control-plane/node-manager.ts +322 -0
  331. package/src/electron/control-plane/protocol.ts +269 -0
  332. package/src/electron/control-plane/remote-client.ts +517 -0
  333. package/src/electron/control-plane/server.ts +853 -0
  334. package/src/electron/control-plane/settings.ts +401 -0
  335. package/src/electron/control-plane/ssh-tunnel.ts +624 -0
  336. package/src/electron/cron/index.ts +9 -0
  337. package/src/electron/cron/schedule.ts +217 -0
  338. package/src/electron/cron/service.ts +743 -0
  339. package/src/electron/cron/store.ts +165 -0
  340. package/src/electron/cron/types.ts +291 -0
  341. package/src/electron/cron/webhook.ts +303 -0
  342. package/src/electron/database/SecureSettingsRepository.ts +514 -0
  343. package/src/electron/database/TaskLabelRepository.ts +148 -0
  344. package/src/electron/database/repositories.ts +2397 -0
  345. package/src/electron/database/schema.ts +1017 -0
  346. package/src/electron/extensions/index.ts +18 -0
  347. package/src/electron/extensions/loader.ts +336 -0
  348. package/src/electron/extensions/registry.ts +546 -0
  349. package/src/electron/extensions/types.ts +372 -0
  350. package/src/electron/gateway/channel-registry.ts +1267 -0
  351. package/src/electron/gateway/channels/bluebubbles-client.ts +641 -0
  352. package/src/electron/gateway/channels/bluebubbles.ts +509 -0
  353. package/src/electron/gateway/channels/discord.ts +1150 -0
  354. package/src/electron/gateway/channels/email-client.ts +708 -0
  355. package/src/electron/gateway/channels/email.ts +516 -0
  356. package/src/electron/gateway/channels/google-chat.ts +760 -0
  357. package/src/electron/gateway/channels/imessage-client.ts +473 -0
  358. package/src/electron/gateway/channels/imessage.ts +520 -0
  359. package/src/electron/gateway/channels/index.ts +21 -0
  360. package/src/electron/gateway/channels/line-client.ts +598 -0
  361. package/src/electron/gateway/channels/line.ts +559 -0
  362. package/src/electron/gateway/channels/matrix-client.ts +632 -0
  363. package/src/electron/gateway/channels/matrix.ts +655 -0
  364. package/src/electron/gateway/channels/mattermost-client.ts +526 -0
  365. package/src/electron/gateway/channels/mattermost.ts +550 -0
  366. package/src/electron/gateway/channels/signal-client.ts +722 -0
  367. package/src/electron/gateway/channels/signal.ts +666 -0
  368. package/src/electron/gateway/channels/slack.ts +458 -0
  369. package/src/electron/gateway/channels/teams.ts +681 -0
  370. package/src/electron/gateway/channels/telegram.ts +1727 -0
  371. package/src/electron/gateway/channels/twitch-client.ts +665 -0
  372. package/src/electron/gateway/channels/twitch.ts +468 -0
  373. package/src/electron/gateway/channels/types.ts +1002 -0
  374. package/src/electron/gateway/channels/whatsapp.ts +1101 -0
  375. package/src/electron/gateway/context-policy.ts +382 -0
  376. package/src/electron/gateway/index.ts +1274 -0
  377. package/src/electron/gateway/infrastructure.ts +645 -0
  378. package/src/electron/gateway/router.ts +3206 -0
  379. package/src/electron/gateway/security.ts +422 -0
  380. package/src/electron/gateway/session.ts +144 -0
  381. package/src/electron/gateway/tunnel.ts +626 -0
  382. package/src/electron/guardrails/guardrail-manager.ts +380 -0
  383. package/src/electron/hooks/gmail-watcher.ts +355 -0
  384. package/src/electron/hooks/index.ts +30 -0
  385. package/src/electron/hooks/mappings.ts +404 -0
  386. package/src/electron/hooks/server.ts +574 -0
  387. package/src/electron/hooks/settings.ts +466 -0
  388. package/src/electron/hooks/types.ts +245 -0
  389. package/src/electron/ipc/canvas-handlers.ts +223 -0
  390. package/src/electron/ipc/handlers.ts +3661 -0
  391. package/src/electron/ipc/mission-control-handlers.ts +182 -0
  392. package/src/electron/main.ts +496 -0
  393. package/src/electron/mcp/client/MCPClientManager.ts +406 -0
  394. package/src/electron/mcp/client/MCPServerConnection.ts +514 -0
  395. package/src/electron/mcp/client/transports/SSETransport.ts +360 -0
  396. package/src/electron/mcp/client/transports/StdioTransport.ts +355 -0
  397. package/src/electron/mcp/client/transports/WebSocketTransport.ts +384 -0
  398. package/src/electron/mcp/host/MCPHostServer.ts +388 -0
  399. package/src/electron/mcp/host/ToolAdapter.ts +140 -0
  400. package/src/electron/mcp/registry/MCPRegistryManager.ts +565 -0
  401. package/src/electron/mcp/settings.ts +468 -0
  402. package/src/electron/mcp/types.ts +371 -0
  403. package/src/electron/memory/MemoryService.ts +523 -0
  404. package/src/electron/notifications/index.ts +16 -0
  405. package/src/electron/notifications/service.ts +161 -0
  406. package/src/electron/notifications/store.ts +163 -0
  407. package/src/electron/preload.ts +2845 -0
  408. package/src/electron/reports/StandupReportService.ts +356 -0
  409. package/src/electron/security/concurrency.ts +333 -0
  410. package/src/electron/security/index.ts +17 -0
  411. package/src/electron/security/policy-manager.ts +539 -0
  412. package/src/electron/settings/appearance-manager.ts +182 -0
  413. package/src/electron/settings/personality-manager.ts +800 -0
  414. package/src/electron/settings/x-manager.ts +62 -0
  415. package/src/electron/tailscale/exposure.ts +262 -0
  416. package/src/electron/tailscale/index.ts +34 -0
  417. package/src/electron/tailscale/settings.ts +218 -0
  418. package/src/electron/tailscale/tailscale.ts +379 -0
  419. package/src/electron/tray/QuickInputWindow.ts +609 -0
  420. package/src/electron/tray/TrayManager.ts +1005 -0
  421. package/src/electron/tray/index.ts +6 -0
  422. package/src/electron/updater/index.ts +1 -0
  423. package/src/electron/updater/update-manager.ts +447 -0
  424. package/src/electron/utils/env-migration.ts +203 -0
  425. package/src/electron/utils/process.ts +124 -0
  426. package/src/electron/utils/rate-limiter.ts +130 -0
  427. package/src/electron/utils/validation.ts +493 -0
  428. package/src/electron/utils/x-cli.ts +198 -0
  429. package/src/electron/voice/VoiceService.ts +583 -0
  430. package/src/electron/voice/index.ts +9 -0
  431. package/src/electron/voice/voice-settings-manager.ts +403 -0
  432. package/src/renderer/App.tsx +775 -0
  433. package/src/renderer/components/ActivityFeed.tsx +407 -0
  434. package/src/renderer/components/ActivityFeedItem.tsx +285 -0
  435. package/src/renderer/components/AgentRoleCard.tsx +343 -0
  436. package/src/renderer/components/AgentRoleEditor.tsx +805 -0
  437. package/src/renderer/components/AgentSquadSettings.tsx +295 -0
  438. package/src/renderer/components/AgentWorkingStatePanel.tsx +411 -0
  439. package/src/renderer/components/AppearanceSettings.tsx +122 -0
  440. package/src/renderer/components/ApprovalDialog.tsx +100 -0
  441. package/src/renderer/components/BlueBubblesSettings.tsx +505 -0
  442. package/src/renderer/components/BuiltinToolsSettings.tsx +307 -0
  443. package/src/renderer/components/CanvasPreview.tsx +1189 -0
  444. package/src/renderer/components/CommandOutput.tsx +202 -0
  445. package/src/renderer/components/ContextPolicySettings.tsx +523 -0
  446. package/src/renderer/components/ControlPlaneSettings.tsx +1134 -0
  447. package/src/renderer/components/DisclaimerModal.tsx +124 -0
  448. package/src/renderer/components/DiscordSettings.tsx +436 -0
  449. package/src/renderer/components/EmailSettings.tsx +606 -0
  450. package/src/renderer/components/ExtensionsSettings.tsx +542 -0
  451. package/src/renderer/components/FileViewer.tsx +224 -0
  452. package/src/renderer/components/GoogleChatSettings.tsx +535 -0
  453. package/src/renderer/components/GuardrailSettings.tsx +487 -0
  454. package/src/renderer/components/HooksSettings.tsx +581 -0
  455. package/src/renderer/components/ImessageSettings.tsx +484 -0
  456. package/src/renderer/components/LineSettings.tsx +483 -0
  457. package/src/renderer/components/MCPRegistryBrowser.tsx +386 -0
  458. package/src/renderer/components/MCPSettings.tsx +943 -0
  459. package/src/renderer/components/MainContent.tsx +2433 -0
  460. package/src/renderer/components/MatrixSettings.tsx +510 -0
  461. package/src/renderer/components/MattermostSettings.tsx +473 -0
  462. package/src/renderer/components/MemorySettings.tsx +247 -0
  463. package/src/renderer/components/MentionBadge.tsx +87 -0
  464. package/src/renderer/components/MentionInput.tsx +409 -0
  465. package/src/renderer/components/MentionList.tsx +476 -0
  466. package/src/renderer/components/MissionControlPanel.tsx +1995 -0
  467. package/src/renderer/components/NodesSettings.tsx +316 -0
  468. package/src/renderer/components/NotificationPanel.tsx +481 -0
  469. package/src/renderer/components/Onboarding/AwakeningOrb.tsx +44 -0
  470. package/src/renderer/components/Onboarding/Onboarding.tsx +443 -0
  471. package/src/renderer/components/Onboarding/TypewriterText.tsx +102 -0
  472. package/src/renderer/components/Onboarding/index.ts +3 -0
  473. package/src/renderer/components/OnboardingModal.tsx +698 -0
  474. package/src/renderer/components/PairingCodeDisplay.tsx +324 -0
  475. package/src/renderer/components/PersonalitySettings.tsx +597 -0
  476. package/src/renderer/components/QueueSettings.tsx +119 -0
  477. package/src/renderer/components/QuickTaskFAB.tsx +71 -0
  478. package/src/renderer/components/RightPanel.tsx +413 -0
  479. package/src/renderer/components/ScheduledTasksSettings.tsx +1328 -0
  480. package/src/renderer/components/SearchSettings.tsx +328 -0
  481. package/src/renderer/components/Settings.tsx +1504 -0
  482. package/src/renderer/components/Sidebar.tsx +344 -0
  483. package/src/renderer/components/SignalSettings.tsx +673 -0
  484. package/src/renderer/components/SkillHubBrowser.tsx +458 -0
  485. package/src/renderer/components/SkillParameterModal.tsx +185 -0
  486. package/src/renderer/components/SkillsSettings.tsx +451 -0
  487. package/src/renderer/components/SlackSettings.tsx +442 -0
  488. package/src/renderer/components/StandupReportViewer.tsx +614 -0
  489. package/src/renderer/components/TaskBoard.tsx +498 -0
  490. package/src/renderer/components/TaskBoardCard.tsx +357 -0
  491. package/src/renderer/components/TaskBoardColumn.tsx +211 -0
  492. package/src/renderer/components/TaskLabelManager.tsx +472 -0
  493. package/src/renderer/components/TaskQueuePanel.tsx +144 -0
  494. package/src/renderer/components/TaskQuickActions.tsx +492 -0
  495. package/src/renderer/components/TaskTimeline.tsx +216 -0
  496. package/src/renderer/components/TaskView.tsx +162 -0
  497. package/src/renderer/components/TeamsSettings.tsx +518 -0
  498. package/src/renderer/components/TelegramSettings.tsx +421 -0
  499. package/src/renderer/components/Toast.tsx +76 -0
  500. package/src/renderer/components/TraySettings.tsx +189 -0
  501. package/src/renderer/components/TwitchSettings.tsx +511 -0
  502. package/src/renderer/components/UpdateSettings.tsx +295 -0
  503. package/src/renderer/components/VoiceIndicator.tsx +270 -0
  504. package/src/renderer/components/VoiceSettings.tsx +867 -0
  505. package/src/renderer/components/WhatsAppSettings.tsx +721 -0
  506. package/src/renderer/components/WorkingStateEditor.tsx +309 -0
  507. package/src/renderer/components/WorkingStateHistory.tsx +481 -0
  508. package/src/renderer/components/WorkspaceSelector.tsx +150 -0
  509. package/src/renderer/components/XSettings.tsx +311 -0
  510. package/src/renderer/global.d.ts +9 -0
  511. package/src/renderer/hooks/useAgentContext.ts +153 -0
  512. package/src/renderer/hooks/useOnboardingFlow.ts +548 -0
  513. package/src/renderer/hooks/useVoiceInput.ts +268 -0
  514. package/src/renderer/index.html +12 -0
  515. package/src/renderer/main.tsx +10 -0
  516. package/src/renderer/public/cowork-os-logo.png +0 -0
  517. package/src/renderer/quick-input.html +164 -0
  518. package/src/renderer/styles/index.css +14504 -0
  519. package/src/renderer/utils/agentMessages.ts +749 -0
  520. package/src/renderer/utils/voice-directives.ts +169 -0
  521. package/src/shared/channelMessages.ts +213 -0
  522. package/src/shared/types.ts +3608 -0
  523. package/tsconfig.electron.json +26 -0
  524. package/tsconfig.json +26 -0
  525. package/tsconfig.node.json +10 -0
  526. package/vite.config.ts +23 -0
@@ -0,0 +1,634 @@
1
+ /**
2
+ * Custom Skill Loader
3
+ *
4
+ * Loads skills from multiple sources with precedence:
5
+ * - Bundled skills (resources/skills/) - lowest precedence
6
+ * - Managed skills (~/Library/Application Support/cowork-os/skills/) - medium precedence
7
+ * - Workspace skills (workspace/skills/) - highest precedence
8
+ *
9
+ * Skills with the same ID from higher precedence sources override lower ones.
10
+ */
11
+
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+ import { app } from 'electron';
15
+ import {
16
+ CustomSkill,
17
+ SkillSource,
18
+ SkillStatusEntry,
19
+ SkillStatusReport,
20
+ SkillsConfig,
21
+ } from '../../shared/types';
22
+ import { SkillEligibilityChecker, getSkillEligibilityChecker } from './skill-eligibility';
23
+ import { getSkillRegistry } from './skill-registry';
24
+ import { InputSanitizer } from './security';
25
+
26
+ const SKILLS_FOLDER_NAME = 'skills';
27
+ const SKILL_FILE_EXTENSION = '.json';
28
+ const RELOAD_DEBOUNCE_MS = 100; // Debounce rapid reload calls
29
+
30
+ export interface SkillLoaderConfig {
31
+ bundledSkillsDir?: string;
32
+ managedSkillsDir?: string;
33
+ workspaceSkillsDir?: string;
34
+ skillsConfig?: SkillsConfig;
35
+ }
36
+
37
+ export class CustomSkillLoader {
38
+ private bundledSkillsDir: string;
39
+ private managedSkillsDir: string;
40
+ private workspaceSkillsDir: string | null = null;
41
+ private skills: Map<string, CustomSkill> = new Map();
42
+ private initialized: boolean = false;
43
+ private skillsConfig?: SkillsConfig;
44
+ private eligibilityChecker: SkillEligibilityChecker;
45
+
46
+ // Debounce state for reloadSkills
47
+ private reloadDebounceTimer: ReturnType<typeof setTimeout> | null = null;
48
+ private reloadPromise: Promise<CustomSkill[]> | null = null;
49
+ private isReloading: boolean = false;
50
+
51
+ constructor(config?: SkillLoaderConfig) {
52
+ // Bundled skills directory
53
+ const isDev = process.env.NODE_ENV === 'development';
54
+ if (config?.bundledSkillsDir) {
55
+ this.bundledSkillsDir = config.bundledSkillsDir;
56
+ } else if (isDev) {
57
+ this.bundledSkillsDir = path.join(process.cwd(), 'resources', SKILLS_FOLDER_NAME);
58
+ } else {
59
+ this.bundledSkillsDir = path.join(process.resourcesPath || '', SKILLS_FOLDER_NAME);
60
+ }
61
+
62
+ // Managed skills directory (from registry)
63
+ this.managedSkillsDir =
64
+ config?.managedSkillsDir ||
65
+ path.join(app.getPath('userData'), SKILLS_FOLDER_NAME);
66
+
67
+ // Workspace skills directory (set later when workspace is loaded)
68
+ this.workspaceSkillsDir = config?.workspaceSkillsDir || null;
69
+
70
+ // Skills config
71
+ this.skillsConfig = config?.skillsConfig;
72
+
73
+ // Initialize eligibility checker
74
+ this.eligibilityChecker = getSkillEligibilityChecker(this.skillsConfig);
75
+ }
76
+
77
+ /**
78
+ * Initialize the skill loader - loads all skills from all sources
79
+ */
80
+ async initialize(): Promise<void> {
81
+ if (this.initialized) return;
82
+
83
+ // Ensure managed skills directory exists
84
+ if (!fs.existsSync(this.managedSkillsDir)) {
85
+ fs.mkdirSync(this.managedSkillsDir, { recursive: true });
86
+ }
87
+
88
+ // Load all skills
89
+ await this.reloadSkills();
90
+
91
+ this.initialized = true;
92
+ console.log(`[CustomSkillLoader] Initialized with ${this.skills.size} skills`);
93
+ console.log(`[CustomSkillLoader] Bundled: ${this.bundledSkillsDir}`);
94
+ console.log(`[CustomSkillLoader] Managed: ${this.managedSkillsDir}`);
95
+ if (this.workspaceSkillsDir) {
96
+ console.log(`[CustomSkillLoader] Workspace: ${this.workspaceSkillsDir}`);
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Set the workspace skills directory
102
+ */
103
+ setWorkspaceSkillsDir(workspacePath: string): void {
104
+ this.workspaceSkillsDir = path.join(workspacePath, SKILLS_FOLDER_NAME);
105
+ }
106
+
107
+ /**
108
+ * Get directory paths
109
+ */
110
+ getBundledSkillsDir(): string {
111
+ return this.bundledSkillsDir;
112
+ }
113
+
114
+ getManagedSkillsDir(): string {
115
+ return this.managedSkillsDir;
116
+ }
117
+
118
+ getWorkspaceSkillsDir(): string | null {
119
+ return this.workspaceSkillsDir;
120
+ }
121
+
122
+ /**
123
+ * Get the skills directory path (for backward compatibility)
124
+ */
125
+ getSkillsDirectory(): string {
126
+ return this.bundledSkillsDir;
127
+ }
128
+
129
+ /**
130
+ * Load skills from a directory
131
+ */
132
+ private loadSkillsFromDir(dir: string, source: SkillSource): CustomSkill[] {
133
+ const skills: CustomSkill[] = [];
134
+
135
+ if (!fs.existsSync(dir)) {
136
+ return skills;
137
+ }
138
+
139
+ try {
140
+ const files = fs.readdirSync(dir);
141
+ const skillFiles = files.filter((f) => f.endsWith(SKILL_FILE_EXTENSION));
142
+
143
+ for (const file of skillFiles) {
144
+ try {
145
+ const filePath = path.join(dir, file);
146
+ const content = fs.readFileSync(filePath, 'utf-8');
147
+ const skill = JSON.parse(content) as CustomSkill;
148
+
149
+ // Add metadata
150
+ skill.filePath = filePath;
151
+ skill.source = source;
152
+
153
+ // Validate skill has required fields
154
+ if (this.validateSkill(skill)) {
155
+ skills.push(skill);
156
+ } else {
157
+ console.warn(`[CustomSkillLoader] Invalid skill file: ${file}`);
158
+ }
159
+ } catch (error) {
160
+ console.error(`[CustomSkillLoader] Failed to load skill file ${file}:`, error);
161
+ }
162
+ }
163
+ } catch (error) {
164
+ console.error(`[CustomSkillLoader] Failed to read directory ${dir}:`, error);
165
+ }
166
+
167
+ return skills;
168
+ }
169
+
170
+ /**
171
+ * Reload all skills from all sources
172
+ * Precedence: workspace > managed > bundled
173
+ * Uses debouncing to prevent rapid consecutive calls
174
+ */
175
+ async reloadSkills(): Promise<CustomSkill[]> {
176
+ // If already reloading, return the existing promise
177
+ if (this.isReloading && this.reloadPromise) {
178
+ return this.reloadPromise;
179
+ }
180
+
181
+ // Clear any pending debounce timer
182
+ if (this.reloadDebounceTimer) {
183
+ clearTimeout(this.reloadDebounceTimer);
184
+ this.reloadDebounceTimer = null;
185
+ }
186
+
187
+ // Create a debounced reload promise
188
+ this.reloadPromise = new Promise((resolve) => {
189
+ this.reloadDebounceTimer = setTimeout(async () => {
190
+ this.isReloading = true;
191
+ try {
192
+ const result = await this.doReloadSkills();
193
+ resolve(result);
194
+ } finally {
195
+ this.isReloading = false;
196
+ this.reloadPromise = null;
197
+ this.reloadDebounceTimer = null;
198
+ }
199
+ }, RELOAD_DEBOUNCE_MS);
200
+ });
201
+
202
+ return this.reloadPromise;
203
+ }
204
+
205
+ /**
206
+ * Internal method to actually reload skills
207
+ */
208
+ private async doReloadSkills(): Promise<CustomSkill[]> {
209
+ this.skills.clear();
210
+
211
+ // Load from all sources
212
+ const bundledSkills = this.loadSkillsFromDir(this.bundledSkillsDir, 'bundled');
213
+ const managedSkills = this.loadSkillsFromDir(this.managedSkillsDir, 'managed');
214
+ const workspaceSkills = this.workspaceSkillsDir
215
+ ? this.loadSkillsFromDir(this.workspaceSkillsDir, 'workspace')
216
+ : [];
217
+
218
+ // Merge with precedence: bundled < managed < workspace
219
+ for (const skill of bundledSkills) {
220
+ this.skills.set(skill.id, skill);
221
+ }
222
+ for (const skill of managedSkills) {
223
+ this.skills.set(skill.id, skill);
224
+ }
225
+ for (const skill of workspaceSkills) {
226
+ this.skills.set(skill.id, skill);
227
+ }
228
+
229
+ const counts = {
230
+ bundled: bundledSkills.length,
231
+ managed: managedSkills.length,
232
+ workspace: workspaceSkills.length,
233
+ total: this.skills.size,
234
+ };
235
+
236
+ console.log(
237
+ `[CustomSkillLoader] Loaded ${counts.total} skills ` +
238
+ `(bundled: ${counts.bundled}, managed: ${counts.managed}, workspace: ${counts.workspace})`
239
+ );
240
+
241
+ return this.listSkills();
242
+ }
243
+
244
+ /**
245
+ * Validate a skill has all required fields
246
+ */
247
+ private validateSkill(skill: CustomSkill): boolean {
248
+ return !!(
249
+ skill.id &&
250
+ skill.name &&
251
+ skill.description &&
252
+ skill.prompt &&
253
+ typeof skill.id === 'string' &&
254
+ typeof skill.name === 'string' &&
255
+ typeof skill.description === 'string' &&
256
+ typeof skill.prompt === 'string'
257
+ );
258
+ }
259
+
260
+ /**
261
+ * List all loaded skills
262
+ */
263
+ listSkills(): CustomSkill[] {
264
+ return Array.from(this.skills.values()).sort((a, b) => {
265
+ // Sort by priority first (lower = higher priority, default 100)
266
+ const priorityA = a.priority ?? 100;
267
+ const priorityB = b.priority ?? 100;
268
+ if (priorityA !== priorityB) {
269
+ return priorityA - priorityB;
270
+ }
271
+ // Then by category
272
+ if (a.category && b.category && a.category !== b.category) {
273
+ return a.category.localeCompare(b.category);
274
+ }
275
+ // Finally by name
276
+ return a.name.localeCompare(b.name);
277
+ });
278
+ }
279
+
280
+ /**
281
+ * List skills by source
282
+ */
283
+ listSkillsBySource(source: SkillSource): CustomSkill[] {
284
+ return this.listSkills().filter((skill) => skill.source === source);
285
+ }
286
+
287
+ /**
288
+ * List only task skills (excludes guideline skills)
289
+ * Used for the skill dropdown in UI
290
+ */
291
+ listTaskSkills(): CustomSkill[] {
292
+ return this.listSkills().filter((skill) => skill.type !== 'guideline');
293
+ }
294
+
295
+ /**
296
+ * List only guideline skills
297
+ */
298
+ listGuidelineSkills(): CustomSkill[] {
299
+ return this.listSkills().filter((skill) => skill.type === 'guideline');
300
+ }
301
+
302
+ /**
303
+ * Get enabled guideline skills for system prompt injection
304
+ * Returns the combined prompt content of all enabled guideline skills
305
+ * Guidelines are validated and sanitized to prevent injection attacks
306
+ */
307
+ getEnabledGuidelinesPrompt(): string {
308
+ const enabledGuidelines = this.listGuidelineSkills().filter(
309
+ (skill) => skill.enabled !== false
310
+ );
311
+ if (enabledGuidelines.length === 0) {
312
+ return '';
313
+ }
314
+ // Validate and sanitize each guideline before injection
315
+ return enabledGuidelines.map((skill) => {
316
+ const validation = InputSanitizer.validateSkillGuidelines(skill.prompt);
317
+ if (!validation.valid) {
318
+ console.warn(
319
+ `[CustomSkillLoader] Security: Skill "${skill.id}" guidelines contain suspicious patterns:`,
320
+ validation.issues
321
+ );
322
+ return validation.sanitized;
323
+ }
324
+ return skill.prompt;
325
+ }).join('\n\n');
326
+ }
327
+
328
+ /**
329
+ * List skills that can be automatically invoked by the model
330
+ * Excludes guidelines and skills with disableModelInvocation set
331
+ */
332
+ listModelInvocableSkills(): CustomSkill[] {
333
+ return this.listSkills().filter((skill) => {
334
+ // Exclude guideline skills
335
+ if (skill.type === 'guideline') return false;
336
+ // Exclude disabled skills
337
+ if (skill.enabled === false) return false;
338
+ // Exclude skills that explicitly disable model invocation
339
+ if (skill.invocation?.disableModelInvocation === true) return false;
340
+ return true;
341
+ });
342
+ }
343
+
344
+ /**
345
+ * Get formatted skill descriptions for the model's system prompt
346
+ * Groups skills by category and includes parameter info
347
+ */
348
+ getSkillDescriptionsForModel(): string {
349
+ const skills = this.listModelInvocableSkills();
350
+ if (skills.length === 0) {
351
+ return '';
352
+ }
353
+
354
+ // Group skills by category
355
+ const byCategory: Record<string, CustomSkill[]> = {};
356
+ for (const skill of skills) {
357
+ const category = skill.category || 'General';
358
+ if (!byCategory[category]) {
359
+ byCategory[category] = [];
360
+ }
361
+ byCategory[category].push(skill);
362
+ }
363
+
364
+ // Format descriptions
365
+ const lines: string[] = [];
366
+ for (const [category, categorySkills] of Object.entries(byCategory).sort()) {
367
+ lines.push(`\n${category}:`);
368
+ for (const skill of categorySkills) {
369
+ const paramInfo = skill.parameters?.length
370
+ ? ` (params: ${skill.parameters.map(p => p.name + (p.required ? '*' : '')).join(', ')})`
371
+ : '';
372
+ lines.push(`- ${skill.id}: ${skill.description}${paramInfo}`);
373
+ }
374
+ }
375
+
376
+ return lines.join('\n');
377
+ }
378
+
379
+ /**
380
+ * Get a specific skill by ID
381
+ */
382
+ getSkill(id: string): CustomSkill | undefined {
383
+ return this.skills.get(id);
384
+ }
385
+
386
+ /**
387
+ * Expand a skill's prompt template with parameter values
388
+ */
389
+ expandPrompt(
390
+ skill: CustomSkill,
391
+ parameterValues: Record<string, string | number | boolean>
392
+ ): string {
393
+ let prompt = skill.prompt;
394
+
395
+ // Replace {{param}} placeholders with values
396
+ if (skill.parameters) {
397
+ for (const param of skill.parameters) {
398
+ const value = parameterValues[param.name] ?? param.default ?? '';
399
+ const placeholder = new RegExp(`\\{\\{${param.name}\\}\\}`, 'g');
400
+ prompt = prompt.replace(placeholder, String(value));
401
+ }
402
+ }
403
+
404
+ // Remove any remaining unreplaced placeholders
405
+ prompt = prompt.replace(/\{\{[^}]+\}\}/g, '');
406
+
407
+ return prompt.trim();
408
+ }
409
+
410
+ /**
411
+ * Get eligible skills (those that meet all requirements)
412
+ */
413
+ async getEligibleSkills(): Promise<CustomSkill[]> {
414
+ const statusEntries = await this.getSkillStatus();
415
+ return statusEntries.skills
416
+ .filter((entry) => entry.eligible)
417
+ .map((entry) => this.getSkill(entry.id)!)
418
+ .filter(Boolean);
419
+ }
420
+
421
+ /**
422
+ * Get skill status with eligibility information
423
+ */
424
+ async getSkillStatus(): Promise<SkillStatusReport> {
425
+ const skills = this.listSkills();
426
+ const statusEntries = await this.eligibilityChecker.buildStatusEntries(skills);
427
+
428
+ const summary = {
429
+ total: statusEntries.length,
430
+ eligible: statusEntries.filter((s) => s.eligible).length,
431
+ disabled: statusEntries.filter((s) => s.disabled).length,
432
+ missingRequirements: statusEntries.filter(
433
+ (s) => !s.eligible && !s.disabled && !s.blockedByAllowlist
434
+ ).length,
435
+ };
436
+
437
+ return {
438
+ workspaceDir: this.workspaceSkillsDir || '',
439
+ managedSkillsDir: this.managedSkillsDir,
440
+ bundledSkillsDir: this.bundledSkillsDir,
441
+ skills: statusEntries,
442
+ summary,
443
+ };
444
+ }
445
+
446
+ /**
447
+ * Get status for a single skill
448
+ */
449
+ async getSkillStatusEntry(skillId: string): Promise<SkillStatusEntry | null> {
450
+ const skill = this.getSkill(skillId);
451
+ if (!skill) return null;
452
+
453
+ return this.eligibilityChecker.buildStatusEntry(skill);
454
+ }
455
+
456
+ /**
457
+ * Update skills config
458
+ */
459
+ updateConfig(config: SkillsConfig): void {
460
+ this.skillsConfig = config;
461
+ this.eligibilityChecker.updateConfig(config);
462
+ }
463
+
464
+ /**
465
+ * Clear eligibility cache (useful after installing dependencies)
466
+ */
467
+ clearEligibilityCache(): void {
468
+ this.eligibilityChecker.clearCache();
469
+ }
470
+
471
+ /**
472
+ * Create a skill in the workspace directory
473
+ */
474
+ async createWorkspaceSkill(skill: Omit<CustomSkill, 'filePath' | 'source'>): Promise<CustomSkill> {
475
+ if (!this.workspaceSkillsDir) {
476
+ throw new Error('Workspace skills directory not set');
477
+ }
478
+
479
+ // Ensure workspace skills directory exists
480
+ if (!fs.existsSync(this.workspaceSkillsDir)) {
481
+ fs.mkdirSync(this.workspaceSkillsDir, { recursive: true });
482
+ }
483
+
484
+ const filePath = path.join(this.workspaceSkillsDir, `${skill.id}.json`);
485
+ const fullSkill: CustomSkill = {
486
+ ...skill,
487
+ source: 'workspace',
488
+ filePath,
489
+ };
490
+
491
+ fs.writeFileSync(filePath, JSON.stringify(fullSkill, null, 2), 'utf-8');
492
+
493
+ // Reload skills to pick up the new one
494
+ await this.reloadSkills();
495
+
496
+ return fullSkill;
497
+ }
498
+
499
+ /**
500
+ * Update a skill
501
+ */
502
+ async updateSkill(
503
+ skillId: string,
504
+ updates: Partial<Omit<CustomSkill, 'id' | 'filePath' | 'source'>>
505
+ ): Promise<CustomSkill | null> {
506
+ const skill = this.getSkill(skillId);
507
+ if (!skill || !skill.filePath) {
508
+ return null;
509
+ }
510
+
511
+ // Only allow updating workspace and managed skills
512
+ if (skill.source === 'bundled') {
513
+ throw new Error('Cannot update bundled skills');
514
+ }
515
+
516
+ const updatedSkill: CustomSkill = {
517
+ ...skill,
518
+ ...updates,
519
+ };
520
+
521
+ fs.writeFileSync(skill.filePath, JSON.stringify(updatedSkill, null, 2), 'utf-8');
522
+
523
+ // Reload skills to pick up the update
524
+ await this.reloadSkills();
525
+
526
+ return updatedSkill;
527
+ }
528
+
529
+ /**
530
+ * Delete a workspace skill
531
+ */
532
+ async deleteWorkspaceSkill(skillId: string): Promise<boolean> {
533
+ const skill = this.getSkill(skillId);
534
+ if (!skill || !skill.filePath || skill.source !== 'workspace') {
535
+ return false;
536
+ }
537
+
538
+ try {
539
+ fs.unlinkSync(skill.filePath);
540
+ await this.reloadSkills();
541
+ return true;
542
+ } catch {
543
+ return false;
544
+ }
545
+ }
546
+
547
+ /**
548
+ * Delete a managed skill (from registry)
549
+ */
550
+ async deleteManagedSkill(skillId: string): Promise<boolean> {
551
+ const skill = this.getSkill(skillId);
552
+ if (!skill || !skill.filePath || skill.source !== 'managed') {
553
+ return false;
554
+ }
555
+
556
+ try {
557
+ fs.unlinkSync(skill.filePath);
558
+ await this.reloadSkills();
559
+ return true;
560
+ } catch {
561
+ return false;
562
+ }
563
+ }
564
+
565
+ /**
566
+ * Open the managed skills folder in the system file browser
567
+ */
568
+ async openSkillsFolder(): Promise<void> {
569
+ const { shell } = await import('electron');
570
+
571
+ // Ensure directory exists
572
+ if (!fs.existsSync(this.managedSkillsDir)) {
573
+ fs.mkdirSync(this.managedSkillsDir, { recursive: true });
574
+ }
575
+
576
+ await shell.openPath(this.managedSkillsDir);
577
+ }
578
+
579
+ // === Backward compatibility aliases ===
580
+
581
+ /**
582
+ * Create a skill (alias for createWorkspaceSkill)
583
+ * @deprecated Use createWorkspaceSkill instead
584
+ */
585
+ async createSkill(skill: Omit<CustomSkill, 'filePath' | 'source'>): Promise<CustomSkill> {
586
+ // For backward compatibility, if no workspace is set, create in managed dir
587
+ if (!this.workspaceSkillsDir) {
588
+ const filePath = path.join(this.managedSkillsDir, `${skill.id}.json`);
589
+ const fullSkill: CustomSkill = {
590
+ ...skill,
591
+ source: 'managed',
592
+ filePath,
593
+ };
594
+ fs.writeFileSync(filePath, JSON.stringify(fullSkill, null, 2), 'utf-8');
595
+ await this.reloadSkills();
596
+ return fullSkill;
597
+ }
598
+ return this.createWorkspaceSkill(skill);
599
+ }
600
+
601
+ /**
602
+ * Delete a skill (checks both workspace and managed)
603
+ * @deprecated Use deleteWorkspaceSkill or deleteManagedSkill instead
604
+ */
605
+ async deleteSkill(skillId: string): Promise<boolean> {
606
+ const skill = this.getSkill(skillId);
607
+ if (!skill) return false;
608
+
609
+ if (skill.source === 'workspace') {
610
+ return this.deleteWorkspaceSkill(skillId);
611
+ }
612
+ if (skill.source === 'managed') {
613
+ return this.deleteManagedSkill(skillId);
614
+ }
615
+ return false;
616
+ }
617
+ }
618
+
619
+ // Singleton instance
620
+ let instance: CustomSkillLoader | null = null;
621
+
622
+ export function getCustomSkillLoader(config?: SkillLoaderConfig): CustomSkillLoader {
623
+ if (!instance) {
624
+ instance = new CustomSkillLoader(config);
625
+ }
626
+ return instance;
627
+ }
628
+
629
+ /**
630
+ * Reset the singleton instance (useful for testing)
631
+ */
632
+ export function resetCustomSkillLoader(): void {
633
+ instance = null;
634
+ }