cowork-os 0.3.21 → 0.3.25

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 (252) hide show
  1. package/README.md +372 -10
  2. package/connectors/README.md +20 -0
  3. package/connectors/asana-mcp/README.md +24 -0
  4. package/connectors/asana-mcp/dist/index.js +427 -0
  5. package/connectors/asana-mcp/package.json +15 -0
  6. package/connectors/asana-mcp/src/index.ts +553 -0
  7. package/connectors/asana-mcp/tsconfig.json +13 -0
  8. package/connectors/hubspot-mcp/README.md +35 -0
  9. package/connectors/hubspot-mcp/dist/index.js +454 -0
  10. package/connectors/hubspot-mcp/package.json +15 -0
  11. package/connectors/hubspot-mcp/src/index.ts +562 -0
  12. package/connectors/hubspot-mcp/tsconfig.json +13 -0
  13. package/connectors/jira-mcp/README.md +49 -0
  14. package/connectors/jira-mcp/dist/index.js +588 -0
  15. package/connectors/jira-mcp/package.json +15 -0
  16. package/connectors/jira-mcp/src/index.ts +711 -0
  17. package/connectors/jira-mcp/tsconfig.json +13 -0
  18. package/connectors/linear-mcp/README.md +22 -0
  19. package/connectors/linear-mcp/dist/index.js +402 -0
  20. package/connectors/linear-mcp/package.json +15 -0
  21. package/connectors/linear-mcp/src/index.ts +522 -0
  22. package/connectors/linear-mcp/tsconfig.json +13 -0
  23. package/connectors/okta-mcp/README.md +24 -0
  24. package/connectors/okta-mcp/dist/index.js +411 -0
  25. package/connectors/okta-mcp/package.json +15 -0
  26. package/connectors/okta-mcp/src/index.ts +520 -0
  27. package/connectors/okta-mcp/tsconfig.json +13 -0
  28. package/connectors/salesforce-mcp/README.md +47 -0
  29. package/connectors/salesforce-mcp/dist/index.js +584 -0
  30. package/connectors/salesforce-mcp/package.json +15 -0
  31. package/connectors/salesforce-mcp/src/index.ts +722 -0
  32. package/connectors/salesforce-mcp/tsconfig.json +13 -0
  33. package/connectors/servicenow-mcp/README.md +26 -0
  34. package/connectors/servicenow-mcp/dist/index.js +400 -0
  35. package/connectors/servicenow-mcp/package.json +15 -0
  36. package/connectors/servicenow-mcp/src/index.ts +500 -0
  37. package/connectors/servicenow-mcp/tsconfig.json +13 -0
  38. package/connectors/templates/mcp-connector/README.md +31 -0
  39. package/connectors/templates/mcp-connector/package.json +15 -0
  40. package/connectors/templates/mcp-connector/src/index.ts +330 -0
  41. package/connectors/templates/mcp-connector/tsconfig.json +13 -0
  42. package/connectors/zendesk-mcp/README.md +40 -0
  43. package/connectors/zendesk-mcp/dist/index.js +431 -0
  44. package/connectors/zendesk-mcp/package.json +15 -0
  45. package/connectors/zendesk-mcp/src/index.ts +543 -0
  46. package/connectors/zendesk-mcp/tsconfig.json +13 -0
  47. package/dist/electron/electron/agent/custom-skill-loader.js +31 -1
  48. package/dist/electron/electron/agent/daemon.js +189 -13
  49. package/dist/electron/electron/agent/executor.js +895 -78
  50. package/dist/electron/electron/agent/llm/anthropic-compatible-provider.js +177 -0
  51. package/dist/electron/electron/agent/llm/azure-openai-provider.js +328 -0
  52. package/dist/electron/electron/agent/llm/bedrock-provider.js +49 -9
  53. package/dist/electron/electron/agent/llm/github-copilot-provider.js +97 -0
  54. package/dist/electron/electron/agent/llm/groq-provider.js +33 -0
  55. package/dist/electron/electron/agent/llm/index.js +13 -1
  56. package/dist/electron/electron/agent/llm/kimi-provider.js +33 -0
  57. package/dist/electron/electron/agent/llm/openai-compatible-provider.js +116 -0
  58. package/dist/electron/electron/agent/llm/openai-compatible.js +111 -0
  59. package/dist/electron/electron/agent/llm/openai-oauth.js +2 -1
  60. package/dist/electron/electron/agent/llm/openrouter-provider.js +1 -1
  61. package/dist/electron/electron/agent/llm/provider-factory.js +350 -4
  62. package/dist/electron/electron/agent/llm/types.js +66 -1
  63. package/dist/electron/electron/agent/llm/xai-provider.js +33 -0
  64. package/dist/electron/electron/agent/search/provider-factory.js +38 -2
  65. package/dist/electron/electron/agent/tools/box-tools.js +231 -0
  66. package/dist/electron/electron/agent/tools/builtin-settings.js +28 -0
  67. package/dist/electron/electron/agent/tools/dropbox-tools.js +237 -0
  68. package/dist/electron/electron/agent/tools/file-tools.js +66 -3
  69. package/dist/electron/electron/agent/tools/google-drive-tools.js +227 -0
  70. package/dist/electron/electron/agent/tools/grep-tools.js +90 -10
  71. package/dist/electron/electron/agent/tools/image-tools.js +11 -1
  72. package/dist/electron/electron/agent/tools/notion-tools.js +312 -0
  73. package/dist/electron/electron/agent/tools/onedrive-tools.js +217 -0
  74. package/dist/electron/electron/agent/tools/registry.js +548 -10
  75. package/dist/electron/electron/agent/tools/search-tools.js +28 -10
  76. package/dist/electron/electron/agent/tools/sharepoint-tools.js +243 -0
  77. package/dist/electron/electron/agent/tools/shell-tools.js +12 -3
  78. package/dist/electron/electron/agent/tools/x-tools.js +1 -1
  79. package/dist/electron/electron/agents/agent-dispatch.js +63 -0
  80. package/dist/electron/electron/database/repositories.js +19 -5
  81. package/dist/electron/electron/database/schema.js +8 -0
  82. package/dist/electron/electron/gateway/channels/whatsapp.js +55 -0
  83. package/dist/electron/electron/gateway/index.js +75 -1
  84. package/dist/electron/electron/gateway/router.js +209 -154
  85. package/dist/electron/electron/ipc/canvas-handlers.js +5 -0
  86. package/dist/electron/electron/ipc/handlers.js +763 -267
  87. package/dist/electron/electron/main.js +63 -0
  88. package/dist/electron/electron/mcp/oauth/connector-oauth.js +333 -0
  89. package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +503 -154
  90. package/dist/electron/electron/memory/MemoryService.js +2 -1
  91. package/dist/electron/electron/preload.js +78 -1
  92. package/dist/electron/electron/settings/appearance-manager.js +18 -1
  93. package/dist/electron/electron/settings/box-manager.js +54 -0
  94. package/dist/electron/electron/settings/dropbox-manager.js +54 -0
  95. package/dist/electron/electron/settings/google-drive-manager.js +54 -0
  96. package/dist/electron/electron/settings/notion-manager.js +56 -0
  97. package/dist/electron/electron/settings/onedrive-manager.js +54 -0
  98. package/dist/electron/electron/settings/sharepoint-manager.js +54 -0
  99. package/dist/electron/electron/utils/box-api.js +153 -0
  100. package/dist/electron/electron/utils/dropbox-api.js +144 -0
  101. package/dist/electron/electron/utils/env-migration.js +19 -0
  102. package/dist/electron/electron/utils/google-drive-api.js +152 -0
  103. package/dist/electron/electron/utils/notion-api.js +103 -0
  104. package/dist/electron/electron/utils/onedrive-api.js +113 -0
  105. package/dist/electron/electron/utils/sharepoint-api.js +109 -0
  106. package/dist/electron/electron/utils/validation.js +98 -3
  107. package/dist/electron/electron/utils/x-cli.js +1 -1
  108. package/dist/electron/shared/channelMessages.js +284 -3
  109. package/dist/electron/shared/llm-provider-catalog.js +198 -0
  110. package/dist/electron/shared/types.js +90 -1
  111. package/package.json +14 -3
  112. package/resources/skills/nano-banana-pro.json +4 -4
  113. package/resources/skills/openai-image-gen.json +3 -3
  114. package/resources/skills/scripts/gen.py +163 -0
  115. package/resources/skills/scripts/generate_image.py +91 -0
  116. package/src/electron/agent/custom-skill-loader.ts +34 -1
  117. package/src/electron/agent/daemon.ts +210 -14
  118. package/src/electron/agent/executor.ts +1124 -85
  119. package/src/electron/agent/llm/anthropic-compatible-provider.ts +214 -0
  120. package/src/electron/agent/llm/azure-openai-provider.ts +388 -0
  121. package/src/electron/agent/llm/bedrock-provider.ts +62 -9
  122. package/src/electron/agent/llm/github-copilot-provider.ts +117 -0
  123. package/src/electron/agent/llm/groq-provider.ts +39 -0
  124. package/src/electron/agent/llm/index.ts +6 -0
  125. package/src/electron/agent/llm/kimi-provider.ts +39 -0
  126. package/src/electron/agent/llm/openai-compatible-provider.ts +153 -0
  127. package/src/electron/agent/llm/openai-compatible.ts +133 -0
  128. package/src/electron/agent/llm/openai-oauth.ts +2 -1
  129. package/src/electron/agent/llm/openrouter-provider.ts +2 -1
  130. package/src/electron/agent/llm/provider-factory.ts +459 -6
  131. package/src/electron/agent/llm/types.ts +95 -1
  132. package/src/electron/agent/llm/xai-provider.ts +39 -0
  133. package/src/electron/agent/search/provider-factory.ts +43 -2
  134. package/src/electron/agent/tools/box-tools.ts +239 -0
  135. package/src/electron/agent/tools/builtin-settings.ts +36 -0
  136. package/src/electron/agent/tools/dropbox-tools.ts +237 -0
  137. package/src/electron/agent/tools/file-tools.ts +66 -3
  138. package/src/electron/agent/tools/gmail-tools.ts +240 -0
  139. package/src/electron/agent/tools/google-calendar-tools.ts +258 -0
  140. package/src/electron/agent/tools/google-drive-tools.ts +228 -0
  141. package/src/electron/agent/tools/grep-tools.ts +97 -12
  142. package/src/electron/agent/tools/image-tools.ts +11 -1
  143. package/src/electron/agent/tools/notion-tools.ts +330 -0
  144. package/src/electron/agent/tools/onedrive-tools.ts +217 -0
  145. package/src/electron/agent/tools/registry.ts +794 -10
  146. package/src/electron/agent/tools/search-tools.ts +29 -11
  147. package/src/electron/agent/tools/sharepoint-tools.ts +247 -0
  148. package/src/electron/agent/tools/shell-tools.ts +11 -3
  149. package/src/electron/agent/tools/x-tools.ts +1 -1
  150. package/src/electron/agents/agent-dispatch.ts +79 -0
  151. package/src/electron/database/SecureSettingsRepository.ts +7 -1
  152. package/src/electron/database/repositories.ts +58 -6
  153. package/src/electron/database/schema.ts +8 -0
  154. package/src/electron/gateway/channels/discord.ts +4 -0
  155. package/src/electron/gateway/channels/google-chat.ts +3 -0
  156. package/src/electron/gateway/channels/line.ts +3 -0
  157. package/src/electron/gateway/channels/matrix-client.ts +15 -0
  158. package/src/electron/gateway/channels/matrix.ts +31 -0
  159. package/src/electron/gateway/channels/mattermost.ts +3 -0
  160. package/src/electron/gateway/channels/signal.ts +3 -0
  161. package/src/electron/gateway/channels/slack.ts +9 -4
  162. package/src/electron/gateway/channels/teams.ts +4 -0
  163. package/src/electron/gateway/channels/telegram.ts +2 -0
  164. package/src/electron/gateway/channels/twitch.ts +2 -0
  165. package/src/electron/gateway/channels/types.ts +8 -0
  166. package/src/electron/gateway/channels/whatsapp.ts +66 -0
  167. package/src/electron/gateway/index.ts +95 -2
  168. package/src/electron/gateway/router.ts +231 -161
  169. package/src/electron/gateway/security.ts +21 -9
  170. package/src/electron/ipc/canvas-handlers.ts +10 -0
  171. package/src/electron/ipc/handlers.ts +848 -292
  172. package/src/electron/main.ts +35 -0
  173. package/src/electron/mcp/oauth/connector-oauth.ts +448 -0
  174. package/src/electron/mcp/registry/MCPRegistryManager.ts +343 -12
  175. package/src/electron/memory/MemoryService.ts +7 -1
  176. package/src/electron/preload.ts +200 -5
  177. package/src/electron/settings/appearance-manager.ts +20 -2
  178. package/src/electron/settings/box-manager.ts +58 -0
  179. package/src/electron/settings/dropbox-manager.ts +58 -0
  180. package/src/electron/settings/google-workspace-manager.ts +59 -0
  181. package/src/electron/settings/notion-manager.ts +60 -0
  182. package/src/electron/settings/onedrive-manager.ts +58 -0
  183. package/src/electron/settings/sharepoint-manager.ts +58 -0
  184. package/src/electron/utils/box-api.ts +184 -0
  185. package/src/electron/utils/dropbox-api.ts +171 -0
  186. package/src/electron/utils/env-migration.ts +22 -0
  187. package/src/electron/utils/gmail-api.ts +121 -0
  188. package/src/electron/utils/google-calendar-api.ts +115 -0
  189. package/src/electron/utils/google-workspace-api.ts +228 -0
  190. package/src/electron/utils/google-workspace-auth.ts +109 -0
  191. package/src/electron/utils/google-workspace-oauth.ts +232 -0
  192. package/src/electron/utils/notion-api.ts +126 -0
  193. package/src/electron/utils/onedrive-api.ts +137 -0
  194. package/src/electron/utils/sharepoint-api.ts +132 -0
  195. package/src/electron/utils/validation.ts +128 -1
  196. package/src/electron/utils/x-cli.ts +1 -1
  197. package/src/renderer/App.tsx +119 -8
  198. package/src/renderer/components/ActivityFeedItem.tsx +34 -17
  199. package/src/renderer/components/AgentWorkingStatePanel.tsx +7 -5
  200. package/src/renderer/components/AppearanceSettings.tsx +37 -2
  201. package/src/renderer/components/BlueBubblesSettings.tsx +18 -7
  202. package/src/renderer/components/BoxSettings.tsx +203 -0
  203. package/src/renderer/components/BrowserView.tsx +101 -0
  204. package/src/renderer/components/BuiltinToolsSettings.tsx +105 -0
  205. package/src/renderer/components/CanvasPreview.tsx +68 -1
  206. package/src/renderer/components/ConnectorEnvModal.tsx +116 -0
  207. package/src/renderer/components/ConnectorSetupModal.tsx +566 -0
  208. package/src/renderer/components/ConnectorsSettings.tsx +397 -0
  209. package/src/renderer/components/ControlPlaneSettings.tsx +2 -0
  210. package/src/renderer/components/DiscordSettings.tsx +18 -7
  211. package/src/renderer/components/DropboxSettings.tsx +202 -0
  212. package/src/renderer/components/EmailSettings.tsx +18 -7
  213. package/src/renderer/components/FileViewer.tsx +21 -13
  214. package/src/renderer/components/GoogleChatSettings.tsx +17 -7
  215. package/src/renderer/components/GoogleWorkspaceSettings.tsx +332 -0
  216. package/src/renderer/components/ImessageSettings.tsx +22 -11
  217. package/src/renderer/components/LineIcons.tsx +376 -0
  218. package/src/renderer/components/LineSettings.tsx +18 -7
  219. package/src/renderer/components/MCPSettings.tsx +56 -0
  220. package/src/renderer/components/MainContent.tsx +740 -76
  221. package/src/renderer/components/MatrixSettings.tsx +18 -7
  222. package/src/renderer/components/MattermostSettings.tsx +18 -7
  223. package/src/renderer/components/NodesSettings.tsx +58 -99
  224. package/src/renderer/components/NotificationPanel.tsx +25 -11
  225. package/src/renderer/components/NotionSettings.tsx +231 -0
  226. package/src/renderer/components/Onboarding/Onboarding.tsx +13 -1
  227. package/src/renderer/components/OnboardingModal.tsx +70 -1
  228. package/src/renderer/components/OneDriveSettings.tsx +212 -0
  229. package/src/renderer/components/RightPanel.tsx +141 -28
  230. package/src/renderer/components/ScheduledTasksSettings.tsx +10 -62
  231. package/src/renderer/components/SearchSettings.tsx +118 -114
  232. package/src/renderer/components/Settings.tsx +1425 -651
  233. package/src/renderer/components/SharePointSettings.tsx +224 -0
  234. package/src/renderer/components/Sidebar.tsx +94 -19
  235. package/src/renderer/components/SignalSettings.tsx +18 -7
  236. package/src/renderer/components/SkillHubBrowser.tsx +144 -185
  237. package/src/renderer/components/SlackSettings.tsx +18 -7
  238. package/src/renderer/components/TaskQuickActions.tsx +11 -6
  239. package/src/renderer/components/TaskTimeline.tsx +58 -26
  240. package/src/renderer/components/TeamsSettings.tsx +18 -7
  241. package/src/renderer/components/TelegramSettings.tsx +18 -7
  242. package/src/renderer/components/ThemeIcon.tsx +16 -0
  243. package/src/renderer/components/TwitchSettings.tsx +18 -7
  244. package/src/renderer/components/VoiceSettings.tsx +30 -74
  245. package/src/renderer/components/WhatsAppSettings.tsx +48 -37
  246. package/src/renderer/components/WorkingStateHistory.tsx +7 -5
  247. package/src/renderer/components/WorkspaceSelector.tsx +42 -13
  248. package/src/renderer/hooks/useOnboardingFlow.ts +21 -0
  249. package/src/renderer/styles/index.css +2333 -209
  250. package/src/shared/channelMessages.ts +367 -4
  251. package/src/shared/llm-provider-catalog.ts +217 -0
  252. package/src/shared/types.ts +251 -2
