koro-ai 1.0.0

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 (670) hide show
  1. package/AGENTS.md +69 -0
  2. package/BUN_SHELL_MIGRATION_PLAN.md +136 -0
  3. package/Dockerfile +18 -0
  4. package/README.md +15 -0
  5. package/bin/koro +16 -0
  6. package/bin/opencode +179 -0
  7. package/bunfig.toml +7 -0
  8. package/drizzle.config.ts +10 -0
  9. package/git +0 -0
  10. package/migration/20260127222353_familiar_lady_ursula/migration.sql +90 -0
  11. package/migration/20260127222353_familiar_lady_ursula/snapshot.json +796 -0
  12. package/migration/20260211171708_add_project_commands/migration.sql +1 -0
  13. package/migration/20260211171708_add_project_commands/snapshot.json +806 -0
  14. package/migration/20260213144116_wakeful_the_professor/migration.sql +11 -0
  15. package/migration/20260213144116_wakeful_the_professor/snapshot.json +897 -0
  16. package/migration/20260225215848_workspace/migration.sql +7 -0
  17. package/migration/20260225215848_workspace/snapshot.json +959 -0
  18. package/migration/20260227213759_add_session_workspace_id/migration.sql +2 -0
  19. package/migration/20260227213759_add_session_workspace_id/snapshot.json +983 -0
  20. package/migration/20260228203230_blue_harpoon/migration.sql +17 -0
  21. package/migration/20260228203230_blue_harpoon/snapshot.json +1102 -0
  22. package/migration/20260303231226_add_workspace_fields/migration.sql +5 -0
  23. package/migration/20260303231226_add_workspace_fields/snapshot.json +1013 -0
  24. package/migration/20260309230000_move_org_to_state/migration.sql +3 -0
  25. package/migration/20260309230000_move_org_to_state/snapshot.json +1156 -0
  26. package/migration/20260312043431_session_message_cursor/migration.sql +4 -0
  27. package/migration/20260312043431_session_message_cursor/snapshot.json +1168 -0
  28. package/migration/20260323234822_events/migration.sql +13 -0
  29. package/migration/20260323234822_events/snapshot.json +1271 -0
  30. package/package.json +163 -0
  31. package/parsers-config.ts +290 -0
  32. package/script/build-node.ts +56 -0
  33. package/script/build.ts +276 -0
  34. package/script/check-migrations.ts +16 -0
  35. package/script/postinstall.mjs +131 -0
  36. package/script/publish.ts +181 -0
  37. package/script/schema.ts +63 -0
  38. package/script/seed-e2e.ts +60 -0
  39. package/script/upgrade-opentui.ts +64 -0
  40. package/specs/effect-migration.md +294 -0
  41. package/specs/tui-plugins.md +436 -0
  42. package/src/account/account.sql.ts +39 -0
  43. package/src/account/index.ts +424 -0
  44. package/src/account/repo.ts +163 -0
  45. package/src/account/schema.ts +91 -0
  46. package/src/acp/README.md +174 -0
  47. package/src/acp/agent.ts +1763 -0
  48. package/src/acp/session.ts +116 -0
  49. package/src/acp/types.ts +24 -0
  50. package/src/agent/agent.ts +476 -0
  51. package/src/agent/generate.txt +75 -0
  52. package/src/agent/prompt/compaction.txt +15 -0
  53. package/src/agent/prompt/explore.txt +18 -0
  54. package/src/agent/prompt/summary.txt +11 -0
  55. package/src/agent/prompt/title.txt +44 -0
  56. package/src/auth/index.ts +109 -0
  57. package/src/bus/bus-event.ts +40 -0
  58. package/src/bus/global.ts +10 -0
  59. package/src/bus/index.ts +185 -0
  60. package/src/cli/bootstrap.ts +17 -0
  61. package/src/cli/cmd/account.ts +257 -0
  62. package/src/cli/cmd/acp.ts +70 -0
  63. package/src/cli/cmd/agent.ts +245 -0
  64. package/src/cli/cmd/cmd.ts +7 -0
  65. package/src/cli/cmd/db.ts +119 -0
  66. package/src/cli/cmd/debug/agent.ts +167 -0
  67. package/src/cli/cmd/debug/config.ts +16 -0
  68. package/src/cli/cmd/debug/file.ts +97 -0
  69. package/src/cli/cmd/debug/index.ts +48 -0
  70. package/src/cli/cmd/debug/lsp.ts +53 -0
  71. package/src/cli/cmd/debug/ripgrep.ts +87 -0
  72. package/src/cli/cmd/debug/scrap.ts +16 -0
  73. package/src/cli/cmd/debug/skill.ts +16 -0
  74. package/src/cli/cmd/debug/snapshot.ts +52 -0
  75. package/src/cli/cmd/export.ts +89 -0
  76. package/src/cli/cmd/generate.ts +38 -0
  77. package/src/cli/cmd/github.ts +1646 -0
  78. package/src/cli/cmd/import.ts +207 -0
  79. package/src/cli/cmd/mcp.ts +754 -0
  80. package/src/cli/cmd/models.ts +78 -0
  81. package/src/cli/cmd/plug.ts +233 -0
  82. package/src/cli/cmd/pr.ts +127 -0
  83. package/src/cli/cmd/providers.ts +478 -0
  84. package/src/cli/cmd/run.ts +676 -0
  85. package/src/cli/cmd/serve.ts +24 -0
  86. package/src/cli/cmd/session.ts +159 -0
  87. package/src/cli/cmd/stats.ts +410 -0
  88. package/src/cli/cmd/tui/app.tsx +919 -0
  89. package/src/cli/cmd/tui/attach.ts +88 -0
  90. package/src/cli/cmd/tui/component/border.tsx +21 -0
  91. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  92. package/src/cli/cmd/tui/component/dialog-command.tsx +171 -0
  93. package/src/cli/cmd/tui/component/dialog-import-share.tsx +118 -0
  94. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  95. package/src/cli/cmd/tui/component/dialog-model.tsx +179 -0
  96. package/src/cli/cmd/tui/component/dialog-provider.tsx +329 -0
  97. package/src/cli/cmd/tui/component/dialog-session-list.tsx +108 -0
  98. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  99. package/src/cli/cmd/tui/component/dialog-skill.tsx +36 -0
  100. package/src/cli/cmd/tui/component/dialog-stash.tsx +87 -0
  101. package/src/cli/cmd/tui/component/dialog-status.tsx +168 -0
  102. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  103. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
  104. package/src/cli/cmd/tui/component/dialog-variant.tsx +39 -0
  105. package/src/cli/cmd/tui/component/dialog-workspace-list.tsx +320 -0
  106. package/src/cli/cmd/tui/component/error-component.tsx +92 -0
  107. package/src/cli/cmd/tui/component/logo.tsx +85 -0
  108. package/src/cli/cmd/tui/component/plugin-route-missing.tsx +14 -0
  109. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +672 -0
  110. package/src/cli/cmd/tui/component/prompt/frecency.tsx +90 -0
  111. package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
  112. package/src/cli/cmd/tui/component/prompt/index.tsx +1310 -0
  113. package/src/cli/cmd/tui/component/prompt/part.ts +16 -0
  114. package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
  115. package/src/cli/cmd/tui/component/spinner.tsx +24 -0
  116. package/src/cli/cmd/tui/component/startup-loading.tsx +63 -0
  117. package/src/cli/cmd/tui/component/task-panel.tsx +44 -0
  118. package/src/cli/cmd/tui/component/textarea-keybindings.ts +73 -0
  119. package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
  120. package/src/cli/cmd/tui/component/token-bar.tsx +60 -0
  121. package/src/cli/cmd/tui/component/workspace/dialog-session-list.tsx +151 -0
  122. package/src/cli/cmd/tui/context/args.tsx +15 -0
  123. package/src/cli/cmd/tui/context/directory.ts +13 -0
  124. package/src/cli/cmd/tui/context/exit.tsx +60 -0
  125. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  126. package/src/cli/cmd/tui/context/keybind.tsx +105 -0
  127. package/src/cli/cmd/tui/context/kv.tsx +52 -0
  128. package/src/cli/cmd/tui/context/local.tsx +412 -0
  129. package/src/cli/cmd/tui/context/plugin-keybinds.ts +41 -0
  130. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  131. package/src/cli/cmd/tui/context/route.tsx +52 -0
  132. package/src/cli/cmd/tui/context/sdk.tsx +128 -0
  133. package/src/cli/cmd/tui/context/sync.tsx +504 -0
  134. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  135. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  136. package/src/cli/cmd/tui/context/theme/carbonfox.json +248 -0
  137. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
  138. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  139. package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
  140. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  141. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  142. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  143. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  144. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  145. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  146. package/src/cli/cmd/tui/context/theme/gruvbox.json +242 -0
  147. package/src/cli/cmd/tui/context/theme/gtr.json +245 -0
  148. package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
  149. package/src/cli/cmd/tui/context/theme/koro.json +241 -0
  150. package/src/cli/cmd/tui/context/theme/lucent-orng.json +237 -0
  151. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  152. package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
  153. package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
  154. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  155. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  156. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  157. package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
  158. package/src/cli/cmd/tui/context/theme/opencode.json +245 -0
  159. package/src/cli/cmd/tui/context/theme/orng.json +249 -0
  160. package/src/cli/cmd/tui/context/theme/osaka-jade.json +93 -0
  161. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  162. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  163. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  164. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  165. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  166. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  167. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  168. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  169. package/src/cli/cmd/tui/context/theme.tsx +1240 -0
  170. package/src/cli/cmd/tui/context/tui-config.tsx +9 -0
  171. package/src/cli/cmd/tui/event.ts +49 -0
  172. package/src/cli/cmd/tui/feature-plugins/home/footer.tsx +93 -0
  173. package/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +152 -0
  174. package/src/cli/cmd/tui/feature-plugins/home/tips.tsx +50 -0
  175. package/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx +63 -0
  176. package/src/cli/cmd/tui/feature-plugins/sidebar/files.tsx +62 -0
  177. package/src/cli/cmd/tui/feature-plugins/sidebar/footer.tsx +93 -0
  178. package/src/cli/cmd/tui/feature-plugins/sidebar/lsp.tsx +66 -0
  179. package/src/cli/cmd/tui/feature-plugins/sidebar/mcp.tsx +96 -0
  180. package/src/cli/cmd/tui/feature-plugins/sidebar/todo.tsx +48 -0
  181. package/src/cli/cmd/tui/feature-plugins/system/plugins.tsx +270 -0
  182. package/src/cli/cmd/tui/plugin/api.tsx +430 -0
  183. package/src/cli/cmd/tui/plugin/index.ts +3 -0
  184. package/src/cli/cmd/tui/plugin/internal.ts +27 -0
  185. package/src/cli/cmd/tui/plugin/runtime.ts +1033 -0
  186. package/src/cli/cmd/tui/plugin/slots.tsx +60 -0
  187. package/src/cli/cmd/tui/routes/home.tsx +84 -0
  188. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +65 -0
  189. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +110 -0
  190. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
  191. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  192. package/src/cli/cmd/tui/routes/session/footer.tsx +93 -0
  193. package/src/cli/cmd/tui/routes/session/index.tsx +2270 -0
  194. package/src/cli/cmd/tui/routes/session/permission.tsx +691 -0
  195. package/src/cli/cmd/tui/routes/session/question.tsx +468 -0
  196. package/src/cli/cmd/tui/routes/session/sidebar.tsx +74 -0
  197. package/src/cli/cmd/tui/routes/session/subagent-footer.tsx +131 -0
  198. package/src/cli/cmd/tui/thread.ts +232 -0
  199. package/src/cli/cmd/tui/ui/dialog-alert.tsx +59 -0
  200. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +89 -0
  201. package/src/cli/cmd/tui/ui/dialog-export-options.tsx +211 -0
  202. package/src/cli/cmd/tui/ui/dialog-help.tsx +40 -0
  203. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +130 -0
  204. package/src/cli/cmd/tui/ui/dialog-select.tsx +409 -0
  205. package/src/cli/cmd/tui/ui/dialog.tsx +192 -0
  206. package/src/cli/cmd/tui/ui/link.tsx +28 -0
  207. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  208. package/src/cli/cmd/tui/ui/toast.tsx +100 -0
  209. package/src/cli/cmd/tui/util/clipboard.ts +192 -0
  210. package/src/cli/cmd/tui/util/editor.ts +37 -0
  211. package/src/cli/cmd/tui/util/model.ts +23 -0
  212. package/src/cli/cmd/tui/util/scroll.ts +23 -0
  213. package/src/cli/cmd/tui/util/selection.ts +25 -0
  214. package/src/cli/cmd/tui/util/signal.ts +7 -0
  215. package/src/cli/cmd/tui/util/terminal.ts +114 -0
  216. package/src/cli/cmd/tui/util/transcript.ts +112 -0
  217. package/src/cli/cmd/tui/win32.ts +129 -0
  218. package/src/cli/cmd/tui/worker.ts +175 -0
  219. package/src/cli/cmd/uninstall.ts +353 -0
  220. package/src/cli/cmd/upgrade.ts +73 -0
  221. package/src/cli/cmd/web.ts +81 -0
  222. package/src/cli/effect/prompt.ts +25 -0
  223. package/src/cli/error.ts +46 -0
  224. package/src/cli/heap.ts +59 -0
  225. package/src/cli/logo.ts +14 -0
  226. package/src/cli/network.ts +60 -0
  227. package/src/cli/ui.ts +133 -0
  228. package/src/cli/upgrade.ts +31 -0
  229. package/src/command/index.ts +195 -0
  230. package/src/command/template/initialize.txt +66 -0
  231. package/src/command/template/review.txt +101 -0
  232. package/src/config/config.ts +1591 -0
  233. package/src/config/markdown.ts +99 -0
  234. package/src/config/paths.ts +181 -0
  235. package/src/config/tui-migrate.ts +155 -0
  236. package/src/config/tui-schema.ts +36 -0
  237. package/src/config/tui.ts +171 -0
  238. package/src/control-plane/adaptors/index.ts +20 -0
  239. package/src/control-plane/adaptors/worktree.ts +38 -0
  240. package/src/control-plane/schema.ts +17 -0
  241. package/src/control-plane/sse.ts +66 -0
  242. package/src/control-plane/types.ts +21 -0
  243. package/src/control-plane/workspace.sql.ts +17 -0
  244. package/src/control-plane/workspace.ts +154 -0
  245. package/src/effect/cross-spawn-spawner.ts +502 -0
  246. package/src/effect/instance-ref.ts +6 -0
  247. package/src/effect/instance-registry.ts +12 -0
  248. package/src/effect/instance-state.ts +82 -0
  249. package/src/effect/run-service.ts +33 -0
  250. package/src/effect/runner.ts +216 -0
  251. package/src/env/index.ts +28 -0
  252. package/src/file/ignore.ts +82 -0
  253. package/src/file/index.ts +686 -0
  254. package/src/file/protected.ts +59 -0
  255. package/src/file/ripgrep.ts +376 -0
  256. package/src/file/time.ts +133 -0
  257. package/src/file/watcher.ts +171 -0
  258. package/src/filesystem/index.ts +226 -0
  259. package/src/flag/flag.ts +155 -0
  260. package/src/format/formatter.ts +413 -0
  261. package/src/format/index.ts +203 -0
  262. package/src/git/index.ts +303 -0
  263. package/src/global/index.ts +161 -0
  264. package/src/id/id.ts +85 -0
  265. package/src/ide/index.ts +74 -0
  266. package/src/index.ts +240 -0
  267. package/src/installation/index.ts +355 -0
  268. package/src/installation/meta.ts +7 -0
  269. package/src/lsp/client.ts +252 -0
  270. package/src/lsp/index.ts +558 -0
  271. package/src/lsp/language.ts +120 -0
  272. package/src/lsp/launch.ts +21 -0
  273. package/src/lsp/server.ts +1958 -0
  274. package/src/mcp/auth.ts +173 -0
  275. package/src/mcp/index.ts +921 -0
  276. package/src/mcp/oauth-callback.ts +215 -0
  277. package/src/mcp/oauth-provider.ts +185 -0
  278. package/src/memory/index.ts +117 -0
  279. package/src/node.ts +1 -0
  280. package/src/npm/index.ts +180 -0
  281. package/src/orchestrator/agent-registry.ts +38 -0
  282. package/src/orchestrator/conflict.ts +25 -0
  283. package/src/orchestrator/context-manager.ts +22 -0
  284. package/src/orchestrator/index.ts +9 -0
  285. package/src/orchestrator/scheduler.ts +30 -0
  286. package/src/orchestrator/state-tracker.ts +71 -0
  287. package/src/orchestrator/task-manager.ts +69 -0
  288. package/src/patch/index.ts +680 -0
  289. package/src/permission/arity.ts +163 -0
  290. package/src/permission/evaluate.ts +15 -0
  291. package/src/permission/index.ts +325 -0
  292. package/src/permission/schema.ts +17 -0
  293. package/src/plugin/codex.ts +596 -0
  294. package/src/plugin/github-copilot/copilot.ts +353 -0
  295. package/src/plugin/github-copilot/models.ts +144 -0
  296. package/src/plugin/index.ts +281 -0
  297. package/src/plugin/install.ts +439 -0
  298. package/src/plugin/loader.ts +174 -0
  299. package/src/plugin/meta.ts +188 -0
  300. package/src/plugin/shared.ts +307 -0
  301. package/src/project/bootstrap.ts +31 -0
  302. package/src/project/instance.ts +175 -0
  303. package/src/project/project.sql.ts +16 -0
  304. package/src/project/project.ts +519 -0
  305. package/src/project/schema.ts +16 -0
  306. package/src/project/state.ts +70 -0
  307. package/src/project/vcs.ts +240 -0
  308. package/src/provider/auth.ts +253 -0
  309. package/src/provider/error.ts +197 -0
  310. package/src/provider/models-snapshot.ts +60410 -0
  311. package/src/provider/models.ts +162 -0
  312. package/src/provider/provider.ts +1677 -0
  313. package/src/provider/schema.ts +38 -0
  314. package/src/provider/sdk/copilot/README.md +5 -0
  315. package/src/provider/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +170 -0
  316. package/src/provider/sdk/copilot/chat/get-response-metadata.ts +15 -0
  317. package/src/provider/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
  318. package/src/provider/sdk/copilot/chat/openai-compatible-api-types.ts +64 -0
  319. package/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts +815 -0
  320. package/src/provider/sdk/copilot/chat/openai-compatible-chat-options.ts +28 -0
  321. package/src/provider/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +44 -0
  322. package/src/provider/sdk/copilot/chat/openai-compatible-prepare-tools.ts +83 -0
  323. package/src/provider/sdk/copilot/copilot-provider.ts +100 -0
  324. package/src/provider/sdk/copilot/index.ts +2 -0
  325. package/src/provider/sdk/copilot/openai-compatible-error.ts +27 -0
  326. package/src/provider/sdk/copilot/responses/convert-to-openai-responses-input.ts +335 -0
  327. package/src/provider/sdk/copilot/responses/map-openai-responses-finish-reason.ts +22 -0
  328. package/src/provider/sdk/copilot/responses/openai-config.ts +18 -0
  329. package/src/provider/sdk/copilot/responses/openai-error.ts +22 -0
  330. package/src/provider/sdk/copilot/responses/openai-responses-api-types.ts +214 -0
  331. package/src/provider/sdk/copilot/responses/openai-responses-language-model.ts +1769 -0
  332. package/src/provider/sdk/copilot/responses/openai-responses-prepare-tools.ts +173 -0
  333. package/src/provider/sdk/copilot/responses/openai-responses-settings.ts +1 -0
  334. package/src/provider/sdk/copilot/responses/tool/code-interpreter.ts +87 -0
  335. package/src/provider/sdk/copilot/responses/tool/file-search.ts +127 -0
  336. package/src/provider/sdk/copilot/responses/tool/image-generation.ts +114 -0
  337. package/src/provider/sdk/copilot/responses/tool/local-shell.ts +64 -0
  338. package/src/provider/sdk/copilot/responses/tool/web-search-preview.ts +103 -0
  339. package/src/provider/sdk/copilot/responses/tool/web-search.ts +102 -0
  340. package/src/provider/transform.ts +1046 -0
  341. package/src/pty/index.ts +401 -0
  342. package/src/pty/schema.ts +17 -0
  343. package/src/question/index.ts +224 -0
  344. package/src/question/schema.ts +17 -0
  345. package/src/server/error.ts +36 -0
  346. package/src/server/event.ts +7 -0
  347. package/src/server/instance.ts +314 -0
  348. package/src/server/mdns.ts +60 -0
  349. package/src/server/middleware.ts +33 -0
  350. package/src/server/projectors.ts +28 -0
  351. package/src/server/router.ts +99 -0
  352. package/src/server/routes/config.ts +92 -0
  353. package/src/server/routes/event.ts +83 -0
  354. package/src/server/routes/experimental.ts +271 -0
  355. package/src/server/routes/file.ts +197 -0
  356. package/src/server/routes/global.ts +312 -0
  357. package/src/server/routes/mcp.ts +225 -0
  358. package/src/server/routes/permission.ts +69 -0
  359. package/src/server/routes/project.ts +118 -0
  360. package/src/server/routes/provider.ts +171 -0
  361. package/src/server/routes/pty.ts +211 -0
  362. package/src/server/routes/question.ts +99 -0
  363. package/src/server/routes/session.ts +1031 -0
  364. package/src/server/routes/tui.ts +379 -0
  365. package/src/server/routes/workspace.ts +94 -0
  366. package/src/server/server.ts +312 -0
  367. package/src/session/compaction.ts +428 -0
  368. package/src/session/index.ts +887 -0
  369. package/src/session/instruction.ts +258 -0
  370. package/src/session/llm.ts +370 -0
  371. package/src/session/message-v2.ts +1031 -0
  372. package/src/session/message.ts +191 -0
  373. package/src/session/overflow.ts +22 -0
  374. package/src/session/processor.ts +523 -0
  375. package/src/session/projectors.ts +135 -0
  376. package/src/session/prompt/anthropic.txt +105 -0
  377. package/src/session/prompt/beast.txt +147 -0
  378. package/src/session/prompt/build-switch.txt +5 -0
  379. package/src/session/prompt/codex.txt +79 -0
  380. package/src/session/prompt/copilot-gpt-5.txt +143 -0
  381. package/src/session/prompt/default.txt +105 -0
  382. package/src/session/prompt/gemini.txt +155 -0
  383. package/src/session/prompt/gpt.txt +107 -0
  384. package/src/session/prompt/kimi.txt +114 -0
  385. package/src/session/prompt/max-steps.txt +16 -0
  386. package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
  387. package/src/session/prompt/plan.txt +26 -0
  388. package/src/session/prompt/trinity.txt +97 -0
  389. package/src/session/prompt.ts +1908 -0
  390. package/src/session/retry.ts +106 -0
  391. package/src/session/revert.ts +176 -0
  392. package/src/session/schema.ts +38 -0
  393. package/src/session/session.sql.ts +103 -0
  394. package/src/session/status.ts +102 -0
  395. package/src/session/summary.ts +177 -0
  396. package/src/session/system.ts +76 -0
  397. package/src/session/todo.ts +95 -0
  398. package/src/share/share-next.ts +369 -0
  399. package/src/share/share.sql.ts +13 -0
  400. package/src/shell/shell.ts +110 -0
  401. package/src/skill/discovery.ts +116 -0
  402. package/src/skill/index.ts +277 -0
  403. package/src/snapshot/index.ts +571 -0
  404. package/src/sql.d.ts +4 -0
  405. package/src/storage/db.bun.ts +8 -0
  406. package/src/storage/db.node.ts +8 -0
  407. package/src/storage/db.ts +174 -0
  408. package/src/storage/json-migration.ts +425 -0
  409. package/src/storage/schema.sql.ts +10 -0
  410. package/src/storage/schema.ts +5 -0
  411. package/src/storage/storage.ts +353 -0
  412. package/src/sync/README.md +179 -0
  413. package/src/sync/event.sql.ts +16 -0
  414. package/src/sync/index.ts +263 -0
  415. package/src/sync/schema.ts +14 -0
  416. package/src/token/index.ts +77 -0
  417. package/src/tool/apply_patch.ts +281 -0
  418. package/src/tool/apply_patch.txt +33 -0
  419. package/src/tool/bash.ts +496 -0
  420. package/src/tool/bash.txt +117 -0
  421. package/src/tool/batch.ts +183 -0
  422. package/src/tool/batch.txt +24 -0
  423. package/src/tool/codesearch.ts +132 -0
  424. package/src/tool/codesearch.txt +12 -0
  425. package/src/tool/edit.ts +667 -0
  426. package/src/tool/edit.txt +10 -0
  427. package/src/tool/external-directory.ts +37 -0
  428. package/src/tool/glob.ts +78 -0
  429. package/src/tool/glob.txt +6 -0
  430. package/src/tool/grep.ts +156 -0
  431. package/src/tool/grep.txt +8 -0
  432. package/src/tool/invalid.ts +17 -0
  433. package/src/tool/ls.ts +121 -0
  434. package/src/tool/ls.txt +1 -0
  435. package/src/tool/lsp.ts +97 -0
  436. package/src/tool/lsp.txt +19 -0
  437. package/src/tool/multiedit.ts +46 -0
  438. package/src/tool/multiedit.txt +41 -0
  439. package/src/tool/plan-enter.txt +14 -0
  440. package/src/tool/plan-exit.txt +13 -0
  441. package/src/tool/plan.ts +131 -0
  442. package/src/tool/question.ts +46 -0
  443. package/src/tool/question.txt +10 -0
  444. package/src/tool/read.ts +296 -0
  445. package/src/tool/read.txt +14 -0
  446. package/src/tool/registry.ts +248 -0
  447. package/src/tool/schema.ts +17 -0
  448. package/src/tool/skill.ts +105 -0
  449. package/src/tool/task.ts +166 -0
  450. package/src/tool/task.txt +60 -0
  451. package/src/tool/todo.ts +48 -0
  452. package/src/tool/todowrite.txt +167 -0
  453. package/src/tool/tool.ts +112 -0
  454. package/src/tool/truncate.ts +144 -0
  455. package/src/tool/truncation-dir.ts +4 -0
  456. package/src/tool/webfetch.ts +206 -0
  457. package/src/tool/webfetch.txt +13 -0
  458. package/src/tool/websearch.ts +150 -0
  459. package/src/tool/websearch.txt +14 -0
  460. package/src/tool/write.ts +84 -0
  461. package/src/tool/write.txt +8 -0
  462. package/src/util/abort.ts +35 -0
  463. package/src/util/archive.ts +17 -0
  464. package/src/util/color.ts +19 -0
  465. package/src/util/context.ts +25 -0
  466. package/src/util/data-url.ts +9 -0
  467. package/src/util/defer.ts +12 -0
  468. package/src/util/effect-http-client.ts +11 -0
  469. package/src/util/effect-zod.ts +98 -0
  470. package/src/util/error.ts +77 -0
  471. package/src/util/filesystem.ts +245 -0
  472. package/src/util/flock.ts +333 -0
  473. package/src/util/fn.ts +21 -0
  474. package/src/util/format.ts +20 -0
  475. package/src/util/glob.ts +34 -0
  476. package/src/util/hash.ts +7 -0
  477. package/src/util/iife.ts +3 -0
  478. package/src/util/keybind.ts +103 -0
  479. package/src/util/lazy.ts +23 -0
  480. package/src/util/locale.ts +81 -0
  481. package/src/util/lock.ts +98 -0
  482. package/src/util/log.ts +182 -0
  483. package/src/util/network.ts +9 -0
  484. package/src/util/process.ts +176 -0
  485. package/src/util/queue.ts +32 -0
  486. package/src/util/record.ts +3 -0
  487. package/src/util/rpc.ts +66 -0
  488. package/src/util/schema.ts +53 -0
  489. package/src/util/scrap.ts +10 -0
  490. package/src/util/signal.ts +12 -0
  491. package/src/util/timeout.ts +14 -0
  492. package/src/util/token.ts +7 -0
  493. package/src/util/update-schema.ts +13 -0
  494. package/src/util/which.ts +14 -0
  495. package/src/util/wildcard.ts +59 -0
  496. package/src/worktree/index.ts +612 -0
  497. package/sst-env.d.ts +10 -0
  498. package/test/AGENTS.md +81 -0
  499. package/test/account/repo.test.ts +326 -0
  500. package/test/account/service.test.ts +393 -0
  501. package/test/acp/agent-interface.test.ts +51 -0
  502. package/test/acp/event-subscription.test.ts +685 -0
  503. package/test/agent/agent.test.ts +717 -0
  504. package/test/auth/auth.test.ts +58 -0
  505. package/test/bus/bus-effect.test.ts +164 -0
  506. package/test/bus/bus-integration.test.ts +87 -0
  507. package/test/bus/bus.test.ts +219 -0
  508. package/test/cli/account.test.ts +26 -0
  509. package/test/cli/cmd/tui/prompt-part.test.ts +47 -0
  510. package/test/cli/github-action.test.ts +198 -0
  511. package/test/cli/github-remote.test.ts +80 -0
  512. package/test/cli/import.test.ts +54 -0
  513. package/test/cli/plugin-auth-picker.test.ts +120 -0
  514. package/test/cli/tui/keybind-plugin.test.ts +90 -0
  515. package/test/cli/tui/plugin-add.test.ts +107 -0
  516. package/test/cli/tui/plugin-install.test.ts +89 -0
  517. package/test/cli/tui/plugin-lifecycle.test.ts +225 -0
  518. package/test/cli/tui/plugin-loader-entrypoint.test.ts +492 -0
  519. package/test/cli/tui/plugin-loader-pure.test.ts +72 -0
  520. package/test/cli/tui/plugin-loader.test.ts +752 -0
  521. package/test/cli/tui/plugin-toggle.test.ts +159 -0
  522. package/test/cli/tui/slot-replace.test.tsx +47 -0
  523. package/test/cli/tui/theme-store.test.ts +51 -0
  524. package/test/cli/tui/thread.test.ts +128 -0
  525. package/test/cli/tui/transcript.test.ts +426 -0
  526. package/test/config/agent-color.test.ts +71 -0
  527. package/test/config/config.test.ts +2348 -0
  528. package/test/config/fixtures/empty-frontmatter.md +4 -0
  529. package/test/config/fixtures/frontmatter.md +28 -0
  530. package/test/config/fixtures/markdown-header.md +11 -0
  531. package/test/config/fixtures/no-frontmatter.md +1 -0
  532. package/test/config/fixtures/weird-model-id.md +13 -0
  533. package/test/config/markdown.test.ts +228 -0
  534. package/test/config/tui.test.ts +752 -0
  535. package/test/control-plane/sse.test.ts +56 -0
  536. package/test/effect/cross-spawn-spawner.test.ts +412 -0
  537. package/test/effect/instance-state.test.ts +482 -0
  538. package/test/effect/run-service.test.ts +46 -0
  539. package/test/effect/runner.test.ts +523 -0
  540. package/test/fake/provider.ts +81 -0
  541. package/test/file/fsmonitor.test.ts +62 -0
  542. package/test/file/ignore.test.ts +10 -0
  543. package/test/file/index.test.ts +946 -0
  544. package/test/file/path-traversal.test.ts +198 -0
  545. package/test/file/ripgrep.test.ts +54 -0
  546. package/test/file/time.test.ts +445 -0
  547. package/test/file/watcher.test.ts +247 -0
  548. package/test/filesystem/filesystem.test.ts +319 -0
  549. package/test/fixture/db.ts +11 -0
  550. package/test/fixture/fixture.test.ts +26 -0
  551. package/test/fixture/fixture.ts +172 -0
  552. package/test/fixture/flock-worker.ts +72 -0
  553. package/test/fixture/lsp/fake-lsp-server.js +77 -0
  554. package/test/fixture/plug-worker.ts +93 -0
  555. package/test/fixture/plugin-meta-worker.ts +26 -0
  556. package/test/fixture/skills/agents-sdk/SKILL.md +152 -0
  557. package/test/fixture/skills/agents-sdk/references/callable.md +92 -0
  558. package/test/fixture/skills/cloudflare/SKILL.md +211 -0
  559. package/test/fixture/skills/index.json +6 -0
  560. package/test/fixture/tui-plugin.ts +337 -0
  561. package/test/fixture/tui-runtime.ts +27 -0
  562. package/test/format/format.test.ts +171 -0
  563. package/test/git/git.test.ts +128 -0
  564. package/test/ide/ide.test.ts +82 -0
  565. package/test/installation/installation.test.ts +151 -0
  566. package/test/keybind.test.ts +421 -0
  567. package/test/lib/effect.ts +53 -0
  568. package/test/lib/filesystem.ts +10 -0
  569. package/test/lib/llm-server.ts +795 -0
  570. package/test/lsp/client.test.ts +95 -0
  571. package/test/lsp/index.test.ts +55 -0
  572. package/test/lsp/launch.test.ts +22 -0
  573. package/test/lsp/lifecycle.test.ts +147 -0
  574. package/test/mcp/headers.test.ts +153 -0
  575. package/test/mcp/lifecycle.test.ts +750 -0
  576. package/test/mcp/oauth-auto-connect.test.ts +199 -0
  577. package/test/mcp/oauth-browser.test.ts +249 -0
  578. package/test/memory/abort-leak.test.ts +137 -0
  579. package/test/patch/patch.test.ts +348 -0
  580. package/test/permission/arity.test.ts +33 -0
  581. package/test/permission/next.test.ts +1148 -0
  582. package/test/permission-task.test.ts +323 -0
  583. package/test/plugin/auth-override.test.ts +74 -0
  584. package/test/plugin/codex.test.ts +123 -0
  585. package/test/plugin/github-copilot-models.test.ts +117 -0
  586. package/test/plugin/install-concurrency.test.ts +140 -0
  587. package/test/plugin/install.test.ts +570 -0
  588. package/test/plugin/loader-shared.test.ts +1136 -0
  589. package/test/plugin/meta.test.ts +137 -0
  590. package/test/plugin/trigger.test.ts +111 -0
  591. package/test/preload.ts +90 -0
  592. package/test/project/migrate-global.test.ts +140 -0
  593. package/test/project/project.test.ts +459 -0
  594. package/test/project/state.test.ts +115 -0
  595. package/test/project/vcs.test.ts +228 -0
  596. package/test/project/worktree-remove.test.ts +96 -0
  597. package/test/project/worktree.test.ts +173 -0
  598. package/test/provider/amazon-bedrock.test.ts +447 -0
  599. package/test/provider/copilot/convert-to-copilot-messages.test.ts +523 -0
  600. package/test/provider/copilot/copilot-chat-model.test.ts +592 -0
  601. package/test/provider/gitlab-duo.test.ts +412 -0
  602. package/test/provider/provider.test.ts +2284 -0
  603. package/test/provider/transform.test.ts +2839 -0
  604. package/test/pty/pty-output-isolation.test.ts +141 -0
  605. package/test/pty/pty-session.test.ts +92 -0
  606. package/test/pty/pty-shell.test.ts +59 -0
  607. package/test/question/question.test.ts +453 -0
  608. package/test/server/global-session-list.test.ts +89 -0
  609. package/test/server/project-init-git.test.ts +121 -0
  610. package/test/server/session-actions.test.ts +83 -0
  611. package/test/server/session-list.test.ts +98 -0
  612. package/test/server/session-messages.test.ts +159 -0
  613. package/test/server/session-select.test.ts +84 -0
  614. package/test/session/compaction.test.ts +1212 -0
  615. package/test/session/instruction.test.ts +286 -0
  616. package/test/session/llm.test.ts +1098 -0
  617. package/test/session/message-v2.test.ts +957 -0
  618. package/test/session/messages-pagination.test.ts +885 -0
  619. package/test/session/processor-effect.test.ts +747 -0
  620. package/test/session/prompt-effect.test.ts +1241 -0
  621. package/test/session/prompt.test.ts +518 -0
  622. package/test/session/retry.test.ts +232 -0
  623. package/test/session/revert-compact.test.ts +621 -0
  624. package/test/session/session.test.ts +142 -0
  625. package/test/session/snapshot-tool-race.test.ts +242 -0
  626. package/test/session/structured-output-integration.test.ts +233 -0
  627. package/test/session/structured-output.test.ts +391 -0
  628. package/test/session/system.test.ts +59 -0
  629. package/test/share/share-next.test.ts +333 -0
  630. package/test/shell/shell.test.ts +73 -0
  631. package/test/skill/discovery.test.ts +116 -0
  632. package/test/skill/skill.test.ts +392 -0
  633. package/test/snapshot/snapshot.test.ts +1312 -0
  634. package/test/storage/db.test.ts +14 -0
  635. package/test/storage/json-migration.test.ts +849 -0
  636. package/test/storage/storage.test.ts +295 -0
  637. package/test/sync/index.test.ts +191 -0
  638. package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
  639. package/test/tool/apply_patch.test.ts +567 -0
  640. package/test/tool/bash.test.ts +1099 -0
  641. package/test/tool/edit.test.ts +681 -0
  642. package/test/tool/external-directory.test.ts +198 -0
  643. package/test/tool/fixtures/large-image.png +0 -0
  644. package/test/tool/fixtures/models-api.json +65179 -0
  645. package/test/tool/grep.test.ts +111 -0
  646. package/test/tool/question.test.ts +126 -0
  647. package/test/tool/read.test.ts +546 -0
  648. package/test/tool/registry.test.ts +126 -0
  649. package/test/tool/skill.test.ts +167 -0
  650. package/test/tool/task.test.ts +49 -0
  651. package/test/tool/tool-define.test.ts +101 -0
  652. package/test/tool/truncation.test.ts +161 -0
  653. package/test/tool/webfetch.test.ts +101 -0
  654. package/test/tool/write.test.ts +353 -0
  655. package/test/util/data-url.test.ts +14 -0
  656. package/test/util/effect-zod.test.ts +61 -0
  657. package/test/util/error.test.ts +38 -0
  658. package/test/util/filesystem.test.ts +656 -0
  659. package/test/util/flock.test.ts +383 -0
  660. package/test/util/format.test.ts +59 -0
  661. package/test/util/glob.test.ts +164 -0
  662. package/test/util/iife.test.ts +36 -0
  663. package/test/util/lazy.test.ts +50 -0
  664. package/test/util/lock.test.ts +72 -0
  665. package/test/util/module.test.ts +59 -0
  666. package/test/util/process.test.ts +128 -0
  667. package/test/util/timeout.test.ts +21 -0
  668. package/test/util/which.test.ts +100 -0
  669. package/test/util/wildcard.test.ts +90 -0
  670. package/tsconfig.json +23 -0
