dominds 0.1.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 (273) hide show
  1. package/LICENSE +157 -0
  2. package/README.md +250 -0
  3. package/README.zh.md +161 -0
  4. package/dist/access-control.js +253 -0
  5. package/dist/cli/create.js +263 -0
  6. package/dist/cli/read.js +84 -0
  7. package/dist/cli/tui.js +199 -0
  8. package/dist/cli/webui.js +169 -0
  9. package/dist/cli.js +227 -0
  10. package/dist/dialog-factory.js +53 -0
  11. package/dist/dialog-global-registry.js +68 -0
  12. package/dist/dialog-instance-registry.js +78 -0
  13. package/dist/dialog-run-state.js +198 -0
  14. package/dist/dialog.js +1024 -0
  15. package/dist/evt-registry.js +103 -0
  16. package/dist/index.js +8 -0
  17. package/dist/llm/client.js +69 -0
  18. package/dist/llm/defaults.yaml +386 -0
  19. package/dist/llm/driver.js +3214 -0
  20. package/dist/llm/gen/anthropic.js +611 -0
  21. package/dist/llm/gen/codex.js +375 -0
  22. package/dist/llm/gen/mock.js +326 -0
  23. package/dist/llm/gen/openai.js +470 -0
  24. package/dist/llm/gen/registry.js +26 -0
  25. package/dist/llm/gen.js +2 -0
  26. package/dist/llm/tools-projection.js +37 -0
  27. package/dist/log.js +228 -0
  28. package/dist/mcp/config.js +230 -0
  29. package/dist/mcp/sdk-client.js +129 -0
  30. package/dist/mcp/server-runtime.js +57 -0
  31. package/dist/mcp/stdio-client.js +280 -0
  32. package/dist/mcp/supervisor.js +979 -0
  33. package/dist/mcp/tool-names.js +109 -0
  34. package/dist/minds/builtin/cmdr/persona.md +3 -0
  35. package/dist/minds/builtin/dijiang/knowledge.md +287 -0
  36. package/dist/minds/builtin/dijiang/persona.md +7 -0
  37. package/dist/minds/builtin/fuxi/persona.en.md +59 -0
  38. package/dist/minds/builtin/fuxi/persona.zh.md +49 -0
  39. package/dist/minds/builtin/pangu/persona.en.md +78 -0
  40. package/dist/minds/builtin/pangu/persona.zh.md +71 -0
  41. package/dist/minds/load.js +617 -0
  42. package/dist/minds/minds-i18n.js +131 -0
  43. package/dist/minds/system-prompt.js +281 -0
  44. package/dist/persistence.js +3128 -0
  45. package/dist/problems.js +109 -0
  46. package/dist/server/api-routes.js +1031 -0
  47. package/dist/server/auth.js +180 -0
  48. package/dist/server/mime-types.js +32 -0
  49. package/dist/server/prompts-routes.js +543 -0
  50. package/dist/server/server-core.js +235 -0
  51. package/dist/server/setup-routes.js +697 -0
  52. package/dist/server/static-server.js +132 -0
  53. package/dist/server/websocket-handler.js +1011 -0
  54. package/dist/server.js +164 -0
  55. package/dist/shared/async-fifo-mutex.js +36 -0
  56. package/dist/shared/diligence.js +20 -0
  57. package/dist/shared/dotenv.js +144 -0
  58. package/dist/shared/evt.js +195 -0
  59. package/dist/shared/i18n/driver-messages.js +267 -0
  60. package/dist/shared/i18n/text.js +9 -0
  61. package/dist/shared/i18n/tool-result-messages.js +51 -0
  62. package/dist/shared/rtws-cli.js +73 -0
  63. package/dist/shared/runtime-language.js +47 -0
  64. package/dist/shared/team-mgmt-manual.js +116 -0
  65. package/dist/shared/types/context-health.js +2 -0
  66. package/dist/shared/types/dialog.js +11 -0
  67. package/dist/shared/types/i18n.js +2 -0
  68. package/dist/shared/types/index.js +26 -0
  69. package/dist/shared/types/language.js +40 -0
  70. package/dist/shared/types/problems.js +2 -0
  71. package/dist/shared/types/prompts.js +2 -0
  72. package/dist/shared/types/q4h.js +7 -0
  73. package/dist/shared/types/run-state.js +8 -0
  74. package/dist/shared/types/setup.js +2 -0
  75. package/dist/shared/types/storage.js +10 -0
  76. package/dist/shared/types/tellask.js +8 -0
  77. package/dist/shared/types/tools-registry.js +2 -0
  78. package/dist/shared/types/wire.js +12 -0
  79. package/dist/shared/utils/fmt.js +9 -0
  80. package/dist/shared/utils/html.js +20 -0
  81. package/dist/shared/utils/id.js +18 -0
  82. package/dist/shared/utils/inter-dialog-format.js +101 -0
  83. package/dist/shared/utils/time.js +13 -0
  84. package/dist/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  85. package/dist/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  86. package/dist/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  87. package/dist/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  88. package/dist/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  89. package/dist/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  90. package/dist/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  91. package/dist/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  92. package/dist/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  93. package/dist/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  94. package/dist/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  95. package/dist/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  96. package/dist/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  97. package/dist/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  98. package/dist/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  99. package/dist/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  100. package/dist/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  101. package/dist/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  102. package/dist/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  103. package/dist/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  104. package/dist/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  105. package/dist/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  106. package/dist/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  107. package/dist/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  108. package/dist/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  109. package/dist/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  110. package/dist/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  111. package/dist/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  112. package/dist/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  113. package/dist/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  114. package/dist/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  115. package/dist/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  116. package/dist/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  117. package/dist/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  118. package/dist/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  119. package/dist/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  120. package/dist/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  121. package/dist/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  122. package/dist/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  123. package/dist/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  124. package/dist/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  125. package/dist/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  126. package/dist/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  127. package/dist/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  128. package/dist/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  129. package/dist/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  130. package/dist/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  131. package/dist/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  132. package/dist/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  133. package/dist/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  134. package/dist/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  135. package/dist/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  136. package/dist/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  137. package/dist/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  138. package/dist/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  139. package/dist/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  140. package/dist/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  141. package/dist/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  142. package/dist/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  143. package/dist/static/assets/_baseUniq-Crfl3d5Y.js +661 -0
  144. package/dist/static/assets/_baseUniq-Crfl3d5Y.js.map +1 -0
  145. package/dist/static/assets/arc-CbA_x9GD.js +132 -0
  146. package/dist/static/assets/arc-CbA_x9GD.js.map +1 -0
  147. package/dist/static/assets/architectureDiagram-VXUJARFQ-lcFS8ZQJ.js +8685 -0
  148. package/dist/static/assets/architectureDiagram-VXUJARFQ-lcFS8ZQJ.js.map +1 -0
  149. package/dist/static/assets/blockDiagram-VD42YOAC-B3Q36qRc.js +3608 -0
  150. package/dist/static/assets/blockDiagram-VD42YOAC-B3Q36qRc.js.map +1 -0
  151. package/dist/static/assets/c4Diagram-YG6GDRKO-Mt-aq3VH.js +2482 -0
  152. package/dist/static/assets/c4Diagram-YG6GDRKO-Mt-aq3VH.js.map +1 -0
  153. package/dist/static/assets/channel-BVr1Yke-.js +8 -0
  154. package/dist/static/assets/channel-BVr1Yke-.js.map +1 -0
  155. package/dist/static/assets/chunk-4BX2VUAB-qCIn5Iic.js +17 -0
  156. package/dist/static/assets/chunk-4BX2VUAB-qCIn5Iic.js.map +1 -0
  157. package/dist/static/assets/chunk-55IACEB6-q172NeCV.js +14 -0
  158. package/dist/static/assets/chunk-55IACEB6-q172NeCV.js.map +1 -0
  159. package/dist/static/assets/chunk-B4BG7PRW-CMJmtYzq.js +1827 -0
  160. package/dist/static/assets/chunk-B4BG7PRW-CMJmtYzq.js.map +1 -0
  161. package/dist/static/assets/chunk-DI55MBZ5-DiuwwZPL.js +1916 -0
  162. package/dist/static/assets/chunk-DI55MBZ5-DiuwwZPL.js.map +1 -0
  163. package/dist/static/assets/chunk-FMBD7UC4-06sqZTTn.js +20 -0
  164. package/dist/static/assets/chunk-FMBD7UC4-06sqZTTn.js.map +1 -0
  165. package/dist/static/assets/chunk-QN33PNHL-CnpBNkpP.js +25 -0
  166. package/dist/static/assets/chunk-QN33PNHL-CnpBNkpP.js.map +1 -0
  167. package/dist/static/assets/chunk-QZHKN3VN-CNgjMR-e.js +18 -0
  168. package/dist/static/assets/chunk-QZHKN3VN-CNgjMR-e.js.map +1 -0
  169. package/dist/static/assets/chunk-TZMSLE5B-BxtzW6--.js +109 -0
  170. package/dist/static/assets/chunk-TZMSLE5B-BxtzW6--.js.map +1 -0
  171. package/dist/static/assets/classDiagram-2ON5EDUG-29huvmn-.js +23 -0
  172. package/dist/static/assets/classDiagram-2ON5EDUG-29huvmn-.js.map +1 -0
  173. package/dist/static/assets/classDiagram-v2-WZHVMYZB-29huvmn-.js +23 -0
  174. package/dist/static/assets/classDiagram-v2-WZHVMYZB-29huvmn-.js.map +1 -0
  175. package/dist/static/assets/clone-D2OgLSSn.js +9 -0
  176. package/dist/static/assets/clone-D2OgLSSn.js.map +1 -0
  177. package/dist/static/assets/cose-bilkent-S5V4N54A-BNegDCxl.js +4943 -0
  178. package/dist/static/assets/cose-bilkent-S5V4N54A-BNegDCxl.js.map +1 -0
  179. package/dist/static/assets/cytoscape.esm-Bm8DJGmZ.js +30240 -0
  180. package/dist/static/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -0
  181. package/dist/static/assets/dagre-6UL2VRFP-f1XrTRSn.js +695 -0
  182. package/dist/static/assets/dagre-6UL2VRFP-f1XrTRSn.js.map +1 -0
  183. package/dist/static/assets/defaultLocale-DVr69WTU.js +207 -0
  184. package/dist/static/assets/defaultLocale-DVr69WTU.js.map +1 -0
  185. package/dist/static/assets/diagram-PSM6KHXK-8w1WbeDi.js +849 -0
  186. package/dist/static/assets/diagram-PSM6KHXK-8w1WbeDi.js.map +1 -0
  187. package/dist/static/assets/diagram-QEK2KX5R-CF4wtMmR.js +303 -0
  188. package/dist/static/assets/diagram-QEK2KX5R-CF4wtMmR.js.map +1 -0
  189. package/dist/static/assets/diagram-S2PKOQOG-8p3Avgn2.js +213 -0
  190. package/dist/static/assets/diagram-S2PKOQOG-8p3Avgn2.js.map +1 -0
  191. package/dist/static/assets/erDiagram-Q2GNP2WA-BMKLxlM9.js +1159 -0
  192. package/dist/static/assets/erDiagram-Q2GNP2WA-BMKLxlM9.js.map +1 -0
  193. package/dist/static/assets/favicon-Cmg5RbCj.svg +8 -0
  194. package/dist/static/assets/flowDiagram-NV44I4VS-CgEuPNK2.js +2332 -0
  195. package/dist/static/assets/flowDiagram-NV44I4VS-CgEuPNK2.js.map +1 -0
  196. package/dist/static/assets/ganttDiagram-JELNMOA3-bJkDCf-9.js +3681 -0
  197. package/dist/static/assets/ganttDiagram-JELNMOA3-bJkDCf-9.js.map +1 -0
  198. package/dist/static/assets/gitGraphDiagram-NY62KEGX-4QE9kesp.js +1206 -0
  199. package/dist/static/assets/gitGraphDiagram-NY62KEGX-4QE9kesp.js.map +1 -0
  200. package/dist/static/assets/graph-CS0Pmm7c.js +597 -0
  201. package/dist/static/assets/graph-CS0Pmm7c.js.map +1 -0
  202. package/dist/static/assets/index-BS6HnGzC.js +112303 -0
  203. package/dist/static/assets/index-BS6HnGzC.js.map +1 -0
  204. package/dist/static/assets/index-DaIsSzC_.css +483 -0
  205. package/dist/static/assets/infoDiagram-WHAUD3N6-ypBcKfUs.js +34 -0
  206. package/dist/static/assets/infoDiagram-WHAUD3N6-ypBcKfUs.js.map +1 -0
  207. package/dist/static/assets/init-ZxktEp_H.js +17 -0
  208. package/dist/static/assets/init-ZxktEp_H.js.map +1 -0
  209. package/dist/static/assets/journeyDiagram-XKPGCS4Q-QnrxDowJ.js +1255 -0
  210. package/dist/static/assets/journeyDiagram-XKPGCS4Q-QnrxDowJ.js.map +1 -0
  211. package/dist/static/assets/kanban-definition-3W4ZIXB7-CfvEc4z5.js +1048 -0
  212. package/dist/static/assets/kanban-definition-3W4ZIXB7-CfvEc4z5.js.map +1 -0
  213. package/dist/static/assets/layout-8TGxpm23.js +2218 -0
  214. package/dist/static/assets/layout-8TGxpm23.js.map +1 -0
  215. package/dist/static/assets/linear-BATBPQQv.js +341 -0
  216. package/dist/static/assets/linear-BATBPQQv.js.map +1 -0
  217. package/dist/static/assets/min-B3oVH3AC.js +42 -0
  218. package/dist/static/assets/min-B3oVH3AC.js.map +1 -0
  219. package/dist/static/assets/mindmap-definition-VGOIOE7T-L7VLwwF8.js +1127 -0
  220. package/dist/static/assets/mindmap-definition-VGOIOE7T-L7VLwwF8.js.map +1 -0
  221. package/dist/static/assets/ordinal-CxptdPJm.js +77 -0
  222. package/dist/static/assets/ordinal-CxptdPJm.js.map +1 -0
  223. package/dist/static/assets/pieDiagram-ADFJNKIX-CFW3zIhM.js +241 -0
  224. package/dist/static/assets/pieDiagram-ADFJNKIX-CFW3zIhM.js.map +1 -0
  225. package/dist/static/assets/quadrantDiagram-AYHSOK5B-B7ssen3E.js +1338 -0
  226. package/dist/static/assets/quadrantDiagram-AYHSOK5B-B7ssen3E.js.map +1 -0
  227. package/dist/static/assets/requirementDiagram-UZGBJVZJ-D0v5BArv.js +1162 -0
  228. package/dist/static/assets/requirementDiagram-UZGBJVZJ-D0v5BArv.js.map +1 -0
  229. package/dist/static/assets/sankeyDiagram-TZEHDZUN-B7slncJe.js +1195 -0
  230. package/dist/static/assets/sankeyDiagram-TZEHDZUN-B7slncJe.js.map +1 -0
  231. package/dist/static/assets/sequenceDiagram-WL72ISMW-oXU2lRh_.js +3875 -0
  232. package/dist/static/assets/sequenceDiagram-WL72ISMW-oXU2lRh_.js.map +1 -0
  233. package/dist/static/assets/stateDiagram-FKZM4ZOC-CFYsEd0x.js +452 -0
  234. package/dist/static/assets/stateDiagram-FKZM4ZOC-CFYsEd0x.js.map +1 -0
  235. package/dist/static/assets/stateDiagram-v2-4FDKWEC3-C0UWaNA7.js +22 -0
  236. package/dist/static/assets/stateDiagram-v2-4FDKWEC3-C0UWaNA7.js.map +1 -0
  237. package/dist/static/assets/timeline-definition-IT6M3QCI-C3KODUrh.js +1223 -0
  238. package/dist/static/assets/timeline-definition-IT6M3QCI-C3KODUrh.js.map +1 -0
  239. package/dist/static/assets/treemap-KMMF4GRG-DAGDLhj2.js +18753 -0
  240. package/dist/static/assets/treemap-KMMF4GRG-DAGDLhj2.js.map +1 -0
  241. package/dist/static/assets/xychartDiagram-PRI3JC2R-C0J9iwTO.js +1888 -0
  242. package/dist/static/assets/xychartDiagram-PRI3JC2R-C0J9iwTO.js.map +1 -0
  243. package/dist/static/index.html +71 -0
  244. package/dist/static/testing/dom-observation-utils.js +425 -0
  245. package/dist/static/testing/e2e-test-helper.js +3119 -0
  246. package/dist/team.js +1160 -0
  247. package/dist/tellask.js +431 -0
  248. package/dist/tool.js +150 -0
  249. package/dist/tools/apply-patch.js +542 -0
  250. package/dist/tools/builtins.js +196 -0
  251. package/dist/tools/context-health.js +177 -0
  252. package/dist/tools/ctrl.js +478 -0
  253. package/dist/tools/diag.js +583 -0
  254. package/dist/tools/env.js +184 -0
  255. package/dist/tools/fs.js +818 -0
  256. package/dist/tools/mcp.js +138 -0
  257. package/dist/tools/mem.js +349 -0
  258. package/dist/tools/os.js +751 -0
  259. package/dist/tools/prompts/team_mgmt.en.md +70 -0
  260. package/dist/tools/prompts/team_mgmt.zh.md +70 -0
  261. package/dist/tools/prompts/ws_mod.en.md +86 -0
  262. package/dist/tools/prompts/ws_mod.zh.md +87 -0
  263. package/dist/tools/registry-snapshot.js +31 -0
  264. package/dist/tools/registry.js +121 -0
  265. package/dist/tools/ripgrep.js +678 -0
  266. package/dist/tools/team-mgmt.js +3300 -0
  267. package/dist/tools/txt.js +3178 -0
  268. package/dist/utils/id.js +72 -0
  269. package/dist/utils/task-doc.js +236 -0
  270. package/dist/utils/task-package.js +522 -0
  271. package/dist/utils/taskdoc-search.js +280 -0
  272. package/dist/utils/taskdoc.js +400 -0
  273. package/package.json +69 -0