@@ -136,102 +136,88 @@ export function SkillHubBrowser({ onSkillInstalled, onClose }: SkillHubBrowserPr
136
136
 
137
137
  const getStatusBadge = (entry: SkillStatusEntry) => {
138
138
  if (entry.eligible) {
139
- return <span className="badge badge-success">Ready</span>;
139
+ return <span className="settings-badge settings-badge--success">Ready</span>;
140
140
  }
141
141
  if (entry.disabled) {
142
- return <span className="badge badge-warning">Disabled</span>;
142
+ return <span className="settings-badge settings-badge--warning">Disabled</span>;
143
143
  }
144
144
  if (entry.blockedByAllowlist) {
145
- return <span className="badge badge-error">Blocked</span>;
145
+ return <span className="settings-badge settings-badge--error">Blocked</span>;
146
146
  }
147
- return <span className="badge badge-ghost">Missing Requirements</span>;
147
+ return <span className="settings-badge settings-badge--neutral">Missing Requirements</span>;
148
148
  };
149
149
 
150
150
  const renderBrowseTab = () => (
151
- <div className="space-y-4">
152
- {/* Search */}
153
- <div className="flex gap-2">
151
+ <div className="skillhub-tab">
152
+ <div className="input-with-button">
154
153
  <input
155
154
  type="text"
156
155
  placeholder="Search skills..."
157
- className="input input-bordered flex-1"
156
+ className="settings-input"
158
157
  value={searchQuery}
159
158
  onChange={(e) => setSearchQuery(e.target.value)}
160
159
  onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
161
160
  />
162
161
  <button
163
- className="btn btn-primary"
162
+ className="button-secondary button-small"
164
163
  onClick={handleSearch}
165
164
  disabled={isSearching}
166
165
  >
167
- {isSearching ? (
168
- <span className="loading loading-spinner loading-sm" />
169
- ) : (
170
- 'Search'
171
- )}
166
+ {isSearching ? 'Searching...' : 'Search'}
172
167
  </button>
173
168
  </div>
174
169
 
175
- {/* Search Results */}
176
170
  {searchResults.length > 0 ? (
177
- <div className="space-y-2">
171
+ <div className="skillhub-list">
178
172
  {searchResults.map((skill) => (
179
173
  <div
180
174
  key={skill.id}
181
- className={`card bg-base-200 cursor-pointer hover:bg-base-300 transition-colors ${
182
- selectedSkill?.id === skill.id ? 'ring-2 ring-primary' : ''
183
- }`}
175
+ className={`settings-card skillhub-card ${selectedSkill?.id === skill.id ? 'is-selected' : ''}`}
184
176
  onClick={() => setSelectedSkill(skill)}
185
177
  >
186
- <div className="card-body p-4">
187
- <div className="flex items-start justify-between">
188
- <div className="flex items-center gap-2">
189
- <span className="text-2xl">{skill.icon || '📦'}</span>
178
+ <div className="skillhub-card-header">
179
+ <div className="skillhub-card-info">
180
+ <span className="skillhub-icon">{skill.icon || '📦'}</span>
190
181
  <div>
191
- <h3 className="font-semibold">{skill.name}</h3>
192
- <p className="text-sm text-base-content/70">{skill.description}</p>
182
+ <div className="skillhub-title-row">
183
+ <h4 className="skillhub-title">{skill.name}</h4>
184
+ </div>
185
+ <p className="settings-description skillhub-description">{skill.description}</p>
193
186
  </div>
194
- </div>
195
- <div className="flex items-center gap-2">
196
- {installedSkills.has(skill.id) ? (
197
- <span className="badge badge-success">Installed</span>
198
- ) : (
199
- <button
200
- className="btn btn-primary btn-sm"
201
- onClick={(e) => {
202
- e.stopPropagation();
203
- handleInstall(skill.id);
204
- }}
205
- disabled={installing === skill.id}
206
- >
207
- {installing === skill.id ? (
208
- <span className="loading loading-spinner loading-xs" />
209
- ) : (
210
- 'Install'
211
- )}
212
- </button>
213
- )}
214
- </div>
215
187
  </div>
216
- {skill.tags && skill.tags.length > 0 && (
217
- <div className="flex flex-wrap gap-1 mt-2">
218
- {skill.tags.map((tag) => (
219
- <span key={tag} className="badge badge-outline badge-sm">
220
- {tag}
221
- </span>
222
- ))}
223
- </div>
224
- )}
188
+ <div className="skillhub-card-actions">
189
+ {installedSkills.has(skill.id) ? (
190
+ <span className="settings-badge settings-badge--success">Installed</span>
191
+ ) : (
192
+ <button
193
+ className="button-primary button-small"
194
+ onClick={(e) => {
195
+ e.stopPropagation();
196
+ handleInstall(skill.id);
197
+ }}
198
+ disabled={installing === skill.id}
199
+ >
200
+ {installing === skill.id ? 'Installing...' : 'Install'}
201
+ </button>
202
+ )}
203
+ </div>
225
204
  </div>
205
+ {skill.tags && skill.tags.length > 0 && (
206
+ <div className="skillhub-tags">
207
+ {skill.tags.map((tag) => (
208
+ <span key={tag} className="settings-badge settings-badge--outline">
209
+ {tag}
210
+ </span>
211
+ ))}
212
+ </div>
213
+ )}
226
214
  </div>
227
215
  ))}
228
216
  </div>
229
217
  ) : searchQuery && !isSearching ? (
230
- <div className="text-center py-8 text-base-content/50">
231
- No skills found. Try a different search term.
232
- </div>
218
+ <div className="settings-empty">No skills found. Try a different search term.</div>
233
219
  ) : (
234
- <div className="text-center py-8 text-base-content/50">
220
+ <div className="settings-empty">
235
221
  Search the SkillHub registry to discover and install new skills.
236
222
  </div>
237
223
  )}
@@ -242,67 +228,58 @@ export function SkillHubBrowser({ onSkillInstalled, onClose }: SkillHubBrowserPr
242
228
  const managedSkills = skillStatus?.skills.filter(s => s.source === 'managed') || [];
243
229
 
244
230
  return (
245
- <div className="space-y-4">
246
- <div className="flex justify-between items-center">
247
- <h3 className="font-semibold">Installed from Registry</h3>
248
- <button className="btn btn-ghost btn-sm" onClick={handleOpenFolder}>
249
- Open Folder
250
- </button>
231
+ <div className="skillhub-tab">
232
+ <div className="settings-section-header">
233
+ <h3>Installed from Registry</h3>
234
+ <div className="settings-section-actions">
235
+ <button className="button-secondary button-small" onClick={handleOpenFolder}>
236
+ Open Folder
237
+ </button>
238
+ </div>
251
239
  </div>
252
240
 
253
241
  {managedSkills.length > 0 ? (
254
- <div className="space-y-2">
242
+ <div className="skillhub-list">
255
243
  {managedSkills.map((skill) => (
256
- <div key={skill.id} className="card bg-base-200">
257
- <div className="card-body p-4">
258
- <div className="flex items-start justify-between">
259
- <div className="flex items-center gap-2">
260
- <span className="text-2xl">{skill.icon || '📦'}</span>
261
- <div>
262
- <div className="flex items-center gap-2">
263
- <h3 className="font-semibold">{skill.name}</h3>
264
- {getStatusBadge(skill)}
265
- </div>
266
- <p className="text-sm text-base-content/70">{skill.description}</p>
267
- {skill.metadata?.version && (
268
- <p className="text-xs text-base-content/50">v{skill.metadata.version}</p>
269
- )}
244
+ <div key={skill.id} className="settings-card skillhub-card">
245
+ <div className="skillhub-card-header">
246
+ <div className="skillhub-card-info">
247
+ <span className="skillhub-icon">{skill.icon || '📦'}</span>
248
+ <div>
249
+ <div className="skillhub-title-row">
250
+ <h4 className="skillhub-title">{skill.name}</h4>
251
+ {getStatusBadge(skill)}
270
252
  </div>
271
- </div>
272
- <button
273
- className="btn btn-ghost btn-sm text-error"
274
- onClick={() => handleUninstall(skill.id)}
275
- disabled={installing === skill.id}
276
- >
277
- {installing === skill.id ? (
278
- <span className="loading loading-spinner loading-xs" />
279
- ) : (
280
- 'Uninstall'
281
- )}
282
- </button>
283
- </div>
284
-
285
- {/* Missing requirements */}
286
- {!skill.eligible && (
287
- <div className="mt-2 text-sm">
288
- {skill.missing.bins.length > 0 && (
289
- <p className="text-warning">
290
- Missing binaries: {skill.missing.bins.join(', ')}
291
- </p>
292
- )}
293
- {skill.missing.env.length > 0 && (
294
- <p className="text-warning">
295
- Missing env vars: {skill.missing.env.join(', ')}
296
- </p>
253
+ <p className="settings-description skillhub-description">{skill.description}</p>
254
+ {skill.metadata?.version && (
255
+ <p className="skillhub-meta">v{skill.metadata.version}</p>
297
256
  )}
298
257
  </div>
299
- )}
258
+ </div>
259
+ <button
260
+ className="button-danger button-small"
261
+ onClick={() => handleUninstall(skill.id)}
262
+ disabled={installing === skill.id}
263
+ >
264
+ {installing === skill.id ? 'Uninstalling...' : 'Uninstall'}
265
+ </button>
300
266
  </div>
267
+
268
+ {!skill.eligible && (
269
+ <div className="skillhub-warnings">
270
+ {skill.missing.bins.length > 0 && (
271
+ <p>Missing binaries: {skill.missing.bins.join(', ')}</p>
272
+ )}
273
+ {skill.missing.env.length > 0 && (
274
+ <p>Missing env vars: {skill.missing.env.join(', ')}</p>
275
+ )}
276
+ </div>
277
+ )}
301
278
  </div>
302
279
  ))}
303
280
  </div>
304
281
  ) : (
305
- <div className="text-center py-8 text-base-content/50">
282
+ <div className="settings-empty">
306
283
  No skills installed from registry yet.
307
284
  <br />
308
285
  Browse the registry to discover and install skills.
@@ -315,59 +292,53 @@ export function SkillHubBrowser({ onSkillInstalled, onClose }: SkillHubBrowserPr
315
292
  const renderStatusTab = () => {
316
293
  if (!skillStatus) {
317
294
  return (
318
- <div className="text-center py-8">
319
- <span className="loading loading-spinner" />
320
- </div>
295
+ <div className="settings-empty">Loading skill status...</div>
321
296
  );
322
297
  }
323
298
 
324
299
  return (
325
- <div className="space-y-4">
326
- {/* Summary */}
327
- <div className="stats stats-vertical lg:stats-horizontal shadow w-full">
328
- <div className="stat">
329
- <div className="stat-title">Total Skills</div>
300
+ <div className="skillhub-tab">
301
+ <div className="stats-grid">
302
+ <div className="stat-card">
303
+ <div className="stat-label">Total Skills</div>
330
304
  <div className="stat-value">{skillStatus.summary.total}</div>
331
305
  </div>
332
- <div className="stat">
333
- <div className="stat-title">Ready</div>
334
- <div className="stat-value text-success">{skillStatus.summary.eligible}</div>
306
+ <div className="stat-card">
307
+ <div className="stat-label">Ready</div>
308
+ <div className="stat-value stat-value--success">{skillStatus.summary.eligible}</div>
335
309
  </div>
336
- <div className="stat">
337
- <div className="stat-title">Disabled</div>
338
- <div className="stat-value text-warning">{skillStatus.summary.disabled}</div>
310
+ <div className="stat-card">
311
+ <div className="stat-label">Disabled</div>
312
+ <div className="stat-value stat-value--warning">{skillStatus.summary.disabled}</div>
339
313
  </div>
340
- <div className="stat">
341
- <div className="stat-title">Missing Deps</div>
342
- <div className="stat-value text-error">{skillStatus.summary.missingRequirements}</div>
314
+ <div className="stat-card">
315
+ <div className="stat-label">Missing Deps</div>
316
+ <div className="stat-value stat-value--error">{skillStatus.summary.missingRequirements}</div>
343
317
  </div>
344
318
  </div>
345
319
 
346
- {/* All Skills by Source */}
347
320
  {['bundled', 'managed', 'workspace'].map((source) => {
348
321
  const skills = skillStatus.skills.filter(s => s.source === source);
349
322
  if (skills.length === 0) return null;
350
323
 
351
324
  return (
352
- <div key={source} className="collapse collapse-arrow bg-base-200">
353
- <input type="checkbox" defaultChecked={source !== 'bundled'} />
354
- <div className="collapse-title font-medium capitalize">
355
- {source} Skills ({skills.length})
356
- </div>
357
- <div className="collapse-content">
358
- <div className="space-y-2 pt-2">
359
- {skills.map((skill) => (
360
- <div key={skill.id} className="flex items-center justify-between py-2 border-b border-base-300 last:border-0">
361
- <div className="flex items-center gap-2">
362
- <span>{skill.icon || '📦'}</span>
363
- <span className="font-medium">{skill.name}</span>
364
- </div>
365
- {getStatusBadge(skill)}
325
+ <details key={source} className="skillhub-group" open={source !== 'bundled'}>
326
+ <summary>
327
+ <span className="skillhub-group-title">{source} Skills</span>
328
+ <span className="settings-badge settings-badge--neutral">{skills.length}</span>
329
+ </summary>
330
+ <div className="skillhub-group-content">
331
+ {skills.map((skill) => (
332
+ <div key={skill.id} className="skillhub-group-item">
333
+ <div className="skillhub-group-info">
334
+ <span>{skill.icon || '📦'}</span>
335
+ <span>{skill.name}</span>
366
336
  </div>
367
- ))}
368
- </div>
337
+ {getStatusBadge(skill)}
338
+ </div>
339
+ ))}
369
340
  </div>
370
- </div>
341
+ </details>
371
342
  );
372
343
  })}
373
344
  </div>
@@ -376,79 +347,67 @@ export function SkillHubBrowser({ onSkillInstalled, onClose }: SkillHubBrowserPr
376
347
 
377
348
  // Show initial loading state
378
349
  if (isLoadingStatus) {
379
- return (
380
- <div className="flex flex-col h-full items-center justify-center">
381
- <span className="loading loading-spinner loading-lg" />
382
- <p className="mt-4 text-base-content/70">Loading skills...</p>
383
- </div>
384
- );
350
+ return <div className="settings-loading">Loading skills...</div>;
385
351
  }
386
352
 
387
353
  return (
388
- <div className="flex flex-col h-full">
389
- {/* Header */}
390
- <div className="flex items-center justify-between mb-4">
391
- <div className="flex items-center gap-2">
392
- <h2 className="text-xl font-bold">SkillHub</h2>
393
- {isRefreshing && (
394
- <span className="loading loading-spinner loading-sm" />
395
- )}
396
- </div>
397
- <div className="flex items-center gap-2">
398
- <button
399
- className="btn btn-ghost btn-sm"
400
- onClick={handleRefresh}
401
- disabled={isRefreshing}
402
- title="Refresh skill status"
403
- >
404
- {isRefreshing ? (
405
- <span className="loading loading-spinner loading-xs" />
406
- ) : (
407
- '↻'
408
- )}
409
- </button>
410
- {onClose && (
411
- <button className="btn btn-ghost btn-sm" onClick={onClose}>
412
- Close
354
+ <div className="skillhub-settings">
355
+ <div className="settings-section">
356
+ <div className="settings-section-header">
357
+ <div>
358
+ <h3>SkillHub</h3>
359
+ <p className="settings-description">
360
+ Search the SkillHub registry to discover and install new skills.
361
+ </p>
362
+ </div>
363
+ <div className="settings-section-actions">
364
+ <button
365
+ className="button-secondary button-small"
366
+ onClick={handleRefresh}
367
+ disabled={isRefreshing}
368
+ >
369
+ {isRefreshing ? 'Refreshing...' : 'Refresh'}
413
370
  </button>
414
- )}
371
+ {onClose && (
372
+ <button className="button-secondary button-small" onClick={onClose}>
373
+ Close
374
+ </button>
375
+ )}
376
+ </div>
415
377
  </div>
416
378
  </div>
417
379
 
418
- {/* Error Alert */}
419
380
  {error && (
420
- <div className="alert alert-error mb-4">
381
+ <div className="settings-alert settings-alert-error">
421
382
  <span>{error}</span>
422
- <button className="btn btn-ghost btn-sm" onClick={() => setError(null)}>
383
+ <button className="button-secondary button-small" onClick={() => setError(null)}>
423
384
  Dismiss
424
385
  </button>
425
386
  </div>
426
387
  )}
427
388
 
428
- {/* Tabs */}
429
- <div className="tabs tabs-boxed mb-4">
389
+ <div className="settings-tabs">
430
390
  <button
431
- className={`tab ${activeTab === 'installed' ? 'tab-active' : ''}`}
391
+ className={`settings-tab ${activeTab === 'installed' ? 'active' : ''}`}
432
392
  onClick={() => setActiveTab('installed')}
433
393
  >
434
394
  Installed
435
395
  </button>
436
396
  <button
437
- className={`tab ${activeTab === 'browse' ? 'tab-active' : ''}`}
397
+ className={`settings-tab ${activeTab === 'browse' ? 'active' : ''}`}
438
398
  onClick={() => setActiveTab('browse')}
439
399
  >
440
400
  Browse Registry
441
401
  </button>
442
402
  <button
443
- className={`tab ${activeTab === 'status' ? 'tab-active' : ''}`}
403
+ className={`settings-tab ${activeTab === 'status' ? 'active' : ''}`}
444
404
  onClick={() => setActiveTab('status')}
445
405
  >
446
406
  Status
447
407
  </button>
448
408
  </div>
449
409
 
450
- {/* Tab Content */}
451
- <div className="flex-1 overflow-y-auto">
410
+ <div className="skillhub-tab-content">
452
411
  {activeTab === 'browse' && renderBrowseTab()}
453
412
  {activeTab === 'installed' && renderInstalledTab()}
454
413
  {activeTab === 'status' && renderStatusTab()}
@@ -1,4 +1,4 @@
1
- import { useState, useEffect } from 'react';
1
+ import { useState, useEffect, useCallback } from 'react';
2
2
  import { ChannelData, ChannelUserData, SecurityMode } from '../../shared/types';
3
3
 
4
4
  interface SlackSettingsProps {
@@ -23,11 +23,7 @@ export function SlackSettings({ onStatusChange }: SlackSettingsProps) {
23
23
  // Pairing code state
24
24
  const [pairingCode, setPairingCode] = useState<string | null>(null);
25
25
 
26
- useEffect(() => {
27
- loadChannel();
28
- }, []);
29
-
30
- const loadChannel = async () => {
26
+ const loadChannel = useCallback(async () => {
31
27
  try {
32
28
  setLoading(true);
33
29
  const channels = await window.electronAPI.getGatewayChannels();
@@ -48,7 +44,22 @@ export function SlackSettings({ onStatusChange }: SlackSettingsProps) {
48
44
  } finally {
49
45
  setLoading(false);
50
46
  }
51
- };
47
+ }, [onStatusChange]);
48
+
49
+ useEffect(() => {
50
+ loadChannel();
51
+ }, [loadChannel]);
52
+
53
+ useEffect(() => {
54
+ const unsubscribe = window.electronAPI?.onGatewayUsersUpdated?.((data) => {
55
+ if (data?.channelType !== 'slack') return;
56
+ if (channel && data?.channelId && data.channelId !== channel.id) return;
57
+ loadChannel();
58
+ });
59
+ return () => {
60
+ if (unsubscribe) unsubscribe();
61
+ };
62
+ }, [channel?.id, loadChannel]);
52
63
 
53
64
  const handleAddChannel = async () => {
54
65
  if (!botToken.trim() || !appToken.trim()) return;
@@ -1,4 +1,6 @@
1
1
  import { useState } from 'react';
2
+ import { ThemeIcon } from './ThemeIcon';
3
+ import { BotIcon, CalendarIcon, ClockIcon, ColumnsIcon, FlagIcon, TagIcon } from './LineIcons';
2
4
  import {
3
5
  TaskBoardColumn,
4
6
  TaskLabelData,
@@ -107,7 +109,7 @@ export function TaskQuickActions({
107
109
  className={`action-btn ${activePanel === 'column' ? 'active' : ''}`}
108
110
  onClick={() => setActivePanel(activePanel === 'column' ? null : 'column')}
109
111
  >
110
- <span className="action-icon">📋</span>
112
+ <ThemeIcon className="action-icon" emoji="📋" icon={<ColumnsIcon size={16} />} />
111
113
  Move to Column
112
114
  </button>
113
115
 
@@ -115,7 +117,7 @@ export function TaskQuickActions({
115
117
  className={`action-btn ${activePanel === 'priority' ? 'active' : ''}`}
116
118
  onClick={() => setActivePanel(activePanel === 'priority' ? null : 'priority')}
117
119
  >
118
- <span className="action-icon">!</span>
120
+ <ThemeIcon className="action-icon" emoji="!" icon={<FlagIcon size={16} />} />
119
121
  Set Priority
120
122
  </button>
121
123
 
@@ -123,7 +125,7 @@ export function TaskQuickActions({
123
125
  className={`action-btn ${activePanel === 'labels' ? 'active' : ''}`}
124
126
  onClick={() => setActivePanel(activePanel === 'labels' ? null : 'labels')}
125
127
  >
126
- <span className="action-icon">🏷️</span>
128
+ <ThemeIcon className="action-icon" emoji="🏷️" icon={<TagIcon size={16} />} />
127
129
  Labels
128
130
  </button>
129
131
 
@@ -131,7 +133,7 @@ export function TaskQuickActions({
131
133
  className={`action-btn ${activePanel === 'agent' ? 'active' : ''}`}
132
134
  onClick={() => setActivePanel(activePanel === 'agent' ? null : 'agent')}
133
135
  >
134
- <span className="action-icon">🤖</span>
136
+ <ThemeIcon className="action-icon" emoji="🤖" icon={<BotIcon size={16} />} />
135
137
  Assign Agent
136
138
  </button>
137
139
 
@@ -139,7 +141,7 @@ export function TaskQuickActions({
139
141
  className={`action-btn ${activePanel === 'due' ? 'active' : ''}`}
140
142
  onClick={() => setActivePanel(activePanel === 'due' ? null : 'due')}
141
143
  >
142
- <span className="action-icon">📅</span>
144
+ <ThemeIcon className="action-icon" emoji="📅" icon={<CalendarIcon size={16} />} />
143
145
  Due Date
144
146
  </button>
145
147
 
@@ -147,7 +149,7 @@ export function TaskQuickActions({
147
149
  className={`action-btn ${activePanel === 'estimate' ? 'active' : ''}`}
148
150
  onClick={() => setActivePanel(activePanel === 'estimate' ? null : 'estimate')}
149
151
  >
150
- <span className="action-icon">⏱️</span>
152
+ <ThemeIcon className="action-icon" emoji="⏱️" icon={<ClockIcon size={16} />} />
151
153
  Estimate
152
154
  </button>
153
155
  </div>
@@ -383,6 +385,9 @@ export function TaskQuickActions({
383
385
  font-size: 14px;
384
386
  width: 20px;
385
387
  text-align: center;
388
+ display: inline-flex;
389
+ align-items: center;
390
+ justify-content: center;
386
391
  }
387
392
 
388
393
  .action-panel {