slackhive 0.1.37 → 0.1.39

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 (542) hide show
  1. package/.dockerignore +14 -0
  2. package/.env.example +44 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.yml +65 -0
  4. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.yml +38 -0
  6. package/.github/PULL_REQUEST_TEMPLATE.md +27 -0
  7. package/.github/dependabot.yml +20 -0
  8. package/.github/workflows/audit.yml +149 -0
  9. package/.github/workflows/ci.yml +135 -0
  10. package/CHANGELOG.md +52 -0
  11. package/CODE_OF_CONDUCT.md +37 -0
  12. package/CONTRIBUTING.md +204 -0
  13. package/LICENSE +21 -0
  14. package/README.md +19 -0
  15. package/SECURITY.md +47 -0
  16. package/apps/runner/Dockerfile +33 -0
  17. package/apps/runner/dist/__tests__/channel-restrictions.test.d.ts +8 -0
  18. package/apps/runner/dist/__tests__/channel-restrictions.test.js +63 -0
  19. package/apps/runner/dist/__tests__/channel-restrictions.test.js.map +1 -0
  20. package/apps/runner/dist/__tests__/claude-handler-resolve.test.d.ts +20 -0
  21. package/apps/runner/dist/__tests__/claude-handler-resolve.test.js +178 -0
  22. package/apps/runner/dist/__tests__/claude-handler-resolve.test.js.map +1 -0
  23. package/apps/runner/dist/__tests__/compile-claude-md.test.d.ts +13 -0
  24. package/apps/runner/dist/__tests__/compile-claude-md.test.js +144 -0
  25. package/apps/runner/dist/__tests__/compile-claude-md.test.js.map +1 -0
  26. package/apps/runner/dist/__tests__/memory-sync.test.d.ts +11 -0
  27. package/apps/runner/dist/__tests__/memory-sync.test.js +56 -0
  28. package/apps/runner/dist/__tests__/memory-sync.test.js.map +1 -0
  29. package/apps/runner/dist/__tests__/slack-file-support.test.d.ts +9 -0
  30. package/apps/runner/dist/__tests__/slack-file-support.test.js +271 -0
  31. package/apps/runner/dist/__tests__/slack-file-support.test.js.map +1 -0
  32. package/apps/runner/dist/__tests__/slack-formatting.test.d.ts +12 -0
  33. package/apps/runner/dist/__tests__/slack-formatting.test.js +400 -0
  34. package/apps/runner/dist/__tests__/slack-formatting.test.js.map +1 -0
  35. package/apps/runner/dist/__tests__/thread-context.test.d.ts +12 -0
  36. package/apps/runner/dist/__tests__/thread-context.test.js +182 -0
  37. package/apps/runner/dist/__tests__/thread-context.test.js.map +1 -0
  38. package/apps/runner/dist/agent-runner.d.ts +118 -0
  39. package/apps/runner/dist/agent-runner.js +352 -0
  40. package/apps/runner/dist/agent-runner.js.map +1 -0
  41. package/apps/runner/dist/claude-handler.d.ts +122 -0
  42. package/apps/runner/dist/claude-handler.js +402 -0
  43. package/apps/runner/dist/claude-handler.js.map +1 -0
  44. package/apps/runner/dist/compile-claude-md.d.ts +59 -0
  45. package/apps/runner/dist/compile-claude-md.js +291 -0
  46. package/apps/runner/dist/compile-claude-md.js.map +1 -0
  47. package/apps/runner/dist/correction-handler.d.ts +46 -0
  48. package/apps/runner/dist/correction-handler.js +162 -0
  49. package/apps/runner/dist/correction-handler.js.map +1 -0
  50. package/apps/runner/dist/correction-manager.d.ts +53 -0
  51. package/apps/runner/dist/correction-manager.js +241 -0
  52. package/apps/runner/dist/correction-manager.js.map +1 -0
  53. package/apps/runner/dist/db.d.ts +193 -0
  54. package/apps/runner/dist/db.js +492 -0
  55. package/apps/runner/dist/db.js.map +1 -0
  56. package/apps/runner/dist/index.d.ts +9 -0
  57. package/apps/runner/dist/index.js +43 -0
  58. package/apps/runner/dist/index.js.map +1 -0
  59. package/apps/runner/dist/job-scheduler.d.ts +57 -0
  60. package/apps/runner/dist/job-scheduler.js +150 -0
  61. package/apps/runner/dist/job-scheduler.js.map +1 -0
  62. package/apps/runner/dist/logger.d.ts +32 -0
  63. package/apps/runner/dist/logger.js +52 -0
  64. package/apps/runner/dist/logger.js.map +1 -0
  65. package/apps/runner/dist/mcp-process-manager.d.ts +38 -0
  66. package/apps/runner/dist/mcp-process-manager.js +189 -0
  67. package/apps/runner/dist/mcp-process-manager.js.map +1 -0
  68. package/apps/runner/dist/memory-mcp.d.ts +14 -0
  69. package/apps/runner/dist/memory-mcp.js +88 -0
  70. package/apps/runner/dist/memory-mcp.js.map +1 -0
  71. package/apps/runner/dist/memory-watcher.d.ts +78 -0
  72. package/apps/runner/dist/memory-watcher.js +220 -0
  73. package/apps/runner/dist/memory-watcher.js.map +1 -0
  74. package/apps/runner/dist/slack-handler.d.ts +120 -0
  75. package/apps/runner/dist/slack-handler.js +843 -0
  76. package/apps/runner/dist/slack-handler.js.map +1 -0
  77. package/apps/runner/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  78. package/apps/runner/package.json +42 -0
  79. package/apps/runner/src/__tests__/channel-restrictions.test.ts +75 -0
  80. package/apps/runner/src/__tests__/claude-handler-resolve.test.ts +160 -0
  81. package/apps/runner/src/__tests__/compile-claude-md.test.ts +139 -0
  82. package/apps/runner/src/__tests__/memory-sync.test.ts +59 -0
  83. package/apps/runner/src/__tests__/slack-file-support.test.ts +376 -0
  84. package/apps/runner/src/__tests__/slack-formatting.test.ts +495 -0
  85. package/apps/runner/src/__tests__/thread-context.test.ts +215 -0
  86. package/apps/runner/src/agent-runner.ts +397 -0
  87. package/apps/runner/src/claude-handler.ts +475 -0
  88. package/apps/runner/src/compile-claude-md.ts +283 -0
  89. package/apps/runner/src/correction-handler.ts +191 -0
  90. package/apps/runner/src/correction-manager.ts +285 -0
  91. package/apps/runner/src/db.ts +604 -0
  92. package/apps/runner/src/index.ts +46 -0
  93. package/apps/runner/src/job-scheduler.ts +165 -0
  94. package/apps/runner/src/logger.ts +49 -0
  95. package/apps/runner/src/mcp-process-manager.ts +195 -0
  96. package/apps/runner/src/memory-mcp.ts +85 -0
  97. package/apps/runner/src/memory-watcher.ts +215 -0
  98. package/apps/runner/src/slack-handler.ts +929 -0
  99. package/apps/runner/tsconfig.json +17 -0
  100. package/apps/runner/vitest.config.mts +17 -0
  101. package/apps/web/.eslintrc.json +3 -0
  102. package/apps/web/.next/app-build-manifest.json +323 -0
  103. package/apps/web/.next/app-path-routes-manifest.json +46 -0
  104. package/apps/web/.next/build-manifest.json +33 -0
  105. package/apps/web/.next/cache/.previewinfo +1 -0
  106. package/apps/web/.next/cache/.rscinfo +1 -0
  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 +6 -0
  124. package/apps/web/.next/diagnostics/framework.json +1 -0
  125. package/apps/web/.next/package.json +1 -0
  126. package/apps/web/.next/react-loadable-manifest.json +1 -0
  127. package/apps/web/.next/server/app/_not-found/page.js +2 -0
  128. package/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -0
  129. package/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
  130. package/apps/web/.next/server/app/agents/[slug]/page.js +4 -0
  131. package/apps/web/.next/server/app/agents/[slug]/page.js.nft.json +1 -0
  132. package/apps/web/.next/server/app/agents/[slug]/page_client-reference-manifest.js +1 -0
  133. package/apps/web/.next/server/app/agents/new/page.js +2 -0
  134. package/apps/web/.next/server/app/agents/new/page.js.nft.json +1 -0
  135. package/apps/web/.next/server/app/agents/new/page_client-reference-manifest.js +1 -0
  136. package/apps/web/.next/server/app/api/agents/[id]/access/route.js +1 -0
  137. package/apps/web/.next/server/app/api/agents/[id]/access/route.js.nft.json +1 -0
  138. package/apps/web/.next/server/app/api/agents/[id]/access/route_client-reference-manifest.js +1 -0
  139. package/apps/web/.next/server/app/api/agents/[id]/claude-md/route.js +6 -0
  140. package/apps/web/.next/server/app/api/agents/[id]/claude-md/route.js.nft.json +1 -0
  141. package/apps/web/.next/server/app/api/agents/[id]/claude-md/route_client-reference-manifest.js +1 -0
  142. package/apps/web/.next/server/app/api/agents/[id]/logs/route.js +3 -0
  143. package/apps/web/.next/server/app/api/agents/[id]/logs/route.js.nft.json +1 -0
  144. package/apps/web/.next/server/app/api/agents/[id]/logs/route_client-reference-manifest.js +1 -0
  145. package/apps/web/.next/server/app/api/agents/[id]/manifest/route.js +1 -0
  146. package/apps/web/.next/server/app/api/agents/[id]/manifest/route.js.nft.json +1 -0
  147. package/apps/web/.next/server/app/api/agents/[id]/manifest/route_client-reference-manifest.js +1 -0
  148. package/apps/web/.next/server/app/api/agents/[id]/mcps/route.js +1 -0
  149. package/apps/web/.next/server/app/api/agents/[id]/mcps/route.js.nft.json +1 -0
  150. package/apps/web/.next/server/app/api/agents/[id]/mcps/route_client-reference-manifest.js +1 -0
  151. package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route.js +1 -0
  152. package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route.js.nft.json +1 -0
  153. package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route_client-reference-manifest.js +1 -0
  154. package/apps/web/.next/server/app/api/agents/[id]/memories/route.js +1 -0
  155. package/apps/web/.next/server/app/api/agents/[id]/memories/route.js.nft.json +1 -0
  156. package/apps/web/.next/server/app/api/agents/[id]/memories/route_client-reference-manifest.js +1 -0
  157. package/apps/web/.next/server/app/api/agents/[id]/permissions/route.js +1 -0
  158. package/apps/web/.next/server/app/api/agents/[id]/permissions/route.js.nft.json +1 -0
  159. package/apps/web/.next/server/app/api/agents/[id]/permissions/route_client-reference-manifest.js +1 -0
  160. package/apps/web/.next/server/app/api/agents/[id]/reload/route.js +1 -0
  161. package/apps/web/.next/server/app/api/agents/[id]/reload/route.js.nft.json +1 -0
  162. package/apps/web/.next/server/app/api/agents/[id]/reload/route_client-reference-manifest.js +1 -0
  163. package/apps/web/.next/server/app/api/agents/[id]/restrictions/route.js +1 -0
  164. package/apps/web/.next/server/app/api/agents/[id]/restrictions/route.js.nft.json +1 -0
  165. package/apps/web/.next/server/app/api/agents/[id]/restrictions/route_client-reference-manifest.js +1 -0
  166. package/apps/web/.next/server/app/api/agents/[id]/route.js +33 -0
  167. package/apps/web/.next/server/app/api/agents/[id]/route.js.nft.json +1 -0
  168. package/apps/web/.next/server/app/api/agents/[id]/route_client-reference-manifest.js +1 -0
  169. package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route.js +1 -0
  170. package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route.js.nft.json +1 -0
  171. package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route_client-reference-manifest.js +1 -0
  172. package/apps/web/.next/server/app/api/agents/[id]/skills/route.js +1 -0
  173. package/apps/web/.next/server/app/api/agents/[id]/skills/route.js.nft.json +1 -0
  174. package/apps/web/.next/server/app/api/agents/[id]/skills/route_client-reference-manifest.js +1 -0
  175. package/apps/web/.next/server/app/api/agents/[id]/slack-info/route.js +1 -0
  176. package/apps/web/.next/server/app/api/agents/[id]/slack-info/route.js.nft.json +1 -0
  177. package/apps/web/.next/server/app/api/agents/[id]/slack-info/route_client-reference-manifest.js +1 -0
  178. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route.js +1 -0
  179. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route.js.nft.json +1 -0
  180. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route_client-reference-manifest.js +1 -0
  181. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route.js +1 -0
  182. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route.js.nft.json +1 -0
  183. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route_client-reference-manifest.js +1 -0
  184. package/apps/web/.next/server/app/api/agents/[id]/snapshots/route.js +1 -0
  185. package/apps/web/.next/server/app/api/agents/[id]/snapshots/route.js.nft.json +1 -0
  186. package/apps/web/.next/server/app/api/agents/[id]/snapshots/route_client-reference-manifest.js +1 -0
  187. package/apps/web/.next/server/app/api/agents/[id]/start/route.js +1 -0
  188. package/apps/web/.next/server/app/api/agents/[id]/start/route.js.nft.json +1 -0
  189. package/apps/web/.next/server/app/api/agents/[id]/start/route_client-reference-manifest.js +1 -0
  190. package/apps/web/.next/server/app/api/agents/[id]/stop/route.js +1 -0
  191. package/apps/web/.next/server/app/api/agents/[id]/stop/route.js.nft.json +1 -0
  192. package/apps/web/.next/server/app/api/agents/[id]/stop/route_client-reference-manifest.js +1 -0
  193. package/apps/web/.next/server/app/api/agents/route.js +91 -0
  194. package/apps/web/.next/server/app/api/agents/route.js.nft.json +1 -0
  195. package/apps/web/.next/server/app/api/agents/route_client-reference-manifest.js +1 -0
  196. package/apps/web/.next/server/app/api/auth/login/route.js +1 -0
  197. package/apps/web/.next/server/app/api/auth/login/route.js.nft.json +1 -0
  198. package/apps/web/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -0
  199. package/apps/web/.next/server/app/api/auth/logout/route.js +1 -0
  200. package/apps/web/.next/server/app/api/auth/logout/route.js.nft.json +1 -0
  201. package/apps/web/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -0
  202. package/apps/web/.next/server/app/api/auth/me/route.js +1 -0
  203. package/apps/web/.next/server/app/api/auth/me/route.js.nft.json +1 -0
  204. package/apps/web/.next/server/app/api/auth/me/route_client-reference-manifest.js +1 -0
  205. package/apps/web/.next/server/app/api/auth/users/[id]/route.js +1 -0
  206. package/apps/web/.next/server/app/api/auth/users/[id]/route.js.nft.json +1 -0
  207. package/apps/web/.next/server/app/api/auth/users/[id]/route_client-reference-manifest.js +1 -0
  208. package/apps/web/.next/server/app/api/auth/users/route.js +1 -0
  209. package/apps/web/.next/server/app/api/auth/users/route.js.nft.json +1 -0
  210. package/apps/web/.next/server/app/api/auth/users/route_client-reference-manifest.js +1 -0
  211. package/apps/web/.next/server/app/api/env-vars/[key]/route.js +1 -0
  212. package/apps/web/.next/server/app/api/env-vars/[key]/route.js.nft.json +1 -0
  213. package/apps/web/.next/server/app/api/env-vars/[key]/route_client-reference-manifest.js +1 -0
  214. package/apps/web/.next/server/app/api/env-vars/route.js +1 -0
  215. package/apps/web/.next/server/app/api/env-vars/route.js.nft.json +1 -0
  216. package/apps/web/.next/server/app/api/env-vars/route_client-reference-manifest.js +1 -0
  217. package/apps/web/.next/server/app/api/jobs/[id]/route.js +1 -0
  218. package/apps/web/.next/server/app/api/jobs/[id]/route.js.nft.json +1 -0
  219. package/apps/web/.next/server/app/api/jobs/[id]/route_client-reference-manifest.js +1 -0
  220. package/apps/web/.next/server/app/api/jobs/[id]/runs/route.js +1 -0
  221. package/apps/web/.next/server/app/api/jobs/[id]/runs/route.js.nft.json +1 -0
  222. package/apps/web/.next/server/app/api/jobs/[id]/runs/route_client-reference-manifest.js +1 -0
  223. package/apps/web/.next/server/app/api/jobs/route.js +1 -0
  224. package/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -0
  225. package/apps/web/.next/server/app/api/jobs/route_client-reference-manifest.js +1 -0
  226. package/apps/web/.next/server/app/api/mcps/[id]/route.js +1 -0
  227. package/apps/web/.next/server/app/api/mcps/[id]/route.js.nft.json +1 -0
  228. package/apps/web/.next/server/app/api/mcps/[id]/route_client-reference-manifest.js +1 -0
  229. package/apps/web/.next/server/app/api/mcps/[id]/test/route.js +1 -0
  230. package/apps/web/.next/server/app/api/mcps/[id]/test/route.js.nft.json +1 -0
  231. package/apps/web/.next/server/app/api/mcps/[id]/test/route_client-reference-manifest.js +1 -0
  232. package/apps/web/.next/server/app/api/mcps/route.js +1 -0
  233. package/apps/web/.next/server/app/api/mcps/route.js.nft.json +1 -0
  234. package/apps/web/.next/server/app/api/mcps/route_client-reference-manifest.js +1 -0
  235. package/apps/web/.next/server/app/api/settings/route.js +1 -0
  236. package/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -0
  237. package/apps/web/.next/server/app/api/settings/route_client-reference-manifest.js +1 -0
  238. package/apps/web/.next/server/app/icon.svg/route.js +1 -0
  239. package/apps/web/.next/server/app/icon.svg/route.js.nft.json +1 -0
  240. package/apps/web/.next/server/app/jobs/page.js +2 -0
  241. package/apps/web/.next/server/app/jobs/page.js.nft.json +1 -0
  242. package/apps/web/.next/server/app/jobs/page_client-reference-manifest.js +1 -0
  243. package/apps/web/.next/server/app/login/page.js +2 -0
  244. package/apps/web/.next/server/app/login/page.js.nft.json +1 -0
  245. package/apps/web/.next/server/app/login/page_client-reference-manifest.js +1 -0
  246. package/apps/web/.next/server/app/page.js +2 -0
  247. package/apps/web/.next/server/app/page.js.nft.json +1 -0
  248. package/apps/web/.next/server/app/page_client-reference-manifest.js +1 -0
  249. package/apps/web/.next/server/app/settings/env-vars/page.js +2 -0
  250. package/apps/web/.next/server/app/settings/env-vars/page.js.nft.json +1 -0
  251. package/apps/web/.next/server/app/settings/env-vars/page_client-reference-manifest.js +1 -0
  252. package/apps/web/.next/server/app/settings/mcps/page.js +2 -0
  253. package/apps/web/.next/server/app/settings/mcps/page.js.nft.json +1 -0
  254. package/apps/web/.next/server/app/settings/mcps/page_client-reference-manifest.js +1 -0
  255. package/apps/web/.next/server/app/settings/page.js +2 -0
  256. package/apps/web/.next/server/app/settings/page.js.nft.json +1 -0
  257. package/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -0
  258. package/apps/web/.next/server/app-paths-manifest.json +46 -0
  259. package/apps/web/.next/server/chunks/1157.js +9 -0
  260. package/apps/web/.next/server/chunks/2287.js +1 -0
  261. package/apps/web/.next/server/chunks/3444.js +1 -0
  262. package/apps/web/.next/server/chunks/383.js +6 -0
  263. package/apps/web/.next/server/chunks/4012.js +58 -0
  264. package/apps/web/.next/server/chunks/6791.js +1 -0
  265. package/apps/web/.next/server/chunks/7171.js +1 -0
  266. package/apps/web/.next/server/chunks/8819.js +22 -0
  267. package/apps/web/.next/server/edge-runtime-webpack.js +2 -0
  268. package/apps/web/.next/server/edge-runtime-webpack.js.map +1 -0
  269. package/apps/web/.next/server/interception-route-rewrite-manifest.js +1 -0
  270. package/apps/web/.next/server/middleware-build-manifest.js +1 -0
  271. package/apps/web/.next/server/middleware-manifest.json +32 -0
  272. package/apps/web/.next/server/middleware-react-loadable-manifest.js +1 -0
  273. package/apps/web/.next/server/next-font-manifest.js +1 -0
  274. package/apps/web/.next/server/next-font-manifest.json +1 -0
  275. package/apps/web/.next/server/pages/_app.js +1 -0
  276. package/apps/web/.next/server/pages/_app.js.nft.json +1 -0
  277. package/apps/web/.next/server/pages/_document.js +1 -0
  278. package/apps/web/.next/server/pages/_document.js.nft.json +1 -0
  279. package/apps/web/.next/server/pages/_error.js +19 -0
  280. package/apps/web/.next/server/pages/_error.js.nft.json +1 -0
  281. package/apps/web/.next/server/pages-manifest.json +5 -0
  282. package/apps/web/.next/server/server-reference-manifest.js +1 -0
  283. package/apps/web/.next/server/server-reference-manifest.json +1 -0
  284. package/apps/web/.next/server/src/middleware.js +14 -0
  285. package/apps/web/.next/server/src/middleware.js.map +1 -0
  286. package/apps/web/.next/server/webpack-runtime.js +1 -0
  287. package/apps/web/.next/static/chunks/18-90b700ea37b686a2.js +1 -0
  288. package/apps/web/.next/static/chunks/87c73c54-24122e7b92478d00.js +1 -0
  289. package/apps/web/.next/static/chunks/9664-af80478aa73ba424.js +1 -0
  290. package/apps/web/.next/static/chunks/app/_not-found/page-b9cee17ed89ca24a.js +1 -0
  291. package/apps/web/.next/static/chunks/app/agents/[slug]/page-18369fc3fe1a9a7b.js +1 -0
  292. package/apps/web/.next/static/chunks/app/agents/new/page-bf11cf8901c7e2cd.js +1 -0
  293. package/apps/web/.next/static/chunks/app/api/agents/[id]/access/route-07f0f73ac9839899.js +1 -0
  294. package/apps/web/.next/static/chunks/app/api/agents/[id]/claude-md/route-07f0f73ac9839899.js +1 -0
  295. package/apps/web/.next/static/chunks/app/api/agents/[id]/logs/route-07f0f73ac9839899.js +1 -0
  296. package/apps/web/.next/static/chunks/app/api/agents/[id]/manifest/route-07f0f73ac9839899.js +1 -0
  297. package/apps/web/.next/static/chunks/app/api/agents/[id]/mcps/route-07f0f73ac9839899.js +1 -0
  298. package/apps/web/.next/static/chunks/app/api/agents/[id]/memories/[memId]/route-07f0f73ac9839899.js +1 -0
  299. package/apps/web/.next/static/chunks/app/api/agents/[id]/memories/route-07f0f73ac9839899.js +1 -0
  300. package/apps/web/.next/static/chunks/app/api/agents/[id]/permissions/route-07f0f73ac9839899.js +1 -0
  301. package/apps/web/.next/static/chunks/app/api/agents/[id]/reload/route-07f0f73ac9839899.js +1 -0
  302. package/apps/web/.next/static/chunks/app/api/agents/[id]/restrictions/route-07f0f73ac9839899.js +1 -0
  303. package/apps/web/.next/static/chunks/app/api/agents/[id]/route-07f0f73ac9839899.js +1 -0
  304. package/apps/web/.next/static/chunks/app/api/agents/[id]/skills/[skillId]/route-07f0f73ac9839899.js +1 -0
  305. package/apps/web/.next/static/chunks/app/api/agents/[id]/skills/route-07f0f73ac9839899.js +1 -0
  306. package/apps/web/.next/static/chunks/app/api/agents/[id]/slack-info/route-07f0f73ac9839899.js +1 -0
  307. package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/[sid]/restore/route-07f0f73ac9839899.js +1 -0
  308. package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/[sid]/route-07f0f73ac9839899.js +1 -0
  309. package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/route-07f0f73ac9839899.js +1 -0
  310. package/apps/web/.next/static/chunks/app/api/agents/[id]/start/route-07f0f73ac9839899.js +1 -0
  311. package/apps/web/.next/static/chunks/app/api/agents/[id]/stop/route-07f0f73ac9839899.js +1 -0
  312. package/apps/web/.next/static/chunks/app/api/agents/route-07f0f73ac9839899.js +1 -0
  313. package/apps/web/.next/static/chunks/app/api/auth/login/route-07f0f73ac9839899.js +1 -0
  314. package/apps/web/.next/static/chunks/app/api/auth/logout/route-07f0f73ac9839899.js +1 -0
  315. package/apps/web/.next/static/chunks/app/api/auth/me/route-07f0f73ac9839899.js +1 -0
  316. package/apps/web/.next/static/chunks/app/api/auth/users/[id]/route-07f0f73ac9839899.js +1 -0
  317. package/apps/web/.next/static/chunks/app/api/auth/users/route-07f0f73ac9839899.js +1 -0
  318. package/apps/web/.next/static/chunks/app/api/env-vars/[key]/route-07f0f73ac9839899.js +1 -0
  319. package/apps/web/.next/static/chunks/app/api/env-vars/route-07f0f73ac9839899.js +1 -0
  320. package/apps/web/.next/static/chunks/app/api/jobs/[id]/route-07f0f73ac9839899.js +1 -0
  321. package/apps/web/.next/static/chunks/app/api/jobs/[id]/runs/route-07f0f73ac9839899.js +1 -0
  322. package/apps/web/.next/static/chunks/app/api/jobs/route-07f0f73ac9839899.js +1 -0
  323. package/apps/web/.next/static/chunks/app/api/mcps/[id]/route-07f0f73ac9839899.js +1 -0
  324. package/apps/web/.next/static/chunks/app/api/mcps/[id]/test/route-07f0f73ac9839899.js +1 -0
  325. package/apps/web/.next/static/chunks/app/api/mcps/route-07f0f73ac9839899.js +1 -0
  326. package/apps/web/.next/static/chunks/app/api/settings/route-07f0f73ac9839899.js +1 -0
  327. package/apps/web/.next/static/chunks/app/jobs/page-f5aa89a47c50efd8.js +1 -0
  328. package/apps/web/.next/static/chunks/app/layout-2079f4964aa7314e.js +1 -0
  329. package/apps/web/.next/static/chunks/app/login/layout-07f0f73ac9839899.js +1 -0
  330. package/apps/web/.next/static/chunks/app/login/page-aa259283dc38e8f9.js +1 -0
  331. package/apps/web/.next/static/chunks/app/page-e83437b608104dff.js +1 -0
  332. package/apps/web/.next/static/chunks/app/settings/env-vars/page-06479dbdfb78b76b.js +1 -0
  333. package/apps/web/.next/static/chunks/app/settings/mcps/page-75650686ed6490c7.js +1 -0
  334. package/apps/web/.next/static/chunks/app/settings/page-e1e62fc41ff6cddd.js +1 -0
  335. package/apps/web/.next/static/chunks/framework-811407f832a33072.js +1 -0
  336. package/apps/web/.next/static/chunks/main-3f1cddbdd67b1546.js +1 -0
  337. package/apps/web/.next/static/chunks/main-app-cebd8a6a5ccbf72d.js +1 -0
  338. package/apps/web/.next/static/chunks/pages/_app-50fa07b56b2d29ac.js +1 -0
  339. package/apps/web/.next/static/chunks/pages/_error-fed8688bdd23f211.js +1 -0
  340. package/apps/web/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  341. package/apps/web/.next/static/chunks/webpack-6c05566dba553c97.js +1 -0
  342. package/apps/web/.next/static/css/15371687405525e2.css +5 -0
  343. package/apps/web/.next/static/ikfNbLhuw7jntn35bz0lk/_buildManifest.js +1 -0
  344. package/apps/web/.next/static/ikfNbLhuw7jntn35bz0lk/_ssgManifest.js +1 -0
  345. package/apps/web/.next/trace +5 -0
  346. package/apps/web/.next/types/app/agents/[slug]/page.ts +84 -0
  347. package/apps/web/.next/types/app/agents/new/page.ts +84 -0
  348. package/apps/web/.next/types/app/api/agents/[id]/access/route.ts +347 -0
  349. package/apps/web/.next/types/app/api/agents/[id]/claude-md/route.ts +347 -0
  350. package/apps/web/.next/types/app/api/agents/[id]/logs/route.ts +347 -0
  351. package/apps/web/.next/types/app/api/agents/[id]/manifest/route.ts +347 -0
  352. package/apps/web/.next/types/app/api/agents/[id]/mcps/route.ts +347 -0
  353. package/apps/web/.next/types/app/api/agents/[id]/memories/[memId]/route.ts +347 -0
  354. package/apps/web/.next/types/app/api/agents/[id]/memories/route.ts +347 -0
  355. package/apps/web/.next/types/app/api/agents/[id]/permissions/route.ts +347 -0
  356. package/apps/web/.next/types/app/api/agents/[id]/reload/route.ts +347 -0
  357. package/apps/web/.next/types/app/api/agents/[id]/restrictions/route.ts +347 -0
  358. package/apps/web/.next/types/app/api/agents/[id]/route.ts +347 -0
  359. package/apps/web/.next/types/app/api/agents/[id]/skills/[skillId]/route.ts +347 -0
  360. package/apps/web/.next/types/app/api/agents/[id]/skills/route.ts +347 -0
  361. package/apps/web/.next/types/app/api/agents/[id]/slack-info/route.ts +347 -0
  362. package/apps/web/.next/types/app/api/agents/[id]/snapshots/[sid]/restore/route.ts +347 -0
  363. package/apps/web/.next/types/app/api/agents/[id]/snapshots/[sid]/route.ts +347 -0
  364. package/apps/web/.next/types/app/api/agents/[id]/snapshots/route.ts +347 -0
  365. package/apps/web/.next/types/app/api/agents/[id]/start/route.ts +347 -0
  366. package/apps/web/.next/types/app/api/agents/[id]/stop/route.ts +347 -0
  367. package/apps/web/.next/types/app/api/agents/route.ts +347 -0
  368. package/apps/web/.next/types/app/api/auth/login/route.ts +347 -0
  369. package/apps/web/.next/types/app/api/auth/logout/route.ts +347 -0
  370. package/apps/web/.next/types/app/api/auth/me/route.ts +347 -0
  371. package/apps/web/.next/types/app/api/auth/users/[id]/route.ts +347 -0
  372. package/apps/web/.next/types/app/api/auth/users/route.ts +347 -0
  373. package/apps/web/.next/types/app/api/env-vars/[key]/route.ts +347 -0
  374. package/apps/web/.next/types/app/api/env-vars/route.ts +347 -0
  375. package/apps/web/.next/types/app/api/jobs/[id]/route.ts +347 -0
  376. package/apps/web/.next/types/app/api/jobs/[id]/runs/route.ts +347 -0
  377. package/apps/web/.next/types/app/api/jobs/route.ts +347 -0
  378. package/apps/web/.next/types/app/api/mcps/[id]/route.ts +347 -0
  379. package/apps/web/.next/types/app/api/mcps/[id]/test/route.ts +347 -0
  380. package/apps/web/.next/types/app/api/mcps/route.ts +347 -0
  381. package/apps/web/.next/types/app/api/settings/route.ts +347 -0
  382. package/apps/web/.next/types/app/jobs/page.ts +84 -0
  383. package/apps/web/.next/types/app/login/layout.ts +84 -0
  384. package/apps/web/.next/types/app/login/page.ts +84 -0
  385. package/apps/web/.next/types/app/page.ts +84 -0
  386. package/apps/web/.next/types/app/settings/env-vars/page.ts +84 -0
  387. package/apps/web/.next/types/app/settings/mcps/page.ts +84 -0
  388. package/apps/web/.next/types/app/settings/page.ts +84 -0
  389. package/apps/web/.next/types/cache-life.d.ts +141 -0
  390. package/apps/web/.next/types/package.json +1 -0
  391. package/apps/web/.next/types/routes.d.ts +114 -0
  392. package/apps/web/.next/types/validator.ts +448 -0
  393. package/apps/web/Dockerfile +37 -0
  394. package/apps/web/next-env.d.ts +6 -0
  395. package/apps/web/next.config.js +6 -0
  396. package/apps/web/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  397. package/apps/web/package.json +48 -0
  398. package/apps/web/postcss.config.js +3 -0
  399. package/apps/web/public/logo.svg +17 -0
  400. package/apps/web/src/app/agents/[slug]/page.tsx +2235 -0
  401. package/apps/web/src/app/agents/new/page.tsx +1161 -0
  402. package/apps/web/src/app/api/agents/[id]/access/route.ts +76 -0
  403. package/apps/web/src/app/api/agents/[id]/claude-md/route.ts +111 -0
  404. package/apps/web/src/app/api/agents/[id]/logs/route.ts +84 -0
  405. package/apps/web/src/app/api/agents/[id]/manifest/route.ts +32 -0
  406. package/apps/web/src/app/api/agents/[id]/mcps/route.ts +73 -0
  407. package/apps/web/src/app/api/agents/[id]/memories/[memId]/route.ts +31 -0
  408. package/apps/web/src/app/api/agents/[id]/memories/route.ts +56 -0
  409. package/apps/web/src/app/api/agents/[id]/permissions/route.ts +74 -0
  410. package/apps/web/src/app/api/agents/[id]/reload/route.ts +33 -0
  411. package/apps/web/src/app/api/agents/[id]/restrictions/route.ts +85 -0
  412. package/apps/web/src/app/api/agents/[id]/route.ts +81 -0
  413. package/apps/web/src/app/api/agents/[id]/skills/[skillId]/route.ts +52 -0
  414. package/apps/web/src/app/api/agents/[id]/skills/route.ts +80 -0
  415. package/apps/web/src/app/api/agents/[id]/slack-info/route.ts +38 -0
  416. package/apps/web/src/app/api/agents/[id]/snapshots/[sid]/restore/route.ts +61 -0
  417. package/apps/web/src/app/api/agents/[id]/snapshots/[sid]/route.ts +53 -0
  418. package/apps/web/src/app/api/agents/[id]/snapshots/route.ts +84 -0
  419. package/apps/web/src/app/api/agents/[id]/start/route.ts +35 -0
  420. package/apps/web/src/app/api/agents/[id]/stop/route.ts +35 -0
  421. package/apps/web/src/app/api/agents/route.ts +99 -0
  422. package/apps/web/src/app/api/auth/login/route.ts +39 -0
  423. package/apps/web/src/app/api/auth/logout/route.ts +21 -0
  424. package/apps/web/src/app/api/auth/me/route.ts +24 -0
  425. package/apps/web/src/app/api/auth/users/[id]/route.ts +48 -0
  426. package/apps/web/src/app/api/auth/users/route.ts +63 -0
  427. package/apps/web/src/app/api/env-vars/[key]/route.ts +66 -0
  428. package/apps/web/src/app/api/env-vars/route.ts +59 -0
  429. package/apps/web/src/app/api/jobs/[id]/route.ts +51 -0
  430. package/apps/web/src/app/api/jobs/[id]/runs/route.ts +24 -0
  431. package/apps/web/src/app/api/jobs/route.ts +42 -0
  432. package/apps/web/src/app/api/mcps/[id]/route.ts +60 -0
  433. package/apps/web/src/app/api/mcps/[id]/test/route.ts +195 -0
  434. package/apps/web/src/app/api/mcps/route.ts +72 -0
  435. package/apps/web/src/app/api/settings/route.ts +42 -0
  436. package/apps/web/src/app/globals.css +124 -0
  437. package/apps/web/src/app/icon.svg +17 -0
  438. package/apps/web/src/app/jobs/page.tsx +543 -0
  439. package/apps/web/src/app/layout-shell.tsx +89 -0
  440. package/apps/web/src/app/layout.tsx +18 -0
  441. package/apps/web/src/app/login/layout.tsx +9 -0
  442. package/apps/web/src/app/login/page.tsx +150 -0
  443. package/apps/web/src/app/page.tsx +573 -0
  444. package/apps/web/src/app/settings/env-vars/page.tsx +216 -0
  445. package/apps/web/src/app/settings/mcps/page.tsx +763 -0
  446. package/apps/web/src/app/settings/page.tsx +528 -0
  447. package/apps/web/src/app/sidebar.tsx +345 -0
  448. package/apps/web/src/lib/__tests__/api-guard.test.ts +189 -0
  449. package/apps/web/src/lib/__tests__/auth.test.ts +262 -0
  450. package/apps/web/src/lib/__tests__/boss-registry.test.ts +323 -0
  451. package/apps/web/src/lib/__tests__/compile.test.ts +161 -0
  452. package/apps/web/src/lib/__tests__/db-agent-hierarchy.test.ts +136 -0
  453. package/apps/web/src/lib/__tests__/db-env-vars.test.ts +216 -0
  454. package/apps/web/src/lib/__tests__/db-restrictions.test.ts +117 -0
  455. package/apps/web/src/lib/__tests__/db.integration.test.ts +271 -0
  456. package/apps/web/src/lib/__tests__/diff.test.ts +102 -0
  457. package/apps/web/src/lib/__tests__/mcp-mask.test.ts +274 -0
  458. package/apps/web/src/lib/__tests__/skill-templates.test.ts +237 -0
  459. package/apps/web/src/lib/__tests__/slack-manifest.test.ts +105 -0
  460. package/apps/web/src/lib/api-guard.ts +68 -0
  461. package/apps/web/src/lib/auth-context.tsx +71 -0
  462. package/apps/web/src/lib/auth.ts +128 -0
  463. package/apps/web/src/lib/boss-registry.ts +90 -0
  464. package/apps/web/src/lib/compile.ts +51 -0
  465. package/apps/web/src/lib/db.ts +1196 -0
  466. package/apps/web/src/lib/diff.ts +43 -0
  467. package/apps/web/src/lib/mcp-mask.ts +91 -0
  468. package/apps/web/src/lib/portal.tsx +23 -0
  469. package/apps/web/src/lib/skill-templates.ts +148 -0
  470. package/apps/web/src/lib/slack-manifest.ts +85 -0
  471. package/apps/web/src/middleware.ts +68 -0
  472. package/apps/web/tailwind.config.js +6 -0
  473. package/apps/web/tsconfig.json +23 -0
  474. package/apps/web/vitest.config.mts +21 -0
  475. package/cli/.claude/settings.local.json +6 -0
  476. package/cli/README.md +281 -0
  477. package/cli/node_modules/.package-lock.json +427 -0
  478. package/cli/node_modules/commander/LICENSE +22 -0
  479. package/cli/node_modules/commander/Readme.md +1157 -0
  480. package/cli/node_modules/commander/esm.mjs +16 -0
  481. package/cli/node_modules/commander/index.js +24 -0
  482. package/cli/node_modules/commander/lib/argument.js +149 -0
  483. package/cli/node_modules/commander/lib/command.js +2509 -0
  484. package/cli/node_modules/commander/lib/error.js +39 -0
  485. package/cli/node_modules/commander/lib/help.js +520 -0
  486. package/cli/node_modules/commander/lib/option.js +330 -0
  487. package/cli/node_modules/commander/lib/suggestSimilar.js +101 -0
  488. package/cli/node_modules/commander/package-support.json +16 -0
  489. package/cli/node_modules/commander/package.json +84 -0
  490. package/cli/node_modules/commander/typings/esm.d.mts +3 -0
  491. package/cli/node_modules/commander/typings/index.d.ts +969 -0
  492. package/cli/package-lock.json +449 -0
  493. package/cli/package.json +44 -0
  494. package/cli/src/commands/init.ts +514 -0
  495. package/cli/src/commands/manage.ts +115 -0
  496. package/cli/src/index.ts +63 -0
  497. package/cli/tsconfig.json +14 -0
  498. package/docker-compose.yml +122 -0
  499. package/docs/agents/boss-agents.mdx +108 -0
  500. package/docs/agents/creating-agents.mdx +132 -0
  501. package/docs/agents/memory.mdx +113 -0
  502. package/docs/agents/tools.mdx +103 -0
  503. package/docs/configuration/env-vars.mdx +166 -0
  504. package/docs/configuration/mcp-servers.mdx +203 -0
  505. package/docs/configuration/slack-app.mdx +175 -0
  506. package/docs/docs.json +79 -0
  507. package/docs/favicon.svg +17 -0
  508. package/docs/features/history.mdx +60 -0
  509. package/docs/features/import-export.mdx +77 -0
  510. package/docs/features/logs.mdx +131 -0
  511. package/docs/features/multi-workspace.mdx +90 -0
  512. package/docs/features/scheduled-jobs.mdx +231 -0
  513. package/docs/features/users.mdx +92 -0
  514. package/docs/introduction.mdx +160 -0
  515. package/docs/logo/dark.svg +17 -0
  516. package/docs/logo/light.svg +17 -0
  517. package/docs/logo/wide-dark.svg +12 -0
  518. package/docs/logo/wide-light.svg +12 -0
  519. package/docs/quickstart.mdx +270 -0
  520. package/docs/self-hosting/docker.mdx +151 -0
  521. package/docs/self-hosting/production.mdx +176 -0
  522. package/package.json +20 -36
  523. package/packages/shared/dist/index.d.ts +8 -0
  524. package/packages/shared/dist/index.d.ts.map +1 -0
  525. package/packages/shared/dist/index.js +24 -0
  526. package/packages/shared/dist/index.js.map +1 -0
  527. package/packages/shared/dist/types.d.ts +584 -0
  528. package/packages/shared/dist/types.d.ts.map +1 -0
  529. package/packages/shared/dist/types.js +39 -0
  530. package/packages/shared/dist/types.js.map +1 -0
  531. package/packages/shared/package.json +15 -0
  532. package/packages/shared/src/db/schema.sql +354 -0
  533. package/packages/shared/src/index.ts +8 -0
  534. package/packages/shared/src/types.ts +683 -0
  535. package/packages/shared/tsconfig.json +17 -0
  536. package/scripts/dev.sh +45 -0
  537. /package/{dist → cli/dist}/commands/init.d.ts +0 -0
  538. /package/{dist → cli/dist}/commands/init.js +0 -0
  539. /package/{dist → cli/dist}/commands/manage.d.ts +0 -0
  540. /package/{dist → cli/dist}/commands/manage.js +0 -0
  541. /package/{dist → cli/dist}/index.d.ts +0 -0
  542. /package/{dist → cli/dist}/index.js +0 -0