@@ -0,0 +1,818 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.moveDirTool = exports.moveFileTool = exports.mkDirTool = exports.rmFileTool = exports.rmDirTool = exports.listDirTool = void 0;
7
+ /**
8
+ * Module: tools/fs
9
+ *
10
+ * Filesystem tools: list directories, remove directories/files, create/move paths.
11
+ * Includes helpers for text-file detection and line counting.
12
+ */
13
+ const fs_1 = require("fs");
14
+ const promises_1 = __importDefault(require("fs/promises"));
15
+ const path_1 = __importDefault(require("path"));
16
+ const readline_1 = require("readline");
17
+ const access_control_1 = require("../access-control");
18
+ const log_1 = require("../log");
19
+ const runtime_language_1 = require("../shared/runtime-language");
20
+ function formatSize(bytes) {
21
+ if (bytes === 0)
22
+ return '0 B';
23
+ const k = 1024;
24
+ const sizes = ['B', 'KB', 'MB', 'GB'];
25
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
26
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
27
+ }
28
+ function isTextFile(filename) {
29
+ // prettier-ignore
30
+ const textExtensions = [
31
+ '.txt', '.md', '.js', '.ts', '.jsx', '.tsx', '.json', '.xml', '.html', '.htm',
32
+ '.css', '.scss', '.sass', '.less', '.py', '.java', '.c', '.cpp', '.h', '.hpp',
33
+ '.cs', '.php', '.rb', '.go', '.rs', '.swift', '.kt', '.scala', '.sh', '.bash',
34
+ '.zsh', '.fish', '.ps1', '.bat', '.cmd', '.yml', '.yaml', '.toml', '.ini',
35
+ '.cfg', '.conf', '.config', '.env', '.gitignore', '.gitattributes', '.editorconfig',
36
+ '.prettierrc', '.eslintrc', '.babelrc', '.dockerignore', '.dockerfile', '.makefile',
37
+ '.cmake', '.sql', '.graphql', '.gql', '.vue', '.svelte', '.astro', '.r', '.R',
38
+ '.m', '.mm', '.pl', '.pm', '.lua', '.vim', '.vimrc', '.tmux', '.zshrc',
39
+ '.bashrc', '.profile', '.aliases', '.functions', '.exports', '.path', '.extra', '.log',
40
+ ];
41
+ const ext = path_1.default.extname(filename).toLowerCase();
42
+ const basename = path_1.default.basename(filename).toLowerCase();
43
+ // Check by extension
44
+ if (textExtensions.includes(ext)) {
45
+ return true;
46
+ }
47
+ // Check by common filenames without extensions
48
+ // prettier-ignore
49
+ const textFilenames = [
50
+ 'readme', 'license', 'changelog', 'contributing', 'authors', 'contributors',
51
+ 'copying', 'install', 'news', 'todo', 'makefile', 'dockerfile', 'gemfile',
52
+ 'rakefile', 'procfile', 'vagrantfile', 'gruntfile', 'gulpfile', 'webpack',
53
+ ];
54
+ return textFilenames.includes(basename) || textFilenames.includes(basename.split('.')[0]);
55
+ }
56
+ async function countLines(filePath) {
57
+ try {
58
+ const fileStream = (0, fs_1.createReadStream)(filePath, { encoding: 'utf-8' });
59
+ const rl = (0, readline_1.createInterface)({
60
+ input: fileStream,
61
+ crlfDelay: Infinity, // Handle Windows line endings properly
62
+ });
63
+ let lineCount = 0;
64
+ for await (const line of rl) {
65
+ lineCount++;
66
+ }
67
+ return lineCount;
68
+ }
69
+ catch (err) {
70
+ log_1.log.warn(`Failed to count lines in file ${filePath}:`, err);
71
+ return 0; // Return 0 if file can't be read as text
72
+ }
73
+ }
74
+ exports.listDirTool = {
75
+ type: 'func',
76
+ name: 'list_dir',
77
+ description: 'List directory contents relative to workspace with detailed information (sizes, line counts for text files, symlink targets).',
78
+ descriptionI18n: {
79
+ en: 'List directory contents relative to workspace with detailed information (sizes, line counts for text files, symlink targets).',
80
+ zh: '列出工作区内目录内容(包含大小、文本文件行数、符号链接目标等信息)。',
81
+ },
82
+ parameters: {
83
+ type: 'object',
84
+ additionalProperties: false,
85
+ properties: {
86
+ path: {
87
+ type: 'string',
88
+ description: "Workspace-relative directory path. Defaults to '.'.",
89
+ },
90
+ },
91
+ },
92
+ argsValidation: 'dominds',
93
+ call: async (_dlg, caller, args) => {
94
+ const workLanguage = (0, runtime_language_1.getWorkLanguage)();
95
+ const labels = workLanguage === 'zh'
96
+ ? {
97
+ accessDenied: '❌ **访问被拒绝**\n\n路径必须位于工作区内',
98
+ notFound: (p) => `❌ **未找到**\n\n目录 \`${p}\` 不存在。`,
99
+ notDir: (p) => `❌ **错误**\n\n路径 \`${p}\` 不是目录。`,
100
+ readDirFailed: (msg) => `❌ **错误**\n\n读取目录失败:${msg}`,
101
+ dirHeader: '📁 **目录:**',
102
+ emptyDir: '_此目录为空。_',
103
+ table: {
104
+ name: '名称',
105
+ type: '类型',
106
+ size: '大小',
107
+ lines: '行数',
108
+ target: '目标',
109
+ },
110
+ }
111
+ : {
112
+ accessDenied: '❌ **Access Denied**\n\nPath must be within workspace',
113
+ notFound: (p) => `❌ **Not Found**\n\nDirectory \`${p}\` does not exist.`,
114
+ notDir: (p) => `❌ **Error**\n\nPath \`${p}\` is not a directory.`,
115
+ readDirFailed: (msg) => `❌ **Error**\n\nFailed to read directory: ${msg}`,
116
+ dirHeader: '📁 **Directory:**',
117
+ emptyDir: '_This directory is empty._',
118
+ table: {
119
+ name: 'Name',
120
+ type: 'Type',
121
+ size: 'Size',
122
+ lines: 'Lines',
123
+ target: 'Target',
124
+ },
125
+ };
126
+ let rel = '.';
127
+ const pathValue = args['path'];
128
+ if (typeof pathValue === 'string' && pathValue.trim() !== '')
129
+ rel = pathValue.trim();
130
+ // Resolve path relative to current working directory (workspace)
131
+ const dir = path_1.default.resolve(process.cwd(), rel);
132
+ // Basic security check - ensure path is within workspace
133
+ const cwd = path_1.default.resolve(process.cwd());
134
+ if (!dir.startsWith(cwd)) {
135
+ const content = labels.accessDenied;
136
+ return content;
137
+ }
138
+ // Check member access permissions
139
+ if (!(0, access_control_1.hasReadAccess)(caller, rel)) {
140
+ const content = (0, access_control_1.getAccessDeniedMessage)('read', rel, workLanguage);
141
+ return content;
142
+ }
143
+ try {
144
+ try {
145
+ const stats = await promises_1.default.lstat(dir);
146
+ if (!stats.isDirectory()) {
147
+ const content = labels.notDir(rel);
148
+ return content;
149
+ }
150
+ }
151
+ catch (error) {
152
+ if (typeof error === 'object' &&
153
+ error !== null &&
154
+ 'code' in error &&
155
+ error.code === 'ENOENT') {
156
+ const content = labels.notFound(rel);
157
+ return content;
158
+ }
159
+ const msg = error instanceof Error ? error.message : String(error);
160
+ const content = labels.readDirFailed(msg);
161
+ return content;
162
+ }
163
+ const entries = await promises_1.default.readdir(dir, { withFileTypes: true });
164
+ const data = [];
165
+ for (const entry of entries) {
166
+ const entryPath = path_1.default.join(dir, entry.name);
167
+ const dirEntry = {
168
+ name: entry.name,
169
+ type: 'other',
170
+ };
171
+ try {
172
+ const stats = await promises_1.default.lstat(entryPath);
173
+ if (entry.isDirectory()) {
174
+ dirEntry.type = 'dir';
175
+ dirEntry.size = stats.size;
176
+ }
177
+ else if (entry.isFile()) {
178
+ dirEntry.type = 'file';
179
+ dirEntry.size = stats.size;
180
+ // Count lines for text files
181
+ if (isTextFile(entry.name)) {
182
+ dirEntry.lines = await countLines(entryPath);
183
+ }
184
+ }
185
+ else if (entry.isSymbolicLink()) {
186
+ dirEntry.type = 'symlink';
187
+ dirEntry.size = stats.size;
188
+ try {
189
+ const target = await promises_1.default.readlink(entryPath);
190
+ dirEntry.target = target;
191
+ // If symlink points to a text file, count lines from the target
192
+ try {
193
+ const targetStats = await promises_1.default.stat(entryPath); // Follow the symlink
194
+ if (targetStats.isFile() && isTextFile(entry.name)) {
195
+ dirEntry.lines = await countLines(entryPath);
196
+ }
197
+ }
198
+ catch (err) {
199
+ log_1.log.warn(`Failed to stat symlink target ${entryPath}:`, err);
200
+ // Target doesn't exist or can't be accessed
201
+ }
202
+ }
203
+ catch (err) {
204
+ log_1.log.warn(`Failed to read symlink ${entryPath}:`, err);
205
+ dirEntry.target = '<unreadable>';
206
+ }
207
+ }
208
+ else {
209
+ dirEntry.type = 'other';
210
+ dirEntry.size = stats.size;
211
+ }
212
+ }
213
+ catch (error) {
214
+ // If we can't stat the entry, just include basic info
215
+ if (entry.isDirectory()) {
216
+ dirEntry.type = 'dir';
217
+ }
218
+ else if (entry.isFile()) {
219
+ dirEntry.type = 'file';
220
+ }
221
+ else if (entry.isSymbolicLink()) {
222
+ dirEntry.type = 'symlink';
223
+ dirEntry.target = '<error>';
224
+ }
225
+ }
226
+ data.push(dirEntry);
227
+ }
228
+ const relativeDir = path_1.default.relative(cwd, dir) || '.';
229
+ // Create markdown table for directory entries
230
+ let markdown = `${labels.dirHeader} \`${relativeDir}\`\n\n`;
231
+ if (data.length === 0) {
232
+ markdown += labels.emptyDir;
233
+ }
234
+ else {
235
+ markdown += `| ${labels.table.name} | ${labels.table.type} | ${labels.table.size} | ${labels.table.lines} | ${labels.table.target} |\n`;
236
+ markdown += '|------|------|------|-------|--------|\n';
237
+ for (const entry of data) {
238
+ const typeIcon = entry.type === 'dir'
239
+ ? '📁'
240
+ : entry.type === 'file'
241
+ ? '📄'
242
+ : entry.type === 'symlink'
243
+ ? '🔗'
244
+ : '❓';
245
+ const sizeStr = entry.size ? formatSize(entry.size) : '-';
246
+ const linesStr = entry.lines ? entry.lines.toString() : '-';
247
+ const targetStr = entry.target ? `→ ${entry.target}` : '-';
248
+ markdown += `| ${typeIcon} \`${entry.name}\` | ${entry.type} | ${sizeStr} | ${linesStr} | ${targetStr} |\n`;
249
+ }
250
+ }
251
+ return markdown;
252
+ }
253
+ catch (error) {
254
+ if (typeof error === 'object' &&
255
+ error !== null &&
256
+ 'code' in error &&
257
+ error.code === 'ENOENT') {
258
+ const content = labels.notFound(rel);
259
+ return content;
260
+ }
261
+ if (typeof error === 'object' &&
262
+ error !== null &&
263
+ 'code' in error &&
264
+ error.code === 'ENOTDIR') {
265
+ const content = labels.notDir(rel);
266
+ return content;
267
+ }
268
+ const msg = error instanceof Error ? error.message : String(error);
269
+ const content = labels.readDirFailed(msg);
270
+ return content;
271
+ }
272
+ },
273
+ };
274
+ exports.rmDirTool = {
275
+ type: 'func',
276
+ name: 'rm_dir',
277
+ description: 'Remove a directory relative to workspace.',
278
+ descriptionI18n: {
279
+ en: 'Remove a directory relative to workspace.',
280
+ zh: '删除工作区内目录。',
281
+ },
282
+ parameters: {
283
+ type: 'object',
284
+ additionalProperties: false,
285
+ required: ['path'],
286
+ properties: {
287
+ path: { type: 'string', description: 'Workspace-relative directory path.' },
288
+ recursive: {
289
+ type: 'boolean',
290
+ description: 'When true, remove directory and all contents.',
291
+ },
292
+ },
293
+ },
294
+ argsValidation: 'dominds',
295
+ call: async (_dlg, caller, args) => {
296
+ const workLanguage = (0, runtime_language_1.getWorkLanguage)();
297
+ const labels = workLanguage === 'zh'
298
+ ? {
299
+ formatError: '请使用正确的目录删除参数。\n\n**期望参数:** `{ "path": "<path>", "recursive": true|false }`\n\n**示例:**\n```json\n{ \"path\": \"temp\", \"recursive\": true }\n```',
300
+ dirPathRequired: '❌ **错误**\n\n需要提供目录路径。',
301
+ pathMustBeWithinWorkspace: '❌ **错误**\n\n路径必须位于工作区内。',
302
+ notDir: (p) => `❌ **错误**\n\n\`${p}\` 不是目录。`,
303
+ notEmpty: (p) => `❌ **错误**\n\n目录 \`${p}\` 非空。请设置 \`recursive: true\` 删除非空目录。`,
304
+ removed: (p) => `✅ 已删除目录:\`${p}\`。`,
305
+ doesNotExist: (p) => `❌ **未找到**\n\n目录 \`${p}\` 不存在。`,
306
+ removeFailed: (msg) => `❌ **错误**\n\n删除目录失败:${msg}`,
307
+ }
308
+ : {
309
+ formatError: 'Please use the correct arguments for removing directories.\n\n**Expected args:** `{ "path": "<path>", "recursive": true|false }`\n\n**Example:**\n```json\n{ \"path\": \"temp\", \"recursive\": true }\n```',
310
+ dirPathRequired: '❌ **Error**\n\nDirectory path is required.',
311
+ pathMustBeWithinWorkspace: '❌ **Error**\n\nPath must be within workspace.',
312
+ notDir: (p) => `❌ **Error**\n\n\`${p}\` is not a directory.`,
313
+ notEmpty: (p) => `❌ **Error**\n\nDirectory \`${p}\` is not empty. Set \`recursive: true\` to remove non-empty directories.`,
314
+ removed: (p) => `✅ Removed directory: \`${p}\`.`,
315
+ doesNotExist: (p) => `❌ **Not Found**\n\nDirectory \`${p}\` does not exist.`,
316
+ removeFailed: (msg) => `❌ **Error**\n\nError removing directory: ${msg}`,
317
+ };
318
+ const pathValue = args['path'];
319
+ const rel = typeof pathValue === 'string' ? pathValue.trim() : '';
320
+ if (!rel)
321
+ return labels.dirPathRequired;
322
+ const recursiveValue = args['recursive'];
323
+ const recursive = recursiveValue === undefined ? false : recursiveValue === true ? true : false;
324
+ if (recursiveValue !== undefined && typeof recursiveValue !== 'boolean')
325
+ return labels.formatError;
326
+ // Resolve path relative to current working directory (workspace)
327
+ const targetPath = path_1.default.resolve(process.cwd(), rel);
328
+ // Basic security check - ensure path is within workspace
329
+ const cwd = path_1.default.resolve(process.cwd());
330
+ if (!targetPath.startsWith(cwd)) {
331
+ return labels.pathMustBeWithinWorkspace;
332
+ }
333
+ // Check member write access permissions
334
+ if (!(0, access_control_1.hasWriteAccess)(caller, rel)) {
335
+ return (0, access_control_1.getAccessDeniedMessage)('write', rel, workLanguage);
336
+ }
337
+ try {
338
+ // Check if path exists and is a directory
339
+ const stats = await promises_1.default.lstat(targetPath);
340
+ if (!stats.isDirectory()) {
341
+ return labels.notDir(rel);
342
+ }
343
+ // Check if directory is empty when not using recursive
344
+ if (!recursive) {
345
+ const entries = await promises_1.default.readdir(targetPath);
346
+ if (entries.length > 0) {
347
+ return labels.notEmpty(rel);
348
+ }
349
+ }
350
+ // Remove the directory
351
+ await promises_1.default.rmdir(targetPath, { recursive });
352
+ return labels.removed(rel);
353
+ }
354
+ catch (error) {
355
+ if (typeof error === 'object' &&
356
+ error !== null &&
357
+ 'code' in error &&
358
+ error.code === 'ENOENT') {
359
+ return labels.doesNotExist(rel);
360
+ }
361
+ return labels.removeFailed(error instanceof Error ? error.message : String(error));
362
+ }
363
+ },
364
+ };
365
+ exports.rmFileTool = {
366
+ type: 'func',
367
+ name: 'rm_file',
368
+ description: 'Remove a file relative to workspace.',
369
+ descriptionI18n: {
370
+ en: 'Remove a file relative to workspace.',
371
+ zh: '删除工作区内文件。',
372
+ },
373
+ parameters: {
374
+ type: 'object',
375
+ additionalProperties: false,
376
+ required: ['path'],
377
+ properties: {
378
+ path: { type: 'string', description: 'Workspace-relative file path.' },
379
+ },
380
+ },
381
+ argsValidation: 'dominds',
382
+ call: async (_dlg, caller, args) => {
383
+ const workLanguage = (0, runtime_language_1.getWorkLanguage)();
384
+ const labels = workLanguage === 'zh'
385
+ ? {
386
+ formatError: '请使用正确的文件删除参数。\n\n**期望参数:** `{ \"path\": \"<path>\" }`\n\n**示例:**\n```json\n{ \"path\": \"temp/old-file.txt\" }\n```',
387
+ filePathRequired: '❌ **错误**\n\n需要提供文件路径。',
388
+ pathMustBeWithinWorkspace: '❌ **错误**\n\n路径必须位于工作区内。',
389
+ notFile: (p) => `❌ **错误**\n\n\`${p}\` 不是文件。`,
390
+ removed: (p) => `✅ 已删除文件:\`${p}\`。`,
391
+ doesNotExist: (p) => `❌ **未找到**\n\n文件 \`${p}\` 不存在。`,
392
+ removeFailed: (msg) => `❌ **错误**\n\n删除文件失败:${msg}`,
393
+ }
394
+ : {
395
+ formatError: 'Please use the correct arguments for removing files.\n\n**Expected args:** `{ \"path\": \"<path>\" }`\n\n**Example:**\n```json\n{ \"path\": \"temp/old-file.txt\" }\n```',
396
+ filePathRequired: '❌ **Error**\n\nFile path is required.',
397
+ pathMustBeWithinWorkspace: '❌ **Error**\n\nPath must be within workspace.',
398
+ notFile: (p) => `❌ **Error**\n\n\`${p}\` is not a file.`,
399
+ removed: (p) => `✅ Removed file: \`${p}\`.`,
400
+ doesNotExist: (p) => `❌ **Not Found**\n\nFile \`${p}\` does not exist.`,
401
+ removeFailed: (msg) => `❌ **Error**\n\nError removing file: ${msg}`,
402
+ };
403
+ const pathValue = args['path'];
404
+ const rel = typeof pathValue === 'string' ? pathValue.trim() : '';
405
+ if (!rel)
406
+ return labels.filePathRequired;
407
+ // Resolve path relative to current working directory (workspace)
408
+ const targetPath = path_1.default.resolve(process.cwd(), rel);
409
+ // Basic security check - ensure path is within workspace
410
+ const cwd = path_1.default.resolve(process.cwd());
411
+ if (!targetPath.startsWith(cwd)) {
412
+ return labels.pathMustBeWithinWorkspace;
413
+ }
414
+ // Check member write access permissions
415
+ if (!(0, access_control_1.hasWriteAccess)(caller, rel)) {
416
+ return (0, access_control_1.getAccessDeniedMessage)('write', rel, workLanguage);
417
+ }
418
+ try {
419
+ // Check if path exists and is a file
420
+ const stats = await promises_1.default.lstat(targetPath);
421
+ if (!stats.isFile()) {
422
+ return labels.notFile(rel);
423
+ }
424
+ // Remove the file
425
+ await promises_1.default.unlink(targetPath);
426
+ return labels.removed(rel);
427
+ }
428
+ catch (error) {
429
+ if (typeof error === 'object' &&
430
+ error !== null &&
431
+ 'code' in error &&
432
+ error.code === 'ENOENT') {
433
+ return labels.doesNotExist(rel);
434
+ }
435
+ return labels.removeFailed(error instanceof Error ? error.message : String(error));
436
+ }
437
+ },
438
+ };
439
+ function yamlQuote(value) {
440
+ return `'${value.replace(/'/g, "''")}'`;
441
+ }
442
+ function formatYamlCodeBlock(yaml) {
443
+ return `\`\`\`yaml\n${yaml}\n\`\`\``;
444
+ }
445
+ async function countDirEntries(absPath) {
446
+ let count = 0;
447
+ const entries = await promises_1.default.readdir(absPath, { withFileTypes: true });
448
+ for (const entry of entries) {
449
+ count++;
450
+ if (entry.isDirectory()) {
451
+ count += await countDirEntries(path_1.default.join(absPath, entry.name));
452
+ }
453
+ }
454
+ return count;
455
+ }
456
+ exports.mkDirTool = {
457
+ type: 'func',
458
+ name: 'mk_dir',
459
+ description: 'Create a directory relative to workspace.',
460
+ descriptionI18n: { en: 'Create a directory relative to workspace.', zh: '创建工作区内目录。' },
461
+ parameters: {
462
+ type: 'object',
463
+ additionalProperties: false,
464
+ required: ['path'],
465
+ properties: {
466
+ path: { type: 'string', description: 'Workspace-relative directory path.' },
467
+ parents: { type: 'boolean', description: 'Create parent directories as needed.' },
468
+ },
469
+ },
470
+ argsValidation: 'dominds',
471
+ call: async (_dlg, caller, args) => {
472
+ const workLanguage = (0, runtime_language_1.getWorkLanguage)();
473
+ const pathValue = args['path'];
474
+ const rel = typeof pathValue === 'string' ? pathValue.trim() : '';
475
+ if (!rel) {
476
+ const yaml = [
477
+ `status: error`,
478
+ `error: PATH_REQUIRED`,
479
+ `summary: ${yamlQuote(workLanguage === 'zh' ? 'Mk-dir failed: path required.' : 'Mk-dir failed: path required.')}`,
480
+ ].join('\n');
481
+ return formatYamlCodeBlock(yaml);
482
+ }
483
+ const parentsValue = args['parents'];
484
+ const parents = parentsValue === undefined
485
+ ? true
486
+ : parentsValue === true
487
+ ? true
488
+ : parentsValue === false
489
+ ? false
490
+ : true;
491
+ if (parentsValue !== undefined && typeof parentsValue !== 'boolean') {
492
+ const yaml = [
493
+ `status: error`,
494
+ `error: INVALID_ARGS`,
495
+ `summary: ${yamlQuote(workLanguage === 'zh'
496
+ ? 'Mk-dir failed: invalid args. Expected { path: string, parents?: boolean }.'
497
+ : 'Mk-dir failed: invalid args. Expected { path: string, parents?: boolean }.')}`,
498
+ ].join('\n');
499
+ return formatYamlCodeBlock(yaml);
500
+ }
501
+ const targetPath = path_1.default.resolve(process.cwd(), rel);
502
+ const cwd = path_1.default.resolve(process.cwd());
503
+ if (!targetPath.startsWith(cwd)) {
504
+ const yaml = [
505
+ `status: error`,
506
+ `path: ${yamlQuote(rel)}`,
507
+ `error: PATH_OUTSIDE_WORKSPACE`,
508
+ `summary: ${yamlQuote(workLanguage === 'zh'
509
+ ? 'Mk-dir failed: path must be within workspace.'
510
+ : 'Mk-dir failed: path must be within workspace.')}`,
511
+ ].join('\n');
512
+ return formatYamlCodeBlock(yaml);
513
+ }
514
+ if (!(0, access_control_1.hasWriteAccess)(caller, rel)) {
515
+ return (0, access_control_1.getAccessDeniedMessage)('write', rel, workLanguage);
516
+ }
517
+ try {
518
+ const st = await promises_1.default.lstat(targetPath).catch((err) => {
519
+ if (typeof err === 'object' &&
520
+ err !== null &&
521
+ 'code' in err &&
522
+ err.code === 'ENOENT') {
523
+ return undefined;
524
+ }
525
+ throw err;
526
+ });
527
+ if (st) {
528
+ if (!st.isDirectory()) {
529
+ const yaml = [
530
+ `status: error`,
531
+ `path: ${yamlQuote(rel)}`,
532
+ `error: PATH_EXISTS_NOT_DIR`,
533
+ `summary: ${yamlQuote(workLanguage === 'zh'
534
+ ? 'Mk-dir failed: path exists and is not a directory.'
535
+ : 'Mk-dir failed: path exists and is not a directory.')}`,
536
+ ].join('\n');
537
+ return formatYamlCodeBlock(yaml);
538
+ }
539
+ const yaml = [
540
+ `status: ok`,
541
+ `path: ${yamlQuote(rel)}`,
542
+ `created: false`,
543
+ `summary: ${yamlQuote(`Mk-dir: ${rel} (parents=${parents}).`)}`,
544
+ ].join('\n');
545
+ return formatYamlCodeBlock(yaml);
546
+ }
547
+ await promises_1.default.mkdir(targetPath, { recursive: parents });
548
+ const yaml = [
549
+ `status: ok`,
550
+ `path: ${yamlQuote(rel)}`,
551
+ `created: true`,
552
+ `summary: ${yamlQuote(`Mk-dir: ${rel} (parents=${parents}).`)}`,
553
+ ].join('\n');
554
+ return formatYamlCodeBlock(yaml);
555
+ }
556
+ catch (error) {
557
+ const yaml = [
558
+ `status: error`,
559
+ `path: ${yamlQuote(rel)}`,
560
+ `error: FAILED`,
561
+ `summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
562
+ ].join('\n');
563
+ return formatYamlCodeBlock(yaml);
564
+ }
565
+ },
566
+ };
567
+ exports.moveFileTool = {
568
+ type: 'func',
569
+ name: 'move_file',
570
+ description: 'Move/rename a file relative to workspace.',
571
+ descriptionI18n: {
572
+ en: 'Move/rename a file relative to workspace.',
573
+ zh: '移动/重命名工作区内文件。',
574
+ },
575
+ parameters: {
576
+ type: 'object',
577
+ additionalProperties: false,
578
+ required: ['from', 'to'],
579
+ properties: {
580
+ from: { type: 'string', description: 'Workspace-relative source file path.' },
581
+ to: { type: 'string', description: 'Workspace-relative destination file path.' },
582
+ },
583
+ },
584
+ argsValidation: 'dominds',
585
+ call: async (_dlg, caller, args) => {
586
+ const workLanguage = (0, runtime_language_1.getWorkLanguage)();
587
+ const fromValue = args['from'];
588
+ const toValue = args['to'];
589
+ const from = typeof fromValue === 'string' ? fromValue.trim() : '';
590
+ const to = typeof toValue === 'string' ? toValue.trim() : '';
591
+ if (!from || !to) {
592
+ const yaml = [
593
+ `status: error`,
594
+ `error: INVALID_ARGS`,
595
+ `summary: ${yamlQuote(workLanguage === 'zh'
596
+ ? 'Move-file failed: from/to required.'
597
+ : 'Move-file failed: from/to required.')}`,
598
+ ].join('\n');
599
+ return formatYamlCodeBlock(yaml);
600
+ }
601
+ const absFrom = path_1.default.resolve(process.cwd(), from);
602
+ const absTo = path_1.default.resolve(process.cwd(), to);
603
+ const cwd = path_1.default.resolve(process.cwd());
604
+ if (!absFrom.startsWith(cwd) || !absTo.startsWith(cwd)) {
605
+ const yaml = [
606
+ `status: error`,
607
+ `from: ${yamlQuote(from)}`,
608
+ `to: ${yamlQuote(to)}`,
609
+ `error: PATH_OUTSIDE_WORKSPACE`,
610
+ `summary: ${yamlQuote(workLanguage === 'zh'
611
+ ? 'Move-file failed: paths must be within workspace.'
612
+ : 'Move-file failed: paths must be within workspace.')}`,
613
+ ].join('\n');
614
+ return formatYamlCodeBlock(yaml);
615
+ }
616
+ if (!(0, access_control_1.hasWriteAccess)(caller, from) || !(0, access_control_1.hasWriteAccess)(caller, to)) {
617
+ return (0, access_control_1.getAccessDeniedMessage)('write', from, workLanguage);
618
+ }
619
+ try {
620
+ const st = await promises_1.default.lstat(absFrom);
621
+ if (!st.isFile()) {
622
+ const yaml = [
623
+ `status: error`,
624
+ `from: ${yamlQuote(from)}`,
625
+ `to: ${yamlQuote(to)}`,
626
+ `error: FROM_NOT_FILE`,
627
+ `summary: ${yamlQuote(workLanguage === 'zh'
628
+ ? 'Move-file failed: from is not a file.'
629
+ : 'Move-file failed: from is not a file.')}`,
630
+ ].join('\n');
631
+ return formatYamlCodeBlock(yaml);
632
+ }
633
+ const toParent = path_1.default.dirname(absTo);
634
+ const toParentSt = await promises_1.default.lstat(toParent).catch(() => undefined);
635
+ if (!toParentSt || !toParentSt.isDirectory()) {
636
+ const yaml = [
637
+ `status: error`,
638
+ `from: ${yamlQuote(from)}`,
639
+ `to: ${yamlQuote(to)}`,
640
+ `error: TO_PARENT_NOT_DIR`,
641
+ `summary: ${yamlQuote(workLanguage === 'zh'
642
+ ? 'Move-file failed: destination parent directory does not exist. Use mk_dir first.'
643
+ : 'Move-file failed: destination parent directory does not exist. Use mk_dir first.')}`,
644
+ ].join('\n');
645
+ return formatYamlCodeBlock(yaml);
646
+ }
647
+ const toExists = await promises_1.default
648
+ .lstat(absTo)
649
+ .then(() => true)
650
+ .catch((err) => {
651
+ if (typeof err === 'object' &&
652
+ err !== null &&
653
+ 'code' in err &&
654
+ err.code === 'ENOENT') {
655
+ return false;
656
+ }
657
+ throw err;
658
+ });
659
+ if (toExists) {
660
+ const yaml = [
661
+ `status: error`,
662
+ `from: ${yamlQuote(from)}`,
663
+ `to: ${yamlQuote(to)}`,
664
+ `error: TO_EXISTS`,
665
+ `summary: ${yamlQuote(workLanguage === 'zh'
666
+ ? 'Move-file failed: destination already exists.'
667
+ : 'Move-file failed: destination already exists.')}`,
668
+ ].join('\n');
669
+ return formatYamlCodeBlock(yaml);
670
+ }
671
+ await promises_1.default.rename(absFrom, absTo);
672
+ const yaml = [
673
+ `status: ok`,
674
+ `from: ${yamlQuote(from)}`,
675
+ `to: ${yamlQuote(to)}`,
676
+ `summary: ${yamlQuote(`Move-file: ${from} \u2192 ${to}.`)}`,
677
+ ].join('\n');
678
+ return formatYamlCodeBlock(yaml);
679
+ }
680
+ catch (error) {
681
+ const yaml = [
682
+ `status: error`,
683
+ `from: ${yamlQuote(from)}`,
684
+ `to: ${yamlQuote(to)}`,
685
+ `error: FAILED`,
686
+ `summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
687
+ ].join('\n');
688
+ return formatYamlCodeBlock(yaml);
689
+ }
690
+ },
691
+ };
692
+ exports.moveDirTool = {
693
+ type: 'func',
694
+ name: 'move_dir',
695
+ description: 'Move/rename a directory relative to workspace.',
696
+ descriptionI18n: {
697
+ en: 'Move/rename a directory relative to workspace.',
698
+ zh: '移动/重命名工作区内目录。',
699
+ },
700
+ parameters: {
701
+ type: 'object',
702
+ additionalProperties: false,
703
+ required: ['from', 'to'],
704
+ properties: {
705
+ from: { type: 'string', description: 'Workspace-relative source directory path.' },
706
+ to: { type: 'string', description: 'Workspace-relative destination directory path.' },
707
+ },
708
+ },
709
+ argsValidation: 'dominds',
710
+ call: async (_dlg, caller, args) => {
711
+ const workLanguage = (0, runtime_language_1.getWorkLanguage)();
712
+ const fromValue = args['from'];
713
+ const toValue = args['to'];
714
+ const from = typeof fromValue === 'string' ? fromValue.trim() : '';
715
+ const to = typeof toValue === 'string' ? toValue.trim() : '';
716
+ if (!from || !to) {
717
+ const yaml = [
718
+ `status: error`,
719
+ `error: INVALID_ARGS`,
720
+ `summary: ${yamlQuote(workLanguage === 'zh'
721
+ ? 'Move-dir failed: from/to required.'
722
+ : 'Move-dir failed: from/to required.')}`,
723
+ ].join('\n');
724
+ return formatYamlCodeBlock(yaml);
725
+ }
726
+ const absFrom = path_1.default.resolve(process.cwd(), from);
727
+ const absTo = path_1.default.resolve(process.cwd(), to);
728
+ const cwd = path_1.default.resolve(process.cwd());
729
+ if (!absFrom.startsWith(cwd) || !absTo.startsWith(cwd)) {
730
+ const yaml = [
731
+ `status: error`,
732
+ `from: ${yamlQuote(from)}`,
733
+ `to: ${yamlQuote(to)}`,
734
+ `error: PATH_OUTSIDE_WORKSPACE`,
735
+ `summary: ${yamlQuote(workLanguage === 'zh'
736
+ ? 'Move-dir failed: paths must be within workspace.'
737
+ : 'Move-dir failed: paths must be within workspace.')}`,
738
+ ].join('\n');
739
+ return formatYamlCodeBlock(yaml);
740
+ }
741
+ if (!(0, access_control_1.hasWriteAccess)(caller, from) || !(0, access_control_1.hasWriteAccess)(caller, to)) {
742
+ return (0, access_control_1.getAccessDeniedMessage)('write', from, workLanguage);
743
+ }
744
+ try {
745
+ const st = await promises_1.default.lstat(absFrom);
746
+ if (!st.isDirectory()) {
747
+ const yaml = [
748
+ `status: error`,
749
+ `from: ${yamlQuote(from)}`,
750
+ `to: ${yamlQuote(to)}`,
751
+ `error: FROM_NOT_DIR`,
752
+ `summary: ${yamlQuote(workLanguage === 'zh'
753
+ ? 'Move-dir failed: from is not a directory.'
754
+ : 'Move-dir failed: from is not a directory.')}`,
755
+ ].join('\n');
756
+ return formatYamlCodeBlock(yaml);
757
+ }
758
+ const toParent = path_1.default.dirname(absTo);
759
+ const toParentSt = await promises_1.default.lstat(toParent).catch(() => undefined);
760
+ if (!toParentSt || !toParentSt.isDirectory()) {
761
+ const yaml = [
762
+ `status: error`,
763
+ `from: ${yamlQuote(from)}`,
764
+ `to: ${yamlQuote(to)}`,
765
+ `error: TO_PARENT_NOT_DIR`,
766
+ `summary: ${yamlQuote(workLanguage === 'zh'
767
+ ? 'Move-dir failed: destination parent directory does not exist. Use mk_dir first.'
768
+ : 'Move-dir failed: destination parent directory does not exist. Use mk_dir first.')}`,
769
+ ].join('\n');
770
+ return formatYamlCodeBlock(yaml);
771
+ }
772
+ const toExists = await promises_1.default
773
+ .lstat(absTo)
774
+ .then(() => true)
775
+ .catch((err) => {
776
+ if (typeof err === 'object' &&
777
+ err !== null &&
778
+ 'code' in err &&
779
+ err.code === 'ENOENT') {
780
+ return false;
781
+ }
782
+ throw err;
783
+ });
784
+ if (toExists) {
785
+ const yaml = [
786
+ `status: error`,
787
+ `from: ${yamlQuote(from)}`,
788
+ `to: ${yamlQuote(to)}`,
789
+ `error: TO_EXISTS`,
790
+ `summary: ${yamlQuote(workLanguage === 'zh'
791
+ ? 'Move-dir failed: destination already exists.'
792
+ : 'Move-dir failed: destination already exists.')}`,
793
+ ].join('\n');
794
+ return formatYamlCodeBlock(yaml);
795
+ }
796
+ const movedEntryCount = await countDirEntries(absFrom);
797
+ await promises_1.default.rename(absFrom, absTo);
798
+ const yaml = [
799
+ `status: ok`,
800
+ `from: ${yamlQuote(from)}`,
801
+ `to: ${yamlQuote(to)}`,
802
+ `moved_entry_count: ${movedEntryCount}`,
803
+ `summary: ${yamlQuote(`Move-dir: ${from} \u2192 ${to} (${movedEntryCount} entries).`)}`,
804
+ ].join('\n');
805
+ return formatYamlCodeBlock(yaml);
806
+ }
807
+ catch (error) {
808
+ const yaml = [
809
+ `status: error`,
810
+ `from: ${yamlQuote(from)}`,
811
+ `to: ${yamlQuote(to)}`,
812
+ `error: FAILED`,
813
+ `summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
814
+ ].join('\n');
815
+ return formatYamlCodeBlock(yaml);
816
+ }
817
+ },
818
+ };