@dyyz1993/create-agent 1.9.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 (358) hide show
  1. package/package.json +25 -0
  2. package/src/__tests__/cli.test.ts +85 -0
  3. package/src/__tests__/copy.test.ts +168 -0
  4. package/src/__tests__/templates.test.ts +87 -0
  5. package/src/cli.ts +65 -0
  6. package/src/commands/create.ts +49 -0
  7. package/src/commands/list.ts +12 -0
  8. package/src/commands/status.ts +85 -0
  9. package/src/commands/workspace.ts +129 -0
  10. package/src/lib/copy.ts +285 -0
  11. package/src/lib/templates.ts +57 -0
  12. package/src/lib/types.ts +6 -0
  13. package/templates/agent/.env.example +7 -0
  14. package/templates/agent/LICENSE +21 -0
  15. package/templates/agent/README.md +87 -0
  16. package/templates/agent/bun.lock +1279 -0
  17. package/templates/agent/electrobun.config.ts +27 -0
  18. package/templates/agent/eslint.config.mjs +63 -0
  19. package/templates/agent/llms.txt +24 -0
  20. package/templates/agent/package-lock.json +3670 -0
  21. package/templates/agent/package.json +59 -0
  22. package/templates/agent/postcss.config.js +6 -0
  23. package/templates/agent/scripts/dev.ts +138 -0
  24. package/templates/agent/src/__tests__/server-config.test.ts +59 -0
  25. package/templates/agent/src/bun/index.ts +73 -0
  26. package/templates/agent/src/gateway/__tests__/ws-handler.test.ts +48 -0
  27. package/templates/agent/src/gateway/http-routes.ts +1 -0
  28. package/templates/agent/src/gateway/ipc-transport.ts +68 -0
  29. package/templates/agent/src/gateway/ws-handler.ts +86 -0
  30. package/templates/agent/src/mainview/App.tsx +41 -0
  31. package/templates/agent/src/mainview/__tests__/components/ChatPanel.test.tsx +114 -0
  32. package/templates/agent/src/mainview/__tests__/components/ExplorerSidebar.test.tsx +141 -0
  33. package/templates/agent/src/mainview/__tests__/components/MessageBubble.test.tsx +72 -0
  34. package/templates/agent/src/mainview/__tests__/hooks/use-input-history.test.ts +125 -0
  35. package/templates/agent/src/mainview/__tests__/hooks/use-rpc-init.test.ts +217 -0
  36. package/templates/agent/src/mainview/__tests__/hooks/use-sidebar-resize.test.ts +127 -0
  37. package/templates/agent/src/mainview/__tests__/i18n/i18n.test.ts +48 -0
  38. package/templates/agent/src/mainview/__tests__/setup.ts +37 -0
  39. package/templates/agent/src/mainview/__tests__/stores/use-chat-store.test.ts +83 -0
  40. package/templates/agent/src/mainview/__tests__/stores/use-sidebar-store.test.ts +85 -0
  41. package/templates/agent/src/mainview/components/activity-bar/ActivityBar.tsx +36 -0
  42. package/templates/agent/src/mainview/components/activity-bar/MobileTabBar.tsx +36 -0
  43. package/templates/agent/src/mainview/components/activity-bar/__tests__/ActivityBar.test.tsx +44 -0
  44. package/templates/agent/src/mainview/components/activity-bar/__tests__/MobileTabBar.test.tsx +44 -0
  45. package/templates/agent/src/mainview/components/bash/BashPanel.tsx +206 -0
  46. package/templates/agent/src/mainview/components/bash/__tests__/BashPanel.test.tsx +103 -0
  47. package/templates/agent/src/mainview/components/chat/ChatPanel.tsx +102 -0
  48. package/templates/agent/src/mainview/components/chat/MessageBubble.tsx +86 -0
  49. package/templates/agent/src/mainview/components/common/ErrorBoundary.tsx +58 -0
  50. package/templates/agent/src/mainview/components/common/LanguageSwitcher.tsx +14 -0
  51. package/templates/agent/src/mainview/components/common/ThemeToggle.tsx +24 -0
  52. package/templates/agent/src/mainview/components/common/__tests__/ErrorBoundary.test.tsx +74 -0
  53. package/templates/agent/src/mainview/components/common/__tests__/LanguageSwitcher.test.tsx +44 -0
  54. package/templates/agent/src/mainview/components/common/__tests__/ThemeToggle.test.tsx +41 -0
  55. package/templates/agent/src/mainview/components/debug/DebugPanel.tsx +120 -0
  56. package/templates/agent/src/mainview/components/debug/__tests__/DebugPanel.test.tsx +102 -0
  57. package/templates/agent/src/mainview/components/diff/DiffViewerPanel.tsx +106 -0
  58. package/templates/agent/src/mainview/components/diff/__tests__/DiffViewerPanel.test.tsx +73 -0
  59. package/templates/agent/src/mainview/components/explorer/ConfirmDialog.tsx +53 -0
  60. package/templates/agent/src/mainview/components/explorer/ContextMenu.tsx +82 -0
  61. package/templates/agent/src/mainview/components/explorer/ExplorerSidebar.tsx +221 -0
  62. package/templates/agent/src/mainview/components/explorer/InlineInput.tsx +55 -0
  63. package/templates/agent/src/mainview/components/explorer/TreeNodeItem.tsx +121 -0
  64. package/templates/agent/src/mainview/components/explorer/__tests__/ConfirmDialog.test.tsx +37 -0
  65. package/templates/agent/src/mainview/components/explorer/__tests__/ContextMenu.test.tsx +49 -0
  66. package/templates/agent/src/mainview/components/explorer/__tests__/InlineInput.test.tsx +48 -0
  67. package/templates/agent/src/mainview/components/explorer/__tests__/TreeNodeItem.test.tsx +146 -0
  68. package/templates/agent/src/mainview/components/feed/FeedPanel.tsx +239 -0
  69. package/templates/agent/src/mainview/components/feed/__tests__/FeedPanel.test.tsx +108 -0
  70. package/templates/agent/src/mainview/components/file-preview/FilePreviewOverlay.tsx +56 -0
  71. package/templates/agent/src/mainview/components/file-preview/VirtualizedCodeView.tsx +99 -0
  72. package/templates/agent/src/mainview/components/file-preview/__tests__/FilePreviewOverlay.test.tsx +96 -0
  73. package/templates/agent/src/mainview/components/file-preview/__tests__/VirtualizedCodeView.test.tsx +58 -0
  74. package/templates/agent/src/mainview/components/git/GitBranchSelector.tsx +98 -0
  75. package/templates/agent/src/mainview/components/git/GitCommitInput.tsx +60 -0
  76. package/templates/agent/src/mainview/components/git/GitPanel.tsx +528 -0
  77. package/templates/agent/src/mainview/components/git/__tests__/GitBranchSelector.test.tsx +76 -0
  78. package/templates/agent/src/mainview/components/git/__tests__/GitCommitInput.test.tsx +91 -0
  79. package/templates/agent/src/mainview/components/git/__tests__/GitPanel.test.tsx +174 -0
  80. package/templates/agent/src/mainview/components/layout/AppLayout.tsx +170 -0
  81. package/templates/agent/src/mainview/components/layout/__tests__/AppLayout.test.tsx +163 -0
  82. package/templates/agent/src/mainview/components/rules/RulesPanel.tsx +117 -0
  83. package/templates/agent/src/mainview/components/rules/__tests__/RulesPanel.test.tsx +91 -0
  84. package/templates/agent/src/mainview/components/search/SearchPanel.tsx +404 -0
  85. package/templates/agent/src/mainview/components/search/__tests__/SearchPanel.test.tsx +50 -0
  86. package/templates/agent/src/mainview/components/sidebar/PinButton.tsx +20 -0
  87. package/templates/agent/src/mainview/components/sidebar/__tests__/PinButton.test.tsx +48 -0
  88. package/templates/agent/src/mainview/components/todo/TodoPanel.tsx +141 -0
  89. package/templates/agent/src/mainview/components/todo/__tests__/TodoPanel.test.tsx +91 -0
  90. package/templates/agent/src/mainview/global.d.ts +13 -0
  91. package/templates/agent/src/mainview/hooks/__tests__/use-breakpoint.test.ts +147 -0
  92. package/templates/agent/src/mainview/hooks/use-breakpoint.ts +34 -0
  93. package/templates/agent/src/mainview/hooks/use-input-history.ts +84 -0
  94. package/templates/agent/src/mainview/hooks/use-rpc-init.ts +61 -0
  95. package/templates/agent/src/mainview/hooks/use-sidebar-resize.ts +40 -0
  96. package/templates/agent/src/mainview/index.css +61 -0
  97. package/templates/agent/src/mainview/index.html +12 -0
  98. package/templates/agent/src/mainview/lib/api-client.ts +192 -0
  99. package/templates/agent/src/mainview/lib/i18n/index.ts +30 -0
  100. package/templates/agent/src/mainview/lib/i18n/locales/en.json +157 -0
  101. package/templates/agent/src/mainview/lib/i18n/locales/zh.json +157 -0
  102. package/templates/agent/src/mainview/lib/rpc-cache.ts +94 -0
  103. package/templates/agent/src/mainview/main.tsx +24 -0
  104. package/templates/agent/src/mainview/stores/__tests__/use-app-store.test.ts +86 -0
  105. package/templates/agent/src/mainview/stores/__tests__/use-bash-store.test.ts +99 -0
  106. package/templates/agent/src/mainview/stores/__tests__/use-connection-store.test.ts +26 -0
  107. package/templates/agent/src/mainview/stores/__tests__/use-explorer-store.test.ts +130 -0
  108. package/templates/agent/src/mainview/stores/__tests__/use-feed-store.test.ts +132 -0
  109. package/templates/agent/src/mainview/stores/__tests__/use-git-store.test.ts +161 -0
  110. package/templates/agent/src/mainview/stores/__tests__/use-locale-store.test.ts +32 -0
  111. package/templates/agent/src/mainview/stores/__tests__/use-log-store.test.ts +28 -0
  112. package/templates/agent/src/mainview/stores/__tests__/use-notification-store.test.ts +102 -0
  113. package/templates/agent/src/mainview/stores/__tests__/use-rules-store.test.ts +75 -0
  114. package/templates/agent/src/mainview/stores/__tests__/use-theme-store.test.ts +59 -0
  115. package/templates/agent/src/mainview/stores/__tests__/use-todo-store.test.ts +75 -0
  116. package/templates/agent/src/mainview/stores/message-batcher.ts +25 -0
  117. package/templates/agent/src/mainview/stores/use-app-store.ts +99 -0
  118. package/templates/agent/src/mainview/stores/use-bash-store.ts +93 -0
  119. package/templates/agent/src/mainview/stores/use-chat-store.ts +37 -0
  120. package/templates/agent/src/mainview/stores/use-connection-store.ts +42 -0
  121. package/templates/agent/src/mainview/stores/use-explorer-store.ts +320 -0
  122. package/templates/agent/src/mainview/stores/use-feed-store.ts +129 -0
  123. package/templates/agent/src/mainview/stores/use-git-store.ts +291 -0
  124. package/templates/agent/src/mainview/stores/use-locale-store.ts +27 -0
  125. package/templates/agent/src/mainview/stores/use-log-store.ts +16 -0
  126. package/templates/agent/src/mainview/stores/use-notification-store.ts +82 -0
  127. package/templates/agent/src/mainview/stores/use-rules-store.ts +54 -0
  128. package/templates/agent/src/mainview/stores/use-sidebar-store.ts +99 -0
  129. package/templates/agent/src/mainview/stores/use-theme-store.ts +46 -0
  130. package/templates/agent/src/mainview/stores/use-todo-store.ts +54 -0
  131. package/templates/agent/src/mainview/types/index.ts +33 -0
  132. package/templates/agent/src/mainview/utils/constants.ts +1 -0
  133. package/templates/agent/src/mainview/utils/drop-handler.ts +150 -0
  134. package/templates/agent/src/mainview/utils/file-icon.tsx +24 -0
  135. package/templates/agent/src/mainview/utils/file-utils.ts +35 -0
  136. package/templates/agent/src/server-config.ts +43 -0
  137. package/templates/agent/src/server.ts +89 -0
  138. package/templates/agent/src/shared/handlers/__tests__/bash-handler.test.ts +135 -0
  139. package/templates/agent/src/shared/handlers/__tests__/chat-handler.test.ts +77 -0
  140. package/templates/agent/src/shared/handlers/__tests__/feed-handler.test.ts +108 -0
  141. package/templates/agent/src/shared/handlers/__tests__/file-handler.test.ts +100 -0
  142. package/templates/agent/src/shared/handlers/__tests__/system-handler.test.ts +55 -0
  143. package/templates/agent/src/shared/handlers/__tests__/timer-handler.test.ts +77 -0
  144. package/templates/agent/src/shared/handlers/bash.ts +89 -0
  145. package/templates/agent/src/shared/handlers/chat.ts +179 -0
  146. package/templates/agent/src/shared/handlers/feed.ts +53 -0
  147. package/templates/agent/src/shared/handlers/file.ts +99 -0
  148. package/templates/agent/src/shared/handlers/git.ts +263 -0
  149. package/templates/agent/src/shared/handlers/index.ts +10 -0
  150. package/templates/agent/src/shared/handlers/rules.ts +45 -0
  151. package/templates/agent/src/shared/handlers/system.ts +27 -0
  152. package/templates/agent/src/shared/handlers/timer.ts +34 -0
  153. package/templates/agent/src/shared/handlers/todo.ts +45 -0
  154. package/templates/agent/src/shared/lib/__tests__/bash-security.test.ts +125 -0
  155. package/templates/agent/src/shared/lib/__tests__/logger.test.ts +92 -0
  156. package/templates/agent/src/shared/lib/__tests__/path-security.test.ts +77 -0
  157. package/templates/agent/src/shared/lib/bash-security.ts +64 -0
  158. package/templates/agent/src/shared/lib/logger.ts +130 -0
  159. package/templates/agent/src/shared/lib/path-security.ts +38 -0
  160. package/templates/agent/src/shared/lib/port-registry.ts +103 -0
  161. package/templates/agent/src/shared/modules/bash.ts +19 -0
  162. package/templates/agent/src/shared/modules/chat.ts +20 -0
  163. package/templates/agent/src/shared/modules/feed.ts +30 -0
  164. package/templates/agent/src/shared/modules/file.ts +40 -0
  165. package/templates/agent/src/shared/modules/git.ts +81 -0
  166. package/templates/agent/src/shared/modules/rules.ts +25 -0
  167. package/templates/agent/src/shared/modules/system.ts +8 -0
  168. package/templates/agent/src/shared/modules/timer.ts +11 -0
  169. package/templates/agent/src/shared/modules/todo.ts +27 -0
  170. package/templates/agent/src/shared/register-all-handlers.ts +36 -0
  171. package/templates/agent/src/shared/rpc-schema.ts +35 -0
  172. package/templates/agent/tailwind.config.js +15 -0
  173. package/templates/agent/tsconfig.json +26 -0
  174. package/templates/agent/vite.config.ts +3 -0
  175. package/templates/agent/vitest.config.ts +3 -0
  176. package/templates/chat/.env.example +6 -0
  177. package/templates/chat/LICENSE +21 -0
  178. package/templates/chat/README.md +56 -0
  179. package/templates/chat/bun.lock +1203 -0
  180. package/templates/chat/electrobun.config.ts +27 -0
  181. package/templates/chat/eslint.config.mjs +56 -0
  182. package/templates/chat/llms.txt +24 -0
  183. package/templates/chat/package-lock.json +3670 -0
  184. package/templates/chat/package.json +58 -0
  185. package/templates/chat/postcss.config.js +6 -0
  186. package/templates/chat/scripts/dev.ts +138 -0
  187. package/templates/chat/src/__tests__/server-config.test.ts +59 -0
  188. package/templates/chat/src/bun/index.ts +72 -0
  189. package/templates/chat/src/gateway/__tests__/ws-handler.test.ts +48 -0
  190. package/templates/chat/src/gateway/http-routes.ts +1 -0
  191. package/templates/chat/src/gateway/ipc-transport.ts +68 -0
  192. package/templates/chat/src/gateway/ws-handler.ts +86 -0
  193. package/templates/chat/src/mainview/App.tsx +108 -0
  194. package/templates/chat/src/mainview/__tests__/components/ChatPanel.test.tsx +76 -0
  195. package/templates/chat/src/mainview/__tests__/components/MessageBubble.test.tsx +48 -0
  196. package/templates/chat/src/mainview/__tests__/i18n/i18n.test.ts +48 -0
  197. package/templates/chat/src/mainview/__tests__/setup-verify.test.ts +8 -0
  198. package/templates/chat/src/mainview/__tests__/setup.ts +37 -0
  199. package/templates/chat/src/mainview/__tests__/stores/use-app-store.test.ts +46 -0
  200. package/templates/chat/src/mainview/__tests__/stores/use-chat-store.test.ts +104 -0
  201. package/templates/chat/src/mainview/__tests__/stores/use-connection-store.test.ts +35 -0
  202. package/templates/chat/src/mainview/__tests__/stores/use-log-store.test.ts +45 -0
  203. package/templates/chat/src/mainview/__tests__/stores/use-notification-store.test.ts +102 -0
  204. package/templates/chat/src/mainview/__tests__/stores/use-sidebar-store.test.ts +89 -0
  205. package/templates/chat/src/mainview/components/activity-bar/ActivityBar.tsx +26 -0
  206. package/templates/chat/src/mainview/components/activity-bar/MobileTabBar.tsx +20 -0
  207. package/templates/chat/src/mainview/components/chat/ChatPanel.tsx +66 -0
  208. package/templates/chat/src/mainview/components/chat/MessageBubble.tsx +85 -0
  209. package/templates/chat/src/mainview/components/common/LanguageSwitcher.tsx +15 -0
  210. package/templates/chat/src/mainview/components/common/ThemeToggle.tsx +24 -0
  211. package/templates/chat/src/mainview/components/sidebar/PinButton.tsx +20 -0
  212. package/templates/chat/src/mainview/global.d.ts +13 -0
  213. package/templates/chat/src/mainview/hooks/use-breakpoint.ts +34 -0
  214. package/templates/chat/src/mainview/hooks/use-input-history.ts +84 -0
  215. package/templates/chat/src/mainview/index.css +61 -0
  216. package/templates/chat/src/mainview/index.html +12 -0
  217. package/templates/chat/src/mainview/lib/api-client.ts +175 -0
  218. package/templates/chat/src/mainview/lib/i18n/index.ts +30 -0
  219. package/templates/chat/src/mainview/lib/i18n/locales/en.json +157 -0
  220. package/templates/chat/src/mainview/lib/i18n/locales/zh.json +157 -0
  221. package/templates/chat/src/mainview/main.tsx +20 -0
  222. package/templates/chat/src/mainview/stores/__tests__/use-locale-store.test.ts +32 -0
  223. package/templates/chat/src/mainview/stores/__tests__/use-theme-store.test.ts +59 -0
  224. package/templates/chat/src/mainview/stores/message-batcher.ts +25 -0
  225. package/templates/chat/src/mainview/stores/use-app-store.ts +50 -0
  226. package/templates/chat/src/mainview/stores/use-chat-store.ts +37 -0
  227. package/templates/chat/src/mainview/stores/use-connection-store.ts +42 -0
  228. package/templates/chat/src/mainview/stores/use-locale-store.ts +27 -0
  229. package/templates/chat/src/mainview/stores/use-log-store.ts +16 -0
  230. package/templates/chat/src/mainview/stores/use-notification-store.ts +82 -0
  231. package/templates/chat/src/mainview/stores/use-sidebar-store.ts +98 -0
  232. package/templates/chat/src/mainview/stores/use-theme-store.ts +46 -0
  233. package/templates/chat/src/mainview/types/index.ts +8 -0
  234. package/templates/chat/src/mainview/utils/constants.ts +1 -0
  235. package/templates/chat/src/server-config.ts +42 -0
  236. package/templates/chat/src/server.ts +89 -0
  237. package/templates/chat/src/shared/__tests__/chat-handler.test.ts +71 -0
  238. package/templates/chat/src/shared/__tests__/system-handler.test.ts +70 -0
  239. package/templates/chat/src/shared/handlers/chat.ts +186 -0
  240. package/templates/chat/src/shared/handlers/index.ts +2 -0
  241. package/templates/chat/src/shared/handlers/system.ts +27 -0
  242. package/templates/chat/src/shared/lib/logger.ts +130 -0
  243. package/templates/chat/src/shared/lib/path-security.ts +38 -0
  244. package/templates/chat/src/shared/lib/port-registry.ts +103 -0
  245. package/templates/chat/src/shared/modules/chat.ts +20 -0
  246. package/templates/chat/src/shared/modules/system.ts +8 -0
  247. package/templates/chat/src/shared/register-all-handlers.ts +36 -0
  248. package/templates/chat/src/shared/rpc-schema.ts +11 -0
  249. package/templates/chat/tailwind.config.js +15 -0
  250. package/templates/chat/tsconfig.json +26 -0
  251. package/templates/chat/vite.config.ts +3 -0
  252. package/templates/chat/vitest.config.ts +3 -0
  253. package/templates/general/.env.example +7 -0
  254. package/templates/general/LICENSE +21 -0
  255. package/templates/general/README.md +113 -0
  256. package/templates/general/bun.lock +1266 -0
  257. package/templates/general/electrobun.config.ts +27 -0
  258. package/templates/general/eslint.config.mjs +56 -0
  259. package/templates/general/llms.txt +24 -0
  260. package/templates/general/package-lock.json +3670 -0
  261. package/templates/general/package.json +59 -0
  262. package/templates/general/postcss.config.js +6 -0
  263. package/templates/general/scripts/dev.ts +138 -0
  264. package/templates/general/src/__tests__/server-config.test.ts +59 -0
  265. package/templates/general/src/bun/index.ts +72 -0
  266. package/templates/general/src/gateway/http-routes.ts +1 -0
  267. package/templates/general/src/gateway/ipc-transport.ts +68 -0
  268. package/templates/general/src/gateway/ws-handler.ts +86 -0
  269. package/templates/general/src/mainview/App.tsx +40 -0
  270. package/templates/general/src/mainview/__tests__/components/ChatPanel.test.tsx +82 -0
  271. package/templates/general/src/mainview/__tests__/components/FeedPanel.test.tsx +109 -0
  272. package/templates/general/src/mainview/__tests__/components/GitPanel.test.tsx +151 -0
  273. package/templates/general/src/mainview/__tests__/i18n/i18n.test.ts +48 -0
  274. package/templates/general/src/mainview/__tests__/setup-verify.test.ts +8 -0
  275. package/templates/general/src/mainview/__tests__/setup.ts +37 -0
  276. package/templates/general/src/mainview/components/activity-bar/ActivityBar.tsx +40 -0
  277. package/templates/general/src/mainview/components/activity-bar/MobileTabBar.tsx +29 -0
  278. package/templates/general/src/mainview/components/chat/ChatPanel.tsx +66 -0
  279. package/templates/general/src/mainview/components/chat/MessageBubble.tsx +85 -0
  280. package/templates/general/src/mainview/components/common/LanguageSwitcher.tsx +15 -0
  281. package/templates/general/src/mainview/components/common/ThemeToggle.tsx +24 -0
  282. package/templates/general/src/mainview/components/debug/DebugPanel.tsx +176 -0
  283. package/templates/general/src/mainview/components/diff/DiffViewerPanel.tsx +108 -0
  284. package/templates/general/src/mainview/components/explorer/ConfirmDialog.tsx +52 -0
  285. package/templates/general/src/mainview/components/explorer/ContextMenu.tsx +82 -0
  286. package/templates/general/src/mainview/components/explorer/ExplorerSidebar.tsx +225 -0
  287. package/templates/general/src/mainview/components/explorer/InlineInput.tsx +55 -0
  288. package/templates/general/src/mainview/components/explorer/TreeNodeItem.tsx +121 -0
  289. package/templates/general/src/mainview/components/feed/FeedPanel.tsx +248 -0
  290. package/templates/general/src/mainview/components/file-preview/FilePreviewOverlay.tsx +55 -0
  291. package/templates/general/src/mainview/components/file-preview/VirtualizedCodeView.tsx +99 -0
  292. package/templates/general/src/mainview/components/git/GitBranchSelector.tsx +93 -0
  293. package/templates/general/src/mainview/components/git/GitCommitInput.tsx +60 -0
  294. package/templates/general/src/mainview/components/git/GitPanel.tsx +533 -0
  295. package/templates/general/src/mainview/components/layout/AppLayout.tsx +146 -0
  296. package/templates/general/src/mainview/components/search/SearchPanel.tsx +405 -0
  297. package/templates/general/src/mainview/components/sidebar/PinButton.tsx +20 -0
  298. package/templates/general/src/mainview/global.d.ts +13 -0
  299. package/templates/general/src/mainview/hooks/use-breakpoint.ts +34 -0
  300. package/templates/general/src/mainview/hooks/use-input-history.ts +84 -0
  301. package/templates/general/src/mainview/hooks/use-rpc-init.ts +61 -0
  302. package/templates/general/src/mainview/hooks/use-sidebar-resize.ts +40 -0
  303. package/templates/general/src/mainview/index.css +61 -0
  304. package/templates/general/src/mainview/index.html +12 -0
  305. package/templates/general/src/mainview/lib/api-client.ts +175 -0
  306. package/templates/general/src/mainview/lib/i18n/index.ts +30 -0
  307. package/templates/general/src/mainview/lib/i18n/locales/en.json +157 -0
  308. package/templates/general/src/mainview/lib/i18n/locales/zh.json +157 -0
  309. package/templates/general/src/mainview/main.tsx +20 -0
  310. package/templates/general/src/mainview/stores/__tests__/use-chat-store.test.ts +104 -0
  311. package/templates/general/src/mainview/stores/__tests__/use-connection-store.test.ts +35 -0
  312. package/templates/general/src/mainview/stores/__tests__/use-locale-store.test.ts +32 -0
  313. package/templates/general/src/mainview/stores/__tests__/use-log-store.test.ts +45 -0
  314. package/templates/general/src/mainview/stores/__tests__/use-theme-store.test.ts +59 -0
  315. package/templates/general/src/mainview/stores/message-batcher.ts +25 -0
  316. package/templates/general/src/mainview/stores/use-app-store.ts +104 -0
  317. package/templates/general/src/mainview/stores/use-chat-store.ts +37 -0
  318. package/templates/general/src/mainview/stores/use-connection-store.ts +42 -0
  319. package/templates/general/src/mainview/stores/use-explorer-store.ts +315 -0
  320. package/templates/general/src/mainview/stores/use-feed-store.ts +129 -0
  321. package/templates/general/src/mainview/stores/use-git-store.ts +284 -0
  322. package/templates/general/src/mainview/stores/use-locale-store.ts +27 -0
  323. package/templates/general/src/mainview/stores/use-log-store.ts +16 -0
  324. package/templates/general/src/mainview/stores/use-notification-store.ts +82 -0
  325. package/templates/general/src/mainview/stores/use-sidebar-store.ts +99 -0
  326. package/templates/general/src/mainview/stores/use-theme-store.ts +46 -0
  327. package/templates/general/src/mainview/types/index.ts +33 -0
  328. package/templates/general/src/mainview/utils/constants.ts +1 -0
  329. package/templates/general/src/mainview/utils/drop-handler.ts +150 -0
  330. package/templates/general/src/mainview/utils/file-icon.tsx +24 -0
  331. package/templates/general/src/mainview/utils/file-utils.ts +35 -0
  332. package/templates/general/src/server-config.ts +42 -0
  333. package/templates/general/src/server.ts +89 -0
  334. package/templates/general/src/shared/handlers/__tests__/chat.test.ts +183 -0
  335. package/templates/general/src/shared/handlers/__tests__/system.test.ts +94 -0
  336. package/templates/general/src/shared/handlers/__tests__/timer.test.ts +113 -0
  337. package/templates/general/src/shared/handlers/chat.ts +179 -0
  338. package/templates/general/src/shared/handlers/feed.ts +53 -0
  339. package/templates/general/src/shared/handlers/file.ts +99 -0
  340. package/templates/general/src/shared/handlers/git.ts +263 -0
  341. package/templates/general/src/shared/handlers/index.ts +7 -0
  342. package/templates/general/src/shared/handlers/system.ts +27 -0
  343. package/templates/general/src/shared/handlers/timer.ts +34 -0
  344. package/templates/general/src/shared/lib/logger.ts +130 -0
  345. package/templates/general/src/shared/lib/path-security.ts +38 -0
  346. package/templates/general/src/shared/lib/port-registry.ts +103 -0
  347. package/templates/general/src/shared/modules/chat.ts +20 -0
  348. package/templates/general/src/shared/modules/feed.ts +30 -0
  349. package/templates/general/src/shared/modules/file.ts +40 -0
  350. package/templates/general/src/shared/modules/git.ts +81 -0
  351. package/templates/general/src/shared/modules/system.ts +8 -0
  352. package/templates/general/src/shared/modules/timer.ts +11 -0
  353. package/templates/general/src/shared/register-all-handlers.ts +36 -0
  354. package/templates/general/src/shared/rpc-schema.ts +31 -0
  355. package/templates/general/tailwind.config.js +15 -0
  356. package/templates/general/tsconfig.json +26 -0
  357. package/templates/general/vite.config.ts +3 -0
  358. package/templates/general/vitest.config.ts +3 -0
