slackhive 0.1.40 → 0.1.42

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 (534) hide show
  1. package/cli/dist/index.js +15786 -52
  2. package/package.json +9 -3
  3. package/.dockerignore +0 -14
  4. package/.env.example +0 -44
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -65
  6. package/.github/ISSUE_TEMPLATE/config.yml +0 -5
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -38
  8. package/.github/PULL_REQUEST_TEMPLATE.md +0 -27
  9. package/.github/dependabot.yml +0 -20
  10. package/.github/workflows/audit.yml +0 -149
  11. package/.github/workflows/ci.yml +0 -135
  12. package/CHANGELOG.md +0 -52
  13. package/CODE_OF_CONDUCT.md +0 -37
  14. package/CONTRIBUTING.md +0 -204
  15. package/SECURITY.md +0 -47
  16. package/apps/runner/Dockerfile +0 -33
  17. package/apps/runner/dist/__tests__/channel-restrictions.test.d.ts +0 -8
  18. package/apps/runner/dist/__tests__/channel-restrictions.test.js +0 -63
  19. package/apps/runner/dist/__tests__/channel-restrictions.test.js.map +0 -1
  20. package/apps/runner/dist/__tests__/claude-handler-resolve.test.d.ts +0 -20
  21. package/apps/runner/dist/__tests__/claude-handler-resolve.test.js +0 -178
  22. package/apps/runner/dist/__tests__/claude-handler-resolve.test.js.map +0 -1
  23. package/apps/runner/dist/__tests__/compile-claude-md.test.d.ts +0 -13
  24. package/apps/runner/dist/__tests__/compile-claude-md.test.js +0 -144
  25. package/apps/runner/dist/__tests__/compile-claude-md.test.js.map +0 -1
  26. package/apps/runner/dist/__tests__/memory-sync.test.d.ts +0 -11
  27. package/apps/runner/dist/__tests__/memory-sync.test.js +0 -56
  28. package/apps/runner/dist/__tests__/memory-sync.test.js.map +0 -1
  29. package/apps/runner/dist/__tests__/slack-file-support.test.d.ts +0 -9
  30. package/apps/runner/dist/__tests__/slack-file-support.test.js +0 -271
  31. package/apps/runner/dist/__tests__/slack-file-support.test.js.map +0 -1
  32. package/apps/runner/dist/__tests__/slack-formatting.test.d.ts +0 -12
  33. package/apps/runner/dist/__tests__/slack-formatting.test.js +0 -400
  34. package/apps/runner/dist/__tests__/slack-formatting.test.js.map +0 -1
  35. package/apps/runner/dist/__tests__/thread-context.test.d.ts +0 -12
  36. package/apps/runner/dist/__tests__/thread-context.test.js +0 -182
  37. package/apps/runner/dist/__tests__/thread-context.test.js.map +0 -1
  38. package/apps/runner/dist/agent-runner.d.ts +0 -118
  39. package/apps/runner/dist/agent-runner.js +0 -352
  40. package/apps/runner/dist/agent-runner.js.map +0 -1
  41. package/apps/runner/dist/claude-handler.d.ts +0 -122
  42. package/apps/runner/dist/claude-handler.js +0 -402
  43. package/apps/runner/dist/claude-handler.js.map +0 -1
  44. package/apps/runner/dist/compile-claude-md.d.ts +0 -59
  45. package/apps/runner/dist/compile-claude-md.js +0 -291
  46. package/apps/runner/dist/compile-claude-md.js.map +0 -1
  47. package/apps/runner/dist/correction-handler.d.ts +0 -46
  48. package/apps/runner/dist/correction-handler.js +0 -162
  49. package/apps/runner/dist/correction-handler.js.map +0 -1
  50. package/apps/runner/dist/correction-manager.d.ts +0 -53
  51. package/apps/runner/dist/correction-manager.js +0 -241
  52. package/apps/runner/dist/correction-manager.js.map +0 -1
  53. package/apps/runner/dist/db.d.ts +0 -193
  54. package/apps/runner/dist/db.js +0 -492
  55. package/apps/runner/dist/db.js.map +0 -1
  56. package/apps/runner/dist/index.d.ts +0 -9
  57. package/apps/runner/dist/index.js +0 -43
  58. package/apps/runner/dist/index.js.map +0 -1
  59. package/apps/runner/dist/job-scheduler.d.ts +0 -57
  60. package/apps/runner/dist/job-scheduler.js +0 -150
  61. package/apps/runner/dist/job-scheduler.js.map +0 -1
  62. package/apps/runner/dist/logger.d.ts +0 -32
  63. package/apps/runner/dist/logger.js +0 -52
  64. package/apps/runner/dist/logger.js.map +0 -1
  65. package/apps/runner/dist/mcp-process-manager.d.ts +0 -38
  66. package/apps/runner/dist/mcp-process-manager.js +0 -189
  67. package/apps/runner/dist/mcp-process-manager.js.map +0 -1
  68. package/apps/runner/dist/memory-mcp.d.ts +0 -14
  69. package/apps/runner/dist/memory-mcp.js +0 -88
  70. package/apps/runner/dist/memory-mcp.js.map +0 -1
  71. package/apps/runner/dist/memory-watcher.d.ts +0 -78
  72. package/apps/runner/dist/memory-watcher.js +0 -220
  73. package/apps/runner/dist/memory-watcher.js.map +0 -1
  74. package/apps/runner/dist/slack-handler.d.ts +0 -120
  75. package/apps/runner/dist/slack-handler.js +0 -843
  76. package/apps/runner/dist/slack-handler.js.map +0 -1
  77. package/apps/runner/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  78. package/apps/runner/package.json +0 -42
  79. package/apps/runner/src/__tests__/channel-restrictions.test.ts +0 -75
  80. package/apps/runner/src/__tests__/claude-handler-resolve.test.ts +0 -160
  81. package/apps/runner/src/__tests__/compile-claude-md.test.ts +0 -139
  82. package/apps/runner/src/__tests__/memory-sync.test.ts +0 -59
  83. package/apps/runner/src/__tests__/slack-file-support.test.ts +0 -376
  84. package/apps/runner/src/__tests__/slack-formatting.test.ts +0 -495
  85. package/apps/runner/src/__tests__/thread-context.test.ts +0 -215
  86. package/apps/runner/src/agent-runner.ts +0 -397
  87. package/apps/runner/src/claude-handler.ts +0 -475
  88. package/apps/runner/src/compile-claude-md.ts +0 -283
  89. package/apps/runner/src/correction-handler.ts +0 -191
  90. package/apps/runner/src/correction-manager.ts +0 -285
  91. package/apps/runner/src/db.ts +0 -604
  92. package/apps/runner/src/index.ts +0 -46
  93. package/apps/runner/src/job-scheduler.ts +0 -165
  94. package/apps/runner/src/logger.ts +0 -49
  95. package/apps/runner/src/mcp-process-manager.ts +0 -195
  96. package/apps/runner/src/memory-mcp.ts +0 -85
  97. package/apps/runner/src/memory-watcher.ts +0 -215
  98. package/apps/runner/src/slack-handler.ts +0 -929
  99. package/apps/runner/tsconfig.json +0 -17
  100. package/apps/runner/vitest.config.mts +0 -17
  101. package/apps/web/.eslintrc.json +0 -3
  102. package/apps/web/.next/app-build-manifest.json +0 -323
  103. package/apps/web/.next/app-path-routes-manifest.json +0 -46
  104. package/apps/web/.next/build-manifest.json +0 -33
  105. package/apps/web/.next/cache/.previewinfo +0 -1
  106. package/apps/web/.next/cache/.rscinfo +0 -1
  107. package/apps/web/.next/cache/webpack/client-production/0.pack +0 -0
  108. package/apps/web/.next/cache/webpack/client-production/1.pack +0 -0
  109. package/apps/web/.next/cache/webpack/client-production/2.pack +0 -0
  110. package/apps/web/.next/cache/webpack/client-production/3.pack +0 -0
  111. package/apps/web/.next/cache/webpack/client-production/4.pack +0 -0
  112. package/apps/web/.next/cache/webpack/client-production/index.pack +0 -0
  113. package/apps/web/.next/cache/webpack/client-production/index.pack.old +0 -0
  114. package/apps/web/.next/cache/webpack/edge-server-production/0.pack +0 -0
  115. package/apps/web/.next/cache/webpack/edge-server-production/1.pack +0 -0
  116. package/apps/web/.next/cache/webpack/edge-server-production/index.pack +0 -0
  117. package/apps/web/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  118. package/apps/web/.next/cache/webpack/server-production/0.pack +0 -0
  119. package/apps/web/.next/cache/webpack/server-production/1.pack +0 -0
  120. package/apps/web/.next/cache/webpack/server-production/2.pack +0 -0
  121. package/apps/web/.next/cache/webpack/server-production/index.pack +0 -0
  122. package/apps/web/.next/cache/webpack/server-production/index.pack.old +0 -0
  123. package/apps/web/.next/diagnostics/build-diagnostics.json +0 -6
  124. package/apps/web/.next/diagnostics/framework.json +0 -1
  125. package/apps/web/.next/package.json +0 -1
  126. package/apps/web/.next/react-loadable-manifest.json +0 -1
  127. package/apps/web/.next/server/app/_not-found/page.js +0 -2
  128. package/apps/web/.next/server/app/_not-found/page.js.nft.json +0 -1
  129. package/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  130. package/apps/web/.next/server/app/agents/[slug]/page.js +0 -4
  131. package/apps/web/.next/server/app/agents/[slug]/page.js.nft.json +0 -1
  132. package/apps/web/.next/server/app/agents/[slug]/page_client-reference-manifest.js +0 -1
  133. package/apps/web/.next/server/app/agents/new/page.js +0 -2
  134. package/apps/web/.next/server/app/agents/new/page.js.nft.json +0 -1
  135. package/apps/web/.next/server/app/agents/new/page_client-reference-manifest.js +0 -1
  136. package/apps/web/.next/server/app/api/agents/[id]/access/route.js +0 -1
  137. package/apps/web/.next/server/app/api/agents/[id]/access/route.js.nft.json +0 -1
  138. package/apps/web/.next/server/app/api/agents/[id]/access/route_client-reference-manifest.js +0 -1
  139. package/apps/web/.next/server/app/api/agents/[id]/claude-md/route.js +0 -6
  140. package/apps/web/.next/server/app/api/agents/[id]/claude-md/route.js.nft.json +0 -1
  141. package/apps/web/.next/server/app/api/agents/[id]/claude-md/route_client-reference-manifest.js +0 -1
  142. package/apps/web/.next/server/app/api/agents/[id]/logs/route.js +0 -3
  143. package/apps/web/.next/server/app/api/agents/[id]/logs/route.js.nft.json +0 -1
  144. package/apps/web/.next/server/app/api/agents/[id]/logs/route_client-reference-manifest.js +0 -1
  145. package/apps/web/.next/server/app/api/agents/[id]/manifest/route.js +0 -1
  146. package/apps/web/.next/server/app/api/agents/[id]/manifest/route.js.nft.json +0 -1
  147. package/apps/web/.next/server/app/api/agents/[id]/manifest/route_client-reference-manifest.js +0 -1
  148. package/apps/web/.next/server/app/api/agents/[id]/mcps/route.js +0 -1
  149. package/apps/web/.next/server/app/api/agents/[id]/mcps/route.js.nft.json +0 -1
  150. package/apps/web/.next/server/app/api/agents/[id]/mcps/route_client-reference-manifest.js +0 -1
  151. package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route.js +0 -1
  152. package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route.js.nft.json +0 -1
  153. package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route_client-reference-manifest.js +0 -1
  154. package/apps/web/.next/server/app/api/agents/[id]/memories/route.js +0 -1
  155. package/apps/web/.next/server/app/api/agents/[id]/memories/route.js.nft.json +0 -1
  156. package/apps/web/.next/server/app/api/agents/[id]/memories/route_client-reference-manifest.js +0 -1
  157. package/apps/web/.next/server/app/api/agents/[id]/permissions/route.js +0 -1
  158. package/apps/web/.next/server/app/api/agents/[id]/permissions/route.js.nft.json +0 -1
  159. package/apps/web/.next/server/app/api/agents/[id]/permissions/route_client-reference-manifest.js +0 -1
  160. package/apps/web/.next/server/app/api/agents/[id]/reload/route.js +0 -1
  161. package/apps/web/.next/server/app/api/agents/[id]/reload/route.js.nft.json +0 -1
  162. package/apps/web/.next/server/app/api/agents/[id]/reload/route_client-reference-manifest.js +0 -1
  163. package/apps/web/.next/server/app/api/agents/[id]/restrictions/route.js +0 -1
  164. package/apps/web/.next/server/app/api/agents/[id]/restrictions/route.js.nft.json +0 -1
  165. package/apps/web/.next/server/app/api/agents/[id]/restrictions/route_client-reference-manifest.js +0 -1
  166. package/apps/web/.next/server/app/api/agents/[id]/route.js +0 -33
  167. package/apps/web/.next/server/app/api/agents/[id]/route.js.nft.json +0 -1
  168. package/apps/web/.next/server/app/api/agents/[id]/route_client-reference-manifest.js +0 -1
  169. package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route.js +0 -1
  170. package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route.js.nft.json +0 -1
  171. package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route_client-reference-manifest.js +0 -1
  172. package/apps/web/.next/server/app/api/agents/[id]/skills/route.js +0 -1
  173. package/apps/web/.next/server/app/api/agents/[id]/skills/route.js.nft.json +0 -1
  174. package/apps/web/.next/server/app/api/agents/[id]/skills/route_client-reference-manifest.js +0 -1
  175. package/apps/web/.next/server/app/api/agents/[id]/slack-info/route.js +0 -1
  176. package/apps/web/.next/server/app/api/agents/[id]/slack-info/route.js.nft.json +0 -1
  177. package/apps/web/.next/server/app/api/agents/[id]/slack-info/route_client-reference-manifest.js +0 -1
  178. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route.js +0 -1
  179. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route.js.nft.json +0 -1
  180. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route_client-reference-manifest.js +0 -1
  181. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route.js +0 -1
  182. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route.js.nft.json +0 -1
  183. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route_client-reference-manifest.js +0 -1
  184. package/apps/web/.next/server/app/api/agents/[id]/snapshots/route.js +0 -1
  185. package/apps/web/.next/server/app/api/agents/[id]/snapshots/route.js.nft.json +0 -1
  186. package/apps/web/.next/server/app/api/agents/[id]/snapshots/route_client-reference-manifest.js +0 -1
  187. package/apps/web/.next/server/app/api/agents/[id]/start/route.js +0 -1
  188. package/apps/web/.next/server/app/api/agents/[id]/start/route.js.nft.json +0 -1
  189. package/apps/web/.next/server/app/api/agents/[id]/start/route_client-reference-manifest.js +0 -1
  190. package/apps/web/.next/server/app/api/agents/[id]/stop/route.js +0 -1
  191. package/apps/web/.next/server/app/api/agents/[id]/stop/route.js.nft.json +0 -1
  192. package/apps/web/.next/server/app/api/agents/[id]/stop/route_client-reference-manifest.js +0 -1
  193. package/apps/web/.next/server/app/api/agents/route.js +0 -91
  194. package/apps/web/.next/server/app/api/agents/route.js.nft.json +0 -1
  195. package/apps/web/.next/server/app/api/agents/route_client-reference-manifest.js +0 -1
  196. package/apps/web/.next/server/app/api/auth/login/route.js +0 -1
  197. package/apps/web/.next/server/app/api/auth/login/route.js.nft.json +0 -1
  198. package/apps/web/.next/server/app/api/auth/login/route_client-reference-manifest.js +0 -1
  199. package/apps/web/.next/server/app/api/auth/logout/route.js +0 -1
  200. package/apps/web/.next/server/app/api/auth/logout/route.js.nft.json +0 -1
  201. package/apps/web/.next/server/app/api/auth/logout/route_client-reference-manifest.js +0 -1
  202. package/apps/web/.next/server/app/api/auth/me/route.js +0 -1
  203. package/apps/web/.next/server/app/api/auth/me/route.js.nft.json +0 -1
  204. package/apps/web/.next/server/app/api/auth/me/route_client-reference-manifest.js +0 -1
  205. package/apps/web/.next/server/app/api/auth/users/[id]/route.js +0 -1
  206. package/apps/web/.next/server/app/api/auth/users/[id]/route.js.nft.json +0 -1
  207. package/apps/web/.next/server/app/api/auth/users/[id]/route_client-reference-manifest.js +0 -1
  208. package/apps/web/.next/server/app/api/auth/users/route.js +0 -1
  209. package/apps/web/.next/server/app/api/auth/users/route.js.nft.json +0 -1
  210. package/apps/web/.next/server/app/api/auth/users/route_client-reference-manifest.js +0 -1
  211. package/apps/web/.next/server/app/api/env-vars/[key]/route.js +0 -1
  212. package/apps/web/.next/server/app/api/env-vars/[key]/route.js.nft.json +0 -1
  213. package/apps/web/.next/server/app/api/env-vars/[key]/route_client-reference-manifest.js +0 -1
  214. package/apps/web/.next/server/app/api/env-vars/route.js +0 -1
  215. package/apps/web/.next/server/app/api/env-vars/route.js.nft.json +0 -1
  216. package/apps/web/.next/server/app/api/env-vars/route_client-reference-manifest.js +0 -1
  217. package/apps/web/.next/server/app/api/jobs/[id]/route.js +0 -1
  218. package/apps/web/.next/server/app/api/jobs/[id]/route.js.nft.json +0 -1
  219. package/apps/web/.next/server/app/api/jobs/[id]/route_client-reference-manifest.js +0 -1
  220. package/apps/web/.next/server/app/api/jobs/[id]/runs/route.js +0 -1
  221. package/apps/web/.next/server/app/api/jobs/[id]/runs/route.js.nft.json +0 -1
  222. package/apps/web/.next/server/app/api/jobs/[id]/runs/route_client-reference-manifest.js +0 -1
  223. package/apps/web/.next/server/app/api/jobs/route.js +0 -1
  224. package/apps/web/.next/server/app/api/jobs/route.js.nft.json +0 -1
  225. package/apps/web/.next/server/app/api/jobs/route_client-reference-manifest.js +0 -1
  226. package/apps/web/.next/server/app/api/mcps/[id]/route.js +0 -1
  227. package/apps/web/.next/server/app/api/mcps/[id]/route.js.nft.json +0 -1
  228. package/apps/web/.next/server/app/api/mcps/[id]/route_client-reference-manifest.js +0 -1
  229. package/apps/web/.next/server/app/api/mcps/[id]/test/route.js +0 -1
  230. package/apps/web/.next/server/app/api/mcps/[id]/test/route.js.nft.json +0 -1
  231. package/apps/web/.next/server/app/api/mcps/[id]/test/route_client-reference-manifest.js +0 -1
  232. package/apps/web/.next/server/app/api/mcps/route.js +0 -1
  233. package/apps/web/.next/server/app/api/mcps/route.js.nft.json +0 -1
  234. package/apps/web/.next/server/app/api/mcps/route_client-reference-manifest.js +0 -1
  235. package/apps/web/.next/server/app/api/settings/route.js +0 -1
  236. package/apps/web/.next/server/app/api/settings/route.js.nft.json +0 -1
  237. package/apps/web/.next/server/app/api/settings/route_client-reference-manifest.js +0 -1
  238. package/apps/web/.next/server/app/icon.svg/route.js +0 -1
  239. package/apps/web/.next/server/app/icon.svg/route.js.nft.json +0 -1
  240. package/apps/web/.next/server/app/jobs/page.js +0 -2
  241. package/apps/web/.next/server/app/jobs/page.js.nft.json +0 -1
  242. package/apps/web/.next/server/app/jobs/page_client-reference-manifest.js +0 -1
  243. package/apps/web/.next/server/app/login/page.js +0 -2
  244. package/apps/web/.next/server/app/login/page.js.nft.json +0 -1
  245. package/apps/web/.next/server/app/login/page_client-reference-manifest.js +0 -1
  246. package/apps/web/.next/server/app/page.js +0 -2
  247. package/apps/web/.next/server/app/page.js.nft.json +0 -1
  248. package/apps/web/.next/server/app/page_client-reference-manifest.js +0 -1
  249. package/apps/web/.next/server/app/settings/env-vars/page.js +0 -2
  250. package/apps/web/.next/server/app/settings/env-vars/page.js.nft.json +0 -1
  251. package/apps/web/.next/server/app/settings/env-vars/page_client-reference-manifest.js +0 -1
  252. package/apps/web/.next/server/app/settings/mcps/page.js +0 -2
  253. package/apps/web/.next/server/app/settings/mcps/page.js.nft.json +0 -1
  254. package/apps/web/.next/server/app/settings/mcps/page_client-reference-manifest.js +0 -1
  255. package/apps/web/.next/server/app/settings/page.js +0 -2
  256. package/apps/web/.next/server/app/settings/page.js.nft.json +0 -1
  257. package/apps/web/.next/server/app/settings/page_client-reference-manifest.js +0 -1
  258. package/apps/web/.next/server/app-paths-manifest.json +0 -46
  259. package/apps/web/.next/server/chunks/1157.js +0 -9
  260. package/apps/web/.next/server/chunks/2287.js +0 -1
  261. package/apps/web/.next/server/chunks/3444.js +0 -1
  262. package/apps/web/.next/server/chunks/383.js +0 -6
  263. package/apps/web/.next/server/chunks/4012.js +0 -58
  264. package/apps/web/.next/server/chunks/6791.js +0 -1
  265. package/apps/web/.next/server/chunks/7171.js +0 -1
  266. package/apps/web/.next/server/chunks/8819.js +0 -22
  267. package/apps/web/.next/server/edge-runtime-webpack.js +0 -2
  268. package/apps/web/.next/server/edge-runtime-webpack.js.map +0 -1
  269. package/apps/web/.next/server/interception-route-rewrite-manifest.js +0 -1
  270. package/apps/web/.next/server/middleware-build-manifest.js +0 -1
  271. package/apps/web/.next/server/middleware-manifest.json +0 -32
  272. package/apps/web/.next/server/middleware-react-loadable-manifest.js +0 -1
  273. package/apps/web/.next/server/next-font-manifest.js +0 -1
  274. package/apps/web/.next/server/next-font-manifest.json +0 -1
  275. package/apps/web/.next/server/pages/_app.js +0 -1
  276. package/apps/web/.next/server/pages/_app.js.nft.json +0 -1
  277. package/apps/web/.next/server/pages/_document.js +0 -1
  278. package/apps/web/.next/server/pages/_document.js.nft.json +0 -1
  279. package/apps/web/.next/server/pages/_error.js +0 -19
  280. package/apps/web/.next/server/pages/_error.js.nft.json +0 -1
  281. package/apps/web/.next/server/pages-manifest.json +0 -5
  282. package/apps/web/.next/server/server-reference-manifest.js +0 -1
  283. package/apps/web/.next/server/server-reference-manifest.json +0 -1
  284. package/apps/web/.next/server/src/middleware.js +0 -14
  285. package/apps/web/.next/server/src/middleware.js.map +0 -1
  286. package/apps/web/.next/server/webpack-runtime.js +0 -1
  287. package/apps/web/.next/static/chunks/18-90b700ea37b686a2.js +0 -1
  288. package/apps/web/.next/static/chunks/87c73c54-24122e7b92478d00.js +0 -1
  289. package/apps/web/.next/static/chunks/9664-af80478aa73ba424.js +0 -1
  290. package/apps/web/.next/static/chunks/app/_not-found/page-b9cee17ed89ca24a.js +0 -1
  291. package/apps/web/.next/static/chunks/app/agents/[slug]/page-18369fc3fe1a9a7b.js +0 -1
  292. package/apps/web/.next/static/chunks/app/agents/new/page-bf11cf8901c7e2cd.js +0 -1
  293. package/apps/web/.next/static/chunks/app/api/agents/[id]/access/route-07f0f73ac9839899.js +0 -1
  294. package/apps/web/.next/static/chunks/app/api/agents/[id]/claude-md/route-07f0f73ac9839899.js +0 -1
  295. package/apps/web/.next/static/chunks/app/api/agents/[id]/logs/route-07f0f73ac9839899.js +0 -1
  296. package/apps/web/.next/static/chunks/app/api/agents/[id]/manifest/route-07f0f73ac9839899.js +0 -1
  297. package/apps/web/.next/static/chunks/app/api/agents/[id]/mcps/route-07f0f73ac9839899.js +0 -1
  298. package/apps/web/.next/static/chunks/app/api/agents/[id]/memories/[memId]/route-07f0f73ac9839899.js +0 -1
  299. package/apps/web/.next/static/chunks/app/api/agents/[id]/memories/route-07f0f73ac9839899.js +0 -1
  300. package/apps/web/.next/static/chunks/app/api/agents/[id]/permissions/route-07f0f73ac9839899.js +0 -1
  301. package/apps/web/.next/static/chunks/app/api/agents/[id]/reload/route-07f0f73ac9839899.js +0 -1
  302. package/apps/web/.next/static/chunks/app/api/agents/[id]/restrictions/route-07f0f73ac9839899.js +0 -1
  303. package/apps/web/.next/static/chunks/app/api/agents/[id]/route-07f0f73ac9839899.js +0 -1
  304. package/apps/web/.next/static/chunks/app/api/agents/[id]/skills/[skillId]/route-07f0f73ac9839899.js +0 -1
  305. package/apps/web/.next/static/chunks/app/api/agents/[id]/skills/route-07f0f73ac9839899.js +0 -1
  306. package/apps/web/.next/static/chunks/app/api/agents/[id]/slack-info/route-07f0f73ac9839899.js +0 -1
  307. package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/[sid]/restore/route-07f0f73ac9839899.js +0 -1
  308. package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/[sid]/route-07f0f73ac9839899.js +0 -1
  309. package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/route-07f0f73ac9839899.js +0 -1
  310. package/apps/web/.next/static/chunks/app/api/agents/[id]/start/route-07f0f73ac9839899.js +0 -1
  311. package/apps/web/.next/static/chunks/app/api/agents/[id]/stop/route-07f0f73ac9839899.js +0 -1
  312. package/apps/web/.next/static/chunks/app/api/agents/route-07f0f73ac9839899.js +0 -1
  313. package/apps/web/.next/static/chunks/app/api/auth/login/route-07f0f73ac9839899.js +0 -1
  314. package/apps/web/.next/static/chunks/app/api/auth/logout/route-07f0f73ac9839899.js +0 -1
  315. package/apps/web/.next/static/chunks/app/api/auth/me/route-07f0f73ac9839899.js +0 -1
  316. package/apps/web/.next/static/chunks/app/api/auth/users/[id]/route-07f0f73ac9839899.js +0 -1
  317. package/apps/web/.next/static/chunks/app/api/auth/users/route-07f0f73ac9839899.js +0 -1
  318. package/apps/web/.next/static/chunks/app/api/env-vars/[key]/route-07f0f73ac9839899.js +0 -1
  319. package/apps/web/.next/static/chunks/app/api/env-vars/route-07f0f73ac9839899.js +0 -1
  320. package/apps/web/.next/static/chunks/app/api/jobs/[id]/route-07f0f73ac9839899.js +0 -1
  321. package/apps/web/.next/static/chunks/app/api/jobs/[id]/runs/route-07f0f73ac9839899.js +0 -1
  322. package/apps/web/.next/static/chunks/app/api/jobs/route-07f0f73ac9839899.js +0 -1
  323. package/apps/web/.next/static/chunks/app/api/mcps/[id]/route-07f0f73ac9839899.js +0 -1
  324. package/apps/web/.next/static/chunks/app/api/mcps/[id]/test/route-07f0f73ac9839899.js +0 -1
  325. package/apps/web/.next/static/chunks/app/api/mcps/route-07f0f73ac9839899.js +0 -1
  326. package/apps/web/.next/static/chunks/app/api/settings/route-07f0f73ac9839899.js +0 -1
  327. package/apps/web/.next/static/chunks/app/jobs/page-f5aa89a47c50efd8.js +0 -1
  328. package/apps/web/.next/static/chunks/app/layout-2079f4964aa7314e.js +0 -1
  329. package/apps/web/.next/static/chunks/app/login/layout-07f0f73ac9839899.js +0 -1
  330. package/apps/web/.next/static/chunks/app/login/page-aa259283dc38e8f9.js +0 -1
  331. package/apps/web/.next/static/chunks/app/page-e83437b608104dff.js +0 -1
  332. package/apps/web/.next/static/chunks/app/settings/env-vars/page-06479dbdfb78b76b.js +0 -1
  333. package/apps/web/.next/static/chunks/app/settings/mcps/page-75650686ed6490c7.js +0 -1
  334. package/apps/web/.next/static/chunks/app/settings/page-e1e62fc41ff6cddd.js +0 -1
  335. package/apps/web/.next/static/chunks/framework-811407f832a33072.js +0 -1
  336. package/apps/web/.next/static/chunks/main-3f1cddbdd67b1546.js +0 -1
  337. package/apps/web/.next/static/chunks/main-app-cebd8a6a5ccbf72d.js +0 -1
  338. package/apps/web/.next/static/chunks/pages/_app-50fa07b56b2d29ac.js +0 -1
  339. package/apps/web/.next/static/chunks/pages/_error-fed8688bdd23f211.js +0 -1
  340. package/apps/web/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  341. package/apps/web/.next/static/chunks/webpack-6c05566dba553c97.js +0 -1
  342. package/apps/web/.next/static/css/15371687405525e2.css +0 -5
  343. package/apps/web/.next/static/ikfNbLhuw7jntn35bz0lk/_buildManifest.js +0 -1
  344. package/apps/web/.next/static/ikfNbLhuw7jntn35bz0lk/_ssgManifest.js +0 -1
  345. package/apps/web/.next/trace +0 -5
  346. package/apps/web/.next/types/app/agents/[slug]/page.ts +0 -84
  347. package/apps/web/.next/types/app/agents/new/page.ts +0 -84
  348. package/apps/web/.next/types/app/api/agents/[id]/access/route.ts +0 -347
  349. package/apps/web/.next/types/app/api/agents/[id]/claude-md/route.ts +0 -347
  350. package/apps/web/.next/types/app/api/agents/[id]/logs/route.ts +0 -347
  351. package/apps/web/.next/types/app/api/agents/[id]/manifest/route.ts +0 -347
  352. package/apps/web/.next/types/app/api/agents/[id]/mcps/route.ts +0 -347
  353. package/apps/web/.next/types/app/api/agents/[id]/memories/[memId]/route.ts +0 -347
  354. package/apps/web/.next/types/app/api/agents/[id]/memories/route.ts +0 -347
  355. package/apps/web/.next/types/app/api/agents/[id]/permissions/route.ts +0 -347
  356. package/apps/web/.next/types/app/api/agents/[id]/reload/route.ts +0 -347
  357. package/apps/web/.next/types/app/api/agents/[id]/restrictions/route.ts +0 -347
  358. package/apps/web/.next/types/app/api/agents/[id]/route.ts +0 -347
  359. package/apps/web/.next/types/app/api/agents/[id]/skills/[skillId]/route.ts +0 -347
  360. package/apps/web/.next/types/app/api/agents/[id]/skills/route.ts +0 -347
  361. package/apps/web/.next/types/app/api/agents/[id]/slack-info/route.ts +0 -347
  362. package/apps/web/.next/types/app/api/agents/[id]/snapshots/[sid]/restore/route.ts +0 -347
  363. package/apps/web/.next/types/app/api/agents/[id]/snapshots/[sid]/route.ts +0 -347
  364. package/apps/web/.next/types/app/api/agents/[id]/snapshots/route.ts +0 -347
  365. package/apps/web/.next/types/app/api/agents/[id]/start/route.ts +0 -347
  366. package/apps/web/.next/types/app/api/agents/[id]/stop/route.ts +0 -347
  367. package/apps/web/.next/types/app/api/agents/route.ts +0 -347
  368. package/apps/web/.next/types/app/api/auth/login/route.ts +0 -347
  369. package/apps/web/.next/types/app/api/auth/logout/route.ts +0 -347
  370. package/apps/web/.next/types/app/api/auth/me/route.ts +0 -347
  371. package/apps/web/.next/types/app/api/auth/users/[id]/route.ts +0 -347
  372. package/apps/web/.next/types/app/api/auth/users/route.ts +0 -347
  373. package/apps/web/.next/types/app/api/env-vars/[key]/route.ts +0 -347
  374. package/apps/web/.next/types/app/api/env-vars/route.ts +0 -347
  375. package/apps/web/.next/types/app/api/jobs/[id]/route.ts +0 -347
  376. package/apps/web/.next/types/app/api/jobs/[id]/runs/route.ts +0 -347
  377. package/apps/web/.next/types/app/api/jobs/route.ts +0 -347
  378. package/apps/web/.next/types/app/api/mcps/[id]/route.ts +0 -347
  379. package/apps/web/.next/types/app/api/mcps/[id]/test/route.ts +0 -347
  380. package/apps/web/.next/types/app/api/mcps/route.ts +0 -347
  381. package/apps/web/.next/types/app/api/settings/route.ts +0 -347
  382. package/apps/web/.next/types/app/jobs/page.ts +0 -84
  383. package/apps/web/.next/types/app/login/layout.ts +0 -84
  384. package/apps/web/.next/types/app/login/page.ts +0 -84
  385. package/apps/web/.next/types/app/page.ts +0 -84
  386. package/apps/web/.next/types/app/settings/env-vars/page.ts +0 -84
  387. package/apps/web/.next/types/app/settings/mcps/page.ts +0 -84
  388. package/apps/web/.next/types/app/settings/page.ts +0 -84
  389. package/apps/web/.next/types/cache-life.d.ts +0 -141
  390. package/apps/web/.next/types/package.json +0 -1
  391. package/apps/web/.next/types/routes.d.ts +0 -114
  392. package/apps/web/.next/types/validator.ts +0 -448
  393. package/apps/web/Dockerfile +0 -37
  394. package/apps/web/next-env.d.ts +0 -6
  395. package/apps/web/next.config.js +0 -6
  396. package/apps/web/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  397. package/apps/web/package.json +0 -48
  398. package/apps/web/postcss.config.js +0 -3
  399. package/apps/web/public/logo.svg +0 -17
  400. package/apps/web/src/app/agents/[slug]/page.tsx +0 -2235
  401. package/apps/web/src/app/agents/new/page.tsx +0 -1161
  402. package/apps/web/src/app/api/agents/[id]/access/route.ts +0 -76
  403. package/apps/web/src/app/api/agents/[id]/claude-md/route.ts +0 -111
  404. package/apps/web/src/app/api/agents/[id]/logs/route.ts +0 -84
  405. package/apps/web/src/app/api/agents/[id]/manifest/route.ts +0 -32
  406. package/apps/web/src/app/api/agents/[id]/mcps/route.ts +0 -73
  407. package/apps/web/src/app/api/agents/[id]/memories/[memId]/route.ts +0 -31
  408. package/apps/web/src/app/api/agents/[id]/memories/route.ts +0 -56
  409. package/apps/web/src/app/api/agents/[id]/permissions/route.ts +0 -74
  410. package/apps/web/src/app/api/agents/[id]/reload/route.ts +0 -33
  411. package/apps/web/src/app/api/agents/[id]/restrictions/route.ts +0 -85
  412. package/apps/web/src/app/api/agents/[id]/route.ts +0 -81
  413. package/apps/web/src/app/api/agents/[id]/skills/[skillId]/route.ts +0 -52
  414. package/apps/web/src/app/api/agents/[id]/skills/route.ts +0 -80
  415. package/apps/web/src/app/api/agents/[id]/slack-info/route.ts +0 -38
  416. package/apps/web/src/app/api/agents/[id]/snapshots/[sid]/restore/route.ts +0 -61
  417. package/apps/web/src/app/api/agents/[id]/snapshots/[sid]/route.ts +0 -53
  418. package/apps/web/src/app/api/agents/[id]/snapshots/route.ts +0 -84
  419. package/apps/web/src/app/api/agents/[id]/start/route.ts +0 -35
  420. package/apps/web/src/app/api/agents/[id]/stop/route.ts +0 -35
  421. package/apps/web/src/app/api/agents/route.ts +0 -99
  422. package/apps/web/src/app/api/auth/login/route.ts +0 -39
  423. package/apps/web/src/app/api/auth/logout/route.ts +0 -21
  424. package/apps/web/src/app/api/auth/me/route.ts +0 -24
  425. package/apps/web/src/app/api/auth/users/[id]/route.ts +0 -48
  426. package/apps/web/src/app/api/auth/users/route.ts +0 -63
  427. package/apps/web/src/app/api/env-vars/[key]/route.ts +0 -66
  428. package/apps/web/src/app/api/env-vars/route.ts +0 -59
  429. package/apps/web/src/app/api/jobs/[id]/route.ts +0 -51
  430. package/apps/web/src/app/api/jobs/[id]/runs/route.ts +0 -24
  431. package/apps/web/src/app/api/jobs/route.ts +0 -42
  432. package/apps/web/src/app/api/mcps/[id]/route.ts +0 -60
  433. package/apps/web/src/app/api/mcps/[id]/test/route.ts +0 -195
  434. package/apps/web/src/app/api/mcps/route.ts +0 -72
  435. package/apps/web/src/app/api/settings/route.ts +0 -42
  436. package/apps/web/src/app/globals.css +0 -124
  437. package/apps/web/src/app/icon.svg +0 -17
  438. package/apps/web/src/app/jobs/page.tsx +0 -543
  439. package/apps/web/src/app/layout-shell.tsx +0 -89
  440. package/apps/web/src/app/layout.tsx +0 -18
  441. package/apps/web/src/app/login/layout.tsx +0 -9
  442. package/apps/web/src/app/login/page.tsx +0 -150
  443. package/apps/web/src/app/page.tsx +0 -573
  444. package/apps/web/src/app/settings/env-vars/page.tsx +0 -216
  445. package/apps/web/src/app/settings/mcps/page.tsx +0 -763
  446. package/apps/web/src/app/settings/page.tsx +0 -528
  447. package/apps/web/src/app/sidebar.tsx +0 -345
  448. package/apps/web/src/lib/__tests__/api-guard.test.ts +0 -189
  449. package/apps/web/src/lib/__tests__/auth.test.ts +0 -262
  450. package/apps/web/src/lib/__tests__/boss-registry.test.ts +0 -323
  451. package/apps/web/src/lib/__tests__/compile.test.ts +0 -161
  452. package/apps/web/src/lib/__tests__/db-agent-hierarchy.test.ts +0 -136
  453. package/apps/web/src/lib/__tests__/db-env-vars.test.ts +0 -216
  454. package/apps/web/src/lib/__tests__/db-restrictions.test.ts +0 -117
  455. package/apps/web/src/lib/__tests__/db.integration.test.ts +0 -271
  456. package/apps/web/src/lib/__tests__/diff.test.ts +0 -102
  457. package/apps/web/src/lib/__tests__/mcp-mask.test.ts +0 -274
  458. package/apps/web/src/lib/__tests__/skill-templates.test.ts +0 -237
  459. package/apps/web/src/lib/__tests__/slack-manifest.test.ts +0 -105
  460. package/apps/web/src/lib/api-guard.ts +0 -68
  461. package/apps/web/src/lib/auth-context.tsx +0 -71
  462. package/apps/web/src/lib/auth.ts +0 -128
  463. package/apps/web/src/lib/boss-registry.ts +0 -90
  464. package/apps/web/src/lib/compile.ts +0 -51
  465. package/apps/web/src/lib/db.ts +0 -1196
  466. package/apps/web/src/lib/diff.ts +0 -43
  467. package/apps/web/src/lib/mcp-mask.ts +0 -91
  468. package/apps/web/src/lib/portal.tsx +0 -23
  469. package/apps/web/src/lib/skill-templates.ts +0 -148
  470. package/apps/web/src/lib/slack-manifest.ts +0 -85
  471. package/apps/web/src/middleware.ts +0 -68
  472. package/apps/web/tailwind.config.js +0 -6
  473. package/apps/web/tsconfig.json +0 -23
  474. package/apps/web/vitest.config.mts +0 -21
  475. package/cli/.claude/settings.local.json +0 -6
  476. package/cli/node_modules/.package-lock.json +0 -427
  477. package/cli/node_modules/commander/LICENSE +0 -22
  478. package/cli/node_modules/commander/Readme.md +0 -1157
  479. package/cli/node_modules/commander/esm.mjs +0 -16
  480. package/cli/node_modules/commander/index.js +0 -24
  481. package/cli/node_modules/commander/lib/argument.js +0 -149
  482. package/cli/node_modules/commander/lib/command.js +0 -2509
  483. package/cli/node_modules/commander/lib/error.js +0 -39
  484. package/cli/node_modules/commander/lib/help.js +0 -520
  485. package/cli/node_modules/commander/lib/option.js +0 -330
  486. package/cli/node_modules/commander/lib/suggestSimilar.js +0 -101
  487. package/cli/node_modules/commander/package-support.json +0 -16
  488. package/cli/node_modules/commander/package.json +0 -84
  489. package/cli/node_modules/commander/typings/esm.d.mts +0 -3
  490. package/cli/node_modules/commander/typings/index.d.ts +0 -969
  491. package/cli/package-lock.json +0 -449
  492. package/cli/package.json +0 -44
  493. package/cli/src/commands/init.ts +0 -514
  494. package/cli/src/commands/manage.ts +0 -115
  495. package/cli/src/index.ts +0 -63
  496. package/cli/tsconfig.json +0 -14
  497. package/docker-compose.yml +0 -122
  498. package/docs/agents/boss-agents.mdx +0 -108
  499. package/docs/agents/creating-agents.mdx +0 -132
  500. package/docs/agents/memory.mdx +0 -113
  501. package/docs/agents/tools.mdx +0 -103
  502. package/docs/configuration/env-vars.mdx +0 -166
  503. package/docs/configuration/mcp-servers.mdx +0 -203
  504. package/docs/configuration/slack-app.mdx +0 -175
  505. package/docs/docs.json +0 -79
  506. package/docs/favicon.svg +0 -17
  507. package/docs/features/history.mdx +0 -60
  508. package/docs/features/import-export.mdx +0 -77
  509. package/docs/features/logs.mdx +0 -131
  510. package/docs/features/multi-workspace.mdx +0 -90
  511. package/docs/features/scheduled-jobs.mdx +0 -231
  512. package/docs/features/users.mdx +0 -92
  513. package/docs/introduction.mdx +0 -160
  514. package/docs/logo/dark.svg +0 -17
  515. package/docs/logo/light.svg +0 -17
  516. package/docs/logo/wide-dark.svg +0 -12
  517. package/docs/logo/wide-light.svg +0 -12
  518. package/docs/quickstart.mdx +0 -270
  519. package/docs/self-hosting/docker.mdx +0 -151
  520. package/docs/self-hosting/production.mdx +0 -176
  521. package/packages/shared/dist/index.d.ts +0 -8
  522. package/packages/shared/dist/index.d.ts.map +0 -1
  523. package/packages/shared/dist/index.js +0 -24
  524. package/packages/shared/dist/index.js.map +0 -1
  525. package/packages/shared/dist/types.d.ts +0 -584
  526. package/packages/shared/dist/types.d.ts.map +0 -1
  527. package/packages/shared/dist/types.js +0 -39
  528. package/packages/shared/dist/types.js.map +0 -1
  529. package/packages/shared/package.json +0 -15
  530. package/packages/shared/src/db/schema.sql +0 -354
  531. package/packages/shared/src/index.ts +0 -8
  532. package/packages/shared/src/types.ts +0 -683
  533. package/packages/shared/tsconfig.json +0 -17
  534. package/scripts/dev.sh +0 -45