@@ -0,0 +1,303 @@
1
+ import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner"
2
+ import { Effect, Layer, ServiceMap, Stream } from "effect"
3
+ import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
4
+ import { makeRuntime } from "@/effect/run-service"
5
+
6
+ export namespace Git {
7
+ const cfg = [
8
+ "--no-optional-locks",
9
+ "-c",
10
+ "core.autocrlf=false",
11
+ "-c",
12
+ "core.fsmonitor=false",
13
+ "-c",
14
+ "core.longpaths=true",
15
+ "-c",
16
+ "core.symlinks=true",
17
+ "-c",
18
+ "core.quotepath=false",
19
+ ] as const
20
+
21
+ const out = (result: { text(): string }) => result.text().trim()
22
+ const nuls = (text: string) => text.split("\0").filter(Boolean)
23
+ const fail = (err: unknown) =>
24
+ ({
25
+ exitCode: 1,
26
+ text: () => "",
27
+ stdout: Buffer.alloc(0),
28
+ stderr: Buffer.from(err instanceof Error ? err.message : String(err)),
29
+ }) satisfies Result
30
+
31
+ export type Kind = "added" | "deleted" | "modified"
32
+
33
+ export type Base = {
34
+ readonly name: string
35
+ readonly ref: string
36
+ }
37
+
38
+ export type Item = {
39
+ readonly file: string
40
+ readonly code: string
41
+ readonly status: Kind
42
+ }
43
+
44
+ export type Stat = {
45
+ readonly file: string
46
+ readonly additions: number
47
+ readonly deletions: number
48
+ }
49
+
50
+ export interface Result {
51
+ readonly exitCode: number
52
+ readonly text: () => string
53
+ readonly stdout: Buffer
54
+ readonly stderr: Buffer
55
+ }
56
+
57
+ export interface Options {
58
+ readonly cwd: string
59
+ readonly env?: Record<string, string>
60
+ }
61
+
62
+ export interface Interface {
63
+ readonly run: (args: string[], opts: Options) => Effect.Effect<Result>
64
+ readonly branch: (cwd: string) => Effect.Effect<string | undefined>
65
+ readonly prefix: (cwd: string) => Effect.Effect<string>
66
+ readonly defaultBranch: (cwd: string) => Effect.Effect<Base | undefined>
67
+ readonly hasHead: (cwd: string) => Effect.Effect<boolean>
68
+ readonly mergeBase: (cwd: string, base: string, head?: string) => Effect.Effect<string | undefined>
69
+ readonly show: (cwd: string, ref: string, file: string, prefix?: string) => Effect.Effect<string>
70
+ readonly status: (cwd: string) => Effect.Effect<Item[]>
71
+ readonly diff: (cwd: string, ref: string) => Effect.Effect<Item[]>
72
+ readonly stats: (cwd: string, ref: string) => Effect.Effect<Stat[]>
73
+ }
74
+
75
+ const kind = (code: string): Kind => {
76
+ if (code === "??") return "added"
77
+ if (code.includes("U")) return "modified"
78
+ if (code.includes("A") && !code.includes("D")) return "added"
79
+ if (code.includes("D") && !code.includes("A")) return "deleted"
80
+ return "modified"
81
+ }
82
+
83
+ export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Git") {}
84
+
85
+ export const layer = Layer.effect(
86
+ Service,
87
+ Effect.gen(function* () {
88
+ const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
89
+
90
+ const run = Effect.fn("Git.run")(
91
+ function* (args: string[], opts: Options) {
92
+ const proc = ChildProcess.make("git", [...cfg, ...args], {
93
+ cwd: opts.cwd,
94
+ env: opts.env,
95
+ extendEnv: true,
96
+ stdin: "ignore",
97
+ stdout: "pipe",
98
+ stderr: "pipe",
99
+ })
100
+ const handle = yield* spawner.spawn(proc)
101
+ const [stdout, stderr] = yield* Effect.all(
102
+ [Stream.mkString(Stream.decodeText(handle.stdout)), Stream.mkString(Stream.decodeText(handle.stderr))],
103
+ { concurrency: 2 },
104
+ )
105
+ return {
106
+ exitCode: yield* handle.exitCode,
107
+ text: () => stdout,
108
+ stdout: Buffer.from(stdout),
109
+ stderr: Buffer.from(stderr),
110
+ } satisfies Result
111
+ },
112
+ Effect.scoped,
113
+ Effect.catch((err) => Effect.succeed(fail(err))),
114
+ )
115
+
116
+ const text = Effect.fn("Git.text")(function* (args: string[], opts: Options) {
117
+ return (yield* run(args, opts)).text()
118
+ })
119
+
120
+ const lines = Effect.fn("Git.lines")(function* (args: string[], opts: Options) {
121
+ return (yield* text(args, opts))
122
+ .split(/\r?\n/)
123
+ .map((item) => item.trim())
124
+ .filter(Boolean)
125
+ })
126
+
127
+ const refs = Effect.fnUntraced(function* (cwd: string) {
128
+ return yield* lines(["for-each-ref", "--format=%(refname:short)", "refs/heads"], { cwd })
129
+ })
130
+
131
+ const configured = Effect.fnUntraced(function* (cwd: string, list: string[]) {
132
+ const result = yield* run(["config", "init.defaultBranch"], { cwd })
133
+ const name = out(result)
134
+ if (!name || !list.includes(name)) return
135
+ return { name, ref: name } satisfies Base
136
+ })
137
+
138
+ const primary = Effect.fnUntraced(function* (cwd: string) {
139
+ const list = yield* lines(["remote"], { cwd })
140
+ if (list.includes("origin")) return "origin"
141
+ if (list.length === 1) return list[0]
142
+ if (list.includes("upstream")) return "upstream"
143
+ return list[0]
144
+ })
145
+
146
+ const branch = Effect.fn("Git.branch")(function* (cwd: string) {
147
+ const result = yield* run(["symbolic-ref", "--quiet", "--short", "HEAD"], { cwd })
148
+ if (result.exitCode !== 0) return
149
+ const text = out(result)
150
+ return text || undefined
151
+ })
152
+
153
+ const prefix = Effect.fn("Git.prefix")(function* (cwd: string) {
154
+ const result = yield* run(["rev-parse", "--show-prefix"], { cwd })
155
+ if (result.exitCode !== 0) return ""
156
+ return out(result)
157
+ })
158
+
159
+ const defaultBranch = Effect.fn("Git.defaultBranch")(function* (cwd: string) {
160
+ const remote = yield* primary(cwd)
161
+ if (remote) {
162
+ const head = yield* run(["symbolic-ref", `refs/remotes/${remote}/HEAD`], { cwd })
163
+ if (head.exitCode === 0) {
164
+ const ref = out(head).replace(/^refs\/remotes\//, "")
165
+ const name = ref.startsWith(`${remote}/`) ? ref.slice(`${remote}/`.length) : ""
166
+ if (name) return { name, ref } satisfies Base
167
+ }
168
+ }
169
+
170
+ const list = yield* refs(cwd)
171
+ const next = yield* configured(cwd, list)
172
+ if (next) return next
173
+ if (list.includes("main")) return { name: "main", ref: "main" } satisfies Base
174
+ if (list.includes("master")) return { name: "master", ref: "master" } satisfies Base
175
+ })
176
+
177
+ const hasHead = Effect.fn("Git.hasHead")(function* (cwd: string) {
178
+ const result = yield* run(["rev-parse", "--verify", "HEAD"], { cwd })
179
+ return result.exitCode === 0
180
+ })
181
+
182
+ const mergeBase = Effect.fn("Git.mergeBase")(function* (cwd: string, base: string, head = "HEAD") {
183
+ const result = yield* run(["merge-base", base, head], { cwd })
184
+ if (result.exitCode !== 0) return
185
+ const text = out(result)
186
+ return text || undefined
187
+ })
188
+
189
+ const show = Effect.fn("Git.show")(function* (cwd: string, ref: string, file: string, prefix = "") {
190
+ const target = prefix ? `${prefix}${file}` : file
191
+ const result = yield* run(["show", `${ref}:${target}`], { cwd })
192
+ if (result.exitCode !== 0) return ""
193
+ if (result.stdout.includes(0)) return ""
194
+ return result.text()
195
+ })
196
+
197
+ const status = Effect.fn("Git.status")(function* (cwd: string) {
198
+ return nuls(
199
+ yield* text(["status", "--porcelain=v1", "--untracked-files=all", "--no-renames", "-z", "--", "."], {
200
+ cwd,
201
+ }),
202
+ ).flatMap((item) => {
203
+ const file = item.slice(3)
204
+ if (!file) return []
205
+ const code = item.slice(0, 2)
206
+ return [{ file, code, status: kind(code) } satisfies Item]
207
+ })
208
+ })
209
+
210
+ const diff = Effect.fn("Git.diff")(function* (cwd: string, ref: string) {
211
+ const list = nuls(
212
+ yield* text(["diff", "--no-ext-diff", "--no-renames", "--name-status", "-z", ref, "--", "."], { cwd }),
213
+ )
214
+ return list.flatMap((code, idx) => {
215
+ if (idx % 2 !== 0) return []
216
+ const file = list[idx + 1]
217
+ if (!code || !file) return []
218
+ return [{ file, code, status: kind(code) } satisfies Item]
219
+ })
220
+ })
221
+
222
+ const stats = Effect.fn("Git.stats")(function* (cwd: string, ref: string) {
223
+ return nuls(
224
+ yield* text(["diff", "--no-ext-diff", "--no-renames", "--numstat", "-z", ref, "--", "."], { cwd }),
225
+ ).flatMap((item) => {
226
+ const a = item.indexOf("\t")
227
+ const b = item.indexOf("\t", a + 1)
228
+ if (a === -1 || b === -1) return []
229
+ const file = item.slice(b + 1)
230
+ if (!file) return []
231
+ const adds = item.slice(0, a)
232
+ const dels = item.slice(a + 1, b)
233
+ const additions = adds === "-" ? 0 : Number.parseInt(adds || "0", 10)
234
+ const deletions = dels === "-" ? 0 : Number.parseInt(dels || "0", 10)
235
+ return [
236
+ {
237
+ file,
238
+ additions: Number.isFinite(additions) ? additions : 0,
239
+ deletions: Number.isFinite(deletions) ? deletions : 0,
240
+ } satisfies Stat,
241
+ ]
242
+ })
243
+ })
244
+
245
+ return Service.of({
246
+ run,
247
+ branch,
248
+ prefix,
249
+ defaultBranch,
250
+ hasHead,
251
+ mergeBase,
252
+ show,
253
+ status,
254
+ diff,
255
+ stats,
256
+ })
257
+ }),
258
+ )
259
+
260
+ export const defaultLayer = layer.pipe(Layer.provide(CrossSpawnSpawner.defaultLayer))
261
+
262
+ const { runPromise } = makeRuntime(Service, defaultLayer)
263
+
264
+ export async function run(args: string[], opts: Options) {
265
+ return runPromise((git) => git.run(args, opts))
266
+ }
267
+
268
+ export async function branch(cwd: string) {
269
+ return runPromise((git) => git.branch(cwd))
270
+ }
271
+
272
+ export async function prefix(cwd: string) {
273
+ return runPromise((git) => git.prefix(cwd))
274
+ }
275
+
276
+ export async function defaultBranch(cwd: string) {
277
+ return runPromise((git) => git.defaultBranch(cwd))
278
+ }
279
+
280
+ export async function hasHead(cwd: string) {
281
+ return runPromise((git) => git.hasHead(cwd))
282
+ }
283
+
284
+ export async function mergeBase(cwd: string, base: string, head?: string) {
285
+ return runPromise((git) => git.mergeBase(cwd, base, head))
286
+ }
287
+
288
+ export async function show(cwd: string, ref: string, file: string, prefix?: string) {
289
+ return runPromise((git) => git.show(cwd, ref, file, prefix))
290
+ }
291
+
292
+ export async function status(cwd: string) {
293
+ return runPromise((git) => git.status(cwd))
294
+ }
295
+
296
+ export async function diff(cwd: string, ref: string) {
297
+ return runPromise((git) => git.diff(cwd, ref))
298
+ }
299
+
300
+ export async function stats(cwd: string, ref: string) {
301
+ return runPromise((git) => git.stats(cwd, ref))
302
+ }
303
+ }
@@ -0,0 +1,161 @@
1
+ import fs from "fs/promises"
2
+ import { xdgData, xdgCache, xdgConfig, xdgState } from "xdg-basedir"
3
+ import path from "path"
4
+ import os from "os"
5
+ import { Filesystem } from "../util/filesystem"
6
+
7
+ const app = "koro"
8
+ const opencodeApp = "opencode"
9
+
10
+ const data = path.join(xdgData!, app)
11
+ const cache = path.join(xdgCache!, app)
12
+ const config = path.join(xdgConfig!, app)
13
+ const state = path.join(xdgState!, app)
14
+
15
+ const opencodeData = path.join(xdgData!, opencodeApp)
16
+ const opencodeConfig = path.join(xdgConfig!, opencodeApp)
17
+ const opencodeState = path.join(xdgState!, opencodeApp)
18
+
19
+ export namespace Global {
20
+ export const Path = {
21
+ get home() {
22
+ return process.env.OPENCODE_TEST_HOME || os.homedir()
23
+ },
24
+ data,
25
+ bin: path.join(cache, "bin"),
26
+ log: path.join(data, "log"),
27
+ cache,
28
+ config,
29
+ state,
30
+ }
31
+ }
32
+
33
+ async function copyDir(src: string, dest: string) {
34
+ await fs.mkdir(dest, { recursive: true })
35
+ const entries = await fs.readdir(src, { withFileTypes: true })
36
+ for (const entry of entries) {
37
+ const srcPath = path.join(src, entry.name)
38
+ const destPath = path.join(dest, entry.name)
39
+ if (entry.isDirectory()) {
40
+ await copyDir(srcPath, destPath)
41
+ } else {
42
+ await fs.copyFile(srcPath, destPath)
43
+ }
44
+ }
45
+ }
46
+
47
+ async function migrateFromOpencode() {
48
+ const migratedFlag = path.join(data, ".migrated-from-opencode")
49
+ const exists = await Filesystem.exists(migratedFlag)
50
+ if (exists) return
51
+
52
+ let migrated = false
53
+
54
+ // Migrate global config: plugins, skills, AGENTS.md, tui.json
55
+ try {
56
+ const opencodeConfigExists = await Filesystem.exists(opencodeConfig)
57
+ if (opencodeConfigExists) {
58
+ await fs.mkdir(config, { recursive: true })
59
+
60
+ // Copy plugins
61
+ const pluginsSrc = path.join(opencodeConfig, "plugins")
62
+ const pluginsDest = path.join(config, "plugins")
63
+ if (await Filesystem.exists(pluginsSrc)) {
64
+ await copyDir(pluginsSrc, pluginsDest)
65
+ migrated = true
66
+ }
67
+
68
+ // Copy skills
69
+ const skillsSrc = path.join(opencodeConfig, "skills")
70
+ const skillsDest = path.join(config, "skills")
71
+ if (await Filesystem.exists(skillsSrc)) {
72
+ await copyDir(skillsSrc, skillsDest)
73
+ migrated = true
74
+ }
75
+
76
+ // Copy AGENTS.md
77
+ const agentsSrc = path.join(opencodeConfig, "AGENTS.md")
78
+ const agentsDest = path.join(config, "AGENTS.md")
79
+ if ((await Filesystem.exists(agentsSrc)) && !(await Filesystem.exists(agentsDest))) {
80
+ await fs.copyFile(agentsSrc, agentsDest)
81
+ migrated = true
82
+ }
83
+
84
+ // Copy tui.json
85
+ const tuiSrc = path.join(opencodeConfig, "tui.json")
86
+ const tuiDest = path.join(config, "tui.json")
87
+ if ((await Filesystem.exists(tuiSrc)) && !(await Filesystem.exists(tuiDest))) {
88
+ await fs.copyFile(tuiSrc, tuiDest)
89
+ migrated = true
90
+ }
91
+ }
92
+ } catch {
93
+ // OpenCode config doesn't exist or can't be read — that's fine
94
+ }
95
+
96
+ // Migrate state: model.json (recent models)
97
+ try {
98
+ const modelSrc = path.join(opencodeState, "model.json")
99
+ const modelDest = path.join(state, "model.json")
100
+ if ((await Filesystem.exists(modelSrc)) && !(await Filesystem.exists(modelDest))) {
101
+ await fs.mkdir(state, { recursive: true })
102
+ await fs.copyFile(modelSrc, modelDest)
103
+ migrated = true
104
+ }
105
+ } catch {
106
+ // OpenCode state doesn't exist — that's fine
107
+ }
108
+
109
+ // Migrate project-level configs: scan home directory for projects with .opencode/
110
+ try {
111
+ const home = os.homedir()
112
+ const projects = await fs.readdir(home, { withFileTypes: true })
113
+ for (const project of projects) {
114
+ if (!project.isDirectory()) continue
115
+ const opencodeDir = path.join(home, project.name, ".opencode")
116
+ if (!(await Filesystem.exists(opencodeDir))) continue
117
+
118
+ const koroDir = path.join(home, project.name, ".koro")
119
+ if (await Filesystem.exists(koroDir)) continue
120
+
121
+ // Copy .opencode contents to .koro
122
+ await copyDir(opencodeDir, koroDir)
123
+ migrated = true
124
+ }
125
+ } catch {
126
+ // Can't scan home directory — that's fine
127
+ }
128
+
129
+ // Always set the flag after first run — prevents re-checking on every start
130
+ await Filesystem.write(migratedFlag, "migrated")
131
+ }
132
+
133
+ await Promise.all([
134
+ fs.mkdir(Global.Path.data, { recursive: true }),
135
+ fs.mkdir(Global.Path.config, { recursive: true }),
136
+ fs.mkdir(Global.Path.state, { recursive: true }),
137
+ fs.mkdir(Global.Path.log, { recursive: true }),
138
+ fs.mkdir(Global.Path.bin, { recursive: true }),
139
+ ])
140
+
141
+ // Migrate from OpenCode on first run
142
+ await migrateFromOpencode()
143
+
144
+ const CACHE_VERSION = "21"
145
+
146
+ const version = await Filesystem.readText(path.join(Global.Path.cache, "version")).catch(() => "0")
147
+
148
+ if (version !== CACHE_VERSION) {
149
+ try {
150
+ const contents = await fs.readdir(Global.Path.cache)
151
+ await Promise.all(
152
+ contents.map((item) =>
153
+ fs.rm(path.join(Global.Path.cache, item), {
154
+ recursive: true,
155
+ force: true,
156
+ }),
157
+ ),
158
+ )
159
+ } catch (e) {}
160
+ await Filesystem.write(path.join(Global.Path.cache, "version"), CACHE_VERSION)
161
+ }
package/src/id/id.ts ADDED
@@ -0,0 +1,85 @@
1
+ import z from "zod"
2
+ import { randomBytes } from "crypto"
3
+
4
+ export namespace Identifier {
5
+ const prefixes = {
6
+ event: "evt",
7
+ session: "ses",
8
+ message: "msg",
9
+ permission: "per",
10
+ question: "que",
11
+ user: "usr",
12
+ part: "prt",
13
+ pty: "pty",
14
+ tool: "tool",
15
+ workspace: "wrk",
16
+ } as const
17
+
18
+ export function schema(prefix: keyof typeof prefixes) {
19
+ return z.string().startsWith(prefixes[prefix])
20
+ }
21
+
22
+ const LENGTH = 26
23
+
24
+ // State for monotonic ID generation
25
+ let lastTimestamp = 0
26
+ let counter = 0
27
+
28
+ export function ascending(prefix: keyof typeof prefixes, given?: string) {
29
+ return generateID(prefix, false, given)
30
+ }
31
+
32
+ export function descending(prefix: keyof typeof prefixes, given?: string) {
33
+ return generateID(prefix, true, given)
34
+ }
35
+
36
+ function generateID(prefix: keyof typeof prefixes, descending: boolean, given?: string): string {
37
+ if (!given) {
38
+ return create(prefix, descending)
39
+ }
40
+
41
+ if (!given.startsWith(prefixes[prefix])) {
42
+ throw new Error(`ID ${given} does not start with ${prefixes[prefix]}`)
43
+ }
44
+ return given
45
+ }
46
+
47
+ function randomBase62(length: number): string {
48
+ const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
49
+ let result = ""
50
+ const bytes = randomBytes(length)
51
+ for (let i = 0; i < length; i++) {
52
+ result += chars[bytes[i] % 62]
53
+ }
54
+ return result
55
+ }
56
+
57
+ export function create(prefix: keyof typeof prefixes, descending: boolean, timestamp?: number): string {
58
+ const currentTimestamp = timestamp ?? Date.now()
59
+
60
+ if (currentTimestamp !== lastTimestamp) {
61
+ lastTimestamp = currentTimestamp
62
+ counter = 0
63
+ }
64
+ counter++
65
+
66
+ let now = BigInt(currentTimestamp) * BigInt(0x1000) + BigInt(counter)
67
+
68
+ now = descending ? ~now : now
69
+
70
+ const timeBytes = Buffer.alloc(6)
71
+ for (let i = 0; i < 6; i++) {
72
+ timeBytes[i] = Number((now >> BigInt(40 - 8 * i)) & BigInt(0xff))
73
+ }
74
+
75
+ return prefixes[prefix] + "_" + timeBytes.toString("hex") + randomBase62(LENGTH - 12)
76
+ }
77
+
78
+ /** Extract timestamp from an ascending ID. Does not work with descending IDs. */
79
+ export function timestamp(id: string): number {
80
+ const prefix = id.split("_")[0]
81
+ const hex = id.slice(prefix.length + 1, prefix.length + 13)
82
+ const encoded = BigInt("0x" + hex)
83
+ return Number(encoded / BigInt(0x1000))
84
+ }
85
+ }
@@ -0,0 +1,74 @@
1
+ import { BusEvent } from "@/bus/bus-event"
2
+ import { Bus } from "@/bus"
3
+ import z from "zod"
4
+ import { NamedError } from "@opencode-ai/util/error"
5
+ import { Log } from "../util/log"
6
+ import { Process } from "@/util/process"
7
+
8
+ const SUPPORTED_IDES = [
9
+ { name: "Windsurf" as const, cmd: "windsurf" },
10
+ { name: "Visual Studio Code - Insiders" as const, cmd: "code-insiders" },
11
+ { name: "Visual Studio Code" as const, cmd: "code" },
12
+ { name: "Cursor" as const, cmd: "cursor" },
13
+ { name: "VSCodium" as const, cmd: "codium" },
14
+ ]
15
+
16
+ export namespace Ide {
17
+ const log = Log.create({ service: "ide" })
18
+
19
+ export const Event = {
20
+ Installed: BusEvent.define(
21
+ "ide.installed",
22
+ z.object({
23
+ ide: z.string(),
24
+ }),
25
+ ),
26
+ }
27
+
28
+ export const AlreadyInstalledError = NamedError.create("AlreadyInstalledError", z.object({}))
29
+
30
+ export const InstallFailedError = NamedError.create(
31
+ "InstallFailedError",
32
+ z.object({
33
+ stderr: z.string(),
34
+ }),
35
+ )
36
+
37
+ export function ide() {
38
+ if (process.env["TERM_PROGRAM"] === "vscode") {
39
+ const v = process.env["GIT_ASKPASS"]
40
+ for (const ide of SUPPORTED_IDES) {
41
+ if (v?.includes(ide.name)) return ide.name
42
+ }
43
+ }
44
+ return "unknown"
45
+ }
46
+
47
+ export function alreadyInstalled() {
48
+ return process.env["OPENCODE_CALLER"] === "vscode" || process.env["OPENCODE_CALLER"] === "vscode-insiders"
49
+ }
50
+
51
+ export async function install(ide: (typeof SUPPORTED_IDES)[number]["name"]) {
52
+ const cmd = SUPPORTED_IDES.find((i) => i.name === ide)?.cmd
53
+ if (!cmd) throw new Error(`Unknown IDE: ${ide}`)
54
+
55
+ const p = await Process.run([cmd, "--install-extension", "sst-dev.opencode"], {
56
+ nothrow: true,
57
+ })
58
+ const stdout = p.stdout.toString()
59
+ const stderr = p.stderr.toString()
60
+
61
+ log.info("installed", {
62
+ ide,
63
+ stdout,
64
+ stderr,
65
+ })
66
+
67
+ if (p.code !== 0) {
68
+ throw new InstallFailedError({ stderr })
69
+ }
70
+ if (stdout.includes("already installed")) {
71
+ throw new AlreadyInstalledError({})
72
+ }
73
+ }
74
+ }