@gokulvenkatareddy/cortex 0.1.7

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 (299) hide show
  1. package/README.md +1295 -0
  2. package/apps/octogent/.github/workflows/ci.yml +40 -0
  3. package/apps/octogent/.shims/claude +4 -0
  4. package/apps/octogent/AGENTS.md +71 -0
  5. package/apps/octogent/CONTRIBUTING.md +72 -0
  6. package/apps/octogent/LICENSE +21 -0
  7. package/apps/octogent/README.md +184 -0
  8. package/apps/octogent/apps/api/AGENTS.md +32 -0
  9. package/apps/octogent/apps/api/package.json +19 -0
  10. package/apps/octogent/apps/api/src/agentStateDetection.ts +181 -0
  11. package/apps/octogent/apps/api/src/claudeSessionScanner.ts +235 -0
  12. package/apps/octogent/apps/api/src/claudeSkills.ts +182 -0
  13. package/apps/octogent/apps/api/src/claudeUsage.ts +922 -0
  14. package/apps/octogent/apps/api/src/cli.ts +595 -0
  15. package/apps/octogent/apps/api/src/codeIntelStore.ts +46 -0
  16. package/apps/octogent/apps/api/src/codexUsage.ts +278 -0
  17. package/apps/octogent/apps/api/src/createApiServer/codeIntelRoutes.ts +60 -0
  18. package/apps/octogent/apps/api/src/createApiServer/conversationRoutes.ts +128 -0
  19. package/apps/octogent/apps/api/src/createApiServer/deckRoutes.ts +873 -0
  20. package/apps/octogent/apps/api/src/createApiServer/gitParsers.ts +140 -0
  21. package/apps/octogent/apps/api/src/createApiServer/gitRoutes.ts +214 -0
  22. package/apps/octogent/apps/api/src/createApiServer/miscRoutes.ts +316 -0
  23. package/apps/octogent/apps/api/src/createApiServer/monitorParsers.ts +137 -0
  24. package/apps/octogent/apps/api/src/createApiServer/monitorRoutes.ts +95 -0
  25. package/apps/octogent/apps/api/src/createApiServer/requestHandler.ts +311 -0
  26. package/apps/octogent/apps/api/src/createApiServer/requestParsers.ts +25 -0
  27. package/apps/octogent/apps/api/src/createApiServer/routeHelpers.ts +97 -0
  28. package/apps/octogent/apps/api/src/createApiServer/security.ts +70 -0
  29. package/apps/octogent/apps/api/src/createApiServer/terminalParsers.ts +167 -0
  30. package/apps/octogent/apps/api/src/createApiServer/terminalRoutes.ts +315 -0
  31. package/apps/octogent/apps/api/src/createApiServer/types.ts +24 -0
  32. package/apps/octogent/apps/api/src/createApiServer/uiStateParsers.ts +255 -0
  33. package/apps/octogent/apps/api/src/createApiServer/upgradeHandler.ts +38 -0
  34. package/apps/octogent/apps/api/src/createApiServer/usageRoutes.ts +84 -0
  35. package/apps/octogent/apps/api/src/createApiServer.ts +176 -0
  36. package/apps/octogent/apps/api/src/deck/readDeckTentacles.ts +595 -0
  37. package/apps/octogent/apps/api/src/githubRepoSummary.ts +397 -0
  38. package/apps/octogent/apps/api/src/logging.ts +9 -0
  39. package/apps/octogent/apps/api/src/monitor/defaults.ts +3 -0
  40. package/apps/octogent/apps/api/src/monitor/index.ts +8 -0
  41. package/apps/octogent/apps/api/src/monitor/repository.ts +303 -0
  42. package/apps/octogent/apps/api/src/monitor/service.ts +349 -0
  43. package/apps/octogent/apps/api/src/monitor/types.ts +120 -0
  44. package/apps/octogent/apps/api/src/monitor/xProvider.ts +587 -0
  45. package/apps/octogent/apps/api/src/projectPersistence.ts +377 -0
  46. package/apps/octogent/apps/api/src/prompts/index.ts +10 -0
  47. package/apps/octogent/apps/api/src/prompts/promptResolver.ts +145 -0
  48. package/apps/octogent/apps/api/src/runtimeMetadata.ts +69 -0
  49. package/apps/octogent/apps/api/src/server.ts +80 -0
  50. package/apps/octogent/apps/api/src/setupState.ts +80 -0
  51. package/apps/octogent/apps/api/src/setupStatus.ts +174 -0
  52. package/apps/octogent/apps/api/src/startupPrerequisites.ts +146 -0
  53. package/apps/octogent/apps/api/src/terminalRuntime/channelMessaging.ts +87 -0
  54. package/apps/octogent/apps/api/src/terminalRuntime/claudeTranscript.ts +279 -0
  55. package/apps/octogent/apps/api/src/terminalRuntime/constants.ts +15 -0
  56. package/apps/octogent/apps/api/src/terminalRuntime/conversations.ts +492 -0
  57. package/apps/octogent/apps/api/src/terminalRuntime/gitOperations.ts +341 -0
  58. package/apps/octogent/apps/api/src/terminalRuntime/hookProcessor.ts +405 -0
  59. package/apps/octogent/apps/api/src/terminalRuntime/protocol.ts +46 -0
  60. package/apps/octogent/apps/api/src/terminalRuntime/ptyEnvironment.ts +50 -0
  61. package/apps/octogent/apps/api/src/terminalRuntime/registry.ts +423 -0
  62. package/apps/octogent/apps/api/src/terminalRuntime/sessionRuntime.ts +671 -0
  63. package/apps/octogent/apps/api/src/terminalRuntime/systemClients.ts +432 -0
  64. package/apps/octogent/apps/api/src/terminalRuntime/types.ts +157 -0
  65. package/apps/octogent/apps/api/src/terminalRuntime/worktreeManager.ts +135 -0
  66. package/apps/octogent/apps/api/src/terminalRuntime.ts +567 -0
  67. package/apps/octogent/apps/api/src/usageUtils.ts +16 -0
  68. package/apps/octogent/apps/api/src/ws-shim.d.ts +28 -0
  69. package/apps/octogent/apps/api/tests/agentStateDetection.test.ts +67 -0
  70. package/apps/octogent/apps/api/tests/claudeUsage.test.ts +583 -0
  71. package/apps/octogent/apps/api/tests/codexUsage.test.ts +107 -0
  72. package/apps/octogent/apps/api/tests/createApiServer.test.ts +3207 -0
  73. package/apps/octogent/apps/api/tests/githubRepoSummary.test.ts +100 -0
  74. package/apps/octogent/apps/api/tests/logging.test.ts +33 -0
  75. package/apps/octogent/apps/api/tests/monitorApi.test.ts +467 -0
  76. package/apps/octogent/apps/api/tests/monitorCore.test.ts +104 -0
  77. package/apps/octogent/apps/api/tests/promptResolver.test.ts +109 -0
  78. package/apps/octogent/apps/api/tests/protocol.test.ts +14 -0
  79. package/apps/octogent/apps/api/tests/sessionRuntime.test.ts +608 -0
  80. package/apps/octogent/apps/api/tests/startupPrerequisites.test.ts +70 -0
  81. package/apps/octogent/apps/api/tests/upgradeHandler.test.ts +40 -0
  82. package/apps/octogent/apps/api/tests/xMonitorProvider.test.ts +109 -0
  83. package/apps/octogent/apps/api/tsconfig.json +7 -0
  84. package/apps/octogent/apps/api/vitest.config.ts +7 -0
  85. package/apps/octogent/apps/web/AGENTS.md +38 -0
  86. package/apps/octogent/apps/web/index.html +13 -0
  87. package/apps/octogent/apps/web/package.json +32 -0
  88. package/apps/octogent/apps/web/public/octopus-favicon.svg +26 -0
  89. package/apps/octogent/apps/web/src/App.tsx +646 -0
  90. package/apps/octogent/apps/web/src/app/canvas/types.ts +34 -0
  91. package/apps/octogent/apps/web/src/app/codeIntelAggregation.ts +278 -0
  92. package/apps/octogent/apps/web/src/app/constants.ts +28 -0
  93. package/apps/octogent/apps/web/src/app/conversationNormalizers.ts +135 -0
  94. package/apps/octogent/apps/web/src/app/formatTimestamp.ts +18 -0
  95. package/apps/octogent/apps/web/src/app/githubMetrics.ts +76 -0
  96. package/apps/octogent/apps/web/src/app/githubNormalizers.ts +91 -0
  97. package/apps/octogent/apps/web/src/app/hooks/useAgentRuntimeStates.ts +18 -0
  98. package/apps/octogent/apps/web/src/app/hooks/useBackendLivenessPolling.ts +53 -0
  99. package/apps/octogent/apps/web/src/app/hooks/useCanvasGraphData.ts +449 -0
  100. package/apps/octogent/apps/web/src/app/hooks/useCanvasTransform.ts +260 -0
  101. package/apps/octogent/apps/web/src/app/hooks/useClaudeUsagePolling.ts +40 -0
  102. package/apps/octogent/apps/web/src/app/hooks/useClickOutside.ts +30 -0
  103. package/apps/octogent/apps/web/src/app/hooks/useCodeIntelRuntime.ts +83 -0
  104. package/apps/octogent/apps/web/src/app/hooks/useCodexUsagePolling.ts +35 -0
  105. package/apps/octogent/apps/web/src/app/hooks/useConsoleKeyboardShortcuts.ts +31 -0
  106. package/apps/octogent/apps/web/src/app/hooks/useConversationsRuntime.ts +377 -0
  107. package/apps/octogent/apps/web/src/app/hooks/useForceSimulation.ts +319 -0
  108. package/apps/octogent/apps/web/src/app/hooks/useGitHubPrimaryViewModel.ts +143 -0
  109. package/apps/octogent/apps/web/src/app/hooks/useGithubSummaryPolling.ts +28 -0
  110. package/apps/octogent/apps/web/src/app/hooks/useInitialColumnsHydration.ts +64 -0
  111. package/apps/octogent/apps/web/src/app/hooks/useMonitorRuntime.ts +220 -0
  112. package/apps/octogent/apps/web/src/app/hooks/usePersistedUiState.ts +536 -0
  113. package/apps/octogent/apps/web/src/app/hooks/usePollingData.ts +79 -0
  114. package/apps/octogent/apps/web/src/app/hooks/usePromptLibrary.ts +185 -0
  115. package/apps/octogent/apps/web/src/app/hooks/useTentacleGitLifecycle.ts +530 -0
  116. package/apps/octogent/apps/web/src/app/hooks/useTerminalCompletionNotification.ts +94 -0
  117. package/apps/octogent/apps/web/src/app/hooks/useTerminalMutations.ts +266 -0
  118. package/apps/octogent/apps/web/src/app/hooks/useTerminalStateReconciliation.ts +23 -0
  119. package/apps/octogent/apps/web/src/app/hooks/useUsageHeatmapPolling.ts +43 -0
  120. package/apps/octogent/apps/web/src/app/hooks/useWorkspaceSetup.ts +80 -0
  121. package/apps/octogent/apps/web/src/app/hotkeys.ts +31 -0
  122. package/apps/octogent/apps/web/src/app/monitorNormalizers.ts +145 -0
  123. package/apps/octogent/apps/web/src/app/notificationSounds.ts +164 -0
  124. package/apps/octogent/apps/web/src/app/terminalRuntimeStateStore.ts +261 -0
  125. package/apps/octogent/apps/web/src/app/terminalState.ts +21 -0
  126. package/apps/octogent/apps/web/src/app/types.ts +42 -0
  127. package/apps/octogent/apps/web/src/app/uiStateNormalizers.ts +113 -0
  128. package/apps/octogent/apps/web/src/app/usageNormalizers.ts +58 -0
  129. package/apps/octogent/apps/web/src/components/ActiveAgentsSidebar.tsx +60 -0
  130. package/apps/octogent/apps/web/src/components/ActivityPrimaryView.tsx +21 -0
  131. package/apps/octogent/apps/web/src/components/AgentStateBadge.tsx +47 -0
  132. package/apps/octogent/apps/web/src/components/CanvasPrimaryView.tsx +1532 -0
  133. package/apps/octogent/apps/web/src/components/ClearAllConversationsDialog.tsx +33 -0
  134. package/apps/octogent/apps/web/src/components/CodeIntelArcDiagram.tsx +245 -0
  135. package/apps/octogent/apps/web/src/components/CodeIntelPrimaryView.tsx +104 -0
  136. package/apps/octogent/apps/web/src/components/CodeIntelTreemap.tsx +138 -0
  137. package/apps/octogent/apps/web/src/components/ConsolePrimaryNav.tsx +31 -0
  138. package/apps/octogent/apps/web/src/components/ConversationsPrimaryView.tsx +243 -0
  139. package/apps/octogent/apps/web/src/components/DeckPrimaryView.tsx +613 -0
  140. package/apps/octogent/apps/web/src/components/DeleteTentacleDialog.tsx +91 -0
  141. package/apps/octogent/apps/web/src/components/EmptyOctopus.tsx +715 -0
  142. package/apps/octogent/apps/web/src/components/GitHubPrimaryView.tsx +494 -0
  143. package/apps/octogent/apps/web/src/components/MonitorPrimaryView.tsx +475 -0
  144. package/apps/octogent/apps/web/src/components/PrimaryViewRouter.tsx +99 -0
  145. package/apps/octogent/apps/web/src/components/PromptsPrimaryView.tsx +243 -0
  146. package/apps/octogent/apps/web/src/components/RuntimeStatusStrip.tsx +273 -0
  147. package/apps/octogent/apps/web/src/components/SettingsPrimaryView.tsx +92 -0
  148. package/apps/octogent/apps/web/src/components/SidebarActionPanel.tsx +124 -0
  149. package/apps/octogent/apps/web/src/components/SidebarConversationsList.tsx +279 -0
  150. package/apps/octogent/apps/web/src/components/SidebarPromptsList.tsx +116 -0
  151. package/apps/octogent/apps/web/src/components/TelemetryTape.tsx +106 -0
  152. package/apps/octogent/apps/web/src/components/TentacleGitActionsDialog.tsx +341 -0
  153. package/apps/octogent/apps/web/src/components/Terminal.tsx +524 -0
  154. package/apps/octogent/apps/web/src/components/TerminalPromptPicker.tsx +140 -0
  155. package/apps/octogent/apps/web/src/components/UsageHeatmap.tsx +702 -0
  156. package/apps/octogent/apps/web/src/components/canvas/CanvasTentaclePanel.tsx +485 -0
  157. package/apps/octogent/apps/web/src/components/canvas/CanvasTerminalColumn.tsx +89 -0
  158. package/apps/octogent/apps/web/src/components/canvas/DeleteAllTerminalsDialog.tsx +221 -0
  159. package/apps/octogent/apps/web/src/components/canvas/OctopusNode.tsx +307 -0
  160. package/apps/octogent/apps/web/src/components/canvas/SessionNode.tsx +185 -0
  161. package/apps/octogent/apps/web/src/components/deck/ActionCards.tsx +118 -0
  162. package/apps/octogent/apps/web/src/components/deck/AddTentacleForm.tsx +269 -0
  163. package/apps/octogent/apps/web/src/components/deck/DeckBottomActions.tsx +56 -0
  164. package/apps/octogent/apps/web/src/components/deck/TentaclePod.tsx +334 -0
  165. package/apps/octogent/apps/web/src/components/deck/WorkspaceSetupCard.tsx +105 -0
  166. package/apps/octogent/apps/web/src/components/deck/octopusVisuals.ts +72 -0
  167. package/apps/octogent/apps/web/src/components/terminalReplay.ts +62 -0
  168. package/apps/octogent/apps/web/src/components/terminalWheel.ts +54 -0
  169. package/apps/octogent/apps/web/src/components/ui/ActionButton.tsx +34 -0
  170. package/apps/octogent/apps/web/src/components/ui/ConfirmationDialog.tsx +86 -0
  171. package/apps/octogent/apps/web/src/components/ui/MarkdownContent.tsx +43 -0
  172. package/apps/octogent/apps/web/src/components/ui/SettingsToggle.tsx +34 -0
  173. package/apps/octogent/apps/web/src/components/ui/StatusBadge.tsx +24 -0
  174. package/apps/octogent/apps/web/src/main.tsx +17 -0
  175. package/apps/octogent/apps/web/src/runtime/HttpTerminalSnapshotReader.ts +87 -0
  176. package/apps/octogent/apps/web/src/runtime/runtimeEndpoints.ts +412 -0
  177. package/apps/octogent/apps/web/src/styles/chrome-and-buttons.css +272 -0
  178. package/apps/octogent/apps/web/src/styles/console-canvas-activity.css +358 -0
  179. package/apps/octogent/apps/web/src/styles/console-canvas-canvas.css +1843 -0
  180. package/apps/octogent/apps/web/src/styles/console-canvas-code-intel.css +227 -0
  181. package/apps/octogent/apps/web/src/styles/console-canvas-conversations.css +705 -0
  182. package/apps/octogent/apps/web/src/styles/console-canvas-deck.css +1524 -0
  183. package/apps/octogent/apps/web/src/styles/console-canvas-github.css +541 -0
  184. package/apps/octogent/apps/web/src/styles/console-canvas-monitor.css +595 -0
  185. package/apps/octogent/apps/web/src/styles/console-canvas-pixpack.css +81 -0
  186. package/apps/octogent/apps/web/src/styles/console-canvas-prompts.css +474 -0
  187. package/apps/octogent/apps/web/src/styles/console-canvas-settings.css +207 -0
  188. package/apps/octogent/apps/web/src/styles/console-chrome-status-nav.css +441 -0
  189. package/apps/octogent/apps/web/src/styles/console-overrides-telemetry.css +320 -0
  190. package/apps/octogent/apps/web/src/styles/console-theme-tokens.css +25 -0
  191. package/apps/octogent/apps/web/src/styles/cortex-theme.css +412 -0
  192. package/apps/octogent/apps/web/src/styles/foundation.css +100 -0
  193. package/apps/octogent/apps/web/src/styles/sidebar-and-scrollbars.css +447 -0
  194. package/apps/octogent/apps/web/src/styles/terminal-and-status.css +356 -0
  195. package/apps/octogent/apps/web/src/styles.css +25 -0
  196. package/apps/octogent/apps/web/src/types/ws.d.ts +23 -0
  197. package/apps/octogent/apps/web/tests/CanvasPrimaryView.test.tsx +347 -0
  198. package/apps/octogent/apps/web/tests/HttpTerminalSnapshotReader.test.tsx +54 -0
  199. package/apps/octogent/apps/web/tests/RuntimeStatusStrip.test.tsx +70 -0
  200. package/apps/octogent/apps/web/tests/Terminal.test.tsx +87 -0
  201. package/apps/octogent/apps/web/tests/add-tentacle-form.test.tsx +48 -0
  202. package/apps/octogent/apps/web/tests/app-github-runtime.test.tsx +162 -0
  203. package/apps/octogent/apps/web/tests/app-monitor-runtime.test.tsx +657 -0
  204. package/apps/octogent/apps/web/tests/app-shell-navigation.test.tsx +109 -0
  205. package/apps/octogent/apps/web/tests/app-swarm-refresh.test.tsx +268 -0
  206. package/apps/octogent/apps/web/tests/app-ui-state-persistence.test.tsx +116 -0
  207. package/apps/octogent/apps/web/tests/app-workspace-setup.test.tsx +217 -0
  208. package/apps/octogent/apps/web/tests/canvas-tentacle-panel.test.tsx +195 -0
  209. package/apps/octogent/apps/web/tests/delete-all-terminals-dialog.test.tsx +76 -0
  210. package/apps/octogent/apps/web/tests/githubMetrics.test.tsx +52 -0
  211. package/apps/octogent/apps/web/tests/hotkeys.test.tsx +44 -0
  212. package/apps/octogent/apps/web/tests/runtimeEndpoints.test.tsx +240 -0
  213. package/apps/octogent/apps/web/tests/setup.ts +39 -0
  214. package/apps/octogent/apps/web/tests/tentacle-pod.test.tsx +62 -0
  215. package/apps/octogent/apps/web/tests/terminalReplay.test.ts +71 -0
  216. package/apps/octogent/apps/web/tests/terminalState.test.tsx +49 -0
  217. package/apps/octogent/apps/web/tests/terminalWheel.test.tsx +51 -0
  218. package/apps/octogent/apps/web/tests/test-utils/appTestHarness.ts +48 -0
  219. package/apps/octogent/apps/web/tests/uiPrimitives.test.tsx +31 -0
  220. package/apps/octogent/apps/web/tests/useAgentRuntimeStates.test.tsx +47 -0
  221. package/apps/octogent/apps/web/tsconfig.json +8 -0
  222. package/apps/octogent/apps/web/vite.api.bundle.config.mts +32 -0
  223. package/apps/octogent/apps/web/vite.config.ts +22 -0
  224. package/apps/octogent/bin/octogent +3 -0
  225. package/apps/octogent/biome.json +21 -0
  226. package/apps/octogent/docs/concepts/mental-model.md +79 -0
  227. package/apps/octogent/docs/concepts/runtime-and-api.md +60 -0
  228. package/apps/octogent/docs/concepts/tentacles.md +85 -0
  229. package/apps/octogent/docs/getting-started/installation.md +54 -0
  230. package/apps/octogent/docs/getting-started/quickstart.md +79 -0
  231. package/apps/octogent/docs/guides/inter-agent-messaging.md +43 -0
  232. package/apps/octogent/docs/guides/orchestrating-child-agents.md +49 -0
  233. package/apps/octogent/docs/guides/working-with-todos.md +56 -0
  234. package/apps/octogent/docs/index.md +40 -0
  235. package/apps/octogent/docs/reference/api.md +103 -0
  236. package/apps/octogent/docs/reference/cli.md +71 -0
  237. package/apps/octogent/docs/reference/experimental-features.md +28 -0
  238. package/apps/octogent/docs/reference/filesystem-layout.md +62 -0
  239. package/apps/octogent/docs/reference/troubleshooting.md +49 -0
  240. package/apps/octogent/package.json +35 -0
  241. package/apps/octogent/packages/core/AGENTS.md +31 -0
  242. package/apps/octogent/packages/core/package.json +12 -0
  243. package/apps/octogent/packages/core/src/adapters/InMemoryTerminalSnapshotReader.ts +10 -0
  244. package/apps/octogent/packages/core/src/application/buildTerminalList.ts +13 -0
  245. package/apps/octogent/packages/core/src/domain/agentRuntime.ts +18 -0
  246. package/apps/octogent/packages/core/src/domain/channel.ts +8 -0
  247. package/apps/octogent/packages/core/src/domain/completionSound.ts +14 -0
  248. package/apps/octogent/packages/core/src/domain/conversation.ts +48 -0
  249. package/apps/octogent/packages/core/src/domain/deck.ts +33 -0
  250. package/apps/octogent/packages/core/src/domain/git.ts +32 -0
  251. package/apps/octogent/packages/core/src/domain/monitor.ts +62 -0
  252. package/apps/octogent/packages/core/src/domain/setup.ts +27 -0
  253. package/apps/octogent/packages/core/src/domain/terminal.ts +17 -0
  254. package/apps/octogent/packages/core/src/domain/uiState.ts +22 -0
  255. package/apps/octogent/packages/core/src/domain/usage.ts +60 -0
  256. package/apps/octogent/packages/core/src/index.ts +15 -0
  257. package/apps/octogent/packages/core/src/ports/TerminalSnapshotReader.ts +5 -0
  258. package/apps/octogent/packages/core/src/util/typeCoercion.ts +20 -0
  259. package/apps/octogent/packages/core/tests/buildTerminalList.test.ts +75 -0
  260. package/apps/octogent/packages/core/tsconfig.json +7 -0
  261. package/apps/octogent/packages/core/tsconfig.tsbuildinfo +1 -0
  262. package/apps/octogent/packages/core/vitest.config.ts +7 -0
  263. package/apps/octogent/pnpm-lock.yaml +3212 -0
  264. package/apps/octogent/pnpm-workspace.yaml +3 -0
  265. package/apps/octogent/prompts/meta-prompt-generator.md +223 -0
  266. package/apps/octogent/prompts/octoboss-clean-contexts.md +30 -0
  267. package/apps/octogent/prompts/octoboss-reorganize-tentacles.md +29 -0
  268. package/apps/octogent/prompts/octoboss-reorganize-todos.md +27 -0
  269. package/apps/octogent/prompts/sandbox-init.md +3 -0
  270. package/apps/octogent/prompts/swarm-parent.md +83 -0
  271. package/apps/octogent/prompts/swarm-worker.md +50 -0
  272. package/apps/octogent/prompts/tentacle-context-init.md +1 -0
  273. package/apps/octogent/prompts/tentacle-planner.md +110 -0
  274. package/apps/octogent/prompts/tentacle-reorganize-todos.md +20 -0
  275. package/apps/octogent/prompts/tentacle-update-tentacle.md +18 -0
  276. package/apps/octogent/scripts/build-package.mjs +23 -0
  277. package/apps/octogent/scripts/dev.mjs +158 -0
  278. package/apps/octogent/scripts/smoke-public-install.mjs +271 -0
  279. package/apps/octogent/static/images/octogent-header.png +0 -0
  280. package/apps/octogent/static/images/preview_1.jpg +0 -0
  281. package/apps/octogent/static/images/preview_2.jpg +0 -0
  282. package/apps/octogent/static/images/preview_3.jpg +0 -0
  283. package/apps/octogent/static/images/preview_4.jpg +0 -0
  284. package/apps/octogent/static/images/preview_5.jpg +0 -0
  285. package/apps/octogent/static/images/preview_6.jpg +0 -0
  286. package/apps/octogent/tsconfig.base.json +16 -0
  287. package/bin/AGI +3 -0
  288. package/bin/AGI-install-app +71 -0
  289. package/bin/AGI-ui +16 -0
  290. package/bin/AGI-voice +15 -0
  291. package/bin/AGI-web +16 -0
  292. package/bin/cortex +109 -0
  293. package/bin/cortex-octogent +99 -0
  294. package/bin/import-specifier.mjs +13 -0
  295. package/bin/import-specifier.test.mjs +13 -0
  296. package/bin/octo +150 -0
  297. package/dist/cli.mjs +555650 -0
  298. package/package.json +157 -0
  299. package/scripts/setup-wizard.ts +390 -0