@@ -1,1161 +0,0 @@
1
- 'use client';
2
-
3
- /**
4
- * @fileoverview 5-step agent onboarding wizard.
5
- *
6
- * Steps: Name & Role → Slack App → Credentials → Tools → Review
7
- *
8
- * Route: /agents/new
9
- * @module web/app/agents/new
10
- */
11
-
12
- import React, { useState, useEffect, useRef } from 'react';
13
- import { AlertTriangle, Eye, EyeOff } from 'lucide-react';
14
- import { useRouter } from 'next/navigation';
15
- import type { Agent, McpServer } from '@slackhive/shared';
16
- import { generateSlackManifest } from '@/lib/slack-manifest';
17
-
18
- // ─── State ────────────────────────────────────────────────────────────────────
19
-
20
- interface AgentExportPayload {
21
- version: number;
22
- exportedAt?: string;
23
- claudeMd: string;
24
- skills: { category: string; filename: string; content: string; sortOrder: number }[];
25
- }
26
-
27
- interface WizardState {
28
- name: string; slug: string; description: string; persona: string;
29
- model: string; isBoss: boolean;
30
- reportsToIds: string[];
31
- slackBotToken: string; slackAppToken: string; slackSigningSecret: string;
32
- mcpServerIds: string[];
33
- skillTemplate: 'blank' | 'data-analyst' | 'writer' | 'developer';
34
- importPayload: AgentExportPayload | null;
35
- }
36
-
37
- const INITIAL: WizardState = {
38
- name: '', slug: '', description: '', persona: '',
39
- model: 'claude-opus-4-6', isBoss: false,
40
- reportsToIds: [],
41
- slackBotToken: '', slackAppToken: '', slackSigningSecret: '',
42
- mcpServerIds: [], skillTemplate: 'blank', importPayload: null,
43
- };
44
-
45
- const MODELS = [
46
- { value: 'claude-opus-4-6', label: 'Opus 4.6', sub: 'Most capable' },
47
- { value: 'claude-sonnet-4-6', label: 'Sonnet 4.6', sub: 'Balanced' },
48
- { value: 'claude-haiku-4-5-20251001', label: 'Haiku 4.5', sub: 'Fastest' },
49
- ];
50
-
51
- const TEMPLATES = [
52
- { value: 'blank', label: 'Blank', desc: 'Minimal identity only' },
53
- { value: 'data-analyst', label: 'Data Analyst', desc: 'SQL, Redshift, metrics' },
54
- { value: 'writer', label: 'Writer', desc: 'Content & summaries' },
55
- { value: 'developer', label: 'Developer', desc: 'Code review & dev' },
56
- ];
57
-
58
- // ─── Wizard ───────────────────────────────────────────────────────────────────
59
-
60
- /**
61
- * New agent onboarding wizard.
62
- *
63
- * @returns {JSX.Element}
64
- */
65
- export default function NewAgentWizard() {
66
- const router = useRouter();
67
- const [step, setStep] = useState(0);
68
- const [state, setState] = useState<WizardState>(INITIAL);
69
- const [catalog, setCatalog] = useState<McpServer[]>([]);
70
- const [agents, setAgents] = useState<Agent[]>([]);
71
- const [submitting, setSubmitting] = useState(false);
72
- const [error, setError] = useState('');
73
-
74
- const bosses = agents.filter(a => a.isBoss);
75
-
76
- useEffect(() => {
77
- fetch('/api/mcps').then(r => r.json()).then(setCatalog);
78
- fetch('/api/agents').then(r => r.json()).then((a: Agent[]) => {
79
- setAgents(a);
80
- }).catch(() => {});
81
- }, []);
82
-
83
- const update = (patch: Partial<WizardState>) => setState(s => ({ ...s, ...patch }));
84
-
85
- // Boss agents skip the MCPs & Skills step (their CLAUDE.md is auto-generated)
86
- const totalSteps = state.isBoss ? 4 : 5;
87
- const stepLabels = state.isBoss
88
- ? ['Name & Role', 'Slack App', 'Credentials', 'Review']
89
- : ['Name & Role', 'Slack App', 'Credentials', 'Tools', 'Review'];
90
-
91
- const next = () => setStep(s => Math.min(s + 1, totalSteps - 1));
92
- const back = () => setStep(s => Math.max(s - 1, 0));
93
-
94
- const canNext = () => {
95
- if (step === 0) return !!(state.name && state.slug);
96
- if (step === 2) return !!(state.slackBotToken && state.slackAppToken && state.slackSigningSecret);
97
- return true;
98
- };
99
-
100
- const submit = async () => {
101
- setSubmitting(true); setError('');
102
- try {
103
- const r = await fetch('/api/agents', {
104
- method: 'POST', headers: { 'Content-Type': 'application/json' },
105
- body: JSON.stringify({
106
- ...state,
107
- reportsTo: state.isBoss ? [] : state.reportsToIds,
108
- }),
109
- });
110
- const data = await r.json();
111
- if (!r.ok) { setError(data.error ?? 'Failed to create agent'); return; }
112
-
113
- // Apply imported config if provided
114
- if (state.importPayload) {
115
- const { claudeMd, skills } = state.importPayload;
116
- await fetch(`/api/agents/${data.id}/claude-md`, {
117
- method: 'PUT', headers: { 'Content-Type': 'text/plain' }, body: claudeMd,
118
- });
119
- await Promise.all(skills.map(s =>
120
- fetch(`/api/agents/${data.id}/skills`, {
121
- method: 'POST', headers: { 'Content-Type': 'application/json' },
122
- body: JSON.stringify(s),
123
- })
124
- ));
125
- }
126
-
127
- router.push(`/agents/${data.slug}`);
128
- } finally { setSubmitting(false); }
129
- };
130
-
131
- return (
132
- <div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column' }}>
133
- {/* Top bar */}
134
- <div style={{
135
- padding: '18px 36px', borderBottom: '1px solid var(--border)',
136
- display: 'flex', alignItems: 'center', gap: 12,
137
- background: 'var(--surface)',
138
- }}>
139
- <a href="/" style={{ color: 'var(--muted)', textDecoration: 'none', fontSize: 12 }}>
140
- ← Agents
141
- </a>
142
- <span style={{ color: 'var(--border-2)' }}>/</span>
143
- <span style={{ fontSize: 13, color: 'var(--text)', fontWeight: 500 }}>New Agent</span>
144
- </div>
145
-
146
- {/* Content */}
147
- <div style={{ flex: 1, display: 'flex', padding: '40px 36px', gap: 36, maxWidth: 860, width: '100%' }}>
148
-
149
- {/* ── Left: step nav ───────────────────────────────────────────── */}
150
- <div style={{ width: 180, flexShrink: 0 }}>
151
- <div style={{ position: 'sticky', top: 40 }}>
152
- {stepLabels.map((label, i) => (
153
- <div
154
- key={i}
155
- onClick={() => i < step && setStep(i)}
156
- style={{
157
- display: 'flex', alignItems: 'center', gap: 10,
158
- padding: '9px 0', cursor: i < step ? 'pointer' : 'default',
159
- }}
160
- >
161
- <div style={{
162
- width: 26, height: 26, borderRadius: '50%', flexShrink: 0,
163
- display: 'flex', alignItems: 'center', justifyContent: 'center',
164
- fontSize: i < step ? 11 : 12, fontWeight: 600,
165
- background: i < step ? 'var(--accent)' :
166
- i === step ? 'rgba(59,130,246,0.15)' :
167
- 'var(--border)',
168
- color: i < step ? '#fff' :
169
- i === step ? 'var(--accent)' :
170
- 'var(--subtle)',
171
- border: i === step ? '1.5px solid var(--accent)' : '1.5px solid transparent',
172
- transition: 'all 0.2s',
173
- }}>
174
- {i < step ? '✓' : i + 1}
175
- </div>
176
- <span style={{
177
- fontSize: 13,
178
- fontWeight: i === step ? 600 : 400,
179
- color: i === step ? 'var(--text)' : i < step ? 'var(--muted)' : 'var(--subtle)',
180
- transition: 'color 0.2s',
181
- }}>{label}</span>
182
- </div>
183
- ))}
184
- </div>
185
- </div>
186
-
187
- {/* ── Right: step panel ────────────────────────────────────────── */}
188
- <div style={{ flex: 1, minWidth: 0 }}>
189
- <div
190
- style={{
191
- background: 'var(--surface)',
192
- borderRadius: 'var(--radius-lg)',
193
- padding: '32px',
194
- boxShadow: 'var(--shadow-card)',
195
- }}
196
- className="fade-up"
197
- key={step}
198
- >
199
- {step === 0 && <Step1Identity state={state} update={update} bosses={bosses} />}
200
- {step === 1 && <Step2SlackApp state={state} />}
201
- {step === 2 && <Step3Tokens state={state} update={update} />}
202
- {step === 3 && !state.isBoss && <Step4McpsSkills state={state} update={update} catalog={catalog} />}
203
- {((step === 3 && state.isBoss) || step === 4) && <Step5Review state={state} update={update} catalog={catalog} agents={agents} />}
204
- </div>
205
-
206
- {error && (
207
- <div style={{
208
- marginTop: 12, background: 'rgba(239,68,68,0.1)', border: '1px solid rgba(239,68,68,0.3)',
209
- borderRadius: 8, padding: '10px 14px', fontSize: 13, color: '#f87171',
210
- }}>{error}</div>
211
- )}
212
-
213
- {/* Nav buttons */}
214
- <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 16 }}>
215
- <button
216
- onClick={back} disabled={step === 0}
217
- style={{
218
- background: 'transparent', color: step === 0 ? 'var(--subtle)' : 'var(--muted)',
219
- border: '1.5px solid var(--border)', borderRadius: 'var(--radius)',
220
- padding: '10px 20px', fontSize: 14, fontWeight: 500, cursor: step === 0 ? 'not-allowed' : 'pointer',
221
- fontFamily: 'var(--font-sans)', transition: 'border-color 0.15s, color 0.15s',
222
- }}
223
- onMouseEnter={e => { if (step > 0) { (e.currentTarget as HTMLElement).style.color = 'var(--text)'; (e.currentTarget as HTMLElement).style.borderColor = 'var(--border-2)'; } }}
224
- onMouseLeave={e => { (e.currentTarget as HTMLElement).style.color = 'var(--muted)'; (e.currentTarget as HTMLElement).style.borderColor = 'var(--border)'; }}
225
- >← Back</button>
226
-
227
- {step < totalSteps - 1 ? (
228
- <button
229
- onClick={next} disabled={!canNext()}
230
- style={{
231
- background: canNext() ? 'var(--accent)' : 'var(--border)',
232
- color: '#fff', border: 'none', borderRadius: 'var(--radius)',
233
- padding: '10px 24px', fontSize: 14, fontWeight: 600,
234
- cursor: canNext() ? 'pointer' : 'not-allowed',
235
- fontFamily: 'var(--font-sans)', transition: 'opacity 0.15s, transform 0.15s',
236
- boxShadow: canNext() ? 'var(--shadow-sm)' : 'none',
237
- letterSpacing: '-0.01em',
238
- }}
239
- onMouseEnter={e => { if (canNext()) { (e.currentTarget as HTMLElement).style.opacity = '0.88'; (e.currentTarget as HTMLElement).style.transform = 'translateY(-1px)'; } }}
240
- onMouseLeave={e => { (e.currentTarget as HTMLElement).style.opacity = '1'; (e.currentTarget as HTMLElement).style.transform = 'translateY(0)'; }}
241
- >Continue →</button>
242
- ) : (
243
- <button
244
- onClick={submit}
245
- disabled={submitting || !state.slackBotToken || !state.slackAppToken || !state.slackSigningSecret}
246
- style={{
247
- background: submitting ? 'var(--border)' : '#16a34a',
248
- color: '#fff', border: 'none', borderRadius: 'var(--radius)',
249
- padding: '10px 24px', fontSize: 14, fontWeight: 600,
250
- cursor: submitting ? 'not-allowed' : 'pointer',
251
- fontFamily: 'var(--font-sans)', transition: 'opacity 0.15s, transform 0.15s',
252
- boxShadow: !submitting ? 'var(--shadow-sm)' : 'none',
253
- letterSpacing: '-0.01em',
254
- }}
255
- onMouseEnter={e => { if (!submitting) { (e.currentTarget as HTMLElement).style.opacity = '0.88'; (e.currentTarget as HTMLElement).style.transform = 'translateY(-1px)'; } }}
256
- onMouseLeave={e => { (e.currentTarget as HTMLElement).style.opacity = '1'; (e.currentTarget as HTMLElement).style.transform = 'translateY(0)'; }}
257
- >{submitting ? 'Creating…' : 'Create Agent'}</button>
258
- )}
259
- </div>
260
- </div>
261
- </div>
262
- </div>
263
- );
264
- }
265
-
266
- // ─── Step 1: Identity ─────────────────────────────────────────────────────────
267
-
268
- function Step1Identity({ state, update, bosses }: {
269
- state: WizardState; update: (p: Partial<WizardState>) => void;
270
- bosses: Agent[];
271
- }) {
272
- const autoSlug = (name: string) =>
273
- name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
274
-
275
- const toggleBoss = (id: string) => {
276
- const next = state.reportsToIds.includes(id)
277
- ? state.reportsToIds.filter(x => x !== id)
278
- : [...state.reportsToIds, id];
279
- update({ reportsToIds: next });
280
- };
281
-
282
- return (
283
- <div>
284
- <StepHeader step={1} title="Name your agent" desc="Give it a name and decide if it leads a team or works as a specialist." />
285
- <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14, marginBottom: 14 }}>
286
- <Field label="Agent Name *" value={state.name} placeholder="e.g. GILFOYLE"
287
- onChange={v => update({ name: v, slug: autoSlug(v) })} />
288
- <Field label="Slug *" value={state.slug} placeholder="e.g. gilfoyle"
289
- hint="Lowercase, hyphens only"
290
- onChange={v => update({ slug: v.toLowerCase().replace(/[^a-z0-9-]/g, '') })} />
291
- </div>
292
- <Field label="Description" value={state.description} placeholder="Data warehouse NLQ, Redshift queries, business metrics"
293
- hint="Used by the boss to decide when to delegate here."
294
- onChange={v => update({ description: v })} />
295
- <TextArea label="Persona" value={state.persona}
296
- placeholder="You are GILFOYLE, a cynical but brilliant data engineer…"
297
- hint="Injected into CLAUDE.md as the agent's identity and personality."
298
- rows={3} onChange={v => update({ persona: v })} />
299
-
300
- {/* Model selector */}
301
- <div style={{ marginBottom: 14 }}>
302
- <label style={{ display: 'block', fontSize: 12, fontWeight: 500, color: 'var(--muted)', marginBottom: 8 }}>
303
- Model
304
- </label>
305
- <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8 }}>
306
- {MODELS.map(m => (
307
- <label key={m.value} style={{
308
- display: 'flex', flexDirection: 'column', gap: 2,
309
- padding: '10px 12px', border: `1px solid ${state.model === m.value ? 'var(--accent)' : 'var(--border)'}`,
310
- borderRadius: 8, cursor: 'pointer',
311
- background: state.model === m.value ? 'rgba(59,130,246,0.08)' : 'transparent',
312
- transition: 'border-color 0.15s, background 0.15s',
313
- }}>
314
- <input type="radio" name="model" value={m.value} checked={state.model === m.value}
315
- onChange={() => update({ model: m.value })} style={{ display: 'none' }} />
316
- <span style={{ fontSize: 12.5, fontWeight: 600, color: state.model === m.value ? 'var(--accent)' : 'var(--text)' }}>
317
- {m.label}
318
- </span>
319
- <span style={{ fontSize: 11, color: 'var(--subtle)' }}>{m.sub}</span>
320
- </label>
321
- ))}
322
- </div>
323
- </div>
324
-
325
- {/* Boss toggle */}
326
- <div style={{ marginBottom: 14 }}>
327
- <label style={{
328
- display: 'flex', alignItems: 'center', gap: 10,
329
- padding: '10px 14px', border: `1px solid ${state.isBoss ? 'var(--accent)' : 'var(--border)'}`,
330
- borderRadius: 8, cursor: 'pointer',
331
- background: state.isBoss ? 'rgba(59,130,246,0.08)' : 'transparent',
332
- transition: 'all 0.15s',
333
- }}>
334
- <input type="checkbox" checked={state.isBoss}
335
- onChange={e => update({ isBoss: e.target.checked, reportsToIds: [] })}
336
- style={{ accentColor: 'var(--accent)', width: 14, height: 14 }} />
337
- <div>
338
- <div style={{ fontSize: 13, fontWeight: 600, color: state.isBoss ? 'var(--accent)' : 'var(--text)' }}>
339
- This agent is a Boss
340
- </div>
341
- <div style={{ fontSize: 11.5, color: 'var(--subtle)' }}>
342
- Boss agents orchestrate specialists. Their CLAUDE.md is auto-generated from the team.
343
- </div>
344
- </div>
345
- </label>
346
- </div>
347
-
348
- {/* Reports to — multi-select boss agents (only shown for non-boss agents) */}
349
- {!state.isBoss && (
350
- <div style={{ marginBottom: 14 }}>
351
- <label style={{ display: 'block', fontSize: 12, fontWeight: 500, color: 'var(--muted)', marginBottom: 6 }}>
352
- Reports to
353
- </label>
354
- {bosses.length === 0 ? (
355
- <div style={{
356
- border: '1px dashed var(--border)', borderRadius: 8, padding: '12px 14px',
357
- fontSize: 12.5, color: 'var(--subtle)',
358
- }}>
359
- No boss agents yet — create a boss first, or mark this agent as a boss above.
360
- </div>
361
- ) : (
362
- <div style={{ border: '1px solid var(--border)', borderRadius: 10, overflow: 'hidden' }}>
363
- {bosses.map((boss, i) => (
364
- <label key={boss.id} style={{
365
- display: 'flex', alignItems: 'center', gap: 12, padding: '10px 14px',
366
- cursor: 'pointer',
367
- borderBottom: i < bosses.length - 1 ? '1px solid var(--border)' : 'none',
368
- background: state.reportsToIds.includes(boss.id) ? 'rgba(59,130,246,0.06)' : 'transparent',
369
- transition: 'background 0.12s',
370
- }}>
371
- <input type="checkbox"
372
- checked={state.reportsToIds.includes(boss.id)}
373
- onChange={() => toggleBoss(boss.id)}
374
- style={{ accentColor: 'var(--accent)', width: 14, height: 14 }} />
375
- <span style={{ fontSize: 13, color: 'var(--text)' }}>{boss.name}</span>
376
- </label>
377
- ))}
378
- </div>
379
- )}
380
- {state.reportsToIds.length > 0 && (
381
- <p style={{ margin: '5px 0 0', fontSize: 11, color: 'var(--subtle)' }}>
382
- This agent will appear in the team registry of {state.reportsToIds.length} boss{state.reportsToIds.length > 1 ? 'es' : ''}.
383
- </p>
384
- )}
385
- </div>
386
- )}
387
-
388
- {/* Import config */}
389
- </div>
390
- );
391
- }
392
-
393
- // ─── Import config picker ─────────────────────────────────────────────────────
394
-
395
- function ImportConfigPicker({ value, onChange, compact }: {
396
- value: AgentExportPayload | null;
397
- onChange: (p: AgentExportPayload | null) => void;
398
- compact?: boolean;
399
- }) {
400
- const [error, setError] = useState('');
401
- const ref = useRef<HTMLInputElement>(null);
402
-
403
- const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {
404
- const file = e.target.files?.[0];
405
- if (!file) return;
406
- e.target.value = '';
407
- setError('');
408
- const reader = new FileReader();
409
- reader.onload = (ev) => {
410
- try {
411
- const data = JSON.parse(ev.target?.result as string);
412
- if (typeof data.claudeMd !== 'string' || !Array.isArray(data.skills)) {
413
- setError('Invalid file — must contain claudeMd (string) and skills (array)'); return;
414
- }
415
- if (typeof data.version !== 'number') {
416
- setError('Invalid file — missing version field'); return;
417
- }
418
- onChange(data);
419
- } catch {
420
- setError('Could not parse file — must be valid JSON');
421
- }
422
- };
423
- reader.readAsText(file);
424
- };
425
-
426
- if (compact) {
427
- return (
428
- <div>
429
- {!value ? (
430
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
431
- <div>
432
- <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--text)', marginBottom: 1 }}>Import config</div>
433
- <div style={{ fontSize: 11.5, color: 'var(--subtle)' }}>Load CLAUDE.md + skills from an exported agent</div>
434
- {error && <div style={{ fontSize: 11, color: 'var(--danger)', marginTop: 3 }}>{error}</div>}
435
- </div>
436
- <button type="button" onClick={() => ref.current?.click()} style={{
437
- display: 'flex', alignItems: 'center', gap: 5, flexShrink: 0,
438
- padding: '6px 12px', borderRadius: 6,
439
- border: '1px solid var(--border-2)', background: '#fff',
440
- fontSize: 12, fontWeight: 500, color: 'var(--muted)', cursor: 'pointer',
441
- }}>
442
- <svg width="12" height="12" viewBox="0 0 16 16" fill="none">
443
- <path d="M8 2v9M4 7l4 4 4-4M2 13h12" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
444
- </svg>
445
- Choose file
446
- </button>
447
- </div>
448
- ) : (
449
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
450
- <div>
451
- <div style={{ fontSize: 13, fontWeight: 600, color: '#7c3aed' }}>
452
- Config loaded
453
- </div>
454
- <div style={{ fontSize: 11.5, color: 'var(--muted)' }}>
455
- {value.skills.length} skill{value.skills.length !== 1 ? 's' : ''} · CLAUDE.md included
456
- </div>
457
- </div>
458
- <button type="button" onClick={() => { onChange(null); setError(''); }} style={{
459
- background: 'none', border: 'none', cursor: 'pointer',
460
- fontSize: 11.5, color: 'var(--muted)', padding: '3px 6px',
461
- }}>Remove</button>
462
- </div>
463
- )}
464
- <input ref={ref} type="file" accept=".json" style={{ display: 'none' }} onChange={handleFile} />
465
- </div>
466
- );
467
- }
468
-
469
- return (
470
- <div style={{
471
- marginTop: 20, padding: '14px 16px', borderRadius: 10,
472
- border: `1.5px dashed ${value ? '#8b5cf6' : 'var(--border-2)'}`,
473
- background: value ? 'rgba(139,92,246,0.03)' : 'var(--surface)',
474
- transition: 'all 0.2s',
475
- }}>
476
- {!value ? (
477
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
478
- <div>
479
- <div style={{ fontSize: 13, fontWeight: 500, color: 'var(--text)', marginBottom: 2 }}>
480
- Import from existing config
481
- </div>
482
- <div style={{ fontSize: 12, color: 'var(--muted)' }}>
483
- Load CLAUDE.md + skills from a previously exported agent
484
- </div>
485
- {error && <div style={{ fontSize: 11.5, color: 'var(--danger)', marginTop: 4 }}>{error}</div>}
486
- </div>
487
- <button type="button" onClick={() => ref.current?.click()} style={{
488
- display: 'flex', alignItems: 'center', gap: 6, flexShrink: 0,
489
- padding: '7px 14px', borderRadius: 7,
490
- border: '1.5px solid var(--border-2)', background: '#fff',
491
- fontSize: 12.5, fontWeight: 500, color: 'var(--muted)', cursor: 'pointer',
492
- transition: 'all 0.15s',
493
- }}
494
- onMouseEnter={e => { (e.currentTarget as HTMLElement).style.borderColor = '#8b5cf6'; (e.currentTarget as HTMLElement).style.color = 'var(--text)'; }}
495
- onMouseLeave={e => { (e.currentTarget as HTMLElement).style.borderColor = 'var(--border-2)'; (e.currentTarget as HTMLElement).style.color = 'var(--muted)'; }}
496
- >
497
- <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
498
- <path d="M8 2v9M4 7l4 4 4-4M2 13h12" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
499
- </svg>
500
- Choose file
501
- </button>
502
- </div>
503
- ) : (
504
- <div style={{ display: 'flex', alignItems: 'center', gap: 10, justifyContent: 'space-between' }}>
505
- <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
506
- <div style={{
507
- width: 32, height: 32, borderRadius: 8, flexShrink: 0,
508
- background: 'rgba(139,92,246,0.1)',
509
- display: 'flex', alignItems: 'center', justifyContent: 'center',
510
- }}>
511
- <svg width="15" height="15" viewBox="0 0 16 16" fill="none">
512
- <path d="M9 2H4a1 1 0 00-1 1v10a1 1 0 001 1h8a1 1 0 001-1V6L9 2z" stroke="#8b5cf6" strokeWidth="1.4" strokeLinejoin="round"/>
513
- <path d="M9 2v4h4" stroke="#8b5cf6" strokeWidth="1.4" strokeLinejoin="round"/>
514
- </svg>
515
- </div>
516
- <div>
517
- <div style={{ fontSize: 13, fontWeight: 500, color: 'var(--text)' }}>
518
- Config file loaded
519
- </div>
520
- <div style={{ fontSize: 11.5, color: 'var(--muted)' }}>
521
- {value.skills.length} skill{value.skills.length !== 1 ? 's' : ''} · CLAUDE.md included
522
- {value.exportedAt && ` · ${new Date(value.exportedAt).toLocaleDateString()}`}
523
- </div>
524
- </div>
525
- </div>
526
- <button type="button" onClick={() => { onChange(null); setError(''); }} style={{
527
- background: 'none', border: 'none', cursor: 'pointer',
528
- fontSize: 11.5, color: 'var(--muted)', padding: '4px 8px', borderRadius: 5,
529
- }}>Remove</button>
530
- </div>
531
- )}
532
- <input ref={ref} type="file" accept=".json" style={{ display: 'none' }} onChange={handleFile} />
533
- </div>
534
- );
535
- }
536
-
537
- // ─── Step 2: Slack App ────────────────────────────────────────────────────────
538
-
539
- function Step2SlackApp({ state }: { state: WizardState }) {
540
- const [copied, setCopied] = useState(false);
541
- const manifest = state.name
542
- ? JSON.stringify(generateSlackManifest({ name: state.name, description: state.description, isBoss: state.isBoss }), null, 2)
543
- : '{}';
544
-
545
- const copy = () => { navigator.clipboard.writeText(manifest); setCopied(true); setTimeout(() => setCopied(false), 2000); };
546
-
547
- return (
548
- <div>
549
- <StepHeader step={2} title="Create a Slack app" desc="Paste this manifest into Slack — it configures everything automatically in about 2 minutes." />
550
-
551
- {/* Steps */}
552
- <div style={{ display: 'flex', flexDirection: 'column', gap: 0, marginBottom: 20 }}>
553
- {([
554
- {
555
- n: 1,
556
- title: 'Open api.slack.com/apps',
557
- body: <>Click <Kbd>Create New App</Kbd> → <Kbd>From a manifest</Kbd> and select your workspace.</>,
558
- },
559
- {
560
- n: 2,
561
- title: 'Paste the manifest',
562
- body: <>Switch to the <Kbd>JSON</Kbd> tab, paste the manifest below, click <Kbd>Next</Kbd> → <Kbd>Create</Kbd>.</>,
563
- },
564
- {
565
- n: 3,
566
- title: 'Install to workspace',
567
- body: <>In the sidebar go to <Kbd>Install App</Kbd> → <Kbd>Install to Workspace</Kbd> → Allow. This generates your Bot Token — it won&apos;t appear until you install.</>,
568
- },
569
- ] as { n: number; title: string; body: React.ReactNode }[]).map(({ n, title, body }, i, arr) => (
570
- <div key={n} style={{ display: 'flex', gap: 0 }}>
571
- {/* Connector line + dot */}
572
- <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: 32, flexShrink: 0 }}>
573
- <div style={{
574
- width: 26, height: 26, borderRadius: '50%', flexShrink: 0,
575
- background: 'var(--accent)', color: '#fff',
576
- display: 'flex', alignItems: 'center', justifyContent: 'center',
577
- fontSize: 11, fontWeight: 700, zIndex: 1,
578
- }}>{n}</div>
579
- {i < arr.length - 1 && (
580
- <div style={{ width: 2, flex: 1, background: 'var(--border)', minHeight: 20, marginTop: 4 }} />
581
- )}
582
- </div>
583
- {/* Content */}
584
- <div style={{ paddingLeft: 14, paddingBottom: i < arr.length - 1 ? 20 : 0, paddingTop: 3 }}>
585
- <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--text)', marginBottom: 3 }}>{title}</div>
586
- <div style={{ fontSize: 12.5, color: 'var(--muted)', lineHeight: 1.65 }}>{body}</div>
587
- </div>
588
- </div>
589
- ))}
590
- </div>
591
-
592
- {/* Manifest block */}
593
- <div style={{ background: 'var(--surface-2)', border: '1.5px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' }}>
594
- <div style={{
595
- display: 'flex', justifyContent: 'space-between', alignItems: 'center',
596
- padding: '10px 16px', borderBottom: '1px solid var(--border)',
597
- }}>
598
- <span style={{ fontSize: 11.5, color: 'var(--muted)', fontFamily: 'var(--font-mono)' }}>
599
- {state.name || 'agent'}-manifest.json
600
- </span>
601
- <button onClick={copy} style={{
602
- background: copied ? '#dcfce7' : 'var(--accent)',
603
- color: copied ? '#16a34a' : '#fff',
604
- border: 'none', cursor: 'pointer', fontSize: 12, fontWeight: 600,
605
- fontFamily: 'var(--font-sans)', padding: '5px 12px', borderRadius: 6,
606
- transition: 'all 0.15s',
607
- }}>{copied ? '✓ Copied!' : 'Copy manifest'}</button>
608
- </div>
609
- <pre style={{
610
- margin: 0, padding: '16px', fontSize: 11.5, color: 'var(--accent)',
611
- fontFamily: 'var(--font-mono)', overflow: 'auto', maxHeight: 260, lineHeight: 1.6,
612
- }}>{manifest}</pre>
613
- </div>
614
- </div>
615
- );
616
- }
617
-
618
- // ─── Step 3: Tokens ───────────────────────────────────────────────────────────
619
-
620
- function Step3Tokens({ state, update }: { state: WizardState; update: (p: Partial<WizardState>) => void }) {
621
- return (
622
- <div>
623
- <StepHeader step={3} title="Add your tokens" desc="Three values from your Slack app — paste each one below." />
624
- <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
625
- <TokenCard
626
- label="Bot Token"
627
- prefix="xoxb-"
628
- path={['OAuth & Permissions', 'Bot User OAuth Token']}
629
- screenshot={<BotTokenScreenshot />}
630
- placeholder="xoxb-..."
631
- value={state.slackBotToken}
632
- onChange={v => update({ slackBotToken: v })}
633
- />
634
- <TokenCard
635
- label="App-Level Token"
636
- prefix="xapp-"
637
- path={['Basic Information', 'App-Level Tokens']}
638
- note={<>Click <strong>Generate Token and Scopes</strong>, add scope <code style={{ fontFamily: 'var(--font-mono)', fontSize: 11, background: 'rgba(59,130,246,0.08)', color: 'var(--accent)', padding: '1px 6px', borderRadius: 4 }}>connections:write</code>, then generate.</>}
639
- screenshot={<AppTokenScreenshot />}
640
- placeholder="xapp-..."
641
- value={state.slackAppToken}
642
- onChange={v => update({ slackAppToken: v })}
643
- />
644
- <TokenCard
645
- label="Signing Secret"
646
- prefix=""
647
- path={['Basic Information', 'App Credentials', 'Signing Secret']}
648
- screenshot={<SigningSecretScreenshot />}
649
- placeholder="abc123def..."
650
- value={state.slackSigningSecret}
651
- onChange={v => update({ slackSigningSecret: v })}
652
- />
653
- </div>
654
- </div>
655
- );
656
- }
657
-
658
- // ─── Token card ───────────────────────────────────────────────────────────────
659
-
660
- function TokenCard({ label, prefix, path, note, screenshot, placeholder, value, onChange }: {
661
- label: string; prefix: string;
662
- path: string[];
663
- note?: React.ReactNode;
664
- screenshot?: React.ReactNode;
665
- placeholder: string; value: string; onChange: (v: string) => void;
666
- }) {
667
- const [show, setShow] = useState(false);
668
- const [showScreenshot, setShowScreenshot] = useState(false);
669
- const isDirty = value.length > 0;
670
- const isValid = prefix ? value.startsWith(prefix) : value.length >= 8;
671
-
672
- return (
673
- <div style={{
674
- borderRadius: 'var(--radius)',
675
- border: `1.5px solid ${isDirty ? (isValid ? '#86efac' : '#fca5a5') : 'var(--border)'}`,
676
- background: '#fff',
677
- overflow: 'hidden',
678
- transition: 'border-color 0.2s',
679
- }}>
680
- {/* Header: label + breadcrumb */}
681
- <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
682
- <div style={{ display: 'flex', alignItems: 'center', gap: 10, minWidth: 0 }}>
683
- <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--text)', flexShrink: 0 }}>{label}</span>
684
- <div style={{ display: 'flex', alignItems: 'center', gap: 5, overflow: 'hidden' }}>
685
- {path.map((p, i) => (
686
- <React.Fragment key={i}>
687
- {i > 0 && <span style={{ fontSize: 11, color: 'var(--border-2)' }}>›</span>}
688
- <span style={{
689
- fontSize: 11.5, whiteSpace: 'nowrap',
690
- color: i === path.length - 1 ? 'var(--muted)' : 'var(--subtle)',
691
- fontWeight: i === path.length - 1 ? 500 : 400,
692
- }}>{p}</span>
693
- </React.Fragment>
694
- ))}
695
- </div>
696
- </div>
697
- <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexShrink: 0 }}>
698
- {screenshot && (
699
- <button
700
- type="button"
701
- onClick={() => setShowScreenshot(s => !s)}
702
- style={{
703
- background: showScreenshot ? 'var(--surface-2)' : 'none',
704
- border: '1px solid var(--border)', borderRadius: 5,
705
- padding: '3px 8px', cursor: 'pointer',
706
- color: 'var(--muted)', fontSize: 11, fontWeight: 500,
707
- fontFamily: 'var(--font-sans)', transition: 'all 0.15s',
708
- }}
709
- >{showScreenshot ? 'Hide guide' : 'Where?'}</button>
710
- )}
711
- {isDirty && (
712
- <span style={{ fontSize: 12, fontWeight: 700, color: isValid ? '#16a34a' : '#ef4444' }}>
713
- {isValid ? '✓' : '✗'}
714
- </span>
715
- )}
716
- </div>
717
- </div>
718
-
719
- {/* Screenshot guide */}
720
- {showScreenshot && screenshot && (
721
- <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--border)', background: 'var(--surface-2)' }}>
722
- {screenshot}
723
- </div>
724
- )}
725
-
726
- {/* Note */}
727
- {note && (
728
- <div style={{ padding: '8px 16px', borderBottom: '1px solid var(--border)', fontSize: 12, color: 'var(--muted)', background: 'var(--surface-2)', lineHeight: 1.6 }}>
729
- {note}
730
- </div>
731
- )}
732
-
733
- {/* Input */}
734
- <div style={{ display: 'flex', alignItems: 'center', padding: '0 4px 0 16px', gap: 6 }}>
735
- <input
736
- type={show ? 'text' : 'password'}
737
- value={value}
738
- onChange={e => onChange(e.target.value)}
739
- placeholder={placeholder}
740
- style={{
741
- flex: 1, background: 'transparent', border: 'none',
742
- padding: '12px 0', color: 'var(--text)',
743
- fontSize: 13, fontFamily: value ? 'var(--font-mono)' : 'var(--font-sans)',
744
- outline: 'none',
745
- }}
746
- />
747
- <button
748
- type="button"
749
- onClick={() => setShow(s => !s)}
750
- style={{
751
- background: 'none', border: 'none', cursor: 'pointer',
752
- color: 'var(--subtle)', fontSize: 12, padding: '8px',
753
- fontFamily: 'var(--font-sans)', lineHeight: 1,
754
- }}
755
- >{show ? <EyeOff size={14} /> : <Eye size={14} />}</button>
756
- </div>
757
- </div>
758
- );
759
- }
760
-
761
- // ─── Slack UI screenshot mockups ──────────────────────────────────────────────
762
-
763
- const slackStyles = {
764
- wrap: {
765
- borderRadius: 8, overflow: 'hidden', border: '1px solid #e0e0e0',
766
- fontFamily: 'Lato, -apple-system, sans-serif', fontSize: 12,
767
- } as React.CSSProperties,
768
- chrome: {
769
- background: '#f1f1f1', padding: '6px 10px',
770
- borderBottom: '1px solid #ddd', display: 'flex', alignItems: 'center', gap: 8,
771
- } as React.CSSProperties,
772
- urlBar: {
773
- flex: 1, background: '#fff', border: '1px solid #ddd', borderRadius: 4,
774
- padding: '3px 8px', fontSize: 10.5, color: '#555',
775
- fontFamily: 'monospace', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' as const,
776
- } as React.CSSProperties,
777
- body: { background: '#fff', padding: '14px 16px' } as React.CSSProperties,
778
- sectionTitle: { fontSize: 12, fontWeight: 700, color: '#1d1c1d', marginBottom: 10 } as React.CSSProperties,
779
- row: {
780
- display: 'flex', alignItems: 'center', justifyContent: 'space-between',
781
- padding: '8px 10px', border: '1px solid #e8e8e8', borderRadius: 5,
782
- } as React.CSSProperties,
783
- label: { fontSize: 11.5, color: '#616061' } as React.CSSProperties,
784
- token: { fontSize: 11, fontFamily: 'monospace', color: '#1d1c1d', letterSpacing: 1 } as React.CSSProperties,
785
- greenBtn: {
786
- background: '#007a5a', color: '#fff', border: 'none', borderRadius: 4,
787
- padding: '5px 10px', fontSize: 11, fontWeight: 700, cursor: 'default',
788
- boxShadow: '0 0 0 3px rgba(0,122,90,0.25)',
789
- } as React.CSSProperties,
790
- badge: {
791
- background: '#e8f5f0', color: '#007a5a', border: '1px solid #b8dfd4',
792
- borderRadius: 3, padding: '1px 6px', fontSize: 10.5, fontWeight: 600,
793
- } as React.CSSProperties,
794
- highlight: {
795
- outline: '2.5px solid #007a5a', outlineOffset: 2, borderRadius: 4,
796
- } as React.CSSProperties,
797
- };
798
-
799
- function ChromeBar({ url }: { url: string }) {
800
- return (
801
- <div style={slackStyles.chrome}>
802
- <div style={{ display: 'flex', gap: 4 }}>
803
- {['#ff5f57','#febc2e','#28c840'].map(c => (
804
- <div key={c} style={{ width: 8, height: 8, borderRadius: '50%', background: c }} />
805
- ))}
806
- </div>
807
- <div style={slackStyles.urlBar}>{url}</div>
808
- </div>
809
- );
810
- }
811
-
812
- function SlackLayout({ url, activeNav, children }: { url: string; activeNav: string; children: React.ReactNode }) {
813
- const navItems = ['Basic Information', 'Socket Mode', 'Install App', 'OAuth & Permissions', 'Event Subscriptions', 'App Manifest'];
814
- return (
815
- <div style={slackStyles.wrap}>
816
- <ChromeBar url={url} />
817
- <div style={{ display: 'flex', background: '#fff' }}>
818
- {/* Sidebar */}
819
- <div style={{ width: 148, background: '#f8f8f8', borderRight: '1px solid #e0e0e0', padding: '10px 0', flexShrink: 0 }}>
820
- {navItems.map(item => (
821
- <div key={item} style={{
822
- padding: '5px 12px', fontSize: 11, cursor: 'default',
823
- background: item === activeNav ? '#e8e8e8' : 'transparent',
824
- color: item === activeNav ? '#1d1c1d' : '#616061',
825
- fontWeight: item === activeNav ? 700 : 400,
826
- borderLeft: item === activeNav ? '2px solid #007a5a' : '2px solid transparent',
827
- }}>{item}</div>
828
- ))}
829
- </div>
830
- {/* Content */}
831
- <div style={{ flex: 1, padding: '14px 16px', minWidth: 0 }}>{children}</div>
832
- </div>
833
- </div>
834
- );
835
- }
836
-
837
- function BotTokenScreenshot() {
838
- return (
839
- <SlackLayout url="api.slack.com/apps/A.../oauth" activeNav="OAuth & Permissions">
840
- {/* Install notice */}
841
- <div style={{ background: '#fff8e6', border: '1px solid #f5c842', borderRadius: 5, padding: '7px 10px', marginBottom: 10, fontSize: 11, color: '#7a5c00', lineHeight: 1.5, display: 'flex', alignItems: 'flex-start', gap: 6 }}>
842
- <AlertTriangle size={12} style={{ flexShrink: 0, marginTop: 1 }} />
843
- <span>Token only appears after <strong>Install to Workspace</strong> (sidebar → Install App)</span>
844
- </div>
845
- <div style={slackStyles.sectionTitle}>OAuth Tokens</div>
846
- <div style={{ border: '1px solid #e8e8e8', borderRadius: 5, overflow: 'hidden' }}>
847
- <div style={{ padding: '8px 10px', background: '#fafafa', borderBottom: '1px solid #e8e8e8' }}>
848
- <div style={{ fontSize: 11, color: '#616061', marginBottom: 1 }}>Bot User OAuth Token</div>
849
- <div style={{ ...slackStyles.token, fontSize: 10.5 }}>xoxb-••••••••••••-••••••••••••-••••••••••••</div>
850
- <div style={{ fontSize: 10, color: '#616061', marginTop: 2 }}>Access Level: Workspace</div>
851
- </div>
852
- <div style={{ padding: '7px 10px', display: 'flex', justifyContent: 'flex-end' }}>
853
- <button style={{ ...slackStyles.greenBtn, ...slackStyles.highlight }}>Copy</button>
854
- </div>
855
- </div>
856
- </SlackLayout>
857
- );
858
- }
859
-
860
- function AppTokenScreenshot() {
861
- return (
862
- <SlackLayout url="api.slack.com/apps/A.../general" activeNav="Basic Information">
863
- <div style={slackStyles.sectionTitle}>App-Level Tokens</div>
864
- <div style={{ fontSize: 11, color: '#616061', marginBottom: 10, lineHeight: 1.5 }}>
865
- App-level tokens represent your app across organizations.
866
- </div>
867
- <div style={{ marginBottom: 10 }}>
868
- <button style={{ ...slackStyles.greenBtn, ...slackStyles.highlight }}>Generate Token and Scopes</button>
869
- </div>
870
- <div style={{ border: '1px solid #e8e8e8', borderRadius: 5, padding: '8px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
871
- <div>
872
- <div style={{ fontSize: 11, fontWeight: 700, color: '#1d1c1d', marginBottom: 4 }}>SlackHive</div>
873
- <span style={slackStyles.badge}>connections:write</span>
874
- </div>
875
- <div style={slackStyles.token}>xapp-••••••••••••</div>
876
- </div>
877
- </SlackLayout>
878
- );
879
- }
880
-
881
- function SigningSecretScreenshot() {
882
- return (
883
- <SlackLayout url="api.slack.com/apps/A.../general" activeNav="Basic Information">
884
- <div style={slackStyles.sectionTitle}>App Credentials</div>
885
- <div style={{ fontSize: 11, color: '#616061', marginBottom: 10, lineHeight: 1.5 }}>
886
- These credentials allow your app to access the Slack API. Keep them secret.
887
- </div>
888
- {[
889
- { label: 'App ID', value: 'A0AN••••••9', mono: true },
890
- { label: 'Client ID', value: '9186••••••••••••••••', mono: true },
891
- { label: 'Client Secret', value: '••••••••••', mono: true },
892
- ].map(row => (
893
- <div key={row.label} style={{ display: 'flex', justifyContent: 'space-between', padding: '5px 0', borderBottom: '1px solid #f0f0f0', fontSize: 11, color: '#616061' }}>
894
- <span>{row.label}</span>
895
- <span style={{ fontFamily: 'monospace', color: '#1d1c1d' }}>{row.value}</span>
896
- </div>
897
- ))}
898
- {/* Signing Secret highlighted */}
899
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 10px', marginTop: 6, border: '1px solid #e8e8e8', borderRadius: 5, ...slackStyles.highlight }}>
900
- <div>
901
- <div style={{ fontSize: 11, fontWeight: 700, color: '#1d1c1d', marginBottom: 2 }}>Signing Secret</div>
902
- <div style={slackStyles.token}>••••••••••••••••••••••••</div>
903
- </div>
904
- <button style={slackStyles.greenBtn}>Show</button>
905
- </div>
906
- </SlackLayout>
907
- );
908
- }
909
-
910
- // ─── Kbd helper ───────────────────────────────────────────────────────────────
911
-
912
- function Kbd({ children }: { children: React.ReactNode }) {
913
- return (
914
- <kbd style={{
915
- display: 'inline-block', fontFamily: 'var(--font-sans)', fontSize: 11.5, fontWeight: 600,
916
- background: 'var(--surface-3)', color: 'var(--text)',
917
- border: '1px solid var(--border)', borderRadius: 5,
918
- padding: '1px 7px', lineHeight: 1.7, whiteSpace: 'nowrap',
919
- }}>{children}</kbd>
920
- );
921
- }
922
-
923
- // ─── Step 4: MCPs & Skills ────────────────────────────────────────────────────
924
-
925
- function Step4McpsSkills({
926
- state, update, catalog,
927
- }: { state: WizardState; update: (p: Partial<WizardState>) => void; catalog: McpServer[] }) {
928
- const toggle = (id: string) => {
929
- const ids = state.mcpServerIds.includes(id)
930
- ? state.mcpServerIds.filter(x => x !== id)
931
- : [...state.mcpServerIds, id];
932
- update({ mcpServerIds: ids });
933
- };
934
-
935
- return (
936
- <div>
937
- <StepHeader step={4} title="Tools & Skills" desc="Attach MCP servers and pick a starting skill template. You can change these anytime." />
938
-
939
- {/* MCP list */}
940
- <div style={{ marginBottom: 22 }}>
941
- <label style={{ display: 'block', fontSize: 12, fontWeight: 500, color: 'var(--muted)', marginBottom: 8, letterSpacing: '0.06em', textTransform: 'uppercase' }}>
942
- MCP Servers
943
- </label>
944
- {catalog.length === 0 ? (
945
- <div style={{
946
- border: '1px dashed var(--border)', borderRadius: 8, padding: '16px',
947
- fontSize: 12.5, color: 'var(--subtle)', textAlign: 'center',
948
- }}>
949
- No MCP servers yet — you can add them after creation in <strong style={{ color: 'var(--muted)' }}>Settings → MCP Catalog</strong>
950
- </div>
951
- ) : (
952
- <div style={{ border: '1px solid var(--border)', borderRadius: 10, overflow: 'hidden' }}>
953
- {catalog.map((mcp, i) => (
954
- <label key={mcp.id} style={{
955
- display: 'flex', alignItems: 'center', gap: 12, padding: '11px 14px',
956
- cursor: mcp.enabled ? 'pointer' : 'not-allowed',
957
- borderBottom: i < catalog.length - 1 ? '1px solid var(--border)' : 'none',
958
- opacity: mcp.enabled ? 1 : 0.4, transition: 'background 0.12s',
959
- }}
960
- onMouseEnter={e => { if (mcp.enabled) (e.currentTarget as HTMLElement).style.background = 'rgba(255,255,255,0.03)'; }}
961
- onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = 'transparent'; }}
962
- >
963
- <input type="checkbox" checked={state.mcpServerIds.includes(mcp.id)}
964
- onChange={() => toggle(mcp.id)} disabled={!mcp.enabled}
965
- style={{ accentColor: 'var(--accent)', width: 14, height: 14 }} />
966
- <div>
967
- <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
968
- <span style={{ fontSize: 13, fontWeight: 500, color: 'var(--text)' }}>{mcp.name}</span>
969
- <span style={{
970
- fontSize: 10.5, fontFamily: 'var(--font-mono)',
971
- color: 'var(--muted)', background: 'var(--border)', padding: '1px 6px', borderRadius: 4,
972
- }}>{mcp.type}</span>
973
- </div>
974
- {mcp.description && <p style={{ margin: 0, fontSize: 11.5, color: 'var(--subtle)' }}>{mcp.description}</p>}
975
- </div>
976
- </label>
977
- ))}
978
- </div>
979
- )}
980
- </div>
981
-
982
- {/* Template grid */}
983
- <div>
984
- <label style={{ display: 'block', fontSize: 12, fontWeight: 500, color: 'var(--muted)', marginBottom: 8, letterSpacing: '0.06em', textTransform: 'uppercase' }}>
985
- Skill Template
986
- </label>
987
- <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
988
- {TEMPLATES.map(t => {
989
- const active = state.skillTemplate === t.value && !state.importPayload;
990
- return (
991
- <label key={t.value} style={{
992
- padding: '12px 14px', border: `1px solid ${active ? 'var(--accent)' : 'var(--border)'}`,
993
- borderRadius: 8, cursor: 'pointer',
994
- background: active ? 'rgba(59,130,246,0.08)' : 'transparent',
995
- transition: 'all 0.15s',
996
- }}>
997
- <input type="radio" name="template" value={t.value} checked={active}
998
- onChange={() => update({ skillTemplate: t.value as WizardState['skillTemplate'], importPayload: null })}
999
- style={{ display: 'none' }} />
1000
- <div style={{ fontSize: 13, fontWeight: 600, color: active ? 'var(--accent)' : 'var(--text)', marginBottom: 2 }}>
1001
- {t.label}
1002
- </div>
1003
- <div style={{ fontSize: 11.5, color: 'var(--subtle)' }}>{t.desc}</div>
1004
- </label>
1005
- );
1006
- })}
1007
-
1008
- {/* Import tile */}
1009
- {(() => {
1010
- const active = !!state.importPayload;
1011
- return (
1012
- <div style={{
1013
- padding: '12px 14px',
1014
- border: `1px solid ${active ? '#8b5cf6' : 'var(--border)'}`,
1015
- borderRadius: 8,
1016
- background: active ? 'rgba(139,92,246,0.08)' : 'transparent',
1017
- transition: 'all 0.15s',
1018
- gridColumn: 'span 2',
1019
- }}>
1020
- <ImportConfigPicker
1021
- value={state.importPayload}
1022
- onChange={payload => update({ importPayload: payload })}
1023
- compact
1024
- />
1025
- </div>
1026
- );
1027
- })()}
1028
- </div>
1029
- </div>
1030
- </div>
1031
- );
1032
- }
1033
-
1034
- // ─── Step 5: Review ───────────────────────────────────────────────────────────
1035
-
1036
- function Step5Review({ state, update, catalog, agents }: { state: WizardState; update: (p: Partial<WizardState>) => void; catalog: McpServer[]; agents: Agent[] }) {
1037
- const assignedBosses = agents.filter(a => state.reportsToIds.includes(a.id));
1038
- const assignedMcps = catalog.filter(m => state.mcpServerIds.includes(m.id));
1039
- const template = state.importPayload
1040
- ? `Import (${state.importPayload.skills.length} skills)`
1041
- : (TEMPLATES.find(t => t.value === state.skillTemplate)?.label ?? state.skillTemplate);
1042
-
1043
- const reportsToValue = state.isBoss
1044
- ? '—'
1045
- : assignedBosses.length > 0
1046
- ? assignedBosses.map(b => b.name).join(', ')
1047
- : 'None';
1048
-
1049
- return (
1050
- <div>
1051
- <StepHeader step={state.isBoss ? 4 : 5} title="Looks good?" desc="Review the details below, then hit Create Agent to launch." />
1052
-
1053
- <div style={{
1054
- background: 'var(--surface-2)', border: '1px solid var(--border)',
1055
- borderRadius: 10, overflow: 'hidden', marginBottom: 18,
1056
- }}>
1057
- {[
1058
- { label: 'Name', value: state.name, mono: false },
1059
- { label: 'Slug', value: `@${state.slug}`, mono: true },
1060
- { label: 'Model', value: state.model, mono: true },
1061
- { label: 'Role', value: state.isBoss ? 'Boss (orchestrator)' : 'Specialist', mono: false },
1062
- { label: 'Reports to', value: reportsToValue, mono: false },
1063
- { label: 'Description', value: state.description || '—', mono: false },
1064
- { label: 'Bot Token', value: `${state.slackBotToken.slice(0, 12)}…`, mono: true },
1065
- { label: 'App Token', value: `${state.slackAppToken.slice(0, 12)}…`, mono: true },
1066
- { label: 'Signing Secret',value: '••••••••', mono: true },
1067
- ...(!state.isBoss ? [
1068
- { label: 'MCPs', value: assignedMcps.length > 0 ? assignedMcps.map(m => m.name).join(', ') : 'None', mono: false },
1069
- { label: 'Skill Template',value: template, mono: false },
1070
- ] : [
1071
- { label: 'Skills', value: 'Auto-generated from team registry', mono: false },
1072
- ]),
1073
- ].map((row, i, arr) => (
1074
- <div key={row.label} style={{
1075
- display: 'flex', alignItems: 'baseline', gap: 12,
1076
- padding: '10px 16px',
1077
- borderBottom: i < arr.length - 1 ? '1px solid var(--border)' : 'none',
1078
- }}>
1079
- <span style={{ width: 130, flexShrink: 0, fontSize: 12, color: 'var(--subtle)' }}>{row.label}</span>
1080
- <span style={{
1081
- fontSize: 13, color: 'var(--text)',
1082
- fontFamily: row.mono ? 'var(--font-mono)' : 'var(--font-sans)',
1083
- }}>{row.value}</span>
1084
- </div>
1085
- ))}
1086
- </div>
1087
-
1088
- <div style={{
1089
- background: '#f0fdf4', border: '1px solid #bbf7d0',
1090
- borderRadius: 'var(--radius)', padding: '14px 16px', fontSize: 13, color: '#15803d', lineHeight: 1.65,
1091
- }}>
1092
- Once created, the runner picks up the agent automatically and connects to Slack.
1093
- Manage skills, MCPs, and channel permissions from the agent detail page.
1094
- </div>
1095
- </div>
1096
- );
1097
- }
1098
-
1099
- // ─── Shared primitives ────────────────────────────────────────────────────────
1100
-
1101
- function StepHeader({ step, title, desc }: { step: number; title: string; desc: string }) {
1102
- return (
1103
- <div style={{ marginBottom: 22 }}>
1104
- <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--accent)', letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: 4 }}>
1105
- Step {step}
1106
- </div>
1107
- <h2 style={{ margin: '0 0 4px', fontSize: 18, fontWeight: 700, color: 'var(--text)', letterSpacing: '-0.02em' }}>
1108
- {title}
1109
- </h2>
1110
- <p style={{ margin: 0, fontSize: 13, color: 'var(--muted)' }}>{desc}</p>
1111
- </div>
1112
- );
1113
- }
1114
-
1115
- function Field({ label, value, onChange, placeholder, hint, type = 'text' }: {
1116
- label: string; value: string; onChange: (v: string) => void;
1117
- placeholder?: string; hint?: string; type?: string;
1118
- }) {
1119
- return (
1120
- <div style={{ marginBottom: 14 }}>
1121
- <label style={{ display: 'block', fontSize: 12, fontWeight: 500, color: 'var(--muted)', marginBottom: 5 }}>
1122
- {label}
1123
- </label>
1124
- <input type={type} value={value} onChange={e => onChange(e.target.value)} placeholder={placeholder}
1125
- style={{
1126
- width: '100%', background: 'var(--surface-2)', border: '1.5px solid var(--border)',
1127
- borderRadius: 'var(--radius)', padding: '10px 14px', color: 'var(--text)',
1128
- fontSize: 14, fontFamily: 'var(--font-sans)', outline: 'none',
1129
- transition: 'border-color 0.15s',
1130
- }}
1131
- onFocus={e => (e.currentTarget.style.borderColor = 'var(--accent)')}
1132
- onBlur={e => (e.currentTarget.style.borderColor = 'var(--border)')}
1133
- />
1134
- {hint && <p style={{ margin: '5px 0 0', fontSize: 12, color: 'var(--subtle)' }}>{hint}</p>}
1135
- </div>
1136
- );
1137
- }
1138
-
1139
- function TextArea({ label, value, onChange, placeholder, hint, rows = 3 }: {
1140
- label: string; value: string; onChange: (v: string) => void;
1141
- placeholder?: string; hint?: string; rows?: number;
1142
- }) {
1143
- return (
1144
- <div style={{ marginBottom: 14 }}>
1145
- <label style={{ display: 'block', fontSize: 12, fontWeight: 500, color: 'var(--muted)', marginBottom: 5 }}>
1146
- {label}
1147
- </label>
1148
- <textarea value={value} onChange={e => onChange(e.target.value)} placeholder={placeholder} rows={rows}
1149
- style={{
1150
- width: '100%', background: 'var(--surface-2)', border: '1.5px solid var(--border)',
1151
- borderRadius: 'var(--radius)', padding: '10px 14px', color: 'var(--text)',
1152
- fontSize: 14, fontFamily: 'var(--font-sans)', outline: 'none', resize: 'vertical',
1153
- transition: 'border-color 0.15s', lineHeight: 1.55,
1154
- }}
1155
- onFocus={e => (e.currentTarget.style.borderColor = 'var(--accent)')}
1156
- onBlur={e => (e.currentTarget.style.borderColor = 'var(--border)')}
1157
- />
1158
- {hint && <p style={{ margin: '5px 0 0', fontSize: 12, color: 'var(--subtle)' }}>{hint}</p>}
1159
- </div>
1160
- );
1161
- }