@wonderwhy-er/desktop-commander 0.2.38 → 0.2.39

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 (364) hide show
  1. package/README.md +53 -2
  2. package/dist/handlers/filesystem-handlers.d.ts +5 -0
  3. package/dist/handlers/filesystem-handlers.js +14 -2
  4. package/dist/remote-device/desktop-commander-integration.js +1 -1
  5. package/dist/search-manager.js +31 -38
  6. package/dist/server.js +8 -3
  7. package/dist/terminal-manager.js +4 -2
  8. package/dist/tools/edit.js +34 -1
  9. package/dist/tools/filesystem.js +91 -3
  10. package/dist/tools/improved-process-tools.js +2 -1
  11. package/dist/ui/config-editor/app.js +840 -0
  12. package/dist/ui/config-editor/array-modal.d.ts +19 -0
  13. package/dist/ui/config-editor/array-modal.js +185 -0
  14. package/dist/ui/config-editor/config-editor-runtime.js +65 -14096
  15. package/dist/ui/config-editor/main.js +2 -0
  16. package/dist/ui/config-editor/src/App.d.ts +43 -0
  17. package/dist/ui/config-editor/src/components/layout.d.ts +4 -0
  18. package/dist/ui/config-editor/src/components/layout.js +83 -0
  19. package/dist/ui/config-editor/src/components/toolbar.d.ts +1 -0
  20. package/dist/ui/config-editor/src/components/toolbar.js +21 -0
  21. package/dist/ui/config-editor/src/config-values.d.ts +6 -0
  22. package/dist/ui/config-editor/src/config-values.js +61 -0
  23. package/dist/ui/config-editor/src/contracts.d.ts +14 -0
  24. package/dist/ui/config-editor/src/contracts.js +3 -0
  25. package/dist/ui/config-editor/src/directory-browser.d.ts +6 -0
  26. package/dist/ui/config-editor/src/directory-browser.js +71 -0
  27. package/dist/ui/config-editor/src/layout.d.ts +5 -0
  28. package/dist/ui/config-editor/src/layout.js +90 -0
  29. package/dist/ui/config-editor/src/parsing.d.ts +5 -0
  30. package/dist/ui/config-editor/src/parsing.js +50 -0
  31. package/dist/ui/config-editor/src/toolbar.d.ts +1 -0
  32. package/dist/ui/config-editor/src/toolbar.js +18 -0
  33. package/dist/ui/config-editor/src/types.d.ts +17 -0
  34. package/dist/ui/config-editor/src/types.js +3 -0
  35. package/dist/ui/config-editor/src/utils/config-values.d.ts +9 -0
  36. package/dist/ui/config-editor/src/utils/config-values.js +61 -0
  37. package/dist/ui/config-editor/src/utils/directory-browser.d.ts +31 -0
  38. package/dist/ui/config-editor/src/utils/directory-browser.js +201 -0
  39. package/dist/ui/config-editor/src/utils/parsing.d.ts +8 -0
  40. package/dist/ui/config-editor/src/utils/parsing.js +50 -0
  41. package/dist/ui/config-editor/styles.css +2 -1
  42. package/dist/ui/file-preview/{src/app.d.ts → app.d.ts} +1 -1
  43. package/dist/ui/file-preview/app.js +2020 -0
  44. package/dist/ui/file-preview/components/code-viewer.d.ts +6 -0
  45. package/dist/ui/file-preview/components/code-viewer.js +73 -0
  46. package/dist/ui/file-preview/components/highlighting.d.ts +2 -0
  47. package/dist/ui/file-preview/components/highlighting.js +54 -0
  48. package/dist/ui/file-preview/components/html-renderer.d.ts +5 -0
  49. package/dist/ui/file-preview/components/html-renderer.js +47 -0
  50. package/dist/ui/file-preview/components/markdown-renderer.d.ts +1 -0
  51. package/dist/ui/file-preview/components/markdown-renderer.js +67 -0
  52. package/dist/ui/file-preview/components/toolbar.d.ts +6 -0
  53. package/dist/ui/file-preview/components/toolbar.js +75 -0
  54. package/dist/ui/file-preview/image-preview.d.ts +3 -0
  55. package/dist/ui/file-preview/image-preview.js +21 -0
  56. package/dist/ui/file-preview/main.js +5 -0
  57. package/dist/ui/file-preview/markdown/editor.d.ts +36 -0
  58. package/dist/ui/file-preview/markdown/editor.js +643 -0
  59. package/dist/ui/file-preview/markdown/linking.d.ts +9 -0
  60. package/dist/ui/file-preview/markdown/linking.js +210 -0
  61. package/dist/ui/file-preview/markdown/outline.d.ts +7 -0
  62. package/dist/ui/file-preview/markdown/outline.js +40 -0
  63. package/dist/ui/file-preview/markdown/preview.d.ts +8 -0
  64. package/dist/ui/file-preview/markdown/preview.js +33 -0
  65. package/dist/ui/file-preview/markdown/slugify.d.ts +3 -0
  66. package/dist/ui/file-preview/markdown/slugify.js +31 -0
  67. package/dist/ui/file-preview/markdown/toc.d.ts +11 -0
  68. package/dist/ui/file-preview/markdown/toc.js +75 -0
  69. package/dist/ui/file-preview/markdown/utils.d.ts +1 -0
  70. package/dist/ui/file-preview/markdown/utils.js +15 -0
  71. package/dist/ui/file-preview/markdown/workspace-controller.d.ts +25 -0
  72. package/dist/ui/file-preview/markdown/workspace-controller.js +40 -0
  73. package/dist/ui/file-preview/preview-runtime.js +384 -26533
  74. package/dist/ui/file-preview/shared/preview-file-types.d.ts +1 -1
  75. package/dist/ui/file-preview/src/App.d.ts +4 -0
  76. package/dist/ui/file-preview/src/App.js +564 -0
  77. package/dist/ui/file-preview/src/components/CodeViewer.d.ts +6 -0
  78. package/dist/ui/file-preview/src/components/CodeViewer.js +60 -0
  79. package/dist/ui/file-preview/src/components/HtmlRenderer.d.ts +8 -0
  80. package/dist/ui/file-preview/src/components/HtmlRenderer.js +45 -0
  81. package/dist/ui/file-preview/src/components/MarkdownRenderer.d.ts +1 -0
  82. package/dist/ui/file-preview/src/components/MarkdownRenderer.js +15 -0
  83. package/dist/ui/file-preview/src/components/editor-toolbar.d.ts +15 -0
  84. package/dist/ui/file-preview/src/components/editor-toolbar.js +384 -0
  85. package/dist/ui/file-preview/src/components/markdown-editor.d.ts +29 -0
  86. package/dist/ui/file-preview/src/components/markdown-editor.js +535 -0
  87. package/dist/ui/file-preview/src/components/markdown-renderer.js +47 -9
  88. package/dist/ui/file-preview/src/directory-controller.d.ts +8 -0
  89. package/dist/ui/file-preview/src/directory-controller.js +233 -0
  90. package/dist/ui/file-preview/src/document-layout.d.ts +20 -0
  91. package/dist/ui/file-preview/src/document-layout.js +109 -0
  92. package/dist/ui/file-preview/src/document-outline.d.ts +17 -0
  93. package/dist/ui/file-preview/src/document-outline.js +97 -0
  94. package/dist/ui/file-preview/src/document-workspace.d.ts +19 -0
  95. package/dist/ui/file-preview/src/document-workspace.js +33 -0
  96. package/dist/ui/file-preview/src/file-type-handlers.d.ts +10 -0
  97. package/dist/ui/file-preview/src/file-type-handlers.js +98 -0
  98. package/dist/ui/file-preview/src/host/external-actions.d.ts +19 -0
  99. package/dist/ui/file-preview/src/host/external-actions.js +94 -0
  100. package/dist/ui/file-preview/src/host/selection-context.d.ts +9 -0
  101. package/dist/ui/file-preview/src/host/selection-context.js +106 -0
  102. package/dist/ui/file-preview/src/markdown/block-merge.d.ts +25 -0
  103. package/dist/ui/file-preview/src/markdown/block-merge.js +86 -0
  104. package/dist/ui/file-preview/src/markdown/conflict-dialog.d.ts +40 -0
  105. package/dist/ui/file-preview/src/markdown/conflict-dialog.js +163 -0
  106. package/dist/ui/file-preview/src/markdown/controller.d.ts +38 -0
  107. package/dist/ui/file-preview/src/markdown/controller.js +921 -0
  108. package/dist/ui/file-preview/src/markdown/editor.d.ts +35 -0
  109. package/dist/ui/file-preview/src/markdown/editor.js +691 -0
  110. package/dist/ui/file-preview/src/markdown/link-modal.d.ts +13 -0
  111. package/dist/ui/file-preview/src/markdown/link-modal.js +213 -0
  112. package/dist/ui/file-preview/src/markdown/linking.d.ts +16 -0
  113. package/dist/ui/file-preview/src/markdown/linking.js +228 -0
  114. package/dist/ui/file-preview/src/markdown/outline.d.ts +2 -0
  115. package/dist/ui/file-preview/src/markdown/outline.js +16 -0
  116. package/dist/ui/file-preview/src/markdown/parser.d.ts +30 -0
  117. package/dist/ui/file-preview/src/markdown/parser.js +38 -0
  118. package/dist/ui/file-preview/src/markdown/preview.d.ts +1 -0
  119. package/dist/ui/file-preview/src/markdown/preview.js +20 -0
  120. package/dist/ui/file-preview/src/markdown/raw-editor.d.ts +8 -0
  121. package/dist/ui/file-preview/src/markdown/raw-editor.js +61 -0
  122. package/dist/ui/file-preview/src/markdown/selection-toolbar.d.ts +14 -0
  123. package/dist/ui/file-preview/src/markdown/selection-toolbar.js +128 -0
  124. package/dist/ui/file-preview/src/markdown/slugify.d.ts +3 -0
  125. package/dist/ui/file-preview/src/markdown/slugify.js +31 -0
  126. package/dist/ui/file-preview/src/markdown/toc.d.ts +11 -0
  127. package/dist/ui/file-preview/src/markdown/toc.js +75 -0
  128. package/dist/ui/file-preview/src/markdown/utils.d.ts +1 -0
  129. package/dist/ui/file-preview/src/markdown/utils.js +15 -0
  130. package/dist/ui/file-preview/src/markdown-workspace/editor.d.ts +36 -0
  131. package/dist/ui/file-preview/src/markdown-workspace/editor.js +643 -0
  132. package/dist/ui/file-preview/src/markdown-workspace/linking.d.ts +9 -0
  133. package/dist/ui/file-preview/src/markdown-workspace/linking.js +210 -0
  134. package/dist/ui/file-preview/src/markdown-workspace/outline.d.ts +7 -0
  135. package/dist/ui/file-preview/src/markdown-workspace/outline.js +40 -0
  136. package/dist/ui/file-preview/src/markdown-workspace/preview.d.ts +8 -0
  137. package/dist/ui/file-preview/src/markdown-workspace/preview.js +33 -0
  138. package/dist/ui/file-preview/src/markdown-workspace/slugify.d.ts +3 -0
  139. package/dist/ui/file-preview/src/markdown-workspace/slugify.js +31 -0
  140. package/dist/ui/file-preview/src/markdown-workspace/toc.d.ts +11 -0
  141. package/dist/ui/file-preview/src/markdown-workspace/toc.js +75 -0
  142. package/dist/ui/file-preview/src/markdown-workspace/utils.d.ts +1 -0
  143. package/dist/ui/file-preview/src/markdown-workspace/utils.js +15 -0
  144. package/dist/ui/file-preview/src/markdown-workspace/workspace-controller.d.ts +25 -0
  145. package/dist/ui/file-preview/src/markdown-workspace/workspace-controller.js +40 -0
  146. package/dist/ui/file-preview/src/model.d.ts +34 -0
  147. package/dist/ui/file-preview/src/panel-actions.d.ts +17 -0
  148. package/dist/ui/file-preview/src/panel-actions.js +182 -0
  149. package/dist/ui/file-preview/src/path-utils.d.ts +6 -0
  150. package/dist/ui/file-preview/src/path-utils.js +64 -0
  151. package/dist/ui/file-preview/src/payload-utils.d.ts +11 -0
  152. package/dist/ui/file-preview/src/payload-utils.js +94 -0
  153. package/dist/ui/file-preview/styles.css +1066 -233
  154. package/dist/ui/file-preview/types.d.ts +1 -0
  155. package/dist/ui/server-integration.d.ts +13 -0
  156. package/dist/ui/server-integration.js +31 -0
  157. package/dist/ui/shared/ToolHeader.d.ts +9 -0
  158. package/dist/ui/shared/ToolHeader.js +29 -0
  159. package/dist/ui/shared/app-bootstrap.d.ts +9 -0
  160. package/dist/ui/shared/app-bootstrap.js +15 -0
  161. package/dist/ui/shared/guards.d.ts +1 -0
  162. package/dist/ui/shared/guards.js +3 -0
  163. package/dist/ui/shared/host-lifecycle.d.ts +1 -0
  164. package/dist/ui/shared/host-lifecycle.js +8 -2
  165. package/dist/ui/shared/widget-state.d.ts +6 -1
  166. package/dist/ui/shared/widget-state.js +102 -4
  167. package/dist/utils/files/base.d.ts +2 -0
  168. package/dist/utils/open-browser.js +1 -1
  169. package/dist/utils/ui-call-context.d.ts +8 -0
  170. package/dist/utils/ui-call-context.js +72 -0
  171. package/dist/version.d.ts +1 -1
  172. package/dist/version.js +1 -1
  173. package/package.json +6 -1
  174. package/dist/data/spec-kit-prompts.json +0 -123
  175. package/dist/handlers/macos-control-handlers.d.ts +0 -16
  176. package/dist/handlers/macos-control-handlers.js +0 -81
  177. package/dist/handlers/node-handlers.d.ts +0 -6
  178. package/dist/handlers/node-handlers.js +0 -73
  179. package/dist/handlers/test-crash-handler.d.ts +0 -11
  180. package/dist/handlers/test-crash-handler.js +0 -26
  181. package/dist/http-index.d.ts +0 -45
  182. package/dist/http-index.js +0 -51
  183. package/dist/http-server-auto-tunnel.js +0 -667
  184. package/dist/http-server-named-tunnel.d.ts +0 -2
  185. package/dist/http-server-named-tunnel.js +0 -167
  186. package/dist/http-server-tunnel.d.ts +0 -2
  187. package/dist/http-server-tunnel.js +0 -111
  188. package/dist/http-server.d.ts +0 -2
  189. package/dist/http-server.js +0 -270
  190. package/dist/index-oauth.d.ts +0 -2
  191. package/dist/index-oauth.js +0 -201
  192. package/dist/lib.d.ts +0 -10
  193. package/dist/lib.js +0 -10
  194. package/dist/oauth/auth-middleware.d.ts +0 -20
  195. package/dist/oauth/auth-middleware.js +0 -62
  196. package/dist/oauth/index.d.ts +0 -3
  197. package/dist/oauth/index.js +0 -3
  198. package/dist/oauth/oauth-manager.d.ts +0 -80
  199. package/dist/oauth/oauth-manager.js +0 -179
  200. package/dist/oauth/oauth-routes.d.ts +0 -3
  201. package/dist/oauth/oauth-routes.js +0 -377
  202. package/dist/oauth/provider.d.ts +0 -22
  203. package/dist/oauth/provider.js +0 -124
  204. package/dist/oauth/server.d.ts +0 -18
  205. package/dist/oauth/server.js +0 -160
  206. package/dist/oauth/types.d.ts +0 -54
  207. package/dist/oauth/types.js +0 -2
  208. package/dist/remote-device/templates/auth-success.d.ts +0 -1
  209. package/dist/remote-device/templates/auth-success.js +0 -30
  210. package/dist/setup.log +0 -275
  211. package/dist/test-setup.js +0 -14
  212. package/dist/tools/docx/builders/html-builder.d.ts +0 -17
  213. package/dist/tools/docx/builders/html-builder.js +0 -92
  214. package/dist/tools/docx/builders/image.d.ts +0 -14
  215. package/dist/tools/docx/builders/image.js +0 -84
  216. package/dist/tools/docx/builders/index.d.ts +0 -11
  217. package/dist/tools/docx/builders/index.js +0 -11
  218. package/dist/tools/docx/builders/markdown-builder.d.ts +0 -2
  219. package/dist/tools/docx/builders/markdown-builder.js +0 -260
  220. package/dist/tools/docx/builders/paragraph.d.ts +0 -12
  221. package/dist/tools/docx/builders/paragraph.js +0 -29
  222. package/dist/tools/docx/builders/table.d.ts +0 -10
  223. package/dist/tools/docx/builders/table.js +0 -138
  224. package/dist/tools/docx/builders/utils.d.ts +0 -5
  225. package/dist/tools/docx/builders/utils.js +0 -18
  226. package/dist/tools/docx/constants.d.ts +0 -32
  227. package/dist/tools/docx/constants.js +0 -61
  228. package/dist/tools/docx/converters/markdown-to-html.d.ts +0 -17
  229. package/dist/tools/docx/converters/markdown-to-html.js +0 -111
  230. package/dist/tools/docx/create.d.ts +0 -21
  231. package/dist/tools/docx/create.js +0 -386
  232. package/dist/tools/docx/dom.d.ts +0 -139
  233. package/dist/tools/docx/dom.js +0 -448
  234. package/dist/tools/docx/errors.d.ts +0 -28
  235. package/dist/tools/docx/errors.js +0 -48
  236. package/dist/tools/docx/extractors/images.d.ts +0 -14
  237. package/dist/tools/docx/extractors/images.js +0 -40
  238. package/dist/tools/docx/extractors/metadata.d.ts +0 -14
  239. package/dist/tools/docx/extractors/metadata.js +0 -64
  240. package/dist/tools/docx/extractors/sections.d.ts +0 -14
  241. package/dist/tools/docx/extractors/sections.js +0 -61
  242. package/dist/tools/docx/html.d.ts +0 -17
  243. package/dist/tools/docx/html.js +0 -111
  244. package/dist/tools/docx/index.d.ts +0 -10
  245. package/dist/tools/docx/index.js +0 -10
  246. package/dist/tools/docx/markdown.d.ts +0 -84
  247. package/dist/tools/docx/markdown.js +0 -507
  248. package/dist/tools/docx/modify.d.ts +0 -28
  249. package/dist/tools/docx/modify.js +0 -271
  250. package/dist/tools/docx/operations/handlers/index.d.ts +0 -39
  251. package/dist/tools/docx/operations/handlers/index.js +0 -152
  252. package/dist/tools/docx/operations/html-manipulator.d.ts +0 -24
  253. package/dist/tools/docx/operations/html-manipulator.js +0 -352
  254. package/dist/tools/docx/operations/index.d.ts +0 -14
  255. package/dist/tools/docx/operations/index.js +0 -61
  256. package/dist/tools/docx/operations/operation-handlers.d.ts +0 -3
  257. package/dist/tools/docx/operations/operation-handlers.js +0 -67
  258. package/dist/tools/docx/operations/preprocessor.d.ts +0 -14
  259. package/dist/tools/docx/operations/preprocessor.js +0 -44
  260. package/dist/tools/docx/operations/xml-replacer.d.ts +0 -9
  261. package/dist/tools/docx/operations/xml-replacer.js +0 -35
  262. package/dist/tools/docx/operations.d.ts +0 -13
  263. package/dist/tools/docx/operations.js +0 -13
  264. package/dist/tools/docx/ops/delete-paragraph-at-body-index.d.ts +0 -11
  265. package/dist/tools/docx/ops/delete-paragraph-at-body-index.js +0 -23
  266. package/dist/tools/docx/ops/header-replace-text-exact.d.ts +0 -13
  267. package/dist/tools/docx/ops/header-replace-text-exact.js +0 -55
  268. package/dist/tools/docx/ops/index.d.ts +0 -17
  269. package/dist/tools/docx/ops/index.js +0 -70
  270. package/dist/tools/docx/ops/insert-image-after-text.d.ts +0 -24
  271. package/dist/tools/docx/ops/insert-image-after-text.js +0 -128
  272. package/dist/tools/docx/ops/insert-paragraph-after-text.d.ts +0 -12
  273. package/dist/tools/docx/ops/insert-paragraph-after-text.js +0 -74
  274. package/dist/tools/docx/ops/insert-table-after-text.d.ts +0 -19
  275. package/dist/tools/docx/ops/insert-table-after-text.js +0 -57
  276. package/dist/tools/docx/ops/replace-hyperlink-url.d.ts +0 -12
  277. package/dist/tools/docx/ops/replace-hyperlink-url.js +0 -37
  278. package/dist/tools/docx/ops/replace-paragraph-at-body-index.d.ts +0 -9
  279. package/dist/tools/docx/ops/replace-paragraph-at-body-index.js +0 -25
  280. package/dist/tools/docx/ops/replace-paragraph-text-exact.d.ts +0 -21
  281. package/dist/tools/docx/ops/replace-paragraph-text-exact.js +0 -36
  282. package/dist/tools/docx/ops/replace-table-cell-text.d.ts +0 -25
  283. package/dist/tools/docx/ops/replace-table-cell-text.js +0 -85
  284. package/dist/tools/docx/ops/set-color-for-paragraph-exact.d.ts +0 -9
  285. package/dist/tools/docx/ops/set-color-for-paragraph-exact.js +0 -24
  286. package/dist/tools/docx/ops/set-color-for-style.d.ts +0 -13
  287. package/dist/tools/docx/ops/set-color-for-style.js +0 -31
  288. package/dist/tools/docx/ops/set-paragraph-style-at-body-index.d.ts +0 -8
  289. package/dist/tools/docx/ops/set-paragraph-style-at-body-index.js +0 -57
  290. package/dist/tools/docx/ops/table-set-cell-text.d.ts +0 -9
  291. package/dist/tools/docx/ops/table-set-cell-text.js +0 -40
  292. package/dist/tools/docx/parsers/image-extractor.d.ts +0 -18
  293. package/dist/tools/docx/parsers/image-extractor.js +0 -61
  294. package/dist/tools/docx/parsers/index.d.ts +0 -9
  295. package/dist/tools/docx/parsers/index.js +0 -9
  296. package/dist/tools/docx/parsers/paragraph-parser.d.ts +0 -2
  297. package/dist/tools/docx/parsers/paragraph-parser.js +0 -88
  298. package/dist/tools/docx/parsers/table-parser.d.ts +0 -9
  299. package/dist/tools/docx/parsers/table-parser.js +0 -72
  300. package/dist/tools/docx/parsers/xml-parser.d.ts +0 -25
  301. package/dist/tools/docx/parsers/xml-parser.js +0 -71
  302. package/dist/tools/docx/parsers/zip-reader.d.ts +0 -23
  303. package/dist/tools/docx/parsers/zip-reader.js +0 -52
  304. package/dist/tools/docx/read.d.ts +0 -27
  305. package/dist/tools/docx/read.js +0 -308
  306. package/dist/tools/docx/relationships.d.ts +0 -22
  307. package/dist/tools/docx/relationships.js +0 -76
  308. package/dist/tools/docx/structure.d.ts +0 -25
  309. package/dist/tools/docx/structure.js +0 -102
  310. package/dist/tools/docx/styled-html-parser.d.ts +0 -23
  311. package/dist/tools/docx/styled-html-parser.js +0 -1262
  312. package/dist/tools/docx/types.d.ts +0 -213
  313. package/dist/tools/docx/types.js +0 -5
  314. package/dist/tools/docx/utils/escaping.d.ts +0 -13
  315. package/dist/tools/docx/utils/escaping.js +0 -26
  316. package/dist/tools/docx/utils/images.d.ts +0 -9
  317. package/dist/tools/docx/utils/images.js +0 -26
  318. package/dist/tools/docx/utils/index.d.ts +0 -12
  319. package/dist/tools/docx/utils/index.js +0 -17
  320. package/dist/tools/docx/utils/markdown.d.ts +0 -13
  321. package/dist/tools/docx/utils/markdown.js +0 -32
  322. package/dist/tools/docx/utils/paths.d.ts +0 -15
  323. package/dist/tools/docx/utils/paths.js +0 -27
  324. package/dist/tools/docx/utils/versioning.d.ts +0 -25
  325. package/dist/tools/docx/utils/versioning.js +0 -55
  326. package/dist/tools/docx/utils.d.ts +0 -101
  327. package/dist/tools/docx/utils.js +0 -299
  328. package/dist/tools/docx/validate.d.ts +0 -33
  329. package/dist/tools/docx/validate.js +0 -49
  330. package/dist/tools/docx/validators.d.ts +0 -13
  331. package/dist/tools/docx/validators.js +0 -40
  332. package/dist/tools/docx/write.d.ts +0 -17
  333. package/dist/tools/docx/write.js +0 -88
  334. package/dist/tools/docx/xml-view-test.js +0 -63
  335. package/dist/tools/docx/xml-view.d.ts +0 -56
  336. package/dist/tools/docx/xml-view.js +0 -169
  337. package/dist/tools/docx/zip.d.ts +0 -21
  338. package/dist/tools/docx/zip.js +0 -35
  339. package/dist/tools/macos-control/ax-adapter.d.ts +0 -55
  340. package/dist/tools/macos-control/ax-adapter.js +0 -438
  341. package/dist/tools/macos-control/cdp-adapter.d.ts +0 -23
  342. package/dist/tools/macos-control/cdp-adapter.js +0 -402
  343. package/dist/tools/macos-control/orchestrator.d.ts +0 -77
  344. package/dist/tools/macos-control/orchestrator.js +0 -136
  345. package/dist/tools/macos-control/role-aliases.d.ts +0 -5
  346. package/dist/tools/macos-control/role-aliases.js +0 -34
  347. package/dist/tools/macos-control/types.d.ts +0 -129
  348. package/dist/tools/pdf-processor.d.ts +0 -1
  349. package/dist/tools/pdf-processor.js +0 -3
  350. package/dist/tools/search.d.ts +0 -32
  351. package/dist/tools/search.js +0 -202
  352. package/dist/ui/file-preview/src/app.js +0 -714
  353. package/dist/utils/crash-logger.d.ts +0 -18
  354. package/dist/utils/crash-logger.js +0 -44
  355. package/dist/utils/dedent.d.ts +0 -8
  356. package/dist/utils/dedent.js +0 -38
  357. /package/dist/ui/config-editor/{src/app.d.ts → app.d.ts} +0 -0
  358. /package/dist/{http-server-auto-tunnel.d.ts → ui/config-editor/main.d.ts} +0 -0
  359. /package/dist/ui/config-editor/src/{app.js → App.js} +0 -0
  360. /package/dist/{test-docx.d.ts → ui/file-preview/main.d.ts} +0 -0
  361. /package/dist/ui/file-preview/src/components/{toolbar.d.ts → Toolbar.d.ts} +0 -0
  362. /package/dist/ui/file-preview/src/components/{toolbar.js → Toolbar.js} +0 -0
  363. /package/dist/{tools/docx/xml-view-test.d.ts → ui/file-preview/src/model.js} +0 -0
  364. /package/dist/{tools/macos-control → ui/file-preview}/types.js +0 -0
