@myrialabs/clopen 0.0.1

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 (456) hide show
  1. package/.env.example +6 -0
  2. package/.github/workflows/release.yml +60 -0
  3. package/.github/workflows/test.yml +40 -0
  4. package/CONTRIBUTING.md +499 -0
  5. package/LICENSE +21 -0
  6. package/README.md +209 -0
  7. package/backend/index.ts +156 -0
  8. package/backend/lib/chat/helpers.ts +42 -0
  9. package/backend/lib/chat/index.ts +2 -0
  10. package/backend/lib/chat/stream-manager.ts +1126 -0
  11. package/backend/lib/database/README.md +77 -0
  12. package/backend/lib/database/index.ts +119 -0
  13. package/backend/lib/database/migrations/001_create_projects_table.ts +31 -0
  14. package/backend/lib/database/migrations/002_create_chat_sessions_table.ts +33 -0
  15. package/backend/lib/database/migrations/003_create_messages_table.ts +32 -0
  16. package/backend/lib/database/migrations/004_create_prompt_templates_table.ts +34 -0
  17. package/backend/lib/database/migrations/005_create_settings_table.ts +24 -0
  18. package/backend/lib/database/migrations/006_add_user_to_messages.ts +58 -0
  19. package/backend/lib/database/migrations/007_create_stream_states_table.ts +41 -0
  20. package/backend/lib/database/migrations/008_create_message_snapshots_table.ts +62 -0
  21. package/backend/lib/database/migrations/009_add_delta_snapshot_fields.ts +41 -0
  22. package/backend/lib/database/migrations/010_add_soft_delete_and_branch_support.ts +70 -0
  23. package/backend/lib/database/migrations/011_git_like_commit_graph.ts +156 -0
  24. package/backend/lib/database/migrations/012_add_file_change_statistics.ts +41 -0
  25. package/backend/lib/database/migrations/013_checkpoint_tree_state.ts +118 -0
  26. package/backend/lib/database/migrations/014_add_engine_to_sessions.ts +18 -0
  27. package/backend/lib/database/migrations/015_add_model_to_sessions.ts +18 -0
  28. package/backend/lib/database/migrations/016_create_user_projects_table.ts +34 -0
  29. package/backend/lib/database/migrations/017_add_current_session_to_user_projects.ts +32 -0
  30. package/backend/lib/database/migrations/018_create_claude_accounts_table.ts +24 -0
  31. package/backend/lib/database/migrations/019_add_claude_account_to_sessions.ts +18 -0
  32. package/backend/lib/database/migrations/020_add_snapshot_tree_hash.ts +32 -0
  33. package/backend/lib/database/migrations/021_drop_prompt_templates_table.ts +33 -0
  34. package/backend/lib/database/migrations/index.ts +154 -0
  35. package/backend/lib/database/queries/checkpoint-queries.ts +87 -0
  36. package/backend/lib/database/queries/engine-queries.ts +75 -0
  37. package/backend/lib/database/queries/index.ts +9 -0
  38. package/backend/lib/database/queries/message-queries.ts +472 -0
  39. package/backend/lib/database/queries/project-queries.ts +117 -0
  40. package/backend/lib/database/queries/session-queries.ts +271 -0
  41. package/backend/lib/database/queries/settings-queries.ts +34 -0
  42. package/backend/lib/database/queries/snapshot-queries.ts +326 -0
  43. package/backend/lib/database/queries/utils-queries.ts +59 -0
  44. package/backend/lib/database/seeders/index.ts +13 -0
  45. package/backend/lib/database/seeders/settings_seeder.ts +84 -0
  46. package/backend/lib/database/utils/connection.ts +174 -0
  47. package/backend/lib/database/utils/index.ts +4 -0
  48. package/backend/lib/database/utils/migration-runner.ts +118 -0
  49. package/backend/lib/database/utils/seeder-runner.ts +121 -0
  50. package/backend/lib/engine/adapters/claude/environment.ts +164 -0
  51. package/backend/lib/engine/adapters/claude/error-handler.ts +60 -0
  52. package/backend/lib/engine/adapters/claude/index.ts +1 -0
  53. package/backend/lib/engine/adapters/claude/path-utils.ts +38 -0
  54. package/backend/lib/engine/adapters/claude/stream.ts +177 -0
  55. package/backend/lib/engine/adapters/opencode/index.ts +2 -0
  56. package/backend/lib/engine/adapters/opencode/message-converter.ts +862 -0
  57. package/backend/lib/engine/adapters/opencode/server.ts +104 -0
  58. package/backend/lib/engine/adapters/opencode/stream.ts +755 -0
  59. package/backend/lib/engine/index.ts +196 -0
  60. package/backend/lib/engine/types.ts +58 -0
  61. package/backend/lib/files/file-operations.ts +478 -0
  62. package/backend/lib/files/file-reading.ts +308 -0
  63. package/backend/lib/files/file-watcher.ts +383 -0
  64. package/backend/lib/files/path-browsing.ts +382 -0
  65. package/backend/lib/git/git-executor.ts +88 -0
  66. package/backend/lib/git/git-parser.ts +411 -0
  67. package/backend/lib/git/git-service.ts +505 -0
  68. package/backend/lib/mcp/README.md +1144 -0
  69. package/backend/lib/mcp/config.ts +316 -0
  70. package/backend/lib/mcp/index.ts +35 -0
  71. package/backend/lib/mcp/project-context.ts +236 -0
  72. package/backend/lib/mcp/servers/browser-automation/actions.ts +156 -0
  73. package/backend/lib/mcp/servers/browser-automation/browser.ts +419 -0
  74. package/backend/lib/mcp/servers/browser-automation/index.ts +791 -0
  75. package/backend/lib/mcp/servers/browser-automation/inspection.ts +501 -0
  76. package/backend/lib/mcp/servers/helper.ts +143 -0
  77. package/backend/lib/mcp/servers/index.ts +45 -0
  78. package/backend/lib/mcp/servers/weather/get-temperature.ts +56 -0
  79. package/backend/lib/mcp/servers/weather/index.ts +31 -0
  80. package/backend/lib/mcp/stdio-server.ts +103 -0
  81. package/backend/lib/mcp/types.ts +65 -0
  82. package/backend/lib/preview/browser/browser-audio-capture.ts +86 -0
  83. package/backend/lib/preview/browser/browser-console-manager.ts +263 -0
  84. package/backend/lib/preview/browser/browser-dialog-handler.ts +222 -0
  85. package/backend/lib/preview/browser/browser-interaction-handler.ts +421 -0
  86. package/backend/lib/preview/browser/browser-mcp-control.ts +415 -0
  87. package/backend/lib/preview/browser/browser-native-ui-handler.ts +512 -0
  88. package/backend/lib/preview/browser/browser-navigation-tracker.ts +104 -0
  89. package/backend/lib/preview/browser/browser-pool.ts +357 -0
  90. package/backend/lib/preview/browser/browser-preview-service.ts +882 -0
  91. package/backend/lib/preview/browser/browser-tab-manager.ts +935 -0
  92. package/backend/lib/preview/browser/browser-video-capture.ts +695 -0
  93. package/backend/lib/preview/browser/scripts/audio-stream.ts +292 -0
  94. package/backend/lib/preview/browser/scripts/cursor-tracking.ts +85 -0
  95. package/backend/lib/preview/browser/scripts/video-stream.ts +438 -0
  96. package/backend/lib/preview/browser/types.ts +359 -0
  97. package/backend/lib/preview/index.ts +24 -0
  98. package/backend/lib/project/index.ts +2 -0
  99. package/backend/lib/project/status-manager.ts +182 -0
  100. package/backend/lib/shared/index.ts +2 -0
  101. package/backend/lib/shared/port-utils.ts +25 -0
  102. package/backend/lib/shared/process-manager.ts +281 -0
  103. package/backend/lib/snapshot/blob-store.ts +227 -0
  104. package/backend/lib/snapshot/gitignore.ts +307 -0
  105. package/backend/lib/snapshot/helpers.ts +397 -0
  106. package/backend/lib/snapshot/snapshot-service.ts +483 -0
  107. package/backend/lib/terminal/helpers.ts +14 -0
  108. package/backend/lib/terminal/index.ts +8 -0
  109. package/backend/lib/terminal/pty-manager.ts +4 -0
  110. package/backend/lib/terminal/pty-session-manager.ts +387 -0
  111. package/backend/lib/terminal/shell-utils.ts +313 -0
  112. package/backend/lib/terminal/stream-manager.ts +293 -0
  113. package/backend/lib/tunnel/global-tunnel-manager.ts +243 -0
  114. package/backend/lib/tunnel/project-tunnel-manager.ts +311 -0
  115. package/backend/lib/user/helpers.ts +87 -0
  116. package/backend/lib/utils/ws.ts +944 -0
  117. package/backend/lib/vite-dev.ts +353 -0
  118. package/backend/middleware/cors.ts +15 -0
  119. package/backend/middleware/error-handler.ts +49 -0
  120. package/backend/middleware/logger.ts +9 -0
  121. package/backend/types/api.ts +24 -0
  122. package/backend/ws/README.md +1505 -0
  123. package/backend/ws/chat/background.ts +198 -0
  124. package/backend/ws/chat/index.ts +21 -0
  125. package/backend/ws/chat/stream.ts +707 -0
  126. package/backend/ws/engine/claude/accounts.ts +401 -0
  127. package/backend/ws/engine/claude/index.ts +13 -0
  128. package/backend/ws/engine/claude/status.ts +43 -0
  129. package/backend/ws/engine/index.ts +14 -0
  130. package/backend/ws/engine/opencode/index.ts +11 -0
  131. package/backend/ws/engine/opencode/status.ts +30 -0
  132. package/backend/ws/engine/utils.ts +36 -0
  133. package/backend/ws/files/index.ts +30 -0
  134. package/backend/ws/files/read.ts +189 -0
  135. package/backend/ws/files/search.ts +453 -0
  136. package/backend/ws/files/watch.ts +124 -0
  137. package/backend/ws/files/write.ts +143 -0
  138. package/backend/ws/git/branch.ts +106 -0
  139. package/backend/ws/git/commit.ts +39 -0
  140. package/backend/ws/git/conflict.ts +68 -0
  141. package/backend/ws/git/diff.ts +69 -0
  142. package/backend/ws/git/index.ts +24 -0
  143. package/backend/ws/git/log.ts +41 -0
  144. package/backend/ws/git/remote.ts +214 -0
  145. package/backend/ws/git/staging.ts +84 -0
  146. package/backend/ws/git/status.ts +90 -0
  147. package/backend/ws/index.ts +69 -0
  148. package/backend/ws/mcp/index.ts +61 -0
  149. package/backend/ws/messages/crud.ts +74 -0
  150. package/backend/ws/messages/index.ts +14 -0
  151. package/backend/ws/preview/browser/cleanup.ts +129 -0
  152. package/backend/ws/preview/browser/console.ts +114 -0
  153. package/backend/ws/preview/browser/interact.ts +513 -0
  154. package/backend/ws/preview/browser/mcp.ts +129 -0
  155. package/backend/ws/preview/browser/native-ui.ts +235 -0
  156. package/backend/ws/preview/browser/stats.ts +55 -0
  157. package/backend/ws/preview/browser/tab-info.ts +126 -0
  158. package/backend/ws/preview/browser/tab.ts +166 -0
  159. package/backend/ws/preview/browser/webcodecs.ts +293 -0
  160. package/backend/ws/preview/index.ts +146 -0
  161. package/backend/ws/projects/crud.ts +113 -0
  162. package/backend/ws/projects/index.ts +25 -0
  163. package/backend/ws/projects/presence.ts +46 -0
  164. package/backend/ws/projects/status.ts +116 -0
  165. package/backend/ws/sessions/crud.ts +327 -0
  166. package/backend/ws/sessions/index.ts +33 -0
  167. package/backend/ws/settings/crud.ts +112 -0
  168. package/backend/ws/settings/index.ts +14 -0
  169. package/backend/ws/snapshot/index.ts +17 -0
  170. package/backend/ws/snapshot/restore.ts +173 -0
  171. package/backend/ws/snapshot/timeline.ts +141 -0
  172. package/backend/ws/system/index.ts +14 -0
  173. package/backend/ws/system/operations.ts +49 -0
  174. package/backend/ws/terminal/index.ts +40 -0
  175. package/backend/ws/terminal/persistence.ts +153 -0
  176. package/backend/ws/terminal/session.ts +382 -0
  177. package/backend/ws/terminal/stream.ts +79 -0
  178. package/backend/ws/tunnel/index.ts +14 -0
  179. package/backend/ws/tunnel/operations.ts +91 -0
  180. package/backend/ws/types.ts +20 -0
  181. package/backend/ws/user/crud.ts +156 -0
  182. package/backend/ws/user/index.ts +14 -0
  183. package/bin/clopen.ts +307 -0
  184. package/bun.lock +1352 -0
  185. package/frontend/App.svelte +34 -0
  186. package/frontend/app.css +313 -0
  187. package/frontend/lib/app-environment.ts +10 -0
  188. package/frontend/lib/components/chat/ChatInterface.svelte +407 -0
  189. package/frontend/lib/components/chat/formatters/ErrorMessage.svelte +57 -0
  190. package/frontend/lib/components/chat/formatters/MessageFormatter.svelte +224 -0
  191. package/frontend/lib/components/chat/formatters/TextMessage.svelte +395 -0
  192. package/frontend/lib/components/chat/formatters/Tools.svelte +70 -0
  193. package/frontend/lib/components/chat/formatters/index.ts +3 -0
  194. package/frontend/lib/components/chat/input/ChatInput.svelte +421 -0
  195. package/frontend/lib/components/chat/input/components/ChatInputActions.svelte +78 -0
  196. package/frontend/lib/components/chat/input/components/DragDropOverlay.svelte +30 -0
  197. package/frontend/lib/components/chat/input/components/EditModeIndicator.svelte +33 -0
  198. package/frontend/lib/components/chat/input/components/EngineModelPicker.svelte +619 -0
  199. package/frontend/lib/components/chat/input/components/FileAttachmentPreview.svelte +48 -0
  200. package/frontend/lib/components/chat/input/components/LoadingIndicator.svelte +31 -0
  201. package/frontend/lib/components/chat/input/composables/use-animations.svelte.ts +201 -0
  202. package/frontend/lib/components/chat/input/composables/use-chat-actions.svelte.ts +148 -0
  203. package/frontend/lib/components/chat/input/composables/use-file-handling.svelte.ts +216 -0
  204. package/frontend/lib/components/chat/input/composables/use-input-state.svelte.ts +357 -0
  205. package/frontend/lib/components/chat/input/composables/use-textarea-resize.svelte.ts +57 -0
  206. package/frontend/lib/components/chat/message/ChatMessage.svelte +478 -0
  207. package/frontend/lib/components/chat/message/ChatMessages.svelte +541 -0
  208. package/frontend/lib/components/chat/message/DateSeparator.svelte +86 -0
  209. package/frontend/lib/components/chat/message/MessageBubble.svelte +86 -0
  210. package/frontend/lib/components/chat/message/MessageHeader.svelte +157 -0
  211. package/frontend/lib/components/chat/modal/DebugModal.svelte +59 -0
  212. package/frontend/lib/components/chat/modal/TokenUsageModal.svelte +124 -0
  213. package/frontend/lib/components/chat/shared/index.ts +2 -0
  214. package/frontend/lib/components/chat/shared/utils.ts +116 -0
  215. package/frontend/lib/components/chat/tools/BashOutputTool.svelte +35 -0
  216. package/frontend/lib/components/chat/tools/BashTool.svelte +46 -0
  217. package/frontend/lib/components/chat/tools/CustomMcpTool.svelte +139 -0
  218. package/frontend/lib/components/chat/tools/EditTool.svelte +48 -0
  219. package/frontend/lib/components/chat/tools/ExitPlanModeTool.svelte +32 -0
  220. package/frontend/lib/components/chat/tools/GlobTool.svelte +51 -0
  221. package/frontend/lib/components/chat/tools/GrepTool.svelte +90 -0
  222. package/frontend/lib/components/chat/tools/KillShellTool.svelte +26 -0
  223. package/frontend/lib/components/chat/tools/ListMcpResourcesTool.svelte +31 -0
  224. package/frontend/lib/components/chat/tools/NotebookEditTool.svelte +38 -0
  225. package/frontend/lib/components/chat/tools/ReadMcpResourceTool.svelte +34 -0
  226. package/frontend/lib/components/chat/tools/ReadTool.svelte +41 -0
  227. package/frontend/lib/components/chat/tools/TaskTool.svelte +64 -0
  228. package/frontend/lib/components/chat/tools/TodoWriteTool.svelte +75 -0
  229. package/frontend/lib/components/chat/tools/WebFetchTool.svelte +35 -0
  230. package/frontend/lib/components/chat/tools/WebSearchTool.svelte +84 -0
  231. package/frontend/lib/components/chat/tools/WriteTool.svelte +33 -0
  232. package/frontend/lib/components/chat/tools/components/CodeBlock.svelte +79 -0
  233. package/frontend/lib/components/chat/tools/components/DiffBlock.svelte +408 -0
  234. package/frontend/lib/components/chat/tools/components/FileHeader.svelte +45 -0
  235. package/frontend/lib/components/chat/tools/components/InfoLine.svelte +19 -0
  236. package/frontend/lib/components/chat/tools/components/StatsBadges.svelte +27 -0
  237. package/frontend/lib/components/chat/tools/components/TerminalCommand.svelte +54 -0
  238. package/frontend/lib/components/chat/tools/components/index.ts +7 -0
  239. package/frontend/lib/components/chat/tools/index.ts +26 -0
  240. package/frontend/lib/components/chat/widgets/FloatingTodoList.svelte +249 -0
  241. package/frontend/lib/components/chat/widgets/TokenUsage.svelte +78 -0
  242. package/frontend/lib/components/checkpoint/TimelineModal.svelte +391 -0
  243. package/frontend/lib/components/checkpoint/timeline/TimelineEdge.svelte +26 -0
  244. package/frontend/lib/components/checkpoint/timeline/TimelineGraph.svelte +87 -0
  245. package/frontend/lib/components/checkpoint/timeline/TimelineNode.svelte +108 -0
  246. package/frontend/lib/components/checkpoint/timeline/TimelineVersionGroup.svelte +59 -0
  247. package/frontend/lib/components/checkpoint/timeline/animation.ts +168 -0
  248. package/frontend/lib/components/checkpoint/timeline/config.ts +44 -0
  249. package/frontend/lib/components/checkpoint/timeline/graph-builder.ts +304 -0
  250. package/frontend/lib/components/checkpoint/timeline/types.ts +65 -0
  251. package/frontend/lib/components/checkpoint/timeline/utils.ts +53 -0
  252. package/frontend/lib/components/common/Alert.svelte +139 -0
  253. package/frontend/lib/components/common/AvatarBubble.svelte +56 -0
  254. package/frontend/lib/components/common/Button.svelte +71 -0
  255. package/frontend/lib/components/common/Card.svelte +102 -0
  256. package/frontend/lib/components/common/Checkbox.svelte +48 -0
  257. package/frontend/lib/components/common/Dialog.svelte +249 -0
  258. package/frontend/lib/components/common/FolderBrowser.svelte +843 -0
  259. package/frontend/lib/components/common/Icon.svelte +58 -0
  260. package/frontend/lib/components/common/Input.svelte +72 -0
  261. package/frontend/lib/components/common/Lightbox.svelte +233 -0
  262. package/frontend/lib/components/common/LoadingScreen.svelte +52 -0
  263. package/frontend/lib/components/common/LoadingSpinner.svelte +48 -0
  264. package/frontend/lib/components/common/Modal.svelte +177 -0
  265. package/frontend/lib/components/common/ModalProvider.svelte +28 -0
  266. package/frontend/lib/components/common/ModelSelector.svelte +110 -0
  267. package/frontend/lib/components/common/MonacoEditor.svelte +569 -0
  268. package/frontend/lib/components/common/NotificationToast.svelte +113 -0
  269. package/frontend/lib/components/common/PageTemplate.svelte +76 -0
  270. package/frontend/lib/components/common/ProjectUserAvatars.svelte +79 -0
  271. package/frontend/lib/components/common/Select.svelte +98 -0
  272. package/frontend/lib/components/common/Textarea.svelte +80 -0
  273. package/frontend/lib/components/common/ThemeToggle.svelte +44 -0
  274. package/frontend/lib/components/common/lucide-icons.ts +1642 -0
  275. package/frontend/lib/components/common/material-icons.ts +1082 -0
  276. package/frontend/lib/components/common/xterm/XTerm.svelte +796 -0
  277. package/frontend/lib/components/common/xterm/index.ts +16 -0
  278. package/frontend/lib/components/common/xterm/terminal-config.ts +68 -0
  279. package/frontend/lib/components/common/xterm/types.ts +31 -0
  280. package/frontend/lib/components/common/xterm/xterm-service.ts +353 -0
  281. package/frontend/lib/components/files/FileNode.svelte +384 -0
  282. package/frontend/lib/components/files/FileTree.svelte +681 -0
  283. package/frontend/lib/components/files/FileViewer.svelte +728 -0
  284. package/frontend/lib/components/files/SearchResults.svelte +303 -0
  285. package/frontend/lib/components/git/BranchManager.svelte +458 -0
  286. package/frontend/lib/components/git/ChangesSection.svelte +107 -0
  287. package/frontend/lib/components/git/CommitForm.svelte +76 -0
  288. package/frontend/lib/components/git/ConflictResolver.svelte +158 -0
  289. package/frontend/lib/components/git/DiffViewer.svelte +364 -0
  290. package/frontend/lib/components/git/FileChangeItem.svelte +97 -0
  291. package/frontend/lib/components/git/GitButton.svelte +33 -0
  292. package/frontend/lib/components/git/GitLog.svelte +361 -0
  293. package/frontend/lib/components/git/GitModal.svelte +80 -0
  294. package/frontend/lib/components/history/HistoryModal.svelte +563 -0
  295. package/frontend/lib/components/history/HistoryView.svelte +615 -0
  296. package/frontend/lib/components/index.ts +34 -0
  297. package/frontend/lib/components/preview/browser/BrowserPreview.svelte +549 -0
  298. package/frontend/lib/components/preview/browser/components/Canvas.svelte +1058 -0
  299. package/frontend/lib/components/preview/browser/components/ConsolePanel.svelte +757 -0
  300. package/frontend/lib/components/preview/browser/components/Container.svelte +450 -0
  301. package/frontend/lib/components/preview/browser/components/ContextMenu.svelte +236 -0
  302. package/frontend/lib/components/preview/browser/components/SelectDropdown.svelte +224 -0
  303. package/frontend/lib/components/preview/browser/components/Toolbar.svelte +339 -0
  304. package/frontend/lib/components/preview/browser/components/VirtualCursor.svelte +36 -0
  305. package/frontend/lib/components/preview/browser/core/cleanup.svelte.ts +155 -0
  306. package/frontend/lib/components/preview/browser/core/coordinator.svelte.ts +837 -0
  307. package/frontend/lib/components/preview/browser/core/interactions.svelte.ts +113 -0
  308. package/frontend/lib/components/preview/browser/core/mcp-handlers.svelte.ts +296 -0
  309. package/frontend/lib/components/preview/browser/core/native-ui-handlers.svelte.ts +391 -0
  310. package/frontend/lib/components/preview/browser/core/stream-handler.svelte.ts +231 -0
  311. package/frontend/lib/components/preview/browser/core/tab-manager.svelte.ts +210 -0
  312. package/frontend/lib/components/preview/browser/core/tab-operations.svelte.ts +239 -0
  313. package/frontend/lib/components/preview/index.ts +2 -0
  314. package/frontend/lib/components/settings/SettingsModal.svelte +235 -0
  315. package/frontend/lib/components/settings/SettingsView.svelte +36 -0
  316. package/frontend/lib/components/settings/appearance/AppearanceSettings.svelte +51 -0
  317. package/frontend/lib/components/settings/appearance/LayoutPresetSettings.svelte +160 -0
  318. package/frontend/lib/components/settings/appearance/LayoutPreview.svelte +76 -0
  319. package/frontend/lib/components/settings/engines/AIEnginesSettings.svelte +917 -0
  320. package/frontend/lib/components/settings/general/AdvancedSettings.svelte +187 -0
  321. package/frontend/lib/components/settings/general/DataManagementSettings.svelte +203 -0
  322. package/frontend/lib/components/settings/general/GeneralSettings.svelte +10 -0
  323. package/frontend/lib/components/settings/model/ModelSettings.svelte +357 -0
  324. package/frontend/lib/components/settings/notifications/NotificationSettings.svelte +205 -0
  325. package/frontend/lib/components/settings/user/UserSettings.svelte +197 -0
  326. package/frontend/lib/components/terminal/Terminal.svelte +368 -0
  327. package/frontend/lib/components/terminal/TerminalTabs.svelte +87 -0
  328. package/frontend/lib/components/terminal/TerminalView.svelte +55 -0
  329. package/frontend/lib/components/tunnel/TunnelActive.svelte +142 -0
  330. package/frontend/lib/components/tunnel/TunnelButton.svelte +54 -0
  331. package/frontend/lib/components/tunnel/TunnelInactive.svelte +284 -0
  332. package/frontend/lib/components/tunnel/TunnelModal.svelte +47 -0
  333. package/frontend/lib/components/tunnel/TunnelQRCode.svelte +49 -0
  334. package/frontend/lib/components/workspace/DesktopNavigator.svelte +382 -0
  335. package/frontend/lib/components/workspace/MobileNavigator.svelte +403 -0
  336. package/frontend/lib/components/workspace/PanelContainer.svelte +100 -0
  337. package/frontend/lib/components/workspace/PanelHeader.svelte +505 -0
  338. package/frontend/lib/components/workspace/ViewMenu.svelte +162 -0
  339. package/frontend/lib/components/workspace/WorkspaceLayout.svelte +169 -0
  340. package/frontend/lib/components/workspace/layout/DesktopLayout.svelte +15 -0
  341. package/frontend/lib/components/workspace/layout/MobileLayout.svelte +17 -0
  342. package/frontend/lib/components/workspace/layout/split-pane/Container.svelte +42 -0
  343. package/frontend/lib/components/workspace/layout/split-pane/Handle.svelte +85 -0
  344. package/frontend/lib/components/workspace/layout/split-pane/Layout.svelte +37 -0
  345. package/frontend/lib/components/workspace/panels/ChatPanel.svelte +274 -0
  346. package/frontend/lib/components/workspace/panels/FilesPanel.svelte +1261 -0
  347. package/frontend/lib/components/workspace/panels/GitPanel.svelte +1560 -0
  348. package/frontend/lib/components/workspace/panels/PreviewPanel.svelte +150 -0
  349. package/frontend/lib/components/workspace/panels/TerminalPanel.svelte +73 -0
  350. package/frontend/lib/constants/preview.ts +45 -0
  351. package/frontend/lib/services/chat/chat.service.ts +704 -0
  352. package/frontend/lib/services/chat/index.ts +7 -0
  353. package/frontend/lib/services/notification/global-stream-monitor.ts +86 -0
  354. package/frontend/lib/services/notification/index.ts +8 -0
  355. package/frontend/lib/services/notification/push.service.ts +144 -0
  356. package/frontend/lib/services/notification/sound.service.ts +127 -0
  357. package/frontend/lib/services/preview/browser/browser-console.service.ts +61 -0
  358. package/frontend/lib/services/preview/browser/browser-webcodecs.service.ts +1499 -0
  359. package/frontend/lib/services/preview/browser/mcp-integration.svelte.ts +67 -0
  360. package/frontend/lib/services/preview/index.ts +23 -0
  361. package/frontend/lib/services/project/index.ts +8 -0
  362. package/frontend/lib/services/project/status.service.ts +159 -0
  363. package/frontend/lib/services/snapshot/snapshot.service.ts +47 -0
  364. package/frontend/lib/services/terminal/background/index.ts +130 -0
  365. package/frontend/lib/services/terminal/background/session-restore.ts +274 -0
  366. package/frontend/lib/services/terminal/background/stream-manager.ts +286 -0
  367. package/frontend/lib/services/terminal/index.ts +14 -0
  368. package/frontend/lib/services/terminal/persistence.service.ts +260 -0
  369. package/frontend/lib/services/terminal/project.service.ts +953 -0
  370. package/frontend/lib/services/terminal/session.service.ts +364 -0
  371. package/frontend/lib/services/terminal/terminal.service.ts +369 -0
  372. package/frontend/lib/stores/core/app.svelte.ts +117 -0
  373. package/frontend/lib/stores/core/files.svelte.ts +73 -0
  374. package/frontend/lib/stores/core/presence.svelte.ts +48 -0
  375. package/frontend/lib/stores/core/projects.svelte.ts +317 -0
  376. package/frontend/lib/stores/core/sessions.svelte.ts +383 -0
  377. package/frontend/lib/stores/features/claude-accounts.svelte.ts +58 -0
  378. package/frontend/lib/stores/features/models.svelte.ts +89 -0
  379. package/frontend/lib/stores/features/settings.svelte.ts +87 -0
  380. package/frontend/lib/stores/features/terminal.svelte.ts +701 -0
  381. package/frontend/lib/stores/features/tunnel.svelte.ts +161 -0
  382. package/frontend/lib/stores/features/user.svelte.ts +96 -0
  383. package/frontend/lib/stores/ui/chat-input.svelte.ts +57 -0
  384. package/frontend/lib/stores/ui/chat-model.svelte.ts +61 -0
  385. package/frontend/lib/stores/ui/dialog.svelte.ts +59 -0
  386. package/frontend/lib/stores/ui/edit-mode.svelte.ts +214 -0
  387. package/frontend/lib/stores/ui/notification.svelte.ts +166 -0
  388. package/frontend/lib/stores/ui/settings-modal.svelte.ts +88 -0
  389. package/frontend/lib/stores/ui/theme.svelte.ts +179 -0
  390. package/frontend/lib/stores/ui/workspace.svelte.ts +754 -0
  391. package/frontend/lib/types/native-ui.ts +73 -0
  392. package/frontend/lib/utils/chat/date-separator.ts +39 -0
  393. package/frontend/lib/utils/chat/message-grouper.ts +219 -0
  394. package/frontend/lib/utils/chat/message-processor.ts +135 -0
  395. package/frontend/lib/utils/chat/tool-handler.ts +161 -0
  396. package/frontend/lib/utils/chat/virtual-scroll.svelte.ts +142 -0
  397. package/frontend/lib/utils/click-outside.ts +20 -0
  398. package/frontend/lib/utils/context-manager.ts +257 -0
  399. package/frontend/lib/utils/file-icon-mappings.ts +769 -0
  400. package/frontend/lib/utils/folder-icon-mappings.ts +1030 -0
  401. package/frontend/lib/utils/git-status.ts +68 -0
  402. package/frontend/lib/utils/platform.ts +113 -0
  403. package/frontend/lib/utils/port-check.ts +65 -0
  404. package/frontend/lib/utils/terminalFormatter.ts +207 -0
  405. package/frontend/lib/utils/theme.ts +6 -0
  406. package/frontend/lib/utils/tree-visualizer.ts +320 -0
  407. package/frontend/lib/utils/ws.ts +44 -0
  408. package/frontend/main.ts +13 -0
  409. package/index.html +70 -0
  410. package/package.json +111 -0
  411. package/scripts/generate-icons.ts +87 -0
  412. package/scripts/pre-publish-check.sh +142 -0
  413. package/scripts/setup-hooks.sh +134 -0
  414. package/scripts/validate-branch-name.sh +47 -0
  415. package/scripts/validate-commit-msg.sh +42 -0
  416. package/shared/constants/engines.ts +134 -0
  417. package/shared/types/database/connection.ts +16 -0
  418. package/shared/types/database/index.ts +6 -0
  419. package/shared/types/database/schema.ts +141 -0
  420. package/shared/types/engine/index.ts +45 -0
  421. package/shared/types/filesystem/index.ts +22 -0
  422. package/shared/types/git.ts +171 -0
  423. package/shared/types/messaging/index.ts +239 -0
  424. package/shared/types/messaging/tool.ts +526 -0
  425. package/shared/types/network/api.ts +18 -0
  426. package/shared/types/network/index.ts +5 -0
  427. package/shared/types/stores/app.ts +23 -0
  428. package/shared/types/stores/dialog.ts +21 -0
  429. package/shared/types/stores/index.ts +3 -0
  430. package/shared/types/stores/settings.ts +15 -0
  431. package/shared/types/terminal/index.ts +44 -0
  432. package/shared/types/ui/components.ts +61 -0
  433. package/shared/types/ui/icons.ts +23 -0
  434. package/shared/types/ui/index.ts +22 -0
  435. package/shared/types/ui/notifications.ts +14 -0
  436. package/shared/types/ui/theme.ts +12 -0
  437. package/shared/types/websocket/index.ts +43 -0
  438. package/shared/types/window.d.ts +13 -0
  439. package/shared/utils/anonymous-user.ts +168 -0
  440. package/shared/utils/async.ts +10 -0
  441. package/shared/utils/diff-calculator.ts +184 -0
  442. package/shared/utils/file-type-detection.ts +166 -0
  443. package/shared/utils/logger.ts +158 -0
  444. package/shared/utils/message-formatter.ts +79 -0
  445. package/shared/utils/path.ts +47 -0
  446. package/shared/utils/ws-client.ts +768 -0
  447. package/shared/utils/ws-server.ts +660 -0
  448. package/static/audio/notification.ogg +0 -0
  449. package/static/favicon.svg +8 -0
  450. package/static/fonts/dm-sans/dm-sans-italic-latin-ext.woff2 +0 -0
  451. package/static/fonts/dm-sans/dm-sans-italic-latin.woff2 +0 -0
  452. package/static/fonts/dm-sans/dm-sans-normal-latin-ext.woff2 +0 -0
  453. package/static/fonts/dm-sans/dm-sans-normal-latin.woff2 +0 -0
  454. package/static/fonts/dm-sans.css +96 -0
  455. package/svelte.config.js +20 -0
  456. package/vite.config.ts +33 -0