@@ -0,0 +1,514 @@
1
+ /**
2
+ * @fileoverview `slackhive init` — clone, configure, and start SlackHive.
3
+ *
4
+ * @module cli/commands/init
5
+ */
6
+
7
+ import { execSync, spawn } from 'child_process';
8
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
9
+ import { join, resolve } from 'path';
10
+ import chalk from 'chalk';
11
+ import ora from 'ora';
12
+ import prompts from 'prompts';
13
+
14
+ const REPO_URL = 'https://github.com/pelago-labs/slackhive.git';
15
+
16
+ interface InitOptions {
17
+ dir: string;
18
+ skipStart?: boolean;
19
+ }
20
+
21
+ function detectClaudeBin(): string {
22
+ let claudeBin: string;
23
+ try {
24
+ claudeBin = execSync('which claude', { encoding: 'utf-8' }).trim();
25
+ } catch {
26
+ throw new Error('Claude Code not found. Please install Claude Code first.');
27
+ }
28
+ if (!claudeBin) throw new Error('Claude Code not found. Please install Claude Code first.');
29
+ return claudeBin;
30
+ }
31
+
32
+ interface OAuthCredentials {
33
+ accessToken: string;
34
+ refreshToken: string;
35
+ }
36
+
37
+ /**
38
+ * Parses a JSON credential blob and extracts OAuth tokens.
39
+ */
40
+ function parseOAuthFromJson(json: string): OAuthCredentials | null {
41
+ try {
42
+ const parsed = JSON.parse(json);
43
+ const oauth = parsed?.claudeAiOauth;
44
+ if (oauth?.accessToken && oauth?.refreshToken) {
45
+ return { accessToken: oauth.accessToken, refreshToken: oauth.refreshToken };
46
+ }
47
+ } catch { /* invalid json */ }
48
+ return null;
49
+ }
50
+
51
+ /**
52
+ * Extracts the OAuth credentials from the OS credential store.
53
+ * Tries macOS Keychain, then Linux secret-tool (GNOME Keyring).
54
+ * Returns access + refresh tokens, or null if not found.
55
+ */
56
+ function extractOAuthCredentials(): OAuthCredentials | null {
57
+ // macOS: read from Keychain
58
+ try {
59
+ const creds = execSync('security find-generic-password -s "Claude Code-credentials" -w', {
60
+ encoding: 'utf-8',
61
+ stdio: ['pipe', 'pipe', 'ignore'],
62
+ }).trim();
63
+ const result = parseOAuthFromJson(creds);
64
+ if (result) return result;
65
+ } catch { /* not macOS or not found */ }
66
+
67
+ // Linux: try secret-tool (GNOME Keyring)
68
+ try {
69
+ const creds = execSync('secret-tool lookup service "Claude Code-credentials"', {
70
+ encoding: 'utf-8',
71
+ stdio: ['pipe', 'pipe', 'ignore'],
72
+ }).trim();
73
+ const result = parseOAuthFromJson(creds);
74
+ if (result) return result;
75
+ } catch { /* not available */ }
76
+
77
+ // Fallback: read credentials file directly (headless Linux / no keyring)
78
+ try {
79
+ const credPath = join(process.env.HOME || '~', '.claude', '.credentials.json');
80
+ if (existsSync(credPath)) {
81
+ const creds = readFileSync(credPath, 'utf-8').trim();
82
+ const result = parseOAuthFromJson(creds);
83
+ if (result) return result;
84
+ }
85
+ } catch { /* file not readable or invalid */ }
86
+
87
+ return null;
88
+ }
89
+
90
+ /**
91
+ * Runs `slackhive init` — interactive setup wizard.
92
+ *
93
+ * @param {InitOptions} opts - CLI options.
94
+ */
95
+ export async function init(opts: InitOptions): Promise<void> {
96
+ const dir = resolve(opts.dir);
97
+
98
+ const O = chalk.hex('#D97757').bold;
99
+ const W = chalk.hex('#EBE6E0').bold;
100
+ console.log('');
101
+ console.log(' ' + W('│ │'));
102
+ console.log(' ' + W('───┼───┼───'));
103
+ console.log(' ' + O('>') + W(' ──┼──') + O('█') + W('┼──'));
104
+ console.log(' ' + W('│ │'));
105
+ console.log('');
106
+ console.log(chalk.bold(' SlackHive') + chalk.gray(' — AI agent teams on Slack'));
107
+ console.log('');
108
+
109
+ // ── Step 1: Check prerequisites ───────────────────────────────────────────
110
+ console.log(chalk.bold.hex('#D97757')(' [1/4]') + chalk.bold(' Checking prerequisites'));
111
+ console.log('');
112
+
113
+ const checks = [
114
+ { name: 'Docker daemon', cmd: 'docker info', errMsg: 'Docker is not running. Please start Docker Desktop and try again.' },
115
+ { name: 'Docker Compose', cmd: 'docker compose version', errMsg: 'Docker Compose not found. Please install Docker Desktop.' },
116
+ { name: 'Git', cmd: 'git --version', errMsg: 'Git not found. Please install Git first.' },
117
+ ];
118
+
119
+ for (const check of checks) {
120
+ const spinner = ora(` Checking ${check.name}...`).start();
121
+ try {
122
+ execSync(check.cmd, { stdio: 'ignore' });
123
+ spinner.succeed(chalk.green(`${check.name} ready`));
124
+ } catch {
125
+ spinner.fail(chalk.red(`${check.name}: ${check.errMsg}`));
126
+ process.exit(1);
127
+ }
128
+ }
129
+ console.log('');
130
+
131
+ // ── Step 2: Clone ─────────────────────────────────────────────────────────
132
+ console.log(chalk.bold.hex('#D97757')(' [2/4]') + chalk.bold(' Getting SlackHive'));
133
+ console.log('');
134
+
135
+ if (existsSync(dir)) {
136
+ console.log(chalk.yellow(` note: Directory ${opts.dir} already exists — using existing`));
137
+ } else {
138
+ const spinner = ora(' Cloning repository...').start();
139
+ try {
140
+ execSync(`git clone --depth 1 ${REPO_URL} "${dir}"`, { stdio: 'ignore' });
141
+ spinner.succeed('Repository cloned');
142
+ } catch {
143
+ spinner.fail('Failed to clone repository');
144
+ process.exit(1);
145
+ }
146
+ }
147
+ console.log('');
148
+
149
+ // ── Step 3: Configure .env ────────────────────────────────────────────────
150
+ const envPath = join(dir, '.env');
151
+ const freshEnv = !existsSync(envPath);
152
+
153
+ if (freshEnv) {
154
+ console.log(chalk.bold.hex('#D97757')(' [3/4]') + chalk.bold(' Configure environment'));
155
+ console.log('');
156
+
157
+ const authMode = await prompts({
158
+ type: 'select',
159
+ name: 'mode',
160
+ message: 'Claude authentication',
161
+ choices: [
162
+ { title: 'API Key — pay-per-use via Anthropic API', value: 'apikey' },
163
+ { title: 'Subscription — run `claude login` first', value: 'subscription' },
164
+ ],
165
+ });
166
+
167
+ if (!authMode.mode) {
168
+ console.log(chalk.red('\n Setup cancelled.'));
169
+ process.exit(1);
170
+ }
171
+
172
+ const questions: prompts.PromptObject[] = [];
173
+ let oauthCreds: OAuthCredentials | null = null;
174
+
175
+ if (authMode.mode === 'apikey') {
176
+ questions.push({
177
+ type: 'text',
178
+ name: 'anthropicKey',
179
+ message: 'Anthropic API key',
180
+ validate: (v: string) => v.startsWith('sk-') ? true : 'Must start with sk-',
181
+ });
182
+ } else {
183
+ // Claude subscription mode — extract OAuth token
184
+ const claudeDir = join(process.env.HOME || '~', '.claude');
185
+ if (!existsSync(claudeDir)) {
186
+ console.log(chalk.yellow('\n warning: ~/.claude not found. Run `claude login` first, then re-run `slackhive init`.'));
187
+ process.exit(1);
188
+ }
189
+ console.log(chalk.green(' ✓') + ' Found ~/.claude credentials');
190
+
191
+ const spinner = ora(' Extracting OAuth credentials...').start();
192
+ oauthCreds = extractOAuthCredentials();
193
+ if (oauthCreds) {
194
+ spinner.succeed('OAuth credentials extracted');
195
+ } else {
196
+ spinner.warn('Could not auto-extract credentials from keychain');
197
+ console.log(chalk.gray(' On Linux/headless servers, paste your OAuth token manually.'));
198
+ console.log(chalk.gray(' Get it from a machine where you ran `claude login`:'));
199
+ console.log(chalk.gray(' security find-generic-password -s "Claude Code-credentials" -w'));
200
+ console.log('');
201
+
202
+ const tokenResponse = await prompts([
203
+ { type: 'password', name: 'accessToken', message: 'OAuth access token (sk-ant-oat01-...)', validate: (v: string) => v.startsWith('sk-ant-oat') ? true : 'Must start with sk-ant-oat' },
204
+ { type: 'password', name: 'refreshToken', message: 'OAuth refresh token (sk-ant-ort01-...)', validate: (v: string) => v.startsWith('sk-ant-ort') ? true : 'Must start with sk-ant-ort' },
205
+ ]);
206
+ if (!tokenResponse.accessToken || !tokenResponse.refreshToken) {
207
+ console.log(chalk.red('\n Setup cancelled. Use API Key mode instead on headless servers.'));
208
+ process.exit(1);
209
+ }
210
+ oauthCreds = { accessToken: tokenResponse.accessToken, refreshToken: tokenResponse.refreshToken };
211
+ }
212
+ }
213
+
214
+ questions.push(
215
+ { type: 'text', name: 'adminUsername', message: 'Admin username', initial: 'admin' },
216
+ { type: 'password', name: 'adminPassword', message: 'Admin password', validate: (v: string) => v.length >= 6 ? true : 'At least 6 characters' },
217
+ { type: 'text', name: 'postgresPassword', message: 'Postgres password', initial: randomSecret().slice(0, 16) },
218
+ { type: 'text', name: 'redisPassword', message: 'Redis password', initial: randomSecret().slice(0, 16) },
219
+ );
220
+
221
+ const response = await prompts(questions);
222
+
223
+ if (!response.adminPassword) {
224
+ console.log(chalk.red('\n Setup cancelled.'));
225
+ process.exit(1);
226
+ }
227
+
228
+ let envContent = '# Generated by slackhive init\n\n';
229
+ if (authMode.mode === 'apikey') {
230
+ envContent += `ANTHROPIC_API_KEY=${response.anthropicKey}\n`;
231
+ } else {
232
+ envContent += `# Claude Code subscription — OAuth credentials from keychain\n`;
233
+ envContent += `CLAUDE_CODE_OAUTH_TOKEN=${oauthCreds!.accessToken}\n`;
234
+ envContent += `CLAUDE_CODE_OAUTH_REFRESH_TOKEN=${oauthCreds!.refreshToken}\n`;
235
+ }
236
+ envContent += `\nPOSTGRES_DB=slackhive\n`;
237
+ envContent += `POSTGRES_USER=slackhive\n`;
238
+ envContent += `POSTGRES_PASSWORD=${response.postgresPassword}\n`;
239
+ envContent += `\nREDIS_PASSWORD=${response.redisPassword}\n`;
240
+ envContent += `\nADMIN_USERNAME=${response.adminUsername}\n`;
241
+ envContent += `ADMIN_PASSWORD=${response.adminPassword}\n`;
242
+ envContent += `AUTH_SECRET=${randomSecret()}\n`;
243
+ envContent += `ENV_SECRET_KEY=${randomSecret()}\n`;
244
+ envContent += `\nNODE_ENV=production\n`;
245
+
246
+ writeFileSync(envPath, envContent);
247
+ console.log('');
248
+ console.log(chalk.green(' ✓') + ' .env file created');
249
+ console.log('');
250
+ } else {
251
+ console.log(chalk.bold.hex('#D97757')(' [3/4]') + chalk.bold(' Configure environment'));
252
+ console.log('');
253
+ // Check if existing .env is missing required keys
254
+ const envContents = existsSync(envPath) ? require('fs').readFileSync(envPath, 'utf-8') : '';
255
+ const missingKeys: string[] = [];
256
+ if (!envContents.includes('REDIS_PASSWORD=')) missingKeys.push('REDIS_PASSWORD');
257
+ if (!envContents.includes('AUTH_SECRET=')) missingKeys.push('AUTH_SECRET');
258
+ if (!envContents.includes('ENV_SECRET_KEY=')) missingKeys.push('ENV_SECRET_KEY');
259
+ if (missingKeys.length > 0) {
260
+ console.log(chalk.yellow(` warning: .env is missing: ${missingKeys.join(', ')} — patching...`));
261
+ let patch = '';
262
+ if (!envContents.includes('REDIS_PASSWORD=')) patch += `\nREDIS_PASSWORD=${randomSecret().slice(0, 16)}\n`;
263
+ if (!envContents.includes('AUTH_SECRET=')) patch += `AUTH_SECRET=${randomSecret()}\n`;
264
+ if (!envContents.includes('ENV_SECRET_KEY=')) patch += `ENV_SECRET_KEY=${randomSecret()}\n`;
265
+ require('fs').appendFileSync(envPath, patch);
266
+ console.log(chalk.green(' ✓') + ' .env patched');
267
+ } else {
268
+ console.log(chalk.yellow(' note: .env already exists — skipping configuration'));
269
+ }
270
+ console.log('');
271
+ }
272
+
273
+ // ── Step 4: Build & start ─────────────────────────────────────────────────
274
+ let webReady = true;
275
+ if (!opts.skipStart) {
276
+ console.log(chalk.bold.hex('#D97757')(' [4/4]') + chalk.bold(' Building & starting services'));
277
+ console.log(chalk.gray(' This takes 3–5 minutes on first run while Docker builds images.'));
278
+ console.log('');
279
+
280
+ // Pre-flight: check Docker has enough disk space (need ~3GB)
281
+ try {
282
+ const dfOut = execSync('docker system df --format "{{.Size}}"', { encoding: 'utf-8' });
283
+ void dfOut; // just checking it runs without error
284
+ } catch {
285
+ console.log(chalk.yellow(' note: Could not check Docker disk usage — continuing anyway'));
286
+ }
287
+
288
+ // Pre-flight: warn if low disk space on host
289
+ try {
290
+ const df = execSync('df -k . | tail -1', { encoding: 'utf-8' }).trim();
291
+ const available = parseInt(df.split(/\s+/)[3]);
292
+ if (!isNaN(available) && available < 3 * 1024 * 1024) {
293
+ console.log(chalk.yellow(` warning: less than 3GB disk space available. Build may fail.`));
294
+ console.log('');
295
+ }
296
+ } catch { /* non-fatal */ }
297
+
298
+ // Remove stale Postgres volume only on fresh init — prevents password mismatch
299
+ // when credentials were just generated. Skip if .env already existed (re-run).
300
+ if (freshEnv) {
301
+ try {
302
+ execSync('docker compose down -v', { cwd: dir, stdio: 'ignore' });
303
+ } catch { /* non-fatal — may not exist yet */ }
304
+ }
305
+
306
+ const buildOk = await runDockerBuild(dir, opts.dir);
307
+
308
+ if (buildOk) {
309
+ // If containers didn't come up during build, retry once silently
310
+ try {
311
+ execSync('docker compose up -d', { cwd: dir, stdio: 'ignore' });
312
+ } catch { /* non-fatal */ }
313
+
314
+ // Wait for web UI — up to 3 minutes
315
+ const webSpinner = ora(' Waiting for web UI to be ready...').start();
316
+ let ready = false;
317
+ for (let i = 0; i < 60; i++) {
318
+ try {
319
+ execSync('curl -sf http://localhost:3001/login', { stdio: 'ignore' });
320
+ ready = true;
321
+ break;
322
+ } catch {
323
+ await sleep(3000);
324
+ }
325
+ }
326
+ if (ready) {
327
+ webSpinner.succeed('Web UI is ready');
328
+ } else {
329
+ webReady = false;
330
+ webSpinner.stopAndPersist({ symbol: ' ' });
331
+ }
332
+ } else {
333
+ webReady = false;
334
+ }
335
+ }
336
+
337
+ // ── Done ──────────────────────────────────────────────────────────────────
338
+ console.log('');
339
+ if (webReady) {
340
+ console.log(' ' + chalk.bgHex('#D97757').black.bold(' SlackHive is ready! '));
341
+ console.log('');
342
+ console.log(` ${chalk.bold('Open:')} ${chalk.cyan('http://localhost:3001')}`);
343
+ } else {
344
+ console.log(' ' + chalk.bold('Setup complete!'));
345
+ console.log('');
346
+ console.log(chalk.gray(' Services are still starting. Once ready:'));
347
+ console.log(` ${chalk.bold('Run:')} ${chalk.cyan('slackhive start')}`);
348
+ console.log(` ${chalk.bold('Open:')} ${chalk.cyan('http://localhost:3001')}`);
349
+ }
350
+ console.log(` ${chalk.bold('Dir:')} ${chalk.gray(dir)}`);
351
+ console.log('');
352
+ console.log(chalk.gray(' Useful commands:'));
353
+ console.log(chalk.gray(' slackhive start — Start services'));
354
+ console.log(chalk.gray(' slackhive stop — Stop services'));
355
+ console.log(chalk.gray(' slackhive status — Show container status'));
356
+ console.log(chalk.gray(' slackhive logs — Tail runner logs'));
357
+ console.log(chalk.gray(' slackhive update — Pull latest & rebuild'));
358
+ console.log('');
359
+ }
360
+
361
+ /**
362
+ * Runs `docker compose up -d --build`.
363
+ * Shows a single updating spinner line with the current build step.
364
+ * Docker's raw progress output is suppressed to keep the terminal clean.
365
+ */
366
+ function runDockerBuild(cwd: string, displayDir: string): Promise<boolean> {
367
+ return new Promise((resolve) => {
368
+ const proc = spawn('docker', ['compose', '--progress', 'plain', 'up', '-d', '--build'], {
369
+ cwd,
370
+ env: { ...process.env },
371
+ });
372
+
373
+ const startTime = Date.now();
374
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
375
+ let frameIdx = 0;
376
+
377
+ // Phased progress tracking
378
+ const phases = [
379
+ { name: 'Installing system packages', weight: 10, pattern: /apk add|fetch.*APKINDEX/i },
380
+ { name: 'Installing npm dependencies', weight: 30, pattern: /npm ci|npm install|added \d+ packages/i },
381
+ { name: 'Compiling TypeScript', weight: 10, pattern: /tsc|--skipLibCheck/i },
382
+ { name: 'Building web app', weight: 30, pattern: /next build|next\.config/i },
383
+ { name: 'Creating containers', weight: 10, pattern: /exporting to image|naming to|exporting layers/i },
384
+ { name: 'Starting services', weight: 10, pattern: /Container .*(Starting|Started|Healthy|Created)/i },
385
+ ];
386
+ let currentPhase = 0;
387
+ let phaseStartTime = Date.now();
388
+
389
+ function getProgress(): number {
390
+ let pct = 0;
391
+ for (let i = 0; i < currentPhase; i++) pct += phases[i].weight;
392
+ // Add partial progress within current phase
393
+ if (currentPhase < phases.length) {
394
+ const elapsed = (Date.now() - phaseStartTime) / 1000;
395
+ const estimatedDuration = currentPhase === 1 ? 90 : currentPhase === 3 ? 100 : 30;
396
+ const partial = Math.min(0.9, elapsed / estimatedDuration);
397
+ pct += phases[currentPhase].weight * partial;
398
+ }
399
+ return Math.min(99, Math.round(pct));
400
+ }
401
+
402
+ function renderBar(): string {
403
+ const pct = getProgress();
404
+ const cols = process.stdout.columns || 80;
405
+ const barWidth = Math.min(20, Math.max(10, cols - 55));
406
+ const filled = Math.round((pct / 100) * barWidth);
407
+ const empty = barWidth - filled;
408
+ const bar = chalk.hex('#D97757')('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
409
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
410
+ const phaseName = currentPhase < phases.length ? phases[currentPhase].name : 'Finishing';
411
+ const frame = frames[frameIdx++ % frames.length];
412
+ const pctStr = String(pct).padStart(2);
413
+ return ` ${chalk.hex('#D97757')(frame)} ${bar} ${chalk.bold(pctStr + '%')} ${phaseName} ${chalk.gray('(' + elapsed + 's)')}`;
414
+ }
415
+
416
+ const spinnerInterval = setInterval(() => {
417
+ process.stdout.write(`\r\x1b[K${renderBar()}`);
418
+ }, 80);
419
+
420
+ let buf = '';
421
+ const errorLines: string[] = [];
422
+
423
+ const onData = (chunk: Buffer) => {
424
+ buf += chunk.toString().replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
425
+ const lines = buf.split('\n');
426
+ buf = lines.pop() ?? '';
427
+ for (const raw of lines) {
428
+ const line = raw.trim();
429
+ if (!line) continue;
430
+
431
+ // Check if we've entered a new phase
432
+ for (let i = currentPhase + 1; i < phases.length; i++) {
433
+ if (phases[i].pattern.test(line)) {
434
+ // Print completed phases
435
+ const elapsed = Math.floor((Date.now() - phaseStartTime) / 1000);
436
+ process.stdout.write('\r\x1b[K');
437
+ console.log(' ' + chalk.green('✓') + ' ' + phases[currentPhase].name + chalk.gray(` (${elapsed}s)`));
438
+ // Skip intermediate phases
439
+ for (let j = currentPhase + 1; j < i; j++) {
440
+ console.log(' ' + chalk.green('✓') + ' ' + phases[j].name + chalk.gray(' (cached)'));
441
+ }
442
+ currentPhase = i;
443
+ phaseStartTime = Date.now();
444
+ break;
445
+ }
446
+ }
447
+
448
+ if (/error/i.test(line)) errorLines.push(line);
449
+ }
450
+ };
451
+
452
+ proc.stdout.on('data', onData);
453
+ proc.stderr.on('data', onData);
454
+
455
+ proc.on('close', (code) => {
456
+ clearInterval(spinnerInterval);
457
+ process.stdout.write('\r\x1b[K');
458
+
459
+ if (code === 0) {
460
+ // Print any remaining phases as done
461
+ const elapsed = Math.floor((Date.now() - phaseStartTime) / 1000);
462
+ if (currentPhase < phases.length) {
463
+ console.log(' ' + chalk.green('✓') + ' ' + phases[currentPhase].name + chalk.gray(` (${elapsed}s)`));
464
+ for (let j = currentPhase + 1; j < phases.length; j++) {
465
+ console.log(' ' + chalk.green('✓') + ' ' + phases[j].name);
466
+ }
467
+ }
468
+ console.log('');
469
+ console.log(' ' + chalk.green('✓') + chalk.bold(' All services started'));
470
+ resolve(true);
471
+ return;
472
+ }
473
+
474
+ console.log(' ' + chalk.red('✗') + ' Failed to start services');
475
+ console.log('');
476
+
477
+ const allErrors = errorLines.join('\n').toLowerCase();
478
+ if (allErrors.includes('no space left') || allErrors.includes('disk full')) {
479
+ console.log(chalk.yellow(' Cause: Docker is out of disk space.'));
480
+ console.log(chalk.gray(' Fix: docker system prune -a'));
481
+ } else if (allErrors.includes('port is already allocated') || allErrors.includes('address already in use')) {
482
+ const portMatch = /bind for .+:(\d+)/.exec(allErrors);
483
+ const port = portMatch ? portMatch[1] : 'a required port';
484
+ console.log(chalk.yellow(` Cause: Port ${port} is already in use.`));
485
+ console.log(chalk.gray(` Fix: stop the process on port ${port} and retry`));
486
+ } else if (allErrors.includes('permission denied') || allErrors.includes('unauthorized')) {
487
+ console.log(chalk.yellow(' Cause: Docker permission denied — is Docker Desktop running?'));
488
+ } else if (allErrors.includes('memory') || allErrors.includes('oom')) {
489
+ console.log(chalk.yellow(' Cause: Docker ran out of memory.'));
490
+ console.log(chalk.gray(' Fix: increase Docker Desktop memory to 4GB+ in Settings → Resources'));
491
+ } else if (allErrors.includes('network') || allErrors.includes('timeout') || allErrors.includes('pull') || allErrors.includes('tls') || allErrors.includes('certificate')) {
492
+ console.log(chalk.yellow(' Cause: Network/TLS error — try restarting Docker Desktop.'));
493
+ } else if (errorLines.length > 0) {
494
+ console.log(chalk.gray(' Error details:'));
495
+ errorLines.slice(-5).forEach(l => console.log(chalk.red(' ' + l)));
496
+ }
497
+
498
+ console.log('');
499
+ console.log(chalk.gray(` To retry: cd ${displayDir} && docker compose up -d --build`));
500
+ resolve(false);
501
+ });
502
+ });
503
+ }
504
+
505
+ function randomSecret(): string {
506
+ const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
507
+ let result = '';
508
+ for (let i = 0; i < 32; i++) result += chars.charAt(Math.floor(Math.random() * chars.length));
509
+ return result;
510
+ }
511
+
512
+ function sleep(ms: number): Promise<void> {
513
+ return new Promise(r => setTimeout(r, ms));
514
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @fileoverview Management commands — start, stop, status, logs, update.
3
+ *
4
+ * All commands look for docker-compose.yml in the current directory
5
+ * or the `slackhive` subdirectory.
6
+ *
7
+ * @module cli/commands/manage
8
+ */
9
+
10
+ import { execSync, spawn } from 'child_process';
11
+ import { existsSync } from 'fs';
12
+ import { join } from 'path';
13
+ import chalk from 'chalk';
14
+ import ora from 'ora';
15
+
16
+ /**
17
+ * Finds the SlackHive project directory.
18
+ * Checks cwd first, then ./slackhive subdirectory.
19
+ *
20
+ * @returns {string} Path to the project directory.
21
+ */
22
+ function findProjectDir(): string {
23
+ if (existsSync(join(process.cwd(), 'docker-compose.yml'))) {
24
+ return process.cwd();
25
+ }
26
+ const sub = join(process.cwd(), 'slackhive');
27
+ if (existsSync(join(sub, 'docker-compose.yml'))) {
28
+ return sub;
29
+ }
30
+ console.log(chalk.red(' Could not find SlackHive project.'));
31
+ console.log(chalk.gray(' Run this command from the SlackHive directory, or run `slackhive init` first.'));
32
+ process.exit(1);
33
+ }
34
+
35
+ /**
36
+ * Start all SlackHive services.
37
+ */
38
+ export async function start(): Promise<void> {
39
+ const dir = findProjectDir();
40
+ const spinner = ora('Starting SlackHive services...').start();
41
+ try {
42
+ execSync('docker compose up -d', { cwd: dir, stdio: 'ignore' });
43
+ spinner.succeed('All services started');
44
+ console.log(chalk.gray(' Web UI: http://localhost:3001'));
45
+ } catch {
46
+ spinner.fail('Failed to start services');
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Stop all SlackHive services.
52
+ */
53
+ export async function stop(): Promise<void> {
54
+ const dir = findProjectDir();
55
+ const spinner = ora('Stopping SlackHive services...').start();
56
+ try {
57
+ execSync('docker compose stop', { cwd: dir, stdio: 'ignore' });
58
+ spinner.succeed('All services stopped');
59
+ } catch {
60
+ spinner.fail('Failed to stop services');
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Show running SlackHive containers.
66
+ */
67
+ export async function status(): Promise<void> {
68
+ const dir = findProjectDir();
69
+ try {
70
+ const output = execSync('docker compose ps', { cwd: dir, encoding: 'utf-8' });
71
+ console.log('');
72
+ console.log(chalk.bold(' SlackHive Status'));
73
+ console.log('');
74
+ console.log(output);
75
+ } catch {
76
+ console.log(chalk.red(' Failed to get status'));
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Tail runner service logs.
82
+ */
83
+ export async function logs(opts: { follow?: boolean }): Promise<void> {
84
+ const dir = findProjectDir();
85
+ const args = ['compose', 'logs', 'runner'];
86
+ if (opts.follow !== false) args.push('-f');
87
+
88
+ const proc = spawn('docker', args, { cwd: dir, stdio: 'inherit' });
89
+ proc.on('error', () => console.log(chalk.red(' Failed to tail logs')));
90
+ }
91
+
92
+ /**
93
+ * Pull latest changes and rebuild.
94
+ */
95
+ export async function update(): Promise<void> {
96
+ const dir = findProjectDir();
97
+
98
+ const pullSpinner = ora('Pulling latest changes...').start();
99
+ try {
100
+ execSync('git pull', { cwd: dir, stdio: 'ignore' });
101
+ pullSpinner.succeed('Code updated');
102
+ } catch {
103
+ pullSpinner.fail('Failed to pull — do you have uncommitted changes?');
104
+ return;
105
+ }
106
+
107
+ const buildSpinner = ora('Rebuilding services (this may take a minute)...').start();
108
+ try {
109
+ execSync('docker compose up -d --build', { cwd: dir, stdio: 'ignore', timeout: 600000 });
110
+ buildSpinner.succeed('Services rebuilt and restarted');
111
+ console.log(chalk.gray(' Web UI: http://localhost:3001'));
112
+ } catch {
113
+ buildSpinner.fail('Failed to rebuild');
114
+ }
115
+ }
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @fileoverview SlackHive CLI — install, configure, and manage SlackHive.
5
+ *
6
+ * Commands:
7
+ * slackhive init — Clone repo, configure .env, start services
8
+ * slackhive start — Start all Docker Compose services
9
+ * slackhive stop — Stop all services
10
+ * slackhive status — Show running containers
11
+ * slackhive logs — Tail runner logs
12
+ * slackhive update — Pull latest changes and rebuild
13
+ *
14
+ * @module cli
15
+ */
16
+
17
+ import { Command } from 'commander';
18
+ import { init } from './commands/init';
19
+ import { start, stop, status, logs, update } from './commands/manage';
20
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
21
+ const { version } = require('../package.json');
22
+
23
+ const program = new Command();
24
+
25
+ program
26
+ .name('slackhive')
27
+ .description('CLI to install and manage SlackHive — AI agent teams on Slack')
28
+ .version(version);
29
+
30
+ program
31
+ .command('init')
32
+ .description('Clone SlackHive repo, configure environment, and start services')
33
+ .option('-d, --dir <path>', 'Directory to install into', 'slackhive')
34
+ .option('--skip-start', 'Skip starting services after init')
35
+ .action(init);
36
+
37
+ program
38
+ .command('start')
39
+ .description('Start all SlackHive services')
40
+ .action(start);
41
+
42
+ program
43
+ .command('stop')
44
+ .description('Stop all SlackHive services')
45
+ .action(stop);
46
+
47
+ program
48
+ .command('status')
49
+ .description('Show running SlackHive containers')
50
+ .action(status);
51
+
52
+ program
53
+ .command('logs')
54
+ .description('Tail runner service logs')
55
+ .option('-f, --follow', 'Follow log output', true)
56
+ .action(logs);
57
+
58
+ program
59
+ .command('update')
60
+ .description('Pull latest changes and rebuild')
61
+ .action(update);
62
+
63
+ program.parse(process.argv);