@@ -0,0 +1,873 @@
1
+ import { join } from "node:path";
2
+
3
+ import {
4
+ addTodoItem,
5
+ createDeckTentacle,
6
+ deleteDeckTentacle,
7
+ deleteTodoItem,
8
+ editTodoItem,
9
+ listDeckAvailableSkills,
10
+ parseTodoProgress,
11
+ readDeckTentacles,
12
+ readDeckVaultFile,
13
+ toggleTodoItem,
14
+ updateDeckTentacleSuggestedSkills,
15
+ } from "../deck/readDeckTentacles";
16
+ import { resolvePrompt } from "../prompts";
17
+ import { MAX_CHILDREN_PER_PARENT, RuntimeInputError } from "../terminalRuntime";
18
+ import type { ApiRouteHandler } from "./routeHelpers";
19
+ import {
20
+ readJsonBodyOrWriteError,
21
+ writeJson,
22
+ writeMethodNotAllowed,
23
+ writeNoContent,
24
+ writeText,
25
+ } from "./routeHelpers";
26
+ import { parseTerminalAgentProvider, parseTerminalWorkspaceMode } from "./terminalParsers";
27
+
28
+ const shellSingleQuote = (value: string) => `'${value.replace(/'/g, `'\\''`)}'`;
29
+
30
+ const buildSingleTodoWorkerPrompt = async ({
31
+ promptsDir,
32
+ workspaceCwd,
33
+ tentacleId,
34
+ tentacleName,
35
+ todoItemText,
36
+ terminalId,
37
+ apiPort,
38
+ }: {
39
+ promptsDir: string;
40
+ workspaceCwd: string;
41
+ tentacleId: string;
42
+ tentacleName: string;
43
+ todoItemText: string;
44
+ terminalId: string;
45
+ apiPort: string;
46
+ }) => {
47
+ const tentacleContextPath = join(workspaceCwd, ".octogent/tentacles", tentacleId);
48
+
49
+ return await resolvePrompt(promptsDir, "swarm-worker", {
50
+ tentacleName,
51
+ tentacleId,
52
+ tentacleContextPath,
53
+ todoItemText,
54
+ terminalId,
55
+ apiPort,
56
+ workspaceContextIntro:
57
+ "You are working in the shared main workspace on the main branch, not in an isolated worktree.",
58
+ workspaceGuidelines: [
59
+ "- You must work in the main project directory. Do NOT create or use git worktrees for this task.",
60
+ "- You are working in the shared main workspace. Keep edits narrow and focused on this one todo item.",
61
+ "- Do NOT create commits. Leave your completed changes uncommitted in the main workspace.",
62
+ "- Do NOT mark todo items done or rewrite tentacle context files unless this specific todo item explicitly requires it.",
63
+ ].join("\n"),
64
+ commitGuidance:
65
+ "- Do NOT commit. Leave your completed changes uncommitted in the shared workspace and report what changed.",
66
+ definitionOfDoneCommitStep:
67
+ "Changes are left uncommitted in the shared main workspace, ready for operator review.",
68
+ workspaceReminder: "Do not commit. Do not use worktrees.",
69
+ parentTerminalId: "",
70
+ parentSection: "",
71
+ });
72
+ };
73
+
74
+ export const handleDeckTentaclesRoute: ApiRouteHandler = async (
75
+ { request, response, requestUrl, corsOrigin },
76
+ { workspaceCwd, projectStateDir },
77
+ ) => {
78
+ if (requestUrl.pathname !== "/api/deck/tentacles") return false;
79
+
80
+ if (request.method === "GET") {
81
+ const tentacles = readDeckTentacles(workspaceCwd, projectStateDir);
82
+ writeJson(response, 200, tentacles, corsOrigin);
83
+ return true;
84
+ }
85
+
86
+ if (request.method === "POST") {
87
+ const bodyReadResult = await readJsonBodyOrWriteError(request, response, corsOrigin);
88
+ if (!bodyReadResult.ok) return true;
89
+
90
+ const body = bodyReadResult.payload as Record<string, unknown> | null;
91
+ const name = body && typeof body.name === "string" ? body.name : "";
92
+ const description = body && typeof body.description === "string" ? body.description : "";
93
+ const color = body && typeof body.color === "string" ? body.color : "#d4a017";
94
+ const suggestedSkills =
95
+ body && Array.isArray(body.suggestedSkills)
96
+ ? body.suggestedSkills.filter((skill): skill is string => typeof skill === "string")
97
+ : [];
98
+
99
+ const rawOctopus =
100
+ body && typeof body.octopus === "object" && body.octopus !== null
101
+ ? (body.octopus as Record<string, unknown>)
102
+ : {};
103
+ const octopus = {
104
+ animation: typeof rawOctopus.animation === "string" ? rawOctopus.animation : null,
105
+ expression: typeof rawOctopus.expression === "string" ? rawOctopus.expression : null,
106
+ accessory: typeof rawOctopus.accessory === "string" ? rawOctopus.accessory : null,
107
+ hairColor: typeof rawOctopus.hairColor === "string" ? rawOctopus.hairColor : null,
108
+ };
109
+
110
+ const result = createDeckTentacle(
111
+ workspaceCwd,
112
+ { name, description, color, octopus, suggestedSkills },
113
+ projectStateDir,
114
+ );
115
+ if (!result.ok) {
116
+ writeJson(response, 400, { error: result.error }, corsOrigin);
117
+ return true;
118
+ }
119
+
120
+ writeJson(response, 201, result.tentacle, corsOrigin);
121
+ return true;
122
+ }
123
+
124
+ writeMethodNotAllowed(response, corsOrigin);
125
+ return true;
126
+ };
127
+
128
+ export const handleDeckSkillsRoute: ApiRouteHandler = async (
129
+ { request, response, requestUrl, corsOrigin },
130
+ { workspaceCwd },
131
+ ) => {
132
+ if (requestUrl.pathname !== "/api/deck/skills") return false;
133
+
134
+ if (request.method !== "GET") {
135
+ writeMethodNotAllowed(response, corsOrigin);
136
+ return true;
137
+ }
138
+
139
+ writeJson(response, 200, listDeckAvailableSkills(workspaceCwd), corsOrigin);
140
+ return true;
141
+ };
142
+
143
+ const DECK_TENTACLE_ITEM_PATTERN = /^\/api\/deck\/tentacles\/([^/]+)$/;
144
+
145
+ export const handleDeckTentacleItemRoute: ApiRouteHandler = async (
146
+ { request, response, requestUrl, corsOrigin },
147
+ { workspaceCwd, projectStateDir },
148
+ ) => {
149
+ const match = requestUrl.pathname.match(DECK_TENTACLE_ITEM_PATTERN);
150
+ if (!match) return false;
151
+
152
+ if (request.method !== "DELETE") {
153
+ writeMethodNotAllowed(response, corsOrigin);
154
+ return true;
155
+ }
156
+
157
+ const tentacleId = decodeURIComponent(match[1] as string);
158
+ const result = deleteDeckTentacle(workspaceCwd, tentacleId, projectStateDir);
159
+ if (!result.ok) {
160
+ writeJson(response, 404, { error: result.error }, corsOrigin);
161
+ return true;
162
+ }
163
+
164
+ writeNoContent(response, 204, corsOrigin);
165
+ return true;
166
+ };
167
+
168
+ const DECK_VAULT_FILE_PATTERN = /^\/api\/deck\/tentacles\/([^/]+)\/files\/([^/]+)$/;
169
+
170
+ export const handleDeckVaultFileRoute: ApiRouteHandler = async (
171
+ { request, response, requestUrl, corsOrigin },
172
+ { workspaceCwd },
173
+ ) => {
174
+ const match = requestUrl.pathname.match(DECK_VAULT_FILE_PATTERN);
175
+ if (!match) return false;
176
+ if (request.method !== "GET") {
177
+ writeMethodNotAllowed(response, corsOrigin);
178
+ return true;
179
+ }
180
+
181
+ const tentacleId = decodeURIComponent(match[1] as string);
182
+ const fileName = decodeURIComponent(match[2] as string);
183
+
184
+ const content = readDeckVaultFile(workspaceCwd, tentacleId, fileName);
185
+ if (content === null) {
186
+ writeJson(response, 404, { error: "Vault file not found" }, corsOrigin);
187
+ return true;
188
+ }
189
+
190
+ writeText(response, 200, content, "text/markdown; charset=utf-8", corsOrigin);
191
+ return true;
192
+ };
193
+
194
+ const DECK_TENTACLE_SKILLS_PATTERN = /^\/api\/deck\/tentacles\/([^/]+)\/skills$/;
195
+
196
+ export const handleDeckTentacleSkillsRoute: ApiRouteHandler = async (
197
+ { request, response, requestUrl, corsOrigin },
198
+ { workspaceCwd, projectStateDir },
199
+ ) => {
200
+ const match = requestUrl.pathname.match(DECK_TENTACLE_SKILLS_PATTERN);
201
+ if (!match) return false;
202
+ if (request.method !== "PATCH") {
203
+ writeMethodNotAllowed(response, corsOrigin);
204
+ return true;
205
+ }
206
+
207
+ const body = await readJsonBodyOrWriteError(request, response, corsOrigin);
208
+ if (!body.ok) return true;
209
+
210
+ const payload = body.payload as Record<string, unknown> | null;
211
+ const suggestedSkills = Array.isArray(payload?.suggestedSkills)
212
+ ? payload.suggestedSkills.filter((skill): skill is string => typeof skill === "string")
213
+ : null;
214
+
215
+ if (suggestedSkills === null) {
216
+ writeJson(response, 400, { error: "suggestedSkills (string[]) is required" }, corsOrigin);
217
+ return true;
218
+ }
219
+
220
+ const tentacleId = decodeURIComponent(match[1] as string);
221
+ const updated = updateDeckTentacleSuggestedSkills(
222
+ workspaceCwd,
223
+ tentacleId,
224
+ suggestedSkills,
225
+ projectStateDir,
226
+ );
227
+ if (!updated) {
228
+ writeJson(response, 404, { error: "Tentacle not found" }, corsOrigin);
229
+ return true;
230
+ }
231
+
232
+ writeJson(response, 200, updated, corsOrigin);
233
+ return true;
234
+ };
235
+
236
+ // ---------------------------------------------------------------------------
237
+ // Deck — Todo toggle
238
+ // ---------------------------------------------------------------------------
239
+
240
+ const DECK_TODO_TOGGLE_PATTERN = /^\/api\/deck\/tentacles\/([^/]+)\/todo\/toggle$/;
241
+
242
+ export const handleDeckTodoToggleRoute: ApiRouteHandler = async (
243
+ { request, response, requestUrl, corsOrigin },
244
+ { workspaceCwd },
245
+ ) => {
246
+ const match = requestUrl.pathname.match(DECK_TODO_TOGGLE_PATTERN);
247
+ if (!match) return false;
248
+ if (request.method !== "PATCH") {
249
+ writeMethodNotAllowed(response, corsOrigin);
250
+ return true;
251
+ }
252
+
253
+ const body = await readJsonBodyOrWriteError(request, response, corsOrigin);
254
+ if (!body.ok) return true;
255
+
256
+ const { itemIndex, done } = body.payload as { itemIndex: unknown; done: unknown };
257
+ if (typeof itemIndex !== "number" || typeof done !== "boolean") {
258
+ writeJson(
259
+ response,
260
+ 400,
261
+ { error: "itemIndex (number) and done (boolean) are required" },
262
+ corsOrigin,
263
+ );
264
+ return true;
265
+ }
266
+
267
+ const tentacleId = decodeURIComponent(match[1] as string);
268
+ const result = toggleTodoItem(workspaceCwd, tentacleId, itemIndex, done);
269
+ if (!result) {
270
+ writeJson(response, 404, { error: "Todo item not found" }, corsOrigin);
271
+ return true;
272
+ }
273
+
274
+ writeJson(response, 200, result, corsOrigin);
275
+ return true;
276
+ };
277
+
278
+ // ---------------------------------------------------------------------------
279
+ // Deck — Todo edit (rename item text)
280
+ // ---------------------------------------------------------------------------
281
+
282
+ const DECK_TODO_EDIT_PATTERN = /^\/api\/deck\/tentacles\/([^/]+)\/todo\/edit$/;
283
+
284
+ export const handleDeckTodoEditRoute: ApiRouteHandler = async (
285
+ { request, response, requestUrl, corsOrigin },
286
+ { workspaceCwd },
287
+ ) => {
288
+ const match = requestUrl.pathname.match(DECK_TODO_EDIT_PATTERN);
289
+ if (!match) return false;
290
+ if (request.method !== "PATCH") {
291
+ writeMethodNotAllowed(response, corsOrigin);
292
+ return true;
293
+ }
294
+
295
+ const body = await readJsonBodyOrWriteError(request, response, corsOrigin);
296
+ if (!body.ok) return true;
297
+
298
+ const { itemIndex, text } = body.payload as { itemIndex: unknown; text: unknown };
299
+ if (typeof itemIndex !== "number" || typeof text !== "string" || text.trim().length === 0) {
300
+ writeJson(
301
+ response,
302
+ 400,
303
+ { error: "itemIndex (number) and text (non-empty string) are required" },
304
+ corsOrigin,
305
+ );
306
+ return true;
307
+ }
308
+
309
+ const tentacleId = decodeURIComponent(match[1] as string);
310
+ const result = editTodoItem(workspaceCwd, tentacleId, itemIndex, text.trim());
311
+ if (!result) {
312
+ writeJson(response, 404, { error: "Todo item not found" }, corsOrigin);
313
+ return true;
314
+ }
315
+
316
+ writeJson(response, 200, result, corsOrigin);
317
+ return true;
318
+ };
319
+
320
+ // ---------------------------------------------------------------------------
321
+ // Deck — Todo add
322
+ // ---------------------------------------------------------------------------
323
+
324
+ const DECK_TODO_ADD_PATTERN = /^\/api\/deck\/tentacles\/([^/]+)\/todo$/;
325
+
326
+ export const handleDeckTodoAddRoute: ApiRouteHandler = async (
327
+ { request, response, requestUrl, corsOrigin },
328
+ { workspaceCwd },
329
+ ) => {
330
+ const match = requestUrl.pathname.match(DECK_TODO_ADD_PATTERN);
331
+ if (!match) return false;
332
+ if (request.method !== "POST") {
333
+ writeMethodNotAllowed(response, corsOrigin);
334
+ return true;
335
+ }
336
+
337
+ const body = await readJsonBodyOrWriteError(request, response, corsOrigin);
338
+ if (!body.ok) return true;
339
+
340
+ const { text } = body.payload as { text: unknown };
341
+ if (typeof text !== "string" || text.trim().length === 0) {
342
+ writeJson(response, 400, { error: "text (non-empty string) is required" }, corsOrigin);
343
+ return true;
344
+ }
345
+
346
+ const tentacleId = decodeURIComponent(match[1] as string);
347
+ const result = addTodoItem(workspaceCwd, tentacleId, text.trim());
348
+ if (!result) {
349
+ writeJson(response, 404, { error: "Tentacle todo.md not found" }, corsOrigin);
350
+ return true;
351
+ }
352
+
353
+ writeJson(response, 201, result, corsOrigin);
354
+ return true;
355
+ };
356
+
357
+ // ---------------------------------------------------------------------------
358
+ // Deck — Todo delete
359
+ // ---------------------------------------------------------------------------
360
+
361
+ const DECK_TODO_DELETE_PATTERN = /^\/api\/deck\/tentacles\/([^/]+)\/todo\/delete$/;
362
+
363
+ export const handleDeckTodoDeleteRoute: ApiRouteHandler = async (
364
+ { request, response, requestUrl, corsOrigin },
365
+ { workspaceCwd },
366
+ ) => {
367
+ const match = requestUrl.pathname.match(DECK_TODO_DELETE_PATTERN);
368
+ if (!match) return false;
369
+ if (request.method !== "POST") {
370
+ writeMethodNotAllowed(response, corsOrigin);
371
+ return true;
372
+ }
373
+
374
+ const body = await readJsonBodyOrWriteError(request, response, corsOrigin);
375
+ if (!body.ok) return true;
376
+
377
+ const { itemIndex } = body.payload as { itemIndex: unknown };
378
+ if (typeof itemIndex !== "number") {
379
+ writeJson(response, 400, { error: "itemIndex (number) is required" }, corsOrigin);
380
+ return true;
381
+ }
382
+
383
+ const tentacleId = decodeURIComponent(match[1] as string);
384
+ const result = deleteTodoItem(workspaceCwd, tentacleId, itemIndex);
385
+ if (!result) {
386
+ writeJson(response, 404, { error: "Todo item not found" }, corsOrigin);
387
+ return true;
388
+ }
389
+
390
+ writeJson(response, 200, result, corsOrigin);
391
+ return true;
392
+ };
393
+
394
+ // ---------------------------------------------------------------------------
395
+ // Deck — Solve a single todo item
396
+ // ---------------------------------------------------------------------------
397
+
398
+ const DECK_TODO_SOLVE_PATTERN = /^\/api\/deck\/tentacles\/([^/]+)\/todo\/solve$/;
399
+
400
+ export const handleDeckTodoSolveRoute: ApiRouteHandler = async (
401
+ { request, response, requestUrl, corsOrigin },
402
+ { runtime, workspaceCwd, projectStateDir, promptsDir, getApiPort },
403
+ ) => {
404
+ const match = requestUrl.pathname.match(DECK_TODO_SOLVE_PATTERN);
405
+ if (!match) return false;
406
+ if (request.method !== "POST") {
407
+ writeMethodNotAllowed(response, corsOrigin);
408
+ return true;
409
+ }
410
+
411
+ const bodyReadResult = await readJsonBodyOrWriteError(request, response, corsOrigin);
412
+ if (!bodyReadResult.ok) return true;
413
+
414
+ const body = (bodyReadResult.payload ?? {}) as Record<string, unknown>;
415
+ const itemIndex = body.itemIndex;
416
+ if (typeof itemIndex !== "number") {
417
+ writeJson(response, 400, { error: "itemIndex (number) is required" }, corsOrigin);
418
+ return true;
419
+ }
420
+
421
+ const agentProviderResult = parseTerminalAgentProvider(body);
422
+ if (agentProviderResult.error) {
423
+ writeJson(response, 400, { error: agentProviderResult.error }, corsOrigin);
424
+ return true;
425
+ }
426
+
427
+ const tentacleId = decodeURIComponent(match[1] as string);
428
+ const todoContent = readDeckVaultFile(workspaceCwd, tentacleId, "todo.md");
429
+ if (todoContent === null) {
430
+ writeJson(response, 404, { error: "Tentacle or todo.md not found." }, corsOrigin);
431
+ return true;
432
+ }
433
+
434
+ const todoResult = parseTodoProgress(todoContent);
435
+ const todoItem = todoResult.items[itemIndex] ?? null;
436
+ if (!todoItem) {
437
+ writeJson(response, 404, { error: "Todo item not found." }, corsOrigin);
438
+ return true;
439
+ }
440
+ if (todoItem.done) {
441
+ writeJson(response, 400, { error: "Todo item is already complete." }, corsOrigin);
442
+ return true;
443
+ }
444
+
445
+ const terminalId = `${tentacleId}-todo-${itemIndex}`;
446
+ const existingTerminal = runtime
447
+ .listTerminalSnapshots()
448
+ .find((terminal) => terminal.terminalId === terminalId);
449
+ if (existingTerminal) {
450
+ writeJson(
451
+ response,
452
+ 409,
453
+ { error: "A solve agent is already active for this todo item.", terminalId },
454
+ corsOrigin,
455
+ );
456
+ return true;
457
+ }
458
+
459
+ const deckTentacles = readDeckTentacles(workspaceCwd, projectStateDir);
460
+ const deckEntry = deckTentacles.find((tentacle) => tentacle.tentacleId === tentacleId);
461
+ const tentacleName = deckEntry?.displayName ?? tentacleId;
462
+
463
+ try {
464
+ const workerPrompt = await buildSingleTodoWorkerPrompt({
465
+ promptsDir,
466
+ workspaceCwd,
467
+ tentacleId,
468
+ tentacleName,
469
+ todoItemText: todoItem.text,
470
+ terminalId,
471
+ apiPort: getApiPort(),
472
+ });
473
+
474
+ const snapshot = runtime.createTerminal({
475
+ terminalId,
476
+ tentacleId,
477
+ tentacleName,
478
+ nameOrigin: "generated",
479
+ autoRenamePromptContext: todoItem.text,
480
+ workspaceMode: "shared",
481
+ ...(agentProviderResult.agentProvider
482
+ ? { agentProvider: agentProviderResult.agentProvider }
483
+ : {}),
484
+ ...(workerPrompt ? { initialPrompt: workerPrompt } : {}),
485
+ });
486
+
487
+ writeJson(
488
+ response,
489
+ 201,
490
+ {
491
+ terminalId: snapshot.terminalId,
492
+ tentacleId,
493
+ itemIndex,
494
+ workspaceMode: "shared",
495
+ },
496
+ corsOrigin,
497
+ );
498
+ return true;
499
+ } catch (error) {
500
+ if (error instanceof RuntimeInputError) {
501
+ writeJson(response, 400, { error: error.message }, corsOrigin);
502
+ return true;
503
+ }
504
+
505
+ throw error;
506
+ }
507
+ };
508
+
509
+ // ---------------------------------------------------------------------------
510
+ // Deck — Swarm
511
+ // ---------------------------------------------------------------------------
512
+
513
+ const DECK_TENTACLE_SWARM_PATTERN = /^\/api\/deck\/tentacles\/([^/]+)\/swarm$/;
514
+
515
+ export const handleDeckTentacleSwarmRoute: ApiRouteHandler = async (
516
+ { request, response, requestUrl, corsOrigin },
517
+ { runtime, workspaceCwd, projectStateDir, promptsDir, getApiPort },
518
+ ) => {
519
+ const match = requestUrl.pathname.match(DECK_TENTACLE_SWARM_PATTERN);
520
+ if (!match) return false;
521
+
522
+ if (request.method !== "POST") {
523
+ writeMethodNotAllowed(response, corsOrigin);
524
+ return true;
525
+ }
526
+
527
+ const tentacleId = decodeURIComponent(match[1] as string);
528
+
529
+ // Read and parse the tentacle's todo.md.
530
+ const todoContent = readDeckVaultFile(workspaceCwd, tentacleId, "todo.md");
531
+ if (todoContent === null) {
532
+ writeJson(response, 404, { error: "Tentacle or todo.md not found." }, corsOrigin);
533
+ return true;
534
+ }
535
+
536
+ const todoResult = parseTodoProgress(todoContent);
537
+ const incompleteItems = todoResult.items
538
+ .map((item, index) => ({ ...item, index }))
539
+ .filter((item) => !item.done);
540
+
541
+ if (incompleteItems.length === 0) {
542
+ writeJson(response, 400, { error: "No incomplete todo items found." }, corsOrigin);
543
+ return true;
544
+ }
545
+
546
+ // Parse optional request body for item filtering and agent provider.
547
+ const bodyReadResult = await readJsonBodyOrWriteError(request, response, corsOrigin);
548
+ if (!bodyReadResult.ok) return true;
549
+ const body = (bodyReadResult.payload ?? {}) as Record<string, unknown>;
550
+
551
+ const agentProviderResult = parseTerminalAgentProvider(body);
552
+ if (agentProviderResult.error) {
553
+ writeJson(response, 400, { error: agentProviderResult.error }, corsOrigin);
554
+ return true;
555
+ }
556
+
557
+ const workspaceModeResult = parseTerminalWorkspaceMode(body);
558
+ if (workspaceModeResult.error) {
559
+ writeJson(response, 400, { error: workspaceModeResult.error }, corsOrigin);
560
+ return true;
561
+ }
562
+ const workerWorkspaceMode =
563
+ body.workspaceMode === undefined ? "worktree" : workspaceModeResult.workspaceMode;
564
+
565
+ // Filter to specific item indices if requested.
566
+ let targetItems = incompleteItems;
567
+ if (Array.isArray(body.todoItemIndices)) {
568
+ const requestedIndices = new Set(
569
+ (body.todoItemIndices as unknown[]).filter((v): v is number => typeof v === "number"),
570
+ );
571
+ targetItems = incompleteItems.filter((item) => requestedIndices.has(item.index));
572
+ if (targetItems.length === 0) {
573
+ writeJson(
574
+ response,
575
+ 400,
576
+ { error: "None of the requested todo item indices are incomplete." },
577
+ corsOrigin,
578
+ );
579
+ return true;
580
+ }
581
+ }
582
+
583
+ if (targetItems.length > MAX_CHILDREN_PER_PARENT) {
584
+ // Todo order is priority order, so overflow items are deferred automatically.
585
+ targetItems = targetItems.slice(0, MAX_CHILDREN_PER_PARENT);
586
+ }
587
+
588
+ // Check for existing swarm terminals to prevent duplicates.
589
+ const existingTerminals = runtime.listTerminalSnapshots();
590
+ const existingSwarmIds = existingTerminals
591
+ .filter((t) => t.terminalId.startsWith(`${tentacleId}-swarm-`))
592
+ .map((t) => t.terminalId);
593
+ if (existingSwarmIds.length > 0) {
594
+ writeJson(
595
+ response,
596
+ 409,
597
+ { error: "A swarm is already active for this tentacle.", existingSwarmIds },
598
+ corsOrigin,
599
+ );
600
+ return true;
601
+ }
602
+
603
+ // Determine base ref: use tentacle's worktree branch if it exists, otherwise HEAD.
604
+ const tentacleTerminal = existingTerminals.find(
605
+ (t) => t.tentacleId === tentacleId && t.workspaceMode === "worktree",
606
+ );
607
+ const baseRef = tentacleTerminal ? `octogent/${tentacleId}` : "HEAD";
608
+
609
+ // Resolve the tentacle display name for prompts.
610
+ const deckTentacles = readDeckTentacles(workspaceCwd, projectStateDir);
611
+ const deckEntry = deckTentacles.find((t) => t.tentacleId === tentacleId);
612
+ const tentacleName = deckEntry?.displayName ?? tentacleId;
613
+
614
+ const apiPort = getApiPort();
615
+ const needsParent = targetItems.length > 1;
616
+ const parentTerminalId = needsParent ? `${tentacleId}-swarm-parent` : null;
617
+ const tentacleContextPath = join(workspaceCwd, ".octogent/tentacles", tentacleId);
618
+ const workers = targetItems.map((item) => ({
619
+ terminalId: `${tentacleId}-swarm-${item.index}`,
620
+ todoIndex: item.index,
621
+ todoText: item.text,
622
+ }));
623
+
624
+ const buildWorkerContextIntro = (): string =>
625
+ workerWorkspaceMode === "worktree"
626
+ ? "You are working on an isolated worktree branch, not the main branch."
627
+ : "You are working in the shared main workspace on the main branch, not in an isolated worktree.";
628
+
629
+ const buildWorkerGuidelines = (terminalId: string): string =>
630
+ workerWorkspaceMode === "worktree"
631
+ ? `- You are working in an isolated git worktree on branch \`octogent/${terminalId}\`. Make changes freely without worrying about conflicts with other agents.`
632
+ : [
633
+ "- You are working in the shared main workspace. Other workers may touch the same files, so keep your edits narrow, avoid broad refactors, and coordinate via your parent if you hit overlap.",
634
+ "- Do NOT create commits in shared mode. Leave your changes uncommitted for the coordinator to review and commit later.",
635
+ "- Do NOT mark todo items done or rewrite tentacle context files unless your assigned todo item explicitly requires it. The coordinator handles the final tentacle-level sync.",
636
+ ].join("\n");
637
+
638
+ const buildWorkerCommitGuidance = (): string =>
639
+ workerWorkspaceMode === "worktree"
640
+ ? "- Commit your changes with a clear commit message describing what you did."
641
+ : "- Do NOT commit in shared mode. Leave your completed changes uncommitted and report DONE with a short summary of what changed.";
642
+
643
+ const buildWorkerDefinitionOfDoneCommitStep = (): string =>
644
+ workerWorkspaceMode === "worktree"
645
+ ? "Changes are committed with a descriptive message."
646
+ : "Changes are left uncommitted in the shared workspace, ready for coordinator review.";
647
+
648
+ const buildWorkerReminder = (): string =>
649
+ workerWorkspaceMode === "worktree" ? "Commit." : "Do not commit in shared mode.";
650
+
651
+ const buildWorkerWorkspaceSection = (): string =>
652
+ workerWorkspaceMode === "worktree"
653
+ ? [
654
+ "Each worker commits to its own isolated branch:",
655
+ "",
656
+ ...workers.map(
657
+ (w) => `- \`octogent/${w.terminalId}\` — item #${w.todoIndex}: ${w.todoText}`,
658
+ ),
659
+ ].join("\n")
660
+ : [
661
+ "Workers are running in the shared main workspace, not in separate worktrees.",
662
+ "",
663
+ "There are no per-worker branches for this swarm. Supervise them carefully to avoid overlapping edits in the same files.",
664
+ ].join("\n");
665
+
666
+ const buildCompletionStrategySection = (baseBranch: string): string =>
667
+ workerWorkspaceMode === "worktree"
668
+ ? [
669
+ `Only begin merging after ALL ${workers.length} workers have reported DONE.`,
670
+ "",
671
+ "### Step-by-step merge process",
672
+ "",
673
+ `1. **Create an integration branch** from \`${baseBranch}\`. First check if a stale integration branch exists from a previous swarm attempt — if so, delete it before proceeding:`,
674
+ " ```bash",
675
+ ` git branch -D octogent_integration_${tentacleId} 2>/dev/null || true`,
676
+ ` git checkout ${baseBranch}`,
677
+ ` git checkout -b octogent_integration_${tentacleId}`,
678
+ " ```",
679
+ "",
680
+ "2. **Merge each worker branch** into the integration branch one at a time. Start with the branch most likely to merge cleanly (fewest changes):",
681
+ " ```bash",
682
+ " git merge <worker-branch-name> --no-edit",
683
+ " ```",
684
+ " If there are conflicts, resolve them carefully. Read the conflicting files and understand both sides before choosing.",
685
+ "",
686
+ "3. **Run tests** on the integration branch after all merges. Do not skip this step.",
687
+ "",
688
+ "4. **If tests pass**, merge the integration branch into the base branch:",
689
+ " ```bash",
690
+ ` git checkout ${baseBranch}`,
691
+ ` git merge octogent_integration_${tentacleId} --no-edit`,
692
+ " ```",
693
+ "",
694
+ "5. **If tests fail**, investigate and fix before merging. Do not merge broken code.",
695
+ "",
696
+ `6. **Update tentacle state/docs** before finalizing. Mark completed items as done in \`.octogent/tentacles/${tentacleId}/todo.md\`, and update \`.octogent/tentacles/${tentacleId}/CONTEXT.md\` or other tentacle markdown files if the merged work changed the reality they describe.`,
697
+ "",
698
+ "7. **Clean up** the integration branch:",
699
+ " ```bash",
700
+ ` git branch -d octogent_integration_${tentacleId}`,
701
+ " ```",
702
+ "",
703
+ "### Merge failure recovery",
704
+ "",
705
+ "If a worker's branch has conflicts that are too complex to resolve, send a message to that worker asking them to rebase their work. Merge the other workers' branches first.",
706
+ ].join("\n")
707
+ : [
708
+ `Only begin final verification after ALL ${workers.length} workers have reported DONE.`,
709
+ "",
710
+ "Workers are sharing the main workspace, so there are no per-worker branches to merge.",
711
+ "",
712
+ "### Step-by-step completion process",
713
+ "",
714
+ `1. **Verify the workspace is on \`${baseBranch}\`** and review the overall diff carefully. Do not assume the combined result is safe just because workers reported DONE.`,
715
+ "",
716
+ "2. **Review the changed files** to ensure workers did not overwrite each other or leave partial edits.",
717
+ "",
718
+ "3. **Run tests** on the shared workspace after all workers report DONE. Do not skip this step.",
719
+ "",
720
+ "4. **If tests fail**, investigate and coordinate fixes. Do not declare the swarm complete while the workspace is broken.",
721
+ "",
722
+ `5. **Update tentacle state/docs** before asking for approval. Mark completed items as done in \`.octogent/tentacles/${tentacleId}/todo.md\`, and update \`.octogent/tentacles/${tentacleId}/CONTEXT.md\` or other tentacle markdown files if the completed work changed the reality they describe. If no tentacle docs need updates, say that explicitly.`,
723
+ "",
724
+ "6. **Wait for explicit user approval** before creating any commit on the shared main branch. Present a concise summary of the reviewed diff, test results, and tentacle-doc updates first.",
725
+ "",
726
+ "7. **Only after approval, create one final commit** on the shared branch that captures the swarm's completed work.",
727
+ "",
728
+ "8. **Report completion** only after the shared workspace is reviewed, tests pass, tentacle docs are synced, approval is granted, and the final commit is created.",
729
+ "",
730
+ "### Shared-workspace failure recovery",
731
+ "",
732
+ "If two workers collide in the same files, stop them from making broad new edits, inspect the current diff, and coordinate targeted follow-up changes instead of pretending there is a clean merge boundary.",
733
+ ].join("\n");
734
+
735
+ try {
736
+ if (!needsParent) {
737
+ const [item] = targetItems;
738
+ const [worker] = workers;
739
+ if (!item || !worker) {
740
+ writeJson(response, 400, { error: "No incomplete todo items found." }, corsOrigin);
741
+ return true;
742
+ }
743
+
744
+ const workerPrompt = await resolvePrompt(promptsDir, "swarm-worker", {
745
+ tentacleName,
746
+ tentacleId,
747
+ tentacleContextPath,
748
+ todoItemText: item.text,
749
+ terminalId: worker.terminalId,
750
+ apiPort,
751
+ workspaceContextIntro: buildWorkerContextIntro(),
752
+ workspaceGuidelines: buildWorkerGuidelines(worker.terminalId),
753
+ commitGuidance: buildWorkerCommitGuidance(),
754
+ definitionOfDoneCommitStep: buildWorkerDefinitionOfDoneCommitStep(),
755
+ workspaceReminder: buildWorkerReminder(),
756
+ parentTerminalId: "",
757
+ parentSection: "",
758
+ });
759
+
760
+ runtime.createTerminal({
761
+ terminalId: worker.terminalId,
762
+ tentacleId,
763
+ ...(workerWorkspaceMode === "worktree" ? { worktreeId: worker.terminalId } : {}),
764
+ tentacleName,
765
+ nameOrigin: "generated",
766
+ autoRenamePromptContext: item.text,
767
+ workspaceMode: workerWorkspaceMode,
768
+ ...(agentProviderResult.agentProvider
769
+ ? { agentProvider: agentProviderResult.agentProvider }
770
+ : {}),
771
+ ...(workerPrompt ? { initialPrompt: workerPrompt } : {}),
772
+ ...(workerWorkspaceMode === "worktree" ? { baseRef } : {}),
773
+ });
774
+ }
775
+
776
+ if (needsParent && parentTerminalId) {
777
+ const workerListing = workers
778
+ .map((w) => `- \`${w.terminalId}\` — item #${w.todoIndex}: ${w.todoText}`)
779
+ .join("\n");
780
+
781
+ const workerSpawnCommands = targetItems
782
+ .map((item) => {
783
+ const workerTerminalId = `${tentacleId}-swarm-${item.index}`;
784
+ const parentSection = [
785
+ "## Communication",
786
+ "",
787
+ `Your parent coordinator is at terminal \`${parentTerminalId}\`.`,
788
+ "When you complete your task, report back:",
789
+ "```bash",
790
+ `node bin/octogent channel send ${parentTerminalId} "DONE: ${item.text}" --from ${workerTerminalId}`,
791
+ "```",
792
+ "If you are blocked, ask for help:",
793
+ "```bash",
794
+ `node bin/octogent channel send ${parentTerminalId} "BLOCKED: <describe what you need>" --from ${workerTerminalId}`,
795
+ "```",
796
+ ].join("\n");
797
+
798
+ const promptVariables = JSON.stringify({
799
+ tentacleName,
800
+ tentacleId,
801
+ tentacleContextPath,
802
+ todoItemText: item.text,
803
+ terminalId: workerTerminalId,
804
+ apiPort,
805
+ workspaceContextIntro: buildWorkerContextIntro(),
806
+ workspaceGuidelines: buildWorkerGuidelines(workerTerminalId),
807
+ commitGuidance: buildWorkerCommitGuidance(),
808
+ definitionOfDoneCommitStep: buildWorkerDefinitionOfDoneCommitStep(),
809
+ workspaceReminder: buildWorkerReminder(),
810
+ parentTerminalId,
811
+ parentSection,
812
+ });
813
+
814
+ const commandParts = [
815
+ "node bin/octogent terminal create",
816
+ `--terminal-id ${shellSingleQuote(workerTerminalId)}`,
817
+ `--tentacle-id ${shellSingleQuote(tentacleId)}`,
818
+ `--parent-terminal-id ${shellSingleQuote(parentTerminalId)}`,
819
+ `--workspace-mode ${workerWorkspaceMode}`,
820
+ `--name ${shellSingleQuote(tentacleName)}`,
821
+ "--name-origin generated",
822
+ `--auto-rename-prompt-context ${shellSingleQuote(item.text)}`,
823
+ "--prompt-template swarm-worker",
824
+ `--prompt-variables ${shellSingleQuote(promptVariables)}`,
825
+ ];
826
+ if (workerWorkspaceMode === "worktree") {
827
+ commandParts.splice(3, 0, `--worktree-id ${shellSingleQuote(workerTerminalId)}`);
828
+ }
829
+ const command = commandParts.join(" ");
830
+
831
+ return `- \`${workerTerminalId}\`:\n \`\`\`bash\n ${command}\n \`\`\``;
832
+ })
833
+ .join("\n");
834
+
835
+ const parentBaseBranch =
836
+ workerWorkspaceMode === "worktree" ? (baseRef === "HEAD" ? "main" : baseRef) : "main";
837
+
838
+ const parentPrompt = await resolvePrompt(promptsDir, "swarm-parent", {
839
+ tentacleName,
840
+ tentacleId,
841
+ workerCount: String(workers.length),
842
+ maxChildrenPerParent: String(MAX_CHILDREN_PER_PARENT),
843
+ workerListing,
844
+ workerWorkspaceSection: buildWorkerWorkspaceSection(),
845
+ workerSpawnCommands,
846
+ completionStrategySection: buildCompletionStrategySection(parentBaseBranch),
847
+ baseBranch: parentBaseBranch,
848
+ terminalId: parentTerminalId,
849
+ apiPort,
850
+ });
851
+
852
+ runtime.createTerminal({
853
+ terminalId: parentTerminalId,
854
+ tentacleId,
855
+ tentacleName: `${tentacleName} (coordinator)`,
856
+ workspaceMode: "shared",
857
+ ...(agentProviderResult.agentProvider
858
+ ? { agentProvider: agentProviderResult.agentProvider }
859
+ : {}),
860
+ ...(parentPrompt ? { initialPrompt: parentPrompt } : {}),
861
+ });
862
+ }
863
+ } catch (error) {
864
+ if (error instanceof RuntimeInputError) {
865
+ writeJson(response, 400, { error: error.message }, corsOrigin);
866
+ return true;
867
+ }
868
+ throw error;
869
+ }
870
+
871
+ writeJson(response, 201, { tentacleId, parentTerminalId, workers }, corsOrigin);
872
+ return true;
873
+ };