@@ -0,0 +1,505 @@
1
+ /**
2
+ * Git Service
3
+ * High-level git operations built on top of executor and parser
4
+ */
5
+
6
+ import { execGit, isGitRepo, getGitRoot } from './git-executor';
7
+ import {
8
+ parseStatus,
9
+ parseBranches,
10
+ parseAheadBehind,
11
+ parseDiff,
12
+ parseLog,
13
+ parseRemotes,
14
+ parseStashList,
15
+ parseConflictMarkers
16
+ } from './git-parser';
17
+ import type {
18
+ GitStatus,
19
+ GitBranchInfo,
20
+ GitFileDiff,
21
+ GitLogResult,
22
+ GitRemote,
23
+ GitStashEntry,
24
+ GitConflictFile
25
+ } from '$shared/types/git';
26
+ import { debug } from '$shared/utils/logger';
27
+
28
+ export class GitService {
29
+ /**
30
+ * Check if path is a git repo
31
+ */
32
+ async isRepo(cwd: string): Promise<boolean> {
33
+ return isGitRepo(cwd);
34
+ }
35
+
36
+ /**
37
+ * Get repository root
38
+ */
39
+ async getRoot(cwd: string): Promise<string | null> {
40
+ return getGitRoot(cwd);
41
+ }
42
+
43
+ /**
44
+ * Initialize a new git repository
45
+ */
46
+ async init(cwd: string, defaultBranch?: string): Promise<void> {
47
+ const args = ['init'];
48
+ if (defaultBranch) args.push('-b', defaultBranch);
49
+ const result = await execGit(args, cwd);
50
+ if (result.exitCode !== 0) {
51
+ throw new Error(`git init failed: ${result.stderr}`);
52
+ }
53
+ }
54
+
55
+ // ============================================
56
+ // Status
57
+ // ============================================
58
+
59
+ async getStatus(cwd: string): Promise<GitStatus> {
60
+ const result = await execGit(['status', '--porcelain=v1', '-u'], cwd);
61
+ if (result.exitCode !== 0) {
62
+ throw new Error(`git status failed: ${result.stderr}`);
63
+ }
64
+ return parseStatus(result.stdout);
65
+ }
66
+
67
+ // ============================================
68
+ // Staging
69
+ // ============================================
70
+
71
+ async stageFile(cwd: string, filePath: string): Promise<void> {
72
+ const result = await execGit(['add', '--', filePath], cwd);
73
+ if (result.exitCode !== 0) {
74
+ throw new Error(`git add failed: ${result.stderr}`);
75
+ }
76
+ }
77
+
78
+ async stageAll(cwd: string): Promise<void> {
79
+ const result = await execGit(['add', '-A'], cwd);
80
+ if (result.exitCode !== 0) {
81
+ throw new Error(`git add -A failed: ${result.stderr}`);
82
+ }
83
+ }
84
+
85
+ async unstageFile(cwd: string, filePath: string): Promise<void> {
86
+ // Try normal reset first, fall back to rm --cached for initial commit
87
+ const result = await execGit(['reset', 'HEAD', '--', filePath], cwd);
88
+ if (result.exitCode !== 0) {
89
+ const fallback = await execGit(['rm', '--cached', '--', filePath], cwd);
90
+ if (fallback.exitCode !== 0) {
91
+ throw new Error(`git unstage failed: ${result.stderr}`);
92
+ }
93
+ }
94
+ }
95
+
96
+ async unstageAll(cwd: string): Promise<void> {
97
+ // Try normal reset first, fall back to rm --cached for initial commit
98
+ const result = await execGit(['reset', 'HEAD'], cwd);
99
+ if (result.exitCode !== 0) {
100
+ const fallback = await execGit(['rm', '-r', '--cached', '.'], cwd);
101
+ if (fallback.exitCode !== 0) {
102
+ throw new Error(`git unstage all failed: ${result.stderr}`);
103
+ }
104
+ }
105
+ }
106
+
107
+ async discardFile(cwd: string, filePath: string): Promise<void> {
108
+ // Check if file is untracked
109
+ const statusResult = await execGit(['status', '--porcelain=v1', '--', filePath], cwd);
110
+ const statusLine = statusResult.stdout.trim();
111
+
112
+ if (statusLine.startsWith('??')) {
113
+ // Untracked file - delete it
114
+ const { unlink } = await import('node:fs/promises');
115
+ const { join } = await import('node:path');
116
+ await unlink(join(cwd, filePath));
117
+ } else {
118
+ // Tracked file - restore it
119
+ const result = await execGit(['checkout', '--', filePath], cwd);
120
+ if (result.exitCode !== 0) {
121
+ throw new Error(`git checkout failed: ${result.stderr}`);
122
+ }
123
+ }
124
+ }
125
+
126
+ async discardAll(cwd: string): Promise<void> {
127
+ // Restore tracked files
128
+ await execGit(['checkout', '--', '.'], cwd);
129
+ // Remove untracked files
130
+ await execGit(['clean', '-fd'], cwd);
131
+ }
132
+
133
+ // ============================================
134
+ // Commit
135
+ // ============================================
136
+
137
+ async commit(cwd: string, message: string): Promise<string> {
138
+ const result = await execGit(['commit', '-m', message], cwd);
139
+ if (result.exitCode !== 0) {
140
+ throw new Error(`git commit failed: ${result.stderr}`);
141
+ }
142
+ // Return the commit hash
143
+ const hashResult = await execGit(['rev-parse', 'HEAD'], cwd);
144
+ return hashResult.stdout.trim();
145
+ }
146
+
147
+ async amendCommit(cwd: string, message?: string): Promise<string> {
148
+ const args = ['commit', '--amend'];
149
+ if (message) {
150
+ args.push('-m', message);
151
+ } else {
152
+ args.push('--no-edit');
153
+ }
154
+ const result = await execGit(args, cwd);
155
+ if (result.exitCode !== 0) {
156
+ throw new Error(`git commit --amend failed: ${result.stderr}`);
157
+ }
158
+ const hashResult = await execGit(['rev-parse', 'HEAD'], cwd);
159
+ return hashResult.stdout.trim();
160
+ }
161
+
162
+ // ============================================
163
+ // Diff
164
+ // ============================================
165
+
166
+ async getDiffUnstaged(cwd: string, filePath?: string): Promise<GitFileDiff[]> {
167
+ const args = ['diff'];
168
+ if (filePath) args.push('--', filePath);
169
+ const result = await execGit(args, cwd);
170
+ return parseDiff(result.stdout);
171
+ }
172
+
173
+ async getDiffStaged(cwd: string, filePath?: string): Promise<GitFileDiff[]> {
174
+ const args = ['diff', '--cached'];
175
+ if (filePath) args.push('--', filePath);
176
+ const result = await execGit(args, cwd);
177
+ return parseDiff(result.stdout);
178
+ }
179
+
180
+ async getDiffCommit(cwd: string, commitHash: string): Promise<GitFileDiff[]> {
181
+ const result = await execGit(['diff', `${commitHash}^`, commitHash], cwd);
182
+ return parseDiff(result.stdout);
183
+ }
184
+
185
+ async getDiffBetween(cwd: string, from: string, to: string): Promise<GitFileDiff[]> {
186
+ const result = await execGit(['diff', from, to], cwd);
187
+ return parseDiff(result.stdout);
188
+ }
189
+
190
+ // ============================================
191
+ // Branches
192
+ // ============================================
193
+
194
+ async getBranches(cwd: string): Promise<GitBranchInfo> {
195
+ const [localResult, remoteResult] = await Promise.all([
196
+ execGit(['branch', '-v', '--no-color'], cwd),
197
+ execGit(['branch', '-r', '-v', '--no-color'], cwd)
198
+ ]);
199
+
200
+ // Handle empty repo (no commits yet) — branch command returns empty
201
+ if (!localResult.stdout.trim()) {
202
+ // Try to get the initial branch name from HEAD
203
+ const headResult = await execGit(['symbolic-ref', '--short', 'HEAD'], cwd);
204
+ const initialBranch = headResult.exitCode === 0 ? headResult.stdout.trim() : 'main';
205
+ return { current: initialBranch, local: [], remote: [], ahead: 0, behind: 0 };
206
+ }
207
+
208
+ const branchInfo = parseBranches(localResult.stdout, remoteResult.stdout);
209
+
210
+ // Get ahead/behind for current branch
211
+ if (branchInfo.current) {
212
+ try {
213
+ const abResult = await execGit(
214
+ ['rev-list', '--left-right', '--count', `${branchInfo.current}...@{upstream}`],
215
+ cwd
216
+ );
217
+ if (abResult.exitCode === 0) {
218
+ const { ahead, behind } = parseAheadBehind(abResult.stdout);
219
+ branchInfo.ahead = ahead;
220
+ branchInfo.behind = behind;
221
+
222
+ // Update the current branch entry too
223
+ const currentBranch = branchInfo.local.find(b => b.isCurrent);
224
+ if (currentBranch) {
225
+ currentBranch.ahead = ahead;
226
+ currentBranch.behind = behind;
227
+ }
228
+ }
229
+ } catch {
230
+ // No upstream configured
231
+ }
232
+ }
233
+
234
+ return branchInfo;
235
+ }
236
+
237
+ async createBranch(cwd: string, name: string, startPoint?: string): Promise<void> {
238
+ const args = ['checkout', '-b', name];
239
+ if (startPoint) args.push(startPoint);
240
+ const result = await execGit(args, cwd);
241
+ if (result.exitCode !== 0) {
242
+ throw new Error(`git checkout -b failed: ${result.stderr}`);
243
+ }
244
+ }
245
+
246
+ async switchBranch(cwd: string, name: string): Promise<void> {
247
+ const result = await execGit(['checkout', name], cwd);
248
+ if (result.exitCode !== 0) {
249
+ throw new Error(`git checkout failed: ${result.stderr}`);
250
+ }
251
+ }
252
+
253
+ async deleteBranch(cwd: string, name: string, force = false): Promise<void> {
254
+ const flag = force ? '-D' : '-d';
255
+ const result = await execGit(['branch', flag, name], cwd);
256
+ if (result.exitCode !== 0) {
257
+ throw new Error(`git branch ${flag} failed: ${result.stderr}`);
258
+ }
259
+ }
260
+
261
+ async renameBranch(cwd: string, oldName: string, newName: string): Promise<void> {
262
+ const result = await execGit(['branch', '-m', oldName, newName], cwd);
263
+ if (result.exitCode !== 0) {
264
+ throw new Error(`git branch -m failed: ${result.stderr}`);
265
+ }
266
+ }
267
+
268
+ async mergeBranch(cwd: string, branchName: string): Promise<{ success: boolean; message: string }> {
269
+ const result = await execGit(['merge', branchName], cwd);
270
+ return {
271
+ success: result.exitCode === 0,
272
+ message: result.exitCode === 0 ? result.stdout : result.stderr
273
+ };
274
+ }
275
+
276
+ // ============================================
277
+ // Log
278
+ // ============================================
279
+
280
+ async getLog(cwd: string, limit = 50, skip = 0, branch?: string): Promise<GitLogResult> {
281
+ const SEPARATOR = '|||';
282
+ const format = `%H${SEPARATOR}%h${SEPARATOR}%an${SEPARATOR}%ae${SEPARATOR}%aI${SEPARATOR}%P${SEPARATOR}%D%n%s%x00`;
283
+
284
+ const args = [
285
+ 'log',
286
+ `--format=${format}`,
287
+ `--max-count=${limit + 1}`, // +1 to check if there are more
288
+ `--skip=${skip}`
289
+ ];
290
+
291
+ if (branch) args.push(branch);
292
+
293
+ const result = await execGit(args, cwd);
294
+ if (result.exitCode !== 0) {
295
+ throw new Error(`git log failed: ${result.stderr}`);
296
+ }
297
+
298
+ const commits = parseLog(result.stdout);
299
+ const hasMore = commits.length > limit;
300
+ if (hasMore) commits.pop(); // Remove the extra one
301
+
302
+ // Get total count
303
+ const countResult = await execGit(['rev-list', '--count', branch || 'HEAD'], cwd);
304
+ const total = parseInt(countResult.stdout.trim()) || commits.length;
305
+
306
+ return { commits, total, hasMore };
307
+ }
308
+
309
+ // ============================================
310
+ // Remote Operations
311
+ // ============================================
312
+
313
+ async getRemotes(cwd: string): Promise<GitRemote[]> {
314
+ const result = await execGit(['remote', '-v'], cwd);
315
+ return parseRemotes(result.stdout);
316
+ }
317
+
318
+ async fetch(cwd: string, remote = 'origin'): Promise<string> {
319
+ const result = await execGit(['fetch', remote, '--prune'], cwd, 60000);
320
+ if (result.exitCode !== 0) {
321
+ throw new Error(`git fetch failed: ${result.stderr}`);
322
+ }
323
+ return result.stderr || result.stdout; // git fetch outputs to stderr
324
+ }
325
+
326
+ async pull(cwd: string, remote = 'origin', branch?: string): Promise<{ success: boolean; message: string }> {
327
+ const args = ['pull', remote];
328
+ if (branch) args.push(branch);
329
+ const result = await execGit(args, cwd, 60000);
330
+ return {
331
+ success: result.exitCode === 0,
332
+ message: result.exitCode === 0 ? result.stdout : result.stderr
333
+ };
334
+ }
335
+
336
+ async push(cwd: string, remote = 'origin', branch?: string, force = false): Promise<{ success: boolean; message: string }> {
337
+ const args = ['push', remote];
338
+ if (branch) args.push(branch);
339
+ if (force) args.push('--force-with-lease');
340
+ // Set upstream if needed
341
+ args.push('-u');
342
+ const result = await execGit(args, cwd, 60000);
343
+ return {
344
+ success: result.exitCode === 0,
345
+ message: result.exitCode === 0 ? (result.stderr || result.stdout) : result.stderr
346
+ };
347
+ }
348
+
349
+ async addRemote(cwd: string, name: string, url: string): Promise<void> {
350
+ const result = await execGit(['remote', 'add', name, url], cwd);
351
+ if (result.exitCode !== 0) {
352
+ throw new Error(`git remote add failed: ${result.stderr}`);
353
+ }
354
+ }
355
+
356
+ async removeRemote(cwd: string, name: string): Promise<void> {
357
+ const result = await execGit(['remote', 'remove', name], cwd);
358
+ if (result.exitCode !== 0) {
359
+ throw new Error(`git remote remove failed: ${result.stderr}`);
360
+ }
361
+ }
362
+
363
+ // ============================================
364
+ // Stash
365
+ // ============================================
366
+
367
+ async stashList(cwd: string): Promise<GitStashEntry[]> {
368
+ const result = await execGit(['stash', 'list'], cwd);
369
+ return parseStashList(result.stdout);
370
+ }
371
+
372
+ async stashSave(cwd: string, message?: string): Promise<void> {
373
+ const args = ['stash', 'push'];
374
+ if (message) args.push('-m', message);
375
+ const result = await execGit(args, cwd);
376
+ if (result.exitCode !== 0) {
377
+ throw new Error(`git stash failed: ${result.stderr}`);
378
+ }
379
+ }
380
+
381
+ async stashPop(cwd: string, index = 0): Promise<void> {
382
+ const result = await execGit(['stash', 'pop', `stash@{${index}}`], cwd);
383
+ if (result.exitCode !== 0) {
384
+ throw new Error(`git stash pop failed: ${result.stderr}`);
385
+ }
386
+ }
387
+
388
+ async stashDrop(cwd: string, index = 0): Promise<void> {
389
+ const result = await execGit(['stash', 'drop', `stash@{${index}}`], cwd);
390
+ if (result.exitCode !== 0) {
391
+ throw new Error(`git stash drop failed: ${result.stderr}`);
392
+ }
393
+ }
394
+
395
+ // ============================================
396
+ // Tags
397
+ // ============================================
398
+
399
+ async getTags(cwd: string): Promise<{ name: string; hash: string; message: string; date: string; isAnnotated: boolean }[]> {
400
+ const result = await execGit(
401
+ ['tag', '-l', '--sort=-creatordate', '--format=%(refname:short)|||%(objectname:short)|||%(contents:subject)|||%(creatordate:iso-strict)|||%(objecttype)'],
402
+ cwd
403
+ );
404
+ if (result.exitCode !== 0) return [];
405
+
406
+ const tags: { name: string; hash: string; message: string; date: string; isAnnotated: boolean }[] = [];
407
+ const lines = result.stdout.split('\n').filter(Boolean);
408
+ for (const line of lines) {
409
+ const parts = line.split('|||');
410
+ if (parts.length >= 2) {
411
+ tags.push({
412
+ name: parts[0],
413
+ hash: parts[1],
414
+ message: parts[2] || '',
415
+ date: parts[3] || '',
416
+ isAnnotated: parts[4] === 'tag'
417
+ });
418
+ }
419
+ }
420
+ return tags;
421
+ }
422
+
423
+ async createTag(cwd: string, name: string, message?: string, commitHash?: string): Promise<void> {
424
+ const args = ['tag'];
425
+ if (message) {
426
+ args.push('-a', name, '-m', message);
427
+ } else {
428
+ args.push(name);
429
+ }
430
+ if (commitHash) args.push(commitHash);
431
+ const result = await execGit(args, cwd);
432
+ if (result.exitCode !== 0) {
433
+ throw new Error(`git tag failed: ${result.stderr}`);
434
+ }
435
+ }
436
+
437
+ async deleteTag(cwd: string, name: string): Promise<void> {
438
+ const result = await execGit(['tag', '-d', name], cwd);
439
+ if (result.exitCode !== 0) {
440
+ throw new Error(`git tag -d failed: ${result.stderr}`);
441
+ }
442
+ }
443
+
444
+ async pushTag(cwd: string, name: string, remote = 'origin'): Promise<{ success: boolean; message: string }> {
445
+ const result = await execGit(['push', remote, name], cwd, 60000);
446
+ return {
447
+ success: result.exitCode === 0,
448
+ message: result.exitCode === 0 ? (result.stderr || result.stdout) : result.stderr
449
+ };
450
+ }
451
+
452
+ // ============================================
453
+ // Conflict Resolution
454
+ // ============================================
455
+
456
+ async getConflictFiles(cwd: string): Promise<GitConflictFile[]> {
457
+ const status = await this.getStatus(cwd);
458
+ const conflicts: GitConflictFile[] = [];
459
+
460
+ for (const file of status.conflicted) {
461
+ try {
462
+ const { readFile } = await import('node:fs/promises');
463
+ const { join } = await import('node:path');
464
+ const content = await readFile(join(cwd, file.path), 'utf-8');
465
+ const markers = parseConflictMarkers(content);
466
+ conflicts.push({ path: file.path, content, markers });
467
+ } catch (err) {
468
+ debug.error('git', `Failed to read conflict file: ${file.path}`, err);
469
+ }
470
+ }
471
+
472
+ return conflicts;
473
+ }
474
+
475
+ async resolveConflict(
476
+ cwd: string,
477
+ filePath: string,
478
+ resolution: 'ours' | 'theirs' | 'custom',
479
+ customContent?: string
480
+ ): Promise<void> {
481
+ if (resolution === 'custom' && customContent !== undefined) {
482
+ // Write custom content
483
+ const { writeFile } = await import('node:fs/promises');
484
+ const { join } = await import('node:path');
485
+ await writeFile(join(cwd, filePath), customContent, 'utf-8');
486
+ } else if (resolution === 'ours') {
487
+ await execGit(['checkout', '--ours', '--', filePath], cwd);
488
+ } else if (resolution === 'theirs') {
489
+ await execGit(['checkout', '--theirs', '--', filePath], cwd);
490
+ }
491
+
492
+ // Stage the resolved file
493
+ await this.stageFile(cwd, filePath);
494
+ }
495
+
496
+ async abortMerge(cwd: string): Promise<void> {
497
+ const result = await execGit(['merge', '--abort'], cwd);
498
+ if (result.exitCode !== 0) {
499
+ throw new Error(`git merge --abort failed: ${result.stderr}`);
500
+ }
501
+ }
502
+ }
503
+
504
+ // Singleton
505
+ export const gitService = new GitService();