package/README.md CHANGED
@@ -37,6 +37,7 @@ Work with code and text, run processes, and automate tasks, going far beyond oth
37
37
  - [How to install](#how-to-install)
38
38
  - [Getting Started](#getting-started)
39
39
  - [Usage](#usage)
40
+ - [File Preview UI & Markdown Editor](#file-preview-ui--markdown-editor)
40
41
  - [Handling Long-Running Commands](#handling-long-running-commands)
41
42
  - [Work in Progress and TODOs](#roadmap)
42
43
  - [Sponsors and Supporters](#support-desktop-commander)
@@ -54,7 +55,7 @@ Execute long-running terminal commands on your computer and manage processes thr
54
55
  ## Features
55
56
 
56
57
  - **Remote AI Control** - Use Desktop Commander from ChatGPT, Claude web, and other AI services via [Remote MCP](https://mcp.desktopcommander.app)
57
- - **File Preview UI** - Visual file previews in Claude Desktop with rendered markdown, inline images, expandable content, and quick "Open in folder" access
58
+ - **File Preview UI** - Visual file previews in Claude Desktop with rendered markdown, inline images, expandable content, built-in markdown editor, and quick "Open in folder" access
58
59
  - **Enhanced terminal commands with interactive process control**
59
60
  - **Execute code in memory (Python, Node.js, R) without saving files**
60
61
  - **Instant data analysis - just ask to analyze CSV/JSON/Excel files**
@@ -336,7 +337,7 @@ Add this to your client's MCP configuration file at the locations below:
336
337
  <details>
337
338
  <summary><b>Cursor</b></summary>
338
339
 
339
- [![Install MCP Server in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=desktop-commander&config=eyJjb21tYW5kIjoibnB4IC15IEB3b25kZXJ3aHktZXIvZGVza3RvcC1jb21tYW5kZXJAbGF0ZXN0In0%3D)
340
+ [Install MCP Server in Cursor](https://cursor.directory/mcp/desktop-commander-mcp)
340
341
 
341
342
  Or add manually to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` in your project folder (project-specific).
342
343
 
@@ -694,6 +695,56 @@ Desktop Commander can be run in Docker containers for **complete isolation from
694
695
  - Claude can see and analyze the actual image content
695
696
  - Default 30-second timeout for URL requests
696
697
 
698
+ ## File Preview UI & Markdown Editor
699
+
700
+ Desktop Commander includes a rich file preview widget in Claude Desktop that renders files visually as AI works with them.
701
+
702
+ ### Supported file types
703
+ - **Markdown** — rendered preview with a built-in editor
704
+ - **Images** — inline display (PNG, JPEG, GIF, WebP, etc.)
705
+ - **Code files** — syntax-highlighted source view
706
+ - **HTML** — rendered preview with toggle to source view
707
+ - **Directories** — interactive tree with expand/collapse and lazy loading
708
+ - **PDF, Excel, DOCX** — native content extraction and display
709
+
710
+ ### Markdown Editor
711
+
712
+ When viewing a `.md` file in Claude Desktop, you can edit it directly inside the preview panel — no need to open a separate app.
713
+
714
+ **How to use:**
715
+ 1. Ask Claude to read or create a markdown file
716
+ 2. Expand the file preview to fullscreen using the **⤢ Expand** button
717
+ 3. The editor activates automatically in fullscreen mode
718
+ 4. Edit your content with a live preview toggle, copy, undo, and save controls
719
+ 5. Changes are saved back to disk; collapse to return to inline view
720
+
721
+ **Editor features:**
722
+ - Live **edit / preview toggle** — switch between raw markdown and rendered output
723
+ - **Auto-save** to disk with save status indicator
724
+ - **Undo** support to revert unsaved changes
725
+ - **Copy** button to grab the full markdown source
726
+ - **Open in editor** — launch your default markdown app directly from the panel
727
+ - Partial-file awareness — loads and merges surrounding lines when the file was only partially read
728
+ - Text selection context — select text in preview mode and the AI can reference your selection
729
+
730
+ ### Directory Browser
731
+
732
+ When Claude runs `list_directory`, the result opens as an interactive file tree inside the preview panel — not just raw text output.
733
+
734
+ **Features:**
735
+ - **Expandable tree** — folders expand and collapse on click; top-level contents shown immediately
736
+ - **Lazy loading** — subfolders load on demand to keep the initial view fast
737
+ - **Large directory handling** — directories with many items show a `⚠ click to load all` button instead of overwhelming the view
738
+ - **Open in Finder/Explorer** — each folder has a quick-open button to reveal it in your file manager
739
+ - **Click to preview** — clicking any file in the tree opens it in the file preview panel directly
740
+ - **Back navigation** — after opening a file from the tree, a ← Back button returns you to the directory view
741
+
742
+ ### Other preview features
743
+ - **Expand / collapse** — toggle between compact summary row and full panel
744
+ - **Open in folder** — reveal the file in Finder/Explorer with one click
745
+ - **Load more lines** — incrementally load content above or below a partial read window
746
+ - **Text selection** — highlight text in any preview; the AI can see and reference your selection
747
+
697
748
  ## Fuzzy Search Log Analysis (npm scripts)
698
749
 
699
750
  The fuzzy search logging system includes convenient npm scripts for analyzing logs outside of the MCP environment:
@@ -1,4 +1,9 @@
1
1
  import { ServerResult } from '../types.js';
2
+ /**
3
+ * Resolve a file path to an absolute path for use in structured content.
4
+ * This ensures "Open in folder" always has a valid absolute path.
5
+ */
6
+ export declare function resolveAbsolutePath(filePath: string): string;
2
7
  /**
3
8
  * Handle read_file command
4
9
  */
@@ -19,7 +19,7 @@ function expandHome(filePath) {
19
19
  * Resolve a file path to an absolute path for use in structured content.
20
20
  * This ensures "Open in folder" always has a valid absolute path.
21
21
  */
22
- function resolveAbsolutePath(filePath) {
22
+ export function resolveAbsolutePath(filePath) {
23
23
  const expanded = expandHome(filePath);
24
24
  return path.isAbsolute(expanded)
25
25
  ? path.resolve(expanded)
@@ -133,7 +133,7 @@ export async function handleReadFile(args) {
133
133
  const textContent = typeof fileResult.content === 'string'
134
134
  ? fileResult.content
135
135
  : fileResult.content.toString('utf8');
136
- const fileType = resolvePreviewFileType(resolvedFilePath);
136
+ const fileType = fileResult.metadata?.isDirectory ? 'directory' : resolvePreviewFileType(resolvedFilePath);
137
137
  return {
138
138
  content: [{ type: "text", text: textContent }],
139
139
  structuredContent: {
@@ -236,11 +236,17 @@ export async function handleWriteFile(args) {
236
236
  await writeFile(parsed.path, parsed.content, parsed.mode);
237
237
  // Provide more informative message based on mode
238
238
  const modeMessage = parsed.mode === 'append' ? 'appended to' : 'wrote to';
239
+ const resolvedWritePath = resolveAbsolutePath(parsed.path);
239
240
  return {
240
241
  content: [{
241
242
  type: "text",
242
243
  text: `Successfully ${modeMessage} ${parsed.path} (${lineCount} lines) ${errorMessage}`
243
244
  }],
245
+ structuredContent: {
246
+ fileName: path.basename(resolvedWritePath),
247
+ filePath: resolvedWritePath,
248
+ fileType: resolvePreviewFileType(resolvedWritePath),
249
+ },
244
250
  };
245
251
  }
246
252
  catch (error) {
@@ -274,8 +280,14 @@ export async function handleListDirectory(args) {
274
280
  const entries = await listDirectory(parsed.path, parsed.depth);
275
281
  const duration = Date.now() - startTime;
276
282
  const resultText = entries.join('\n');
283
+ const resolvedPath = resolveAbsolutePath(parsed.path);
277
284
  return {
278
285
  content: [{ type: "text", text: resultText }],
286
+ structuredContent: {
287
+ fileName: path.basename(resolvedPath),
288
+ filePath: resolvedPath,
289
+ fileType: 'directory',
290
+ },
279
291
  };
280
292
  }
281
293
  catch (error) {
@@ -76,7 +76,7 @@ export class DesktopCommanderIntegration {
76
76
  // We can't run it directly as it's an stdio MCP server that waits for input
77
77
  const whichCommand = process.platform === 'win32' ? 'where' : 'which';
78
78
  console.debug('[DEBUG] Using platform command:', whichCommand, 'on platform:', process.platform);
79
- const check = spawn(whichCommand, [commandName]);
79
+ const check = spawn(whichCommand, [commandName], { windowsHide: true }); // Prevent visible console windows on Windows
80
80
  check.on('error', (err) => {
81
81
  console.debug('[DEBUG] Spawn error for', whichCommand, ':', err.message);
82
82
  reject(err);
@@ -33,7 +33,7 @@ import PizZip from 'pizzip';
33
33
  throw new Error(`Failed to locate ripgrep binary: ${err instanceof Error ? err.message : String(err)}`);
34
34
  }
35
35
  // Start ripgrep process
36
- const rgProcess = spawn(rgPath, args);
36
+ const rgProcess = spawn(rgPath, args, { windowsHide: true }); // Prevent visible console windows on Windows
37
37
  if (!rgProcess.pid) {
38
38
  throw new Error('Failed to start ripgrep process');
39
39
  }
@@ -94,7 +94,8 @@ import PizZip from 'pizzip';
94
94
  const shouldSearchExcel = options.searchType === 'content' &&
95
95
  this.shouldIncludeExcelSearch(options.filePattern, validPath);
96
96
  if (shouldSearchExcel) {
97
- this.searchExcelFiles(validPath, options.pattern, options.ignoreCase !== false, options.maxResults, options.filePattern // Pass filePattern to filter Excel files too
97
+ this.searchExcelFiles(validPath, options.pattern, options.ignoreCase !== false, options.maxResults, options.filePattern, // Pass filePattern to filter Excel files too
98
+ options.literalSearch // Respect literalSearch flag for Office files
98
99
  ).then(excelResults => {
99
100
  // Add Excel results to session (merged after initial response)
100
101
  for (const result of excelResults) {
@@ -110,7 +111,8 @@ import PizZip from 'pizzip';
110
111
  const shouldSearchDocx = options.searchType === 'content' &&
111
112
  this.shouldIncludeDocxSearch(options.filePattern, validPath);
112
113
  if (shouldSearchDocx) {
113
- this.searchDocxFiles(validPath, options.pattern, options.ignoreCase !== false, options.maxResults, options.filePattern).then(docxResults => {
114
+ this.searchDocxFiles(validPath, options.pattern, options.ignoreCase !== false, options.maxResults, options.filePattern, options.literalSearch // Respect literalSearch flag for Office files
115
+ ).then(docxResults => {
114
116
  for (const result of docxResults) {
115
117
  session.results.push(result);
116
118
  session.totalMatches++;
@@ -223,19 +225,11 @@ import PizZip from 'pizzip';
223
225
  * and inject into SearchManager, similar to how file handlers are structured in src/utils/files/
224
226
  * This would allow adding other file type searches (PDF, etc.) without bloating search-manager.ts
225
227
  */
226
- async searchExcelFiles(rootPath, pattern, ignoreCase, maxResults, filePattern) {
228
+ async searchExcelFiles(rootPath, pattern, ignoreCase, maxResults, filePattern, _literalSearch) {
227
229
  const results = [];
228
- // Build regex for matching content
229
- const flags = ignoreCase ? 'i' : '';
230
- let regex;
231
- try {
232
- regex = new RegExp(pattern, flags);
233
- }
234
- catch {
235
- // If pattern is not valid regex, escape it for literal matching
236
- const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
237
- regex = new RegExp(escaped, flags);
238
- }
230
+ // Office file search always uses literal matching to prevent ReDoS.
231
+ // Regex patterns are treated as literal strings — this is intentional.
232
+ const searchTerm = ignoreCase ? pattern.toLowerCase() : pattern;
239
233
  // Find Excel files recursively
240
234
  let excelFiles = await this.findExcelFiles(rootPath);
241
235
  // Filter by filePattern if provided
@@ -246,7 +240,13 @@ import PizZip from 'pizzip';
246
240
  return patterns.some(pat => {
247
241
  // Support glob-like patterns
248
242
  if (pat.includes('*')) {
249
- const regexPat = pat.replace(/\./g, '\\.').replace(/\*/g, '.*');
243
+ // Escape all regex metacharacters first (preserving * for glob expansion),
244
+ // then convert the remaining * wildcards to .* for glob matching.
245
+ // Without this, patterns like report(2024).xlsx or [draft].xlsx would be
246
+ // misinterpreted as regex groups/character-classes.
247
+ const regexPat = pat
248
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&') // escape metacharacters except *
249
+ .replace(/\*/g, '.*'); // glob * → regex .*
250
250
  return new RegExp(`^${regexPat}$`, 'i').test(fileName);
251
251
  }
252
252
  // Exact match (case-insensitive)
@@ -300,12 +300,10 @@ import PizZip from 'pizzip';
300
300
  });
301
301
  // Join all cell values with space for cross-column matching
302
302
  const rowText = rowValues.join(' ');
303
- if (regex.test(rowText)) {
304
- // Extract the matching portion for display
305
- const match = rowText.match(regex);
306
- const matchContext = match
307
- ? this.getMatchContext(rowText, match.index || 0, match[0].length)
308
- : rowText.substring(0, 150);
303
+ const textToSearch = ignoreCase ? rowText.toLowerCase() : rowText;
304
+ const matchIndex = textToSearch.indexOf(searchTerm);
305
+ if (matchIndex !== -1) {
306
+ const matchContext = this.getMatchContext(rowText, matchIndex, searchTerm.length);
309
307
  results.push({
310
308
  file: `${filePath}:${sheetName}!Row${rowNumber}`,
311
309
  line: rowNumber,
@@ -386,17 +384,11 @@ import PizZip from 'pizzip';
386
384
  * Search DOCX files for content matches
387
385
  * Extracts <w:t> text from document.xml and searches it
388
386
  */
389
- async searchDocxFiles(rootPath, pattern, ignoreCase, maxResults, filePattern) {
387
+ async searchDocxFiles(rootPath, pattern, ignoreCase, maxResults, filePattern, _literalSearch) {
390
388
  const results = [];
391
- const flags = ignoreCase ? 'i' : '';
392
- let regex;
393
- try {
394
- regex = new RegExp(pattern, flags);
395
- }
396
- catch {
397
- const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
398
- regex = new RegExp(escaped, flags);
399
- }
389
+ // Office file search always uses literal matching to prevent ReDoS.
390
+ // Regex patterns are treated as literal strings — this is intentional.
391
+ const searchTerm = ignoreCase ? pattern.toLowerCase() : pattern;
400
392
  let docxFiles = await this.findDocxFiles(rootPath);
401
393
  if (filePattern) {
402
394
  const patterns = filePattern.split('|').map(p => p.trim()).filter(Boolean);
@@ -404,7 +396,9 @@ import PizZip from 'pizzip';
404
396
  const fileName = path.basename(filePath);
405
397
  return patterns.some(pat => {
406
398
  if (pat.includes('*')) {
407
- const regexPat = pat.replace(/\./g, '\\.').replace(/\*/g, '.*');
399
+ const regexPat = pat
400
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&') // escape metacharacters except *
401
+ .replace(/\*/g, '.*'); // glob * → regex .*
408
402
  return new RegExp(`^${regexPat}$`, 'i').test(fileName);
409
403
  }
410
404
  return fileName.toLowerCase() === pat.toLowerCase();
@@ -438,11 +432,10 @@ import PizZip from 'pizzip';
438
432
  if (!text || !text.trim())
439
433
  continue;
440
434
  lineNum++;
441
- if (regex.test(text)) {
442
- const match = text.match(regex);
443
- const matchContext = match
444
- ? this.getMatchContext(text, match.index || 0, match[0].length)
445
- : text.substring(0, 150);
435
+ const textToSearch = ignoreCase ? text.toLowerCase() : text;
436
+ const matchIndex = textToSearch.indexOf(searchTerm);
437
+ if (matchIndex !== -1) {
438
+ const matchContext = this.getMatchContext(text, matchIndex, searchTerm.length);
446
439
  const partName = xmlPath === 'word/document.xml' ? '' : `:${xmlPath.replace('word/', '')}`;
447
440
  results.push({
448
441
  file: `${filePath}${partName}`,
package/dist/server.js CHANGED
@@ -137,9 +137,11 @@ deferLog('info', 'Setting up request handlers...');
137
137
  * Check if a tool should be included based on current client
138
138
  */
139
139
  function shouldIncludeTool(toolName) {
140
- // Exclude give_feedback_to_desktop_commander for desktop-commander client
141
- if (toolName === 'give_feedback_to_desktop_commander' && currentClient?.name === 'desktop-commander') {
142
- return false;
140
+ // Exclude these tools for desktop-commander client (DC-specific meta-tools not useful when DC itself is the client)
141
+ if (currentClient?.name === 'desktop-commander-app') {
142
+ if (toolName === 'give_feedback_to_desktop_commander' || toolName === 'get_prompts') {
143
+ return false;
144
+ }
143
145
  }
144
146
  // Add more conditional tool logic here as needed
145
147
  // Example: if (toolName === 'some_tool' && currentClient?.name === 'some_client') return false;
@@ -328,6 +330,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
328
330
  ${PATH_GUIDANCE}
329
331
  ${CMD_PREFIX_DESCRIPTION}`,
330
332
  inputSchema: zodToJsonSchema(WriteFileArgsSchema),
333
+ _meta: buildUiToolMeta(FILE_PREVIEW_RESOURCE_URI, true),
331
334
  annotations: {
332
335
  title: "Write File",
333
336
  readOnlyHint: false,
@@ -447,6 +450,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
447
450
  ${PATH_GUIDANCE}
448
451
  ${CMD_PREFIX_DESCRIPTION}`,
449
452
  inputSchema: zodToJsonSchema(ListDirectoryArgsSchema),
453
+ _meta: buildUiToolMeta(FILE_PREVIEW_RESOURCE_URI, true),
450
454
  annotations: {
451
455
  title: "List Directory Contents",
452
456
  readOnlyHint: true,
@@ -705,6 +709,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
705
709
  ${PATH_GUIDANCE}
706
710
  ${CMD_PREFIX_DESCRIPTION}`,
707
711
  inputSchema: zodToJsonSchema(EditBlockArgsSchema),
712
+ _meta: buildUiToolMeta(FILE_PREVIEW_RESOURCE_URI, true),
708
713
  annotations: {
709
714
  title: "Edit Block",
710
715
  readOnlyHint: false,
@@ -119,7 +119,8 @@ export class TerminalManager {
119
119
  env: {
120
120
  ...process.env,
121
121
  TERM: 'xterm-256color' // Better terminal compatibility
122
- }
122
+ },
123
+ windowsHide: true // Prevent visible console windows on Windows
123
124
  };
124
125
  // Add shell option if needed (for unknown shells)
125
126
  if (spawnConfig.useShellOption) {
@@ -138,7 +139,8 @@ export class TerminalManager {
138
139
  env: {
139
140
  ...process.env,
140
141
  TERM: 'xterm-256color'
141
- }
142
+ },
143
+ windowsHide: true // Prevent visible console windows on Windows
142
144
  };
143
145
  }
144
146
  // Spawn the process with appropriate arguments
@@ -23,6 +23,8 @@ import path from 'path';
23
23
  import { detectLineEnding, normalizeLineEndings } from '../utils/lineEndingHandler.js';
24
24
  import { configManager } from '../config-manager.js';
25
25
  import { fuzzySearchLogger } from '../utils/fuzzySearchLogger.js';
26
+ import { resolvePreviewFileType } from '../ui/file-preview/shared/preview-file-types.js';
27
+ import { resolveAbsolutePath } from '../handlers/filesystem-handlers.js';
26
28
  /**
27
29
  * Threshold for fuzzy matching - similarity must be at least this value to be considered
28
30
  * (0-1 scale where 1 is perfect match and 0 is completely different)
@@ -155,11 +157,30 @@ RECOMMENDATION: For large search/replace operations, consider breaking them into
155
157
  }
156
158
  await writeFile(filePath, newContent);
157
159
  capture('server_edit_block_exact_success', { fileExtension: fileExtension, expectedReplacements, hasWarning: warningMessage !== "" });
160
+ const resolvedEditPath = resolveAbsolutePath(filePath);
161
+ // Show a partial preview centered on the edited area
162
+ const newLines = newContent.split('\n');
163
+ const totalLines = newLines.length;
164
+ const changePos = content.indexOf(normalizedSearch);
165
+ const changeStartLine = changePos >= 0 ? newContent.substring(0, changePos).split('\n').length - 1 : 0;
166
+ const changeLineCount = block.replace.split('\n').length;
167
+ const contextLines = 10;
168
+ const previewStart = Math.max(0, changeStartLine - contextLines);
169
+ const previewEnd = Math.min(totalLines, changeStartLine + changeLineCount + contextLines);
170
+ const previewContent = newLines.slice(previewStart, previewEnd).join('\n');
171
+ const previewLineCount = previewEnd - previewStart;
172
+ const remaining = totalLines - previewEnd;
173
+ const statusLine = `[Reading ${previewLineCount} lines from ${previewStart === 0 ? 'start' : `line ${previewStart}`} (total: ${totalLines} lines, ${remaining} remaining)]\n\n`;
158
174
  return {
159
175
  content: [{
160
176
  type: "text",
161
- text: `Successfully applied ${expectedReplacements} edit${expectedReplacements > 1 ? 's' : ''} to ${filePath}${warningMessage}`
177
+ text: `${statusLine}${previewContent}`
162
178
  }],
179
+ structuredContent: {
180
+ fileName: path.basename(resolvedEditPath),
181
+ filePath: resolvedEditPath,
182
+ fileType: resolvePreviewFileType(resolvedEditPath),
183
+ },
163
184
  };
164
185
  }
165
186
  // If exact match found but count doesn't match expected, inform the user
@@ -337,11 +358,17 @@ export async function handleEditBlock(args) {
337
358
  try {
338
359
  // parsed.range is guaranteed non-empty string by hasRange check above
339
360
  await handler.editRange(validatedPath, parsed.range, content, parsed.options);
361
+ const resolvedRangePath = resolveAbsolutePath(parsed.file_path);
340
362
  return {
341
363
  content: [{
342
364
  type: "text",
343
365
  text: `Successfully updated range ${parsed.range} in ${parsed.file_path}`
344
366
  }],
367
+ structuredContent: {
368
+ fileName: path.basename(resolvedRangePath),
369
+ filePath: resolvedRangePath,
370
+ fileType: resolvePreviewFileType(resolvedRangePath),
371
+ },
345
372
  };
346
373
  }
347
374
  catch (error) {
@@ -366,11 +393,17 @@ export async function handleEditBlock(args) {
366
393
  expected_replacements: parsed.expected_replacements,
367
394
  });
368
395
  if (result.success) {
396
+ const resolvedEditRangePath = resolveAbsolutePath(parsed.file_path);
369
397
  return {
370
398
  content: [{
371
399
  type: "text",
372
400
  text: `Successfully applied ${result.editsApplied} edit(s) to ${parsed.file_path}`
373
401
  }],
402
+ structuredContent: {
403
+ fileName: path.basename(resolvedEditRangePath),
404
+ filePath: resolvedEditRangePath,
405
+ fileType: resolvePreviewFileType(resolvedEditRangePath),
406
+ },
374
407
  };
375
408
  }
376
409
  const errorMsg = result.errors?.map(e => e.error).join('; ') || 'Unknown error';
@@ -46,6 +46,45 @@ async function getDefaultReadLength() {
46
46
  const config = await configManager.getConfig();
47
47
  return config.fileReadLineLimit ?? 1000; // Default to 1000 lines if not set
48
48
  }
49
+ /**
50
+ * Returns a helpful error message when a file operation fails with a permission
51
+ * or timeout error.
52
+ *
53
+ * Lists all common causes without path-based detection — the AI receiving this
54
+ * error should inspect the path and inform the user which cause is most likely
55
+ * (e.g. cloud storage folder, network drive, system file, locked file, etc.)
56
+ */
57
+ function buildPermissionError(filePath, errCode) {
58
+ const isMac = process.platform === 'darwin';
59
+ const isTimeout = errCode === 'ETIMEDOUT';
60
+ const lines = [
61
+ `Cannot read file — ${isTimeout ? 'operation timed out' : 'permission denied'} (${errCode}).`,
62
+ `Path: ${filePath}`,
63
+ ``,
64
+ `[AI: based on the path above, determine which cause is most likely and explain it to the user.]`,
65
+ ``,
66
+ `Possible causes and fixes:`,
67
+ ` 1. File is in cloud storage (Google Drive / iCloud / Dropbox / OneDrive) but not downloaded locally.`,
68
+ ` → Right-click the file and choose "Download Now", "Make Available Offline", or "Keep on This Device".`,
69
+ ` 2. Cloud storage app is not running or not signed in.`,
70
+ ` → Open your cloud storage app and make sure it is syncing.`,
71
+ ` 3. File is on a network drive or virtual filesystem that is currently unavailable.`,
72
+ ` → Check that the network share or drive is mounted and accessible.`,
73
+ ` 4. File has restricted permissions (e.g. system file, locked by another process, or chmod 000).`,
74
+ ` → Check file permissions or close any app that may have the file open.`,
75
+ ` 5. The app does not have permission to access this location (macOS Full Disk Access).`,
76
+ ];
77
+ if (isMac) {
78
+ lines.push(` → Go to System Settings → Privacy & Security → Full Disk Access and enable Claude.`);
79
+ lines.push(` → To open that pane directly, run in terminal:`);
80
+ lines.push(` open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"`);
81
+ lines.push(` Then find "Claude" in the list and enable the toggle next to it.`);
82
+ }
83
+ else {
84
+ lines.push(` → Check that the app has permission to access this file location.`);
85
+ }
86
+ return new Error(lines.join('\n'));
87
+ }
49
88
  // Initialize allowed directories from configuration
50
89
  async function getAllowedDirs() {
51
90
  try {
@@ -307,6 +346,35 @@ export async function readFileFromDisk(filePath, options) {
307
346
  const validPath = await validatePath(filePath);
308
347
  // Get file extension for telemetry
309
348
  const fileExtension = getFileExtension(validPath);
349
+ // Check if path is a directory — return listing instead of EISDIR error
350
+ try {
351
+ const stats = await fs.stat(validPath);
352
+ if (stats.isDirectory()) {
353
+ const dirListOp = async () => {
354
+ const entries = await listDirectory(validPath);
355
+ const listing = entries.join('\n');
356
+ return {
357
+ content: `This is a directory, not a file. Use the list_directory tool instead of read_file for directories.\n\n${listing}`,
358
+ mimeType: 'text/plain',
359
+ metadata: { isImage: false, isDirectory: true }
360
+ };
361
+ };
362
+ const dirResult = await withTimeout(dirListOp(), FILE_OPERATION_TIMEOUTS.FILE_READ, 'Directory listing fallback', null);
363
+ if (dirResult === null) {
364
+ throw new Error(`Directory listing timed out for: ${filePath}`);
365
+ }
366
+ return dirResult;
367
+ }
368
+ }
369
+ catch (error) {
370
+ // If stat itself failed, fall through to the read path which will produce a proper error.
371
+ // But if this was a directory-listing error, re-throw — don't let it fall into the file-read path.
372
+ const err = error;
373
+ if (err.message?.includes('Directory listing') || err.message?.includes('list_directory')) {
374
+ throw error;
375
+ }
376
+ // stat() failed (e.g. ENOENT) — fall through to the read path below
377
+ }
310
378
  // Check file size before attempting to read
311
379
  try {
312
380
  const stats = await fs.stat(validPath);
@@ -357,7 +425,20 @@ export async function readFileFromDisk(filePath, options) {
357
425
  };
358
426
  };
359
427
  // Execute with timeout
360
- const result = await withTimeout(readOperation(), FILE_OPERATION_TIMEOUTS.FILE_READ, `Read file operation for ${filePath}`, null);
428
+ let result;
429
+ try {
430
+ result = await withTimeout(readOperation(), FILE_OPERATION_TIMEOUTS.FILE_READ, `Read file operation for ${filePath}`, null);
431
+ }
432
+ catch (error) {
433
+ const err = error;
434
+ // withTimeout rejects with a plain string "__ERROR__: ... timed out after N seconds"
435
+ // when defaultValue is null — it has no .code property, so check for that too.
436
+ const isWithTimeoutString = typeof error === 'string' && error.startsWith('__ERROR__:');
437
+ if (isWithTimeoutString || err.code === 'EPERM' || err.code === 'EACCES' || err.code === 'ETIMEDOUT') {
438
+ throw buildPermissionError(filePath, isWithTimeoutString ? 'ETIMEDOUT' : err.code);
439
+ }
440
+ throw error;
441
+ }
361
442
  if (result == null) {
362
443
  // Handles the impossible case where withTimeout resolves to null instead of throwing
363
444
  throw new Error('Failed to read the file');
@@ -490,9 +571,16 @@ export async function listDirectory(dirPath, depth = 2) {
490
571
  entries = await fs.readdir(currentPath, { withFileTypes: true });
491
572
  }
492
573
  catch (error) {
493
- // If we can't read this directory (permission denied), show as denied
574
+ const err = error;
494
575
  const displayPath = relativePath || path.basename(currentPath);
495
- results.push(`[DENIED] ${displayPath}`);
576
+ // Keep [DENIED] prefix so UI parser regex still matches.
577
+ // Append a hint for permission/timeout errors so user gets context.
578
+ if (err.code === 'EPERM' || err.code === 'EACCES' || err.code === 'ETIMEDOUT') {
579
+ results.push(`[DENIED] ${displayPath} — not accessible (permission denied, cloud-only file, or Full Disk Access not granted)`);
580
+ }
581
+ else {
582
+ results.push(`[DENIED] ${displayPath}`);
583
+ }
496
584
  return;
497
585
  }
498
586
  // Apply filtering for nested directories (not top level)
@@ -27,7 +27,8 @@ async function executeNodeCode(code, timeout_ms = 30000) {
27
27
  const result = await new Promise((resolve) => {
28
28
  const proc = spawn(process.execPath, [tempFile], {
29
29
  cwd: mcpRoot,
30
- timeout: timeout_ms
30
+ timeout: timeout_ms,
31
+ windowsHide: true // Prevent visible console windows on Windows
31
32
  });
32
33
  let stdout = '';
33
34
  let stderr = '';