@@ -0,0 +1,33 @@
1
+ export type TreeNode = {
2
+ name: string;
3
+ path: string;
4
+ type: "file" | "directory";
5
+ size?: number;
6
+ children?: TreeNode[];
7
+ expanded?: boolean;
8
+ loaded?: boolean;
9
+ };
10
+
11
+ export type DemoMethod = "system.ping" | "system.hello" | "system.echo" | "chat.send";
12
+
13
+ export type FilePreview = {
14
+ path: string;
15
+ name: string;
16
+ content: string | null;
17
+ imageUrl: string | null;
18
+ mimeType: string;
19
+ size: number;
20
+ isText: boolean;
21
+ isImage: boolean;
22
+ totalLines?: number;
23
+ };
24
+
25
+ export type ChatMessage = {
26
+ id: string;
27
+ role: "user" | "assistant";
28
+ content: string;
29
+ timestamp: number;
30
+ };
31
+
32
+ export type EditingType = "rename" | "newFile" | "newDir";
33
+ export type EditingNode = { path: string; type: EditingType };
@@ -0,0 +1 @@
1
+ export const MAX_PREVIEW_SIZE = 500 * 1024; // 500KB
@@ -0,0 +1,150 @@
1
+ import { apiClient } from "../lib/api-client";
2
+
3
+ export interface DropEntry {
4
+ name: string;
5
+ relativePath: string;
6
+ file?: File;
7
+ isDirectory: boolean;
8
+ children?: DropEntry[];
9
+ }
10
+
11
+ /**
12
+ * 递归读取 webkitGetAsEntry,返回扁平文件列表 + 目录结构
13
+ */
14
+ function readEntry(
15
+ entry: FileSystemEntry,
16
+ path: string,
17
+ ): Promise<DropEntry> {
18
+ if (entry.isFile) {
19
+ return new Promise((resolve) => {
20
+ (entry as FileSystemFileEntry).file((file) => {
21
+ resolve({ name: entry.name, relativePath: path, file, isDirectory: false });
22
+ });
23
+ });
24
+ }
25
+ // Directory
26
+ return new Promise((resolve) => {
27
+ const reader = (entry as FileSystemDirectoryEntry).createReader();
28
+ const children: DropEntry[] = [];
29
+
30
+ const readBatch = () => {
31
+ reader.readEntries(async (entries) => {
32
+ if (entries.length === 0) {
33
+ resolve({ name: entry.name, relativePath: path, isDirectory: true, children });
34
+ return;
35
+ }
36
+ for (const e of entries) {
37
+ children.push(await readEntry(e, `${path}/${e.name}`));
38
+ }
39
+ readBatch(); // readEntries may not return all entries in one call
40
+ });
41
+ };
42
+ readBatch();
43
+ });
44
+ }
45
+
46
+ /**
47
+ * 从 DataTransfer 递归读取所有文件和目录
48
+ */
49
+ export async function readDropItems(dataTransfer: DataTransfer): Promise<DropEntry[]> {
50
+ const items = dataTransfer.items;
51
+ if (!items) return [];
52
+
53
+ const entries: DropEntry[] = [];
54
+ const tasks: Promise<void>[] = [];
55
+
56
+ for (let i = 0; i < items.length; i++) {
57
+ const item = items[i];
58
+ // Try webkitGetAsEntry (Chrome, Edge, Safari)
59
+ const entry = item.webkitGetAsEntry?.();
60
+ if (entry) {
61
+ tasks.push(
62
+ readEntry(entry, entry.name).then<void>((e) => { entries.push(e); }),
63
+ );
64
+ }
65
+ }
66
+
67
+ await Promise.all(tasks);
68
+ return entries;
69
+ }
70
+
71
+ /**
72
+ * Web 端:递归上传 entries 到目标目录
73
+ */
74
+ export async function uploadEntriesWeb(entries: DropEntry[], destDir: string): Promise<number> {
75
+ let count = 0;
76
+
77
+ async function process(entry: DropEntry, currentDir: string): Promise<void> {
78
+ if (entry.isDirectory) {
79
+ // Create directory
80
+ const dirPath = `${currentDir}/${entry.name}`;
81
+ await apiClient.call("file.createDir", { dirPath: currentDir, name: entry.name });
82
+ count++;
83
+ if (entry.children) {
84
+ for (const child of entry.children) {
85
+ await process(child, dirPath);
86
+ }
87
+ }
88
+ } else if (entry.file) {
89
+ // Upload file via HTTP
90
+ const filePath = `${currentDir}/${entry.name}`;
91
+ const arrayBuffer = await entry.file.arrayBuffer();
92
+ const baseUrl = apiClient.getBaseUrl();
93
+ const token = apiClient.getAuthToken();
94
+ const res = await fetch(
95
+ `${baseUrl}/file/upload?path=${encodeURIComponent(filePath)}&token=${token}`,
96
+ { method: "POST", body: arrayBuffer },
97
+ );
98
+ if (!res.ok) throw new Error(`Upload failed: ${entry.name}`);
99
+ count++;
100
+ }
101
+ }
102
+
103
+ for (const entry of entries) {
104
+ await process(entry, destDir);
105
+ }
106
+ return count;
107
+ }
108
+
109
+ /**
110
+ * 桌面端:通过 RPC file.copy 直接复制
111
+ */
112
+ export async function importFilesDesktop(entries: DropEntry[], destDir: string): Promise<number> {
113
+ let count = 0;
114
+
115
+ async function process(entry: DropEntry, currentDir: string): Promise<void> {
116
+ if (entry.isDirectory) {
117
+ const dirPath = `${currentDir}/${entry.name}`;
118
+ await apiClient.call("file.createDir", { dirPath: currentDir, name: entry.name });
119
+ count++;
120
+ if (entry.children) {
121
+ for (const child of entry.children) {
122
+ await process(child, dirPath);
123
+ }
124
+ }
125
+ } else if (entry.file) {
126
+ // Desktop: File object has .path property (Electron/Electrobun)
127
+ const srcPath = (entry.file as File & { path?: string }).path;
128
+ if (srcPath) {
129
+ await apiClient.call("file.copy", { srcPath, destDir: currentDir });
130
+ } else {
131
+ // Fallback: no path available, upload via HTTP
132
+ const filePath = `${currentDir}/${entry.name}`;
133
+ const arrayBuffer = await entry.file.arrayBuffer();
134
+ const baseUrl = apiClient.getBaseUrl();
135
+ const token = apiClient.getAuthToken();
136
+ const res = await fetch(
137
+ `${baseUrl}/file/upload?path=${encodeURIComponent(filePath)}&token=${token}`,
138
+ { method: "POST", body: arrayBuffer },
139
+ );
140
+ if (!res.ok) throw new Error(`Upload failed: ${entry.name}`);
141
+ }
142
+ count++;
143
+ }
144
+ }
145
+
146
+ for (const entry of entries) {
147
+ await process(entry, destDir);
148
+ }
149
+ return count;
150
+ }
@@ -0,0 +1,24 @@
1
+ import {
2
+ FolderOpen,
3
+ Folder,
4
+ FileText,
5
+ FileCode,
6
+ Image,
7
+ FileArchive,
8
+ } from "lucide-react";
9
+ import type { TreeNode } from "../types";
10
+
11
+ export function getFileIcon(node: TreeNode) {
12
+ if (node.type === "directory") {
13
+ return node.expanded ? (
14
+ <FolderOpen className="w-4 h-4 text-yellow-400 shrink-0" />
15
+ ) : (
16
+ <Folder className="w-4 h-4 text-yellow-400 shrink-0" />
17
+ );
18
+ }
19
+ const ext = node.name.split(".").pop()?.toLowerCase() || "";
20
+ if (["ts", "tsx", "js", "jsx"].includes(ext)) return <FileCode className="w-4 h-4 text-blue-400 shrink-0" />;
21
+ if (["png", "jpg", "jpeg", "gif", "svg", "webp"].includes(ext)) return <Image className="w-4 h-4 text-green-400 shrink-0" />;
22
+ if (["zip", "gz", "tar", "rar"].includes(ext)) return <FileArchive className="w-4 h-4 text-orange-400 shrink-0" />;
23
+ return <FileText className="w-4 h-4 text-gray-400 shrink-0" />;
24
+ }
@@ -0,0 +1,35 @@
1
+ export function getLanguage(filename: string): string {
2
+ const ext = filename.split(".").pop()?.toLowerCase() || "";
3
+ const map: Record<string, string> = {
4
+ ts: "typescript", tsx: "tsx", js: "javascript", jsx: "jsx",
5
+ mjs: "javascript", cjs: "javascript", mts: "typescript", cts: "typescript",
6
+ json: "json", html: "markup", css: "css", md: "markdown",
7
+ py: "python", rs: "rust", go: "go", sh: "bash", bash: "bash",
8
+ yml: "yaml", yaml: "yaml", toml: "toml", xml: "markup",
9
+ sql: "sql", graphql: "graphql",
10
+ };
11
+ return map[ext] || "";
12
+ }
13
+
14
+ export function isTextFile(filename: string): boolean {
15
+ const ext = filename.split(".").pop()?.toLowerCase() || "";
16
+ const textExts = new Set([
17
+ "ts", "tsx", "js", "jsx", "json", "html", "css", "scss", "less",
18
+ "md", "txt", "py", "rs", "go", "sh", "bash", "yml", "yaml", "toml",
19
+ "xml", "sql", "graphql", "env", "gitignore", "prettierrc", "eslintrc",
20
+ "lock", "log", "conf", "cfg", "ini", "csv", "tsv",
21
+ "mjs", "cjs", "mts", "cts", "map",
22
+ ]);
23
+ return textExts.has(ext);
24
+ }
25
+
26
+ export function isImageFile(filename: string): boolean {
27
+ const ext = filename.split(".").pop()?.toLowerCase() || "";
28
+ return ["png", "jpg", "jpeg", "gif", "svg", "webp", "ico", "bmp"].includes(ext);
29
+ }
30
+
31
+ export function formatSize(bytes: number): string {
32
+ if (bytes < 1024) return `${bytes} B`;
33
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
34
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
35
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Web server configuration — single source of truth.
3
+ * Values are read from environment variables with sensible defaults.
4
+ */
5
+
6
+ export function parseEnvInt(
7
+ key: string,
8
+ value: string | undefined,
9
+ defaultValue: number,
10
+ min: number,
11
+ max: number,
12
+ ): number {
13
+ if (value === undefined || value === "") return defaultValue;
14
+ const parsed = parseInt(value, 10);
15
+ if (isNaN(parsed) || parsed < min || parsed > max) {
16
+ console.warn(
17
+ `[config] Invalid ${key}: "${value}", using default: ${defaultValue}`,
18
+ );
19
+ return defaultValue;
20
+ }
21
+ return parsed;
22
+ }
23
+
24
+ export const config = {
25
+ port: parseEnvInt("PORT", process.env.PORT, 3100, 1024, 65535),
26
+ authToken: process.env.AUTH_TOKEN || "pi-agent-template-token",
27
+ maxUploadSize: parseEnvInt(
28
+ "MAX_UPLOAD_SIZE",
29
+ process.env.MAX_UPLOAD_SIZE,
30
+ 50 * 1024 * 1024,
31
+ 0,
32
+ 1024 * 1024 * 1024,
33
+ ),
34
+ logDir: process.env.LOG_DIR || "logs",
35
+ enableBash: process.env.ENABLE_BASH !== "false",
36
+ corsOrigin: process.env.CORS_ORIGIN || "http://localhost:5173",
37
+ } as const;
38
+
39
+ if (process.env.NODE_ENV === "production" && !process.env.AUTH_TOKEN) {
40
+ console.warn(
41
+ "[security] WARNING: Using default AUTH_TOKEN in production. Set AUTH_TOKEN environment variable.",
42
+ );
43
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Web server entry point — HTTP file endpoints + WebSocket RPC gateway.
3
+ * Port auto-negotiation: tries config.port first, increments on EADDRINUSE.
4
+ * Writes actual port to .server-port for dev orchestration.
5
+ */
6
+
7
+ import { createServer } from "http";
8
+ import { writeFileSync, unlinkSync, existsSync } from "fs";
9
+ import { join, resolve, basename } from "path";
10
+ import { config } from "./server-config";
11
+ import { createHttpHandler } from "./gateway/http-routes";
12
+ import { createWsHandler } from "./gateway/ws-handler";
13
+ import { createLogger, configureLogDir } from "./shared/lib/logger";
14
+ import { registerPort, unregisterPort, formatRegistryForOutput } from "./shared/lib/port-registry";
15
+ import { discoverMethodNames } from "./shared/register-all-handlers";
16
+
17
+ configureLogDir(config.logDir);
18
+ const log = createLogger("server");
19
+
20
+ const PORT_FILE = join(import.meta.dir, "..", ".server-port");
21
+ const PROJECT_ROOT = resolve(import.meta.dir, "..");
22
+ const PROJECT_NAME = basename(PROJECT_ROOT);
23
+
24
+ function cleanupPortFile() {
25
+ try { if (existsSync(PORT_FILE)) unlinkSync(PORT_FILE); } catch {}
26
+ unregisterPort(PROJECT_ROOT);
27
+ }
28
+
29
+ function writePortFile(port: number) {
30
+ writeFileSync(PORT_FILE, String(port), "utf-8");
31
+ }
32
+
33
+ process.on("exit", cleanupPortFile);
34
+ process.on("SIGINT", () => { cleanupPortFile(); process.exit(0); });
35
+ process.on("SIGTERM", () => { cleanupPortFile(); process.exit(0); });
36
+
37
+ const httpServer = createServer();
38
+ const wss = createWsHandler(httpServer, { config });
39
+
40
+ httpServer.on("request", createHttpHandler({
41
+ config,
42
+ getWebSocketClientCount: () => wss.clients.size,
43
+ }));
44
+
45
+ function checkPort(port: number): Promise<number> {
46
+ return new Promise((resolve, reject) => {
47
+ const testServer = createServer();
48
+ testServer.once("error", (err: NodeJS.ErrnoException) => {
49
+ testServer.close();
50
+ reject(err);
51
+ });
52
+ testServer.once("listening", () => {
53
+ testServer.close();
54
+ resolve(port);
55
+ });
56
+ testServer.listen(port);
57
+ });
58
+ }
59
+
60
+ async function findAvailablePort(startPort: number, maxRetries: number = 10): Promise<number> {
61
+ for (let port = startPort; port < startPort + maxRetries; port++) {
62
+ try {
63
+ await checkPort(port);
64
+ return port;
65
+ } catch {
66
+ log.info(`Port ${port} in use, trying ${port + 1}...`);
67
+ }
68
+ }
69
+ throw new Error(`No available port found after ${maxRetries} retries`);
70
+ }
71
+
72
+ async function start() {
73
+ const port = await findAvailablePort(config.port);
74
+ httpServer.listen(port, () => {
75
+ writePortFile(port);
76
+ registerPort(PROJECT_ROOT, port, PROJECT_NAME);
77
+ log.info(`HTTP + WebSocket server running on http://localhost:${port}`);
78
+ log.info(`WebSocket: ws://localhost:${port}/ws (auth required)`);
79
+ log.info(`Available RPC methods: ${discoverMethodNames().join(", ")}`);
80
+ log.info("File endpoints: GET /file/{path}, GET /info/{path}");
81
+ // eslint-disable-next-line no-console
82
+ console.log("\n" + formatRegistryForOutput() + "\n");
83
+ });
84
+ }
85
+
86
+ start().catch((err) => {
87
+ log.error("Server failed to start", { error: err.message });
88
+ process.exit(1);
89
+ });
@@ -0,0 +1,135 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import type { RPCServer } from "@dyyz1993/rpc-core";
3
+
4
+ vi.stubGlobal("Bun", {
5
+ spawn: vi.fn(),
6
+ });
7
+
8
+ vi.mock("../../lib/logger", () => ({
9
+ createLogger: () => ({
10
+ info: vi.fn(),
11
+ error: vi.fn(),
12
+ warn: vi.fn(),
13
+ debug: vi.fn(),
14
+ }),
15
+ }));
16
+
17
+ describe("Bash Handler", () => {
18
+ let registeredHandlers: Record<string, Function>;
19
+ let mockServer: {
20
+ register: ReturnType<typeof vi.fn>;
21
+ emitEvent: ReturnType<typeof vi.fn>;
22
+ };
23
+
24
+ function createMockSubprocess(overrides: { stdout?: string; stderr?: string; exitCode?: number; pid?: number } = {}) {
25
+ return {
26
+ pid: overrides.pid ?? 1234,
27
+ stdout: { text: () => Promise.resolve(overrides.stdout ?? "") },
28
+ stderr: { text: () => Promise.resolve(overrides.stderr ?? "") },
29
+ exited: Promise.resolve(overrides.exitCode ?? 0),
30
+ };
31
+ }
32
+
33
+ beforeEach(async () => {
34
+ vi.resetModules();
35
+ registeredHandlers = {};
36
+ mockServer = {
37
+ register: vi.fn((method: string, handler: Function) => {
38
+ registeredHandlers[method] = handler;
39
+ }),
40
+ emitEvent: vi.fn(),
41
+ };
42
+
43
+ (Bun.spawn as ReturnType<typeof vi.fn>).mockReturnValue(createMockSubprocess());
44
+
45
+ vi.stubGlobal("Response", class {
46
+ private src: any;
47
+ constructor(src: any) { this.src = src; }
48
+ async text() {
49
+ if (this.src && typeof this.src.text === "function") return this.src.text();
50
+ return String(this.src);
51
+ }
52
+ });
53
+
54
+ const { register } = await import("../bash");
55
+ register(mockServer as unknown as RPCServer, { platform: "web" });
56
+ });
57
+
58
+ it("should register bash.execute, bash.kill, bash.listProcesses", () => {
59
+ expect(registeredHandlers["bash.execute"]).toBeDefined();
60
+ expect(registeredHandlers["bash.kill"]).toBeDefined();
61
+ expect(registeredHandlers["bash.listProcesses"]).toBeDefined();
62
+ });
63
+
64
+ it("bash.execute should run command and return output", async () => {
65
+ (Bun.spawn as ReturnType<typeof vi.fn>).mockReturnValue(
66
+ createMockSubprocess({ stdout: "hello output" }),
67
+ );
68
+
69
+ const result = await registeredHandlers["bash.execute"]({ command: "echo hello" });
70
+ expect(result).toHaveProperty("pid");
71
+ expect(result.output).toContain("hello output");
72
+ expect(Bun.spawn).toHaveBeenCalledWith(
73
+ ["echo", "hello"],
74
+ expect.objectContaining({ stdout: "pipe", stderr: "pipe" }),
75
+ );
76
+ });
77
+
78
+ it("bash.execute should include stderr in output", async () => {
79
+ (Bun.spawn as ReturnType<typeof vi.fn>).mockReturnValue(
80
+ createMockSubprocess({ stdout: "", stderr: "error msg", exitCode: 1 }),
81
+ );
82
+
83
+ const result = await registeredHandlers["bash.execute"]({ command: "ls /nonexistent" });
84
+ expect(result.output).toContain("error msg");
85
+ expect(result.output).toContain("[stderr]");
86
+ expect(mockServer.emitEvent).toHaveBeenCalledWith(
87
+ "bash.exit",
88
+ expect.objectContaining({ code: 1 }),
89
+ {},
90
+ );
91
+ });
92
+
93
+ it("bash.execute should handle spawn failure gracefully", async () => {
94
+ (Bun.spawn as ReturnType<typeof vi.fn>).mockImplementation(() => {
95
+ throw new Error("spawn failed");
96
+ });
97
+
98
+ const result = await registeredHandlers["bash.execute"]({ command: "bad-command" });
99
+ expect(result.output).toContain("Error: spawn failed");
100
+ expect(mockServer.emitEvent).toHaveBeenCalledWith(
101
+ "bash.exit",
102
+ expect.objectContaining({ code: 1 }),
103
+ {},
104
+ );
105
+ });
106
+
107
+ it("bash.listProcesses should return tracked processes", async () => {
108
+ (Bun.spawn as ReturnType<typeof vi.fn>).mockReturnValue(
109
+ createMockSubprocess({ stdout: "test" }),
110
+ );
111
+
112
+ await registeredHandlers["bash.execute"]({ command: "echo test" });
113
+ const result = await registeredHandlers["bash.listProcesses"]();
114
+ expect(result.processes).toBeInstanceOf(Array);
115
+ expect(result.processes.length).toBeGreaterThanOrEqual(1);
116
+ expect(result.processes[0]).toHaveProperty("command");
117
+ expect(result.processes[0]).toHaveProperty("pid");
118
+ });
119
+
120
+ it("bash.kill should succeed for tracked process using returned pid", async () => {
121
+ (Bun.spawn as ReturnType<typeof vi.fn>).mockReturnValue(
122
+ createMockSubprocess({ stdout: "ok", pid: 9999 }),
123
+ );
124
+
125
+ const execResult = await registeredHandlers["bash.execute"]({ command: "echo hi" });
126
+
127
+ const killResult = await registeredHandlers["bash.kill"]({ pid: execResult.pid });
128
+ expect(killResult).toEqual({ success: true });
129
+ });
130
+
131
+ it("bash.kill should fail for unknown pid", async () => {
132
+ const result = await registeredHandlers["bash.kill"]({ pid: 99999 });
133
+ expect(result).toEqual({ success: false });
134
+ });
135
+ });
@@ -0,0 +1,77 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+
3
+ vi.mock("../../lib/logger", () => ({
4
+ createLogger: () => ({
5
+ info: vi.fn(),
6
+ error: vi.fn(),
7
+ warn: vi.fn(),
8
+ debug: vi.fn(),
9
+ }),
10
+ }));
11
+
12
+ describe("Chat Handler", () => {
13
+ beforeEach(() => {
14
+ vi.resetModules();
15
+ });
16
+
17
+ describe("generateReply logic", () => {
18
+ it("should handle greeting patterns", () => {
19
+ const greetingPattern = /^(hi|hello|hey|howdy|hola|yo|sup)\b/i;
20
+ expect(greetingPattern.test("hello")).toBe(true);
21
+ expect(greetingPattern.test("hi there")).toBe(true);
22
+ expect(greetingPattern.test("what is this")).toBe(false);
23
+ });
24
+
25
+ it("should match time/date queries", () => {
26
+ const timePattern = /what('?s| is) the (time|date|day)|current (time|date)|what time|today'?s date/i;
27
+ expect(timePattern.test("what's the time")).toBe(true);
28
+ expect(timePattern.test("what is the date")).toBe(true);
29
+ expect(timePattern.test("current time")).toBe(true);
30
+ expect(timePattern.test("hello world")).toBe(false);
31
+ });
32
+
33
+ it("should match math expressions", () => {
34
+ const mathPattern = /(?:what(?:'s| is)\s+)?(\d+(?:\.\d+)?)\s*([+\-*/x×÷^])\s*(\d+(?:\.\d+)?)/;
35
+ expect(mathPattern.test("12 * 8")).toBe(true);
36
+ expect(mathPattern.test("what is 100 / 4")).toBe(true);
37
+ expect(mathPattern.test("3 + 5")).toBe(true);
38
+ expect(mathPattern.test("hello")).toBe(false);
39
+ });
40
+
41
+ it("should compute math correctly", () => {
42
+ const compute = (a: number, op: string, b: number): number => {
43
+ switch (op) {
44
+ case "+": return a + b;
45
+ case "-": return a - b;
46
+ case "*": return a * b;
47
+ case "/": return b !== 0 ? a / b : NaN;
48
+ default: return NaN;
49
+ }
50
+ };
51
+
52
+ expect(compute(3, "+", 5)).toBe(8);
53
+ expect(compute(10, "-", 4)).toBe(6);
54
+ expect(compute(6, "*", 7)).toBe(42);
55
+ expect(compute(20, "/", 4)).toBe(5);
56
+ expect(compute(1, "/", 0)).toBeNaN();
57
+ });
58
+ });
59
+
60
+ describe("register", () => {
61
+ it("should register chat.list and chat.send methods", async () => {
62
+ const registeredMethods: string[] = [];
63
+ const mockServer = {
64
+ register: vi.fn((method: string) => {
65
+ registeredMethods.push(method);
66
+ }),
67
+ emitEvent: vi.fn(),
68
+ };
69
+
70
+ const { register } = await import("../chat");
71
+ register(mockServer as any, { platform: "web" });
72
+
73
+ expect(registeredMethods).toContain("chat.list");
74
+ expect(registeredMethods).toContain("chat.send");
75
+ });
76
+ });
77
+ });