@xiaobuyu/nodesign 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/README.md +156 -106
  2. package/bin/nodesign.js +0 -0
  3. package/package.json +1 -1
  4. package/server/api/README.md +45 -45
  5. package/server/api/_guard.js +75 -75
  6. package/server/api/admin.js +187 -187
  7. package/server/api/assets/docx-page.js +103 -103
  8. package/server/api/assets/helpers.js +42 -22
  9. package/server/api/assets/notes.js +194 -144
  10. package/server/api/assets.js +891 -896
  11. package/server/api/board.js +134 -57
  12. package/server/api/browse.js +138 -138
  13. package/server/api/canvas.js +444 -444
  14. package/server/api/chatai.js +324 -324
  15. package/server/api/exports/build-standalone.js +980 -980
  16. package/server/api/exports/cards.js +118 -118
  17. package/server/api/exports/docx-pdf.js +46 -46
  18. package/server/api/exports/export-page.js +142 -142
  19. package/server/api/exports/handoff.js +147 -147
  20. package/server/api/exports.js +690 -690
  21. package/server/api/instruction.js +75 -75
  22. package/server/api/me.js +116 -116
  23. package/server/api/memory.js +185 -185
  24. package/server/api/pending-changes.js +366 -366
  25. package/server/api/plugins.js +164 -164
  26. package/server/api/projects.js +220 -220
  27. package/server/api/publish.js +64 -64
  28. package/server/api/recent.js +73 -73
  29. package/server/api/sessions.js +589 -589
  30. package/server/api/skills.js +77 -77
  31. package/server/api/standalone-fit.js +168 -168
  32. package/server/api/turn-compose.js +180 -180
  33. package/server/api/turn-inflight.js +44 -44
  34. package/server/auth/README.md +17 -17
  35. package/server/auth/middleware.js +138 -138
  36. package/server/auth/origin-guard.js +71 -71
  37. package/server/auth/session.js +108 -108
  38. package/server/auth/tier.js +113 -113
  39. package/server/auth/users-store.js +348 -348
  40. package/server/edit/README.md +29 -29
  41. package/server/engine/README.md +113 -113
  42. package/server/engine/agent/agent-shared.js +550 -550
  43. package/server/engine/agent/auto-mode-default-hard-deny.txt +4 -4
  44. package/server/engine/agent/auto-mode-rules.js +78 -78
  45. package/server/engine/agent/context.js +418 -418
  46. package/server/engine/agent/events.js +465 -464
  47. package/server/engine/agent/hooks/canvas-validate.js +199 -199
  48. package/server/engine/agent/hooks/failure.js +120 -120
  49. package/server/engine/agent/hooks/file-events.js +75 -75
  50. package/server/engine/agent/hooks/lifecycle.js +233 -233
  51. package/server/engine/agent/hooks/post-canvas-focus.js +80 -80
  52. package/server/engine/agent/hooks/post-guidance.js +206 -206
  53. package/server/engine/agent/hooks/post-subagent-report.js +104 -104
  54. package/server/engine/agent/hooks/post-trim.js +58 -58
  55. package/server/engine/agent/hooks/pre-board-neighborhood.js +34 -34
  56. package/server/engine/agent/hooks/pre-defaults.js +84 -84
  57. package/server/engine/agent/hooks/pre-injectors.js +277 -277
  58. package/server/engine/agent/hooks/pre-performance-log-guard.js +78 -78
  59. package/server/engine/agent/hooks/pre-starter-files.js +61 -61
  60. package/server/engine/agent/hooks/pre-workspace-scope-guard.js +78 -78
  61. package/server/engine/agent/hooks/site-validate.js +94 -94
  62. package/server/engine/agent/hooks/tool-prompts.js +31 -31
  63. package/server/engine/agent/hooks/turn-state-memory.js +56 -56
  64. package/server/engine/agent/hooks/user-prompt-submit.js +258 -215
  65. package/server/engine/agent/hooks.js +354 -354
  66. package/server/engine/agent/init-contract.js +69 -69
  67. package/server/engine/agent/isolation.js +159 -159
  68. package/server/engine/agent/model-table.js +292 -292
  69. package/server/engine/agent/plugin-loader.js +266 -266
  70. package/server/engine/agent/prompts/nodesign-prelude.md +388 -336
  71. package/server/engine/agent/prompts/tools/ask-user-question-protocol.md +71 -71
  72. package/server/engine/agent/prompts/tools/direct-edit-protocol.md +98 -98
  73. package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +583 -583
  74. package/server/engine/agent/prompts/tools/generate-image-cookbook.md +483 -483
  75. package/server/engine/agent/prompts/tools/hybrid-reference.md +100 -100
  76. package/server/engine/agent/prompts/tools/paint-still-cookbook.md +270 -270
  77. package/server/engine/agent/prompts/tools/roll-film-cookbook.md +70 -70
  78. package/server/engine/agent/prompts/tools/site-reference.md +106 -106
  79. package/server/engine/agent/prompts/tools/tweaks-syntax.md +125 -125
  80. package/server/engine/agent/prompts/tools/vision-checker-dispatch.md +48 -48
  81. package/server/engine/agent/session-loop.js +1035 -1024
  82. package/server/engine/agent/session-model.js +164 -164
  83. package/server/engine/agent/skill.js +204 -204
  84. package/server/engine/agent/task-events.js +78 -78
  85. package/server/engine/agents/ds-extractor.md +132 -132
  86. package/server/engine/agents/explorer.md +190 -190
  87. package/server/engine/agents/index.js +230 -230
  88. package/server/engine/agents/schemas/design-system.json +156 -156
  89. package/server/engine/agents/schemas/tweak-schema.json +113 -113
  90. package/server/engine/agents/tweak-proposer.md +127 -127
  91. package/server/engine/agents/vision-checker.md +254 -254
  92. package/server/engine/browse/capture.js +422 -422
  93. package/server/engine/browse/card.js +137 -137
  94. package/server/engine/browse/handover.js +59 -59
  95. package/server/engine/browse/page-digest.js +119 -119
  96. package/server/engine/browse/refs.js +196 -196
  97. package/server/engine/browse/registry.js +307 -307
  98. package/server/engine/browse/screencast.js +170 -170
  99. package/server/engine/browse/state.js +121 -121
  100. package/server/engine/chatai/chat-log.js +79 -79
  101. package/server/engine/chatai/index.js +193 -193
  102. package/server/engine/chatai/openai-compat.js +152 -152
  103. package/server/engine/chatai/orchestrate.js +309 -309
  104. package/server/engine/chatai/perform.js +40 -40
  105. package/server/engine/chatai/summarize.js +79 -79
  106. package/server/engine/mcp/capability-gate.js +52 -52
  107. package/server/engine/mcp/index.js +351 -339
  108. package/server/engine/mcp/param-sanitizer.js +142 -142
  109. package/server/engine/mcp/tools/arrange-on-board.js +124 -124
  110. package/server/engine/mcp/tools/artifact-session.js +278 -278
  111. package/server/engine/mcp/tools/browse-computer.js +421 -421
  112. package/server/engine/mcp/tools/browse-find-batch.js +183 -183
  113. package/server/engine/mcp/tools/browse-screenshot.js +157 -157
  114. package/server/engine/mcp/tools/browse.js +480 -480
  115. package/server/engine/mcp/tools/build-docx.js +101 -101
  116. package/server/engine/mcp/tools/clear-pending-changes.js +99 -99
  117. package/server/engine/mcp/tools/create-on-board.js +152 -155
  118. package/server/engine/mcp/tools/crystallize-skill.js +168 -168
  119. package/server/engine/mcp/tools/edit-sketch.js +167 -0
  120. package/server/engine/mcp/tools/explain-style.js +237 -237
  121. package/server/engine/mcp/tools/export-handoff.js +133 -133
  122. package/server/engine/mcp/tools/expose-tweaks.js +149 -149
  123. package/server/engine/mcp/tools/generate-image.js +842 -842
  124. package/server/engine/mcp/tools/get-computed-styles.js +164 -164
  125. package/server/engine/mcp/tools/get-pending-changes.js +205 -205
  126. package/server/engine/mcp/tools/h3box-ssh.js +84 -84
  127. package/server/engine/mcp/tools/helpers/acquire-page.js +82 -82
  128. package/server/engine/mcp/tools/helpers/motion-lab.js +554 -554
  129. package/server/engine/mcp/tools/helpers/motion-scroll.js +112 -112
  130. package/server/engine/mcp/tools/helpers/perception-page.js +215 -215
  131. package/server/engine/mcp/tools/helpers/reference-download.js +103 -103
  132. package/server/engine/mcp/tools/helpers/rembg-bridge.py +80 -80
  133. package/server/engine/mcp/tools/helpers/shot-pipeline.js +280 -280
  134. package/server/engine/mcp/tools/highlight.js +68 -68
  135. package/server/engine/mcp/tools/list-pages.js +222 -222
  136. package/server/engine/mcp/tools/look-at-board.js +124 -0
  137. package/server/engine/mcp/tools/lookup-tags.js +59 -59
  138. package/server/engine/mcp/tools/navigate-to-page.js +60 -60
  139. package/server/engine/mcp/tools/organize-board.js +64 -64
  140. package/server/engine/mcp/tools/paint-still.js +434 -434
  141. package/server/engine/mcp/tools/pin-to-board.js +130 -130
  142. package/server/engine/mcp/tools/preview-deck.js +130 -130
  143. package/server/engine/mcp/tools/profile-scroll.js +305 -305
  144. package/server/engine/mcp/tools/publish-site.js +102 -102
  145. package/server/engine/mcp/tools/query-elements.js +195 -195
  146. package/server/engine/mcp/tools/read-board.js +190 -103
  147. package/server/engine/mcp/tools/read-document.js +93 -93
  148. package/server/engine/mcp/tools/read-page.js +264 -264
  149. package/server/engine/mcp/tools/read-tavern-json.js +143 -143
  150. package/server/engine/mcp/tools/read-user-view.js +59 -0
  151. package/server/engine/mcp/tools/record-decision.js +140 -140
  152. package/server/engine/mcp/tools/relate-on-board.js +112 -110
  153. package/server/engine/mcp/tools/report-issue.js +115 -115
  154. package/server/engine/mcp/tools/roll-film.js +266 -266
  155. package/server/engine/mcp/tools/screenshot-docx.js +138 -138
  156. package/server/engine/mcp/tools/screenshot-url.js +195 -195
  157. package/server/engine/mcp/tools/screenshot.js +583 -583
  158. package/server/engine/mcp/tools/sketch-on-board.js +337 -0
  159. package/server/engine/mcp/tools/tier-gate.js +96 -96
  160. package/server/engine/mcp/tools/trace-motion.js +215 -215
  161. package/server/engine/mcp/tools/web-search.js +548 -548
  162. package/server/engine/mcp/tools/write-on-board.js +130 -0
  163. package/server/engine/motion/inventory.js +349 -349
  164. package/server/engine/perception/session.js +235 -235
  165. package/server/engine/plugins/nodesign/.claude-plugin/plugin.json +5 -5
  166. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +199 -192
  167. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/canvas.template.html +667 -667
  168. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/hybrid-grid.md +22 -22
  169. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/image-led-cover.md +25 -25
  170. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/portrait.md +23 -23
  171. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/quote-backdrop.md +21 -21
  172. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/section-divider.md +23 -23
  173. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/text-led.md +21 -21
  174. package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +169 -162
  175. package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +407 -407
  176. package/server/engine/plugins/nodesign/skills/docx-craft//346/226/207/346/241/243.template.json +80 -80
  177. package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +235 -228
  178. package/server/engine/plugins/nodesign/skills/rp-craft/patterns//346/274/224/345/207/272/351/241/265-/346/234/200/345/260/217/345/256/236/347/216/260.html +78 -78
  179. package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +268 -268
  180. package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +330 -323
  181. package/server/engine/plugins/nodesign/skills/site-craft/patterns/build-lane.md +134 -134
  182. package/server/engine/plugins/nodesign/skills/site-craft/patterns/cutout-collage.md +61 -61
  183. package/server/engine/plugins/nodesign/skills/site-craft/patterns/mock-app.md +163 -163
  184. package/server/engine/plugins/nodesign/skills/site-craft/patterns/page-transitions.md +86 -86
  185. package/server/engine/runs/active-runs.js +698 -698
  186. package/server/engine/runs/board-tasklist.js +130 -0
  187. package/server/engine/runs/live-turn.js +252 -252
  188. package/server/engine/runs/store.js +364 -364
  189. package/server/engine/runs/turn-relay.js +217 -217
  190. package/server/engine/runtime/workspace.js +164 -164
  191. package/server/index.js +3 -0
  192. package/server/lib/artifact-file-path.js +80 -80
  193. package/server/lib/artifact-target.js +284 -284
  194. package/server/lib/asset-refs.js +239 -239
  195. package/server/lib/async-queue.js +104 -104
  196. package/server/lib/auto-relations.js +130 -130
  197. package/server/lib/binding-types.js +72 -60
  198. package/server/lib/board-graph-export.js +191 -0
  199. package/server/lib/board-groups.js +167 -0
  200. package/server/lib/board-hero.js +76 -0
  201. package/server/lib/board-kind-sizes.js +85 -62
  202. package/server/lib/board-relations.js +133 -133
  203. package/server/lib/browse-proxy.js +180 -180
  204. package/server/lib/canvas-id.js +36 -36
  205. package/server/lib/chalk.js +121 -0
  206. package/server/lib/cover.js +227 -227
  207. package/server/lib/danbooru-tags.js +291 -291
  208. package/server/lib/doc-extract.js +156 -156
  209. package/server/lib/docx/build-from-source.js +260 -260
  210. package/server/lib/docx/build.js +490 -490
  211. package/server/lib/docx/dump-styles.js +287 -287
  212. package/server/lib/docx/fonts/nodesign-cjk.conf +164 -164
  213. package/server/lib/docx/merge-runs.js +129 -129
  214. package/server/lib/docx/numbering.js +218 -218
  215. package/server/lib/docx/order.js +152 -152
  216. package/server/lib/docx/rawzip.js +172 -172
  217. package/server/lib/docx/render.js +92 -92
  218. package/server/lib/docx/text-lint.js +225 -225
  219. package/server/lib/docx/tokens.js +335 -335
  220. package/server/lib/docx/units.js +29 -29
  221. package/server/lib/docx/xml.js +291 -291
  222. package/server/lib/docx-pages.js +187 -187
  223. package/server/lib/export-collect.js +289 -289
  224. package/server/lib/export-package.js +210 -210
  225. package/server/lib/html-srcset.js +145 -145
  226. package/server/lib/image-variant.js +460 -460
  227. package/server/lib/ingress/forward-openai-chat.js +305 -305
  228. package/server/lib/ingress/openai-chat.js +491 -491
  229. package/server/lib/ingress/session-notice.js +66 -66
  230. package/server/lib/ingress/session-routes.js +68 -68
  231. package/server/lib/ingress/slot-probe.js +130 -130
  232. package/server/lib/ingress/upstream-billing.js +52 -52
  233. package/server/lib/ingress/upstream-fail-streak.js +72 -72
  234. package/server/lib/ingress/upstream-truncation.js +48 -48
  235. package/server/lib/issues-store.js +182 -182
  236. package/server/lib/kinds/deck.js +72 -72
  237. package/server/lib/kinds/docx.js +283 -283
  238. package/server/lib/kinds/file-kinds.js +113 -113
  239. package/server/lib/kinds/index.js +202 -202
  240. package/server/lib/kinds/site.js +161 -161
  241. package/server/lib/model-ingress.js +543 -543
  242. package/server/lib/moderation.js +255 -255
  243. package/server/lib/notice-store.js +103 -103
  244. package/server/lib/plugin-install.js +104 -104
  245. package/server/lib/plugin-validator.js +721 -721
  246. package/server/lib/publish-store.js +115 -115
  247. package/server/lib/quick-summary.js +52 -52
  248. package/server/lib/quota.js +270 -270
  249. package/server/lib/rate-window.js +29 -29
  250. package/server/lib/reference-assets.js +92 -92
  251. package/server/lib/region-shot.js +135 -135
  252. package/server/lib/safe-path.js +73 -73
  253. package/server/lib/sdk-session.js +33 -33
  254. package/server/lib/showcase-store.js +92 -92
  255. package/server/lib/site-publish.js +465 -465
  256. package/server/lib/sketch-layout.js +277 -0
  257. package/server/lib/ssrf-guard.js +432 -432
  258. package/server/lib/task-scan.js +158 -158
  259. package/server/lib/tavern-json.js +154 -154
  260. package/server/lib/video-variant.js +237 -237
  261. package/server/lib/workspace-path.js +33 -33
  262. package/server/ops/fix-sdk-musl.mjs +40 -40
  263. package/server/ops/install-macos-fonts.sh +114 -114
  264. package/server/ops/macos-fonts.conf +336 -336
  265. package/server/ops/sandbox-shim/bwrap +57 -57
  266. package/server/projects/assets-summary.js +119 -119
  267. package/server/projects/auto-name.js +53 -53
  268. package/server/projects/board-limits.js +12 -0
  269. package/server/projects/board-sanitize.js +252 -0
  270. package/server/projects/board-store.js +507 -640
  271. package/server/projects/move-entry.js +103 -103
  272. package/server/projects/store.js +270 -270
  273. package/server/projects/ui-config.js +60 -54
  274. package/server/projects/viewpoint-store.js +78 -0
  275. package/server/projects/workspace-templates.js +53 -53
  276. package/server/projects/workspace.js +1025 -1025
  277. package/server/runtime/local-config.js +177 -177
  278. package/server/runtime/profile.js +72 -72
  279. package/server/services/rembg-service.py +334 -334
  280. package/server/shared/README.md +25 -25
  281. package/server/shared/deck.js +54 -54
  282. package/server/shared/time.js +49 -49
  283. package/server/style-pipeline/README.md +53 -53
  284. package/server/ws/broker.js +39 -36
  285. package/server/ws/browse-channel.js +177 -177
  286. package/server/ws/index.js +386 -386
  287. package/web/README.md +89 -89
  288. package/web/dist/assets/BrowserWindow-P_vIdh-G.js +11 -0
  289. package/web/dist/assets/{DeckWindow-4G0xX7P3.js → DeckWindow-BpLv5082.js} +3 -3
  290. package/web/dist/assets/{DocxWindow-BXQsoPkh.js → DocxWindow-DSAU1-tD.js} +4 -4
  291. package/web/dist/assets/MermaidBlock-DR1GikCv.js +313 -0
  292. package/web/dist/assets/SiteWindow-92uBuRRR.js +18 -0
  293. package/web/dist/assets/abnfDiagram-VCTEODGH-uA8Esix4.js +1 -0
  294. package/web/dist/assets/arc-D_khH_xN.js +1 -0
  295. package/web/dist/assets/architectureDiagram-5GKGNRK7-DydJHqOt.js +36 -0
  296. package/web/dist/assets/blockDiagram-NRAW4CY4-B7wBbKiW.js +129 -0
  297. package/web/dist/assets/c4Diagram-UCG6FXSJ-6mrH0x7Y.js +38 -0
  298. package/web/dist/assets/channel-DGkccMO1.js +1 -0
  299. package/web/dist/assets/chunk-2Q5K7J3B-Cuikho5Q.js +1 -0
  300. package/web/dist/assets/chunk-5VM5RSS4-3-KZ5AUz.js +15 -0
  301. package/web/dist/assets/chunk-F27PBJKO-Bkwq2gGU.js +1 -0
  302. package/web/dist/assets/chunk-G27WJ6UU-BLyQzdGE.js +231 -0
  303. package/web/dist/assets/chunk-JWPE2WC7-B7HxxQds.js +1 -0
  304. package/web/dist/assets/chunk-LCL6LL3I-CtXrnUwF.js +206 -0
  305. package/web/dist/assets/chunk-POPQ4Y6H-dxddZlXt.js +1 -0
  306. package/web/dist/assets/chunk-SVP7TREG-DpkmrgHn.js +88 -0
  307. package/web/dist/assets/chunk-XXDRQBXY-BwZ7RbpQ.js +1 -0
  308. package/web/dist/assets/classDiagram-DTDB5LWJ-BGscw3OD.js +1 -0
  309. package/web/dist/assets/classDiagram-v2-JRS7N3AN-BGscw3OD.js +1 -0
  310. package/web/dist/assets/cose-bilkent-JH36ORCC-DGFOsMUt.js +1 -0
  311. package/web/dist/assets/cynefin-OW5HDTMX-DUwn3ehS.js +178 -0
  312. package/web/dist/assets/cynefinDiagram-5FMLGOSQ-DlLAOZ9x.js +62 -0
  313. package/web/dist/assets/cytoscape.esm-BjAbmLdb.js +331 -0
  314. package/web/dist/assets/dagre-3AP2YEHR-CyourLfz.js +4 -0
  315. package/web/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  316. package/web/dist/assets/diagram-S7CK7UJ4-BTMbx2np.js +30 -0
  317. package/web/dist/assets/diagram-UQ7AKVKN-UK94_1-P.js +41 -0
  318. package/web/dist/assets/diagram-VSXAHHWV-C7zS-v9v.js +3 -0
  319. package/web/dist/assets/diagram-VX7I27RA-DKJRa6CK.js +24 -0
  320. package/web/dist/assets/diagram-Z3DM3KII-kUbAfGAp.js +24 -0
  321. package/web/dist/assets/ebnfDiagram-PWID7BFC-CVW16_vb.js +1 -0
  322. package/web/dist/assets/erDiagram-SSCWMZ5O-BRQMFQ5y.js +99 -0
  323. package/web/dist/assets/flowDiagram-A5DVABFB-S34LpLtK.js +168 -0
  324. package/web/dist/assets/ganttDiagram-EL5Y4UJY-BFiqk1-G.js +292 -0
  325. package/web/dist/assets/gitGraphDiagram-WWUBYQGX-LZsrdyyv.js +106 -0
  326. package/web/dist/assets/index-GAamh1p4.js +2152 -0
  327. package/web/dist/assets/infoDiagram-RXCK75RN-0DvN6KIp.js +2 -0
  328. package/web/dist/assets/init-Gi6I4Gst.js +1 -0
  329. package/web/dist/assets/ishikawaDiagram-5VMMS53U-CaY3A-Bx.js +70 -0
  330. package/web/dist/assets/journeyDiagram-EYS64GPL-BBeiaCrj.js +139 -0
  331. package/web/dist/assets/kanban-definition-3QL26DDD-ux42DRXQ.js +89 -0
  332. package/web/dist/assets/layout-CnXiF_NB.js +1 -0
  333. package/web/dist/assets/linear-BMW0u6G6.js +1 -0
  334. package/web/dist/assets/mindmap-definition-FBJOCRG2-hUxSpdKn.js +96 -0
  335. package/web/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  336. package/web/dist/assets/pegDiagram-XKGWAZYB-aCaQPH0w.js +1 -0
  337. package/web/dist/assets/{pending-edit-apply-DTjo2geW.js → pending-edit-apply-AcdOQ9Ij.js} +6 -6
  338. package/web/dist/assets/pieDiagram-E7YTZNPT-CmOEZ8zg.js +39 -0
  339. package/web/dist/assets/quadrantDiagram-AXDQQJYC-VW6fAhsV.js +7 -0
  340. package/web/dist/assets/railroadDiagram-O6MQD6OU-CKJ7Ba5K.js +1 -0
  341. package/web/dist/assets/requirementDiagram-EFPCY7ZU-pNGkq9Vw.js +84 -0
  342. package/web/dist/assets/{rotate-cw-LMT3x1Zj.js → rotate-cw-KAfChbGV.js} +1 -1
  343. package/web/dist/assets/sankeyDiagram-P5KCCOFB-BIMmDTfZ.js +40 -0
  344. package/web/dist/assets/sequenceDiagram-WJ2MYXX4-BBW7wzKY.js +162 -0
  345. package/web/dist/assets/sizeCapture-X5ZJPWSS-DdkXmLYR.js +1 -0
  346. package/web/dist/assets/{square-dashed-mouse-pointer-CBoAkfhi.js → square-dashed-mouse-pointer-CDUlhUP7.js} +1 -1
  347. package/web/dist/assets/stateDiagram-HBIQ2CUA-W313Uz16.js +1 -0
  348. package/web/dist/assets/stateDiagram-v2-4QOOHH4V-DDhJBZzf.js +1 -0
  349. package/web/dist/assets/swimlanes-XN3QIQJK-C29TL26K.js +1 -0
  350. package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BfN-Xdmu.js +8 -0
  351. package/web/dist/assets/timeline-definition-24CTP7MA-CF0JMObX.js +120 -0
  352. package/web/dist/assets/vennDiagram-4TSXK5OY-DrDFsaH3.js +34 -0
  353. package/web/dist/assets/wardleyDiagram-VM6X3IG4-CVrnRIS_.js +78 -0
  354. package/web/dist/assets/xychartDiagram-S5SC5T6Z-BvkN1zsT.js +7 -0
  355. package/web/dist/index.html +17 -17
  356. package/web/dist/assets/BrowserWindow-Dc7J7CYc.js +0 -11
  357. package/web/dist/assets/SiteWindow-D6eS-2gM.js +0 -18
  358. package/web/dist/assets/index-Cbl3ATvp.js +0 -2125
@@ -1,103 +1,190 @@
1
- /**
2
- * mcp/tools/read-board.js —— read_board(2026-08-14,agent 摆位批·读侧)
3
- *
4
- * 让 agent **看得见版面**。在这之前它对画布的了解只有关系线摘要 —— 每件东西
5
- * 坐哪、挨着谁、谁是主角,全是盲区,"摆放"无从谈起。这个工具把 board.json
6
- * 翻译成一张按层分组、按行排读的座次表。
7
- *
8
- * 口径说明(都写进输出,agent 不用猜):
9
- * - 只列**摆过的**:board.json 是稀疏表,刚产出还没排座的产物没有条目
10
- * (前端首排后几百毫秒内落盘,通常都在)
11
- * - 层归属是服务端近似(zone 字段优先,其次沿路径找已知文件夹)
12
- * - 尺寸是形态估算(文字/涂鸦用存档实测值)
13
- */
14
-
15
- import { tool } from '@anthropic-ai/claude-agent-sdk';
16
- import { z } from 'zod';
17
- import { readBoard } from '../../../projects/board-store.js';
18
- import { estimateSize } from '../../../lib/board-kind-sizes.js';
19
- import { layerOf } from '../../../lib/canvas-id.js';
20
- import { relationsDigest } from '../../../lib/board-relations.js';
21
-
22
- /** 同一"行"的 y 容差:入座算法一行内顶对齐,40 世界像素内视作同行 */
23
- const ROW_TOLERANCE = 40;
24
-
25
- function describeEntry(id, entry) {
26
- const sz = estimateSize(id, entry);
27
- const at = `@(${Math.round(entry.x)},${Math.round(entry.y)}) ${Math.round(sz.w)}x${Math.round(sz.h)}`;
28
- if (entry.kind === 'text') {
29
- const t = String(entry.data?.t || '').replace(/\s+/g, ' ').slice(0, 24);
30
- return `- [手写] 「${t}」 ${at} (id: ${id})${entry.by === 'agent' ? ' ·你写的' : ''}`;
31
- }
32
- if (entry.kind === 'scribble') return `- [涂鸦] ${at} (id: ${id})`;
33
- return `- ${id} ${at}${entry.by === 'agent' ? ' ·你摆的' : ''}`;
34
- }
35
-
36
- export function makeReadBoardTool({ projectId }) {
37
- return tool(
38
- 'read_board',
39
- `Read the workbench canvas seating chart: what sits where, row by row, per folder layer.
40
-
41
- Use this BEFORE arranging anything (arrange_on_board) or writing notes near things
42
- (create_on_board) placement without looking is guessing. Coordinates are world
43
- pixels; items listed top-to-bottom, left-to-right per row. Only items that have
44
- been seated appear (freshly produced artifacts get their seat within a second).`,
45
- {
46
- layer: z.string().max(300).optional()
47
- .describe("Folder path to read ('' or omitted = the root desktop plus a folder list)"),
48
- },
49
- async ({ layer }) => {
50
- if (!projectId) {
51
- return { content: [{ type: 'text', text: 'No project bound.' }], isError: true };
52
- }
53
- const board = await readBoard(projectId);
54
- const known = new Set(Object.keys(board.zones || {}));
55
- const want = typeof layer === 'string' ? layer : '';
56
-
57
- // 分层
58
- const byLayer = new Map();
59
- for (const [id, entry] of Object.entries(board.objects || {})) {
60
- if (!Number.isFinite(entry?.x) || !Number.isFinite(entry?.y)) continue;
61
- const l = layerOf(id, entry, known);
62
- if (!byLayer.has(l)) byLayer.set(l, []);
63
- byLayer.get(l).push({ id, entry });
64
- }
65
-
66
- const lines = [];
67
- const renderLayer = (l) => {
68
- const items = (byLayer.get(l) || []).sort((a, b) =>
69
- (a.entry.y - b.entry.y) || (a.entry.x - b.entry.x));
70
- if (!items.length) { lines.push('(这一层还没有摆过的东西)'); return; }
71
- let rowY = null;
72
- for (const { id, entry } of items) {
73
- if (rowY === null || Math.abs(entry.y - rowY) > ROW_TOLERANCE) {
74
- rowY = entry.y;
75
- lines.push(`— y≈${Math.round(rowY)} —`);
76
- }
77
- lines.push(describeEntry(id, entry));
78
- }
79
- };
80
-
81
- lines.push(want ? `文件夹「${want}」的座次:` : '桌面(根层)的座次:');
82
- renderLayer(want);
83
- if (!want) {
84
- const folders = Object.keys(board.zones || {}).sort();
85
- if (folders.length) {
86
- lines.push('', `文件夹卡:${folders.map(f => {
87
- const zz = board.zones[f];
88
- return `${f}@(${Math.round(zz.x)},${Math.round(zz.y)})`;
89
- }).join('、')}`);
90
- }
91
- }
92
- if (board.hero) lines.push('', `★ 显式主角:${board.hero}(arrange_on_board feature/unfeature 管它)`);
93
-
94
- try {
95
- const digest = await relationsDigest(projectId, { limit: 16 });
96
- if (digest) lines.push('', '关系线:', digest);
97
- } catch { /* 关系读不到不挡座次 */ }
98
-
99
- lines.push('', '(口径:稀疏表只列摆过的;层归属为服务端近似;尺寸为形态估算)');
100
- return { content: [{ type: 'text', text: lines.join('\n') }] };
101
- },
102
- );
103
- }
1
+ /**
2
+ * mcp/tools/read-board.js —— read_board(2026-08-14,agent 摆位批·读侧)
3
+ *
4
+ * 让 agent **看得见版面**。在这之前它对画布的了解只有关系线摘要 —— 每件东西
5
+ * 坐哪、挨着谁、谁是主角,全是盲区,"摆放"无从谈起。这个工具把 board.json
6
+ * 翻译成一张按层分组、按行排读的座次表。
7
+ *
8
+ * 口径说明(都写进输出,agent 不用猜):
9
+ * - 只列**摆过的**:board.json 是稀疏表,刚产出还没排座的产物没有条目
10
+ * (前端首排后几百毫秒内落盘,通常都在)
11
+ * - 层归属是服务端近似(zone 字段优先,其次沿路径找已知文件夹)
12
+ * - 尺寸是形态估算(文字/涂鸦用存档实测值)
13
+ */
14
+
15
+ import { tool } from '@anthropic-ai/claude-agent-sdk';
16
+ import { z } from 'zod';
17
+ import { readBoard } from '../../../projects/board-store.js';
18
+ import { estimateSizeOn } from '../../../lib/board-kind-sizes.js';
19
+ import { layerOf } from '../../../lib/canvas-id.js';
20
+ import { relationsDigest, bindingLine } from '../../../lib/board-relations.js';
21
+ import { groupObjects, asciiMinimap, bboxOfRects, relationOf, columnsOf, viewportRelation } from '../../../lib/board-groups.js';
22
+ import { getViewpoint } from '../../../projects/viewpoint-store.js';
23
+ import { chalkExcerpts, CHALK_DIR } from '../../../lib/chalk.js';
24
+ import { getSharedDir } from '../../../projects/workspace.js';
25
+
26
+ /** 同一"行"的 y 容差:入座算法一行内顶对齐,40 世界像素内视作同行 */
27
+ const ROW_TOLERANCE = 40;
28
+
29
+ function describeEntry(board, id, entry, glyph = null, excerpts = null) {
30
+ const sz = estimateSizeOn(board, id, entry);
31
+ const at = `@(${Math.round(entry.x)},${Math.round(entry.y)}) ${Math.round(sz.w)}x${Math.round(sz.h)}`;
32
+ const g = glyph ? `[${glyph}] ` : '';
33
+ const flags = `${entry.staging ? ' 〔草稿〕' : ''}${entry.tag ? ` #${entry.tag}` : ''}`;
34
+ const ch = excerpts?.get(id);
35
+ if (ch) return `- ${g}[板书·${ch.by === 'user' ? '用户' : '你'}写的] 「${ch.first}」 ${at} (path: ${id})${ch.anchor ? ` 关于 ${ch.anchor}` : ''}${ch.replyTo ? ` 回应 ${ch.replyTo}` : ''}${flags}`;
36
+ if (entry.kind === 'text') {
37
+ const t = String(entry.data?.t || '').replace(/\s+/g, ' ').slice(0, entry.data?.format === 'md' ? 60 : 24);
38
+ const md = entry.data?.format === 'md' ? 'md' : '手写';
39
+ return `- ${g}[${md}] 「${t}」 ${at} (id: ${id})${entry.by === 'agent' ? ' ·你写的' : ''}${flags}`;
40
+ }
41
+ if (entry.kind === 'scribble') return `- ${g}[涂鸦] ${at} (id: ${id})${entry.by === 'agent' ? ' ·你画的' : ''}${flags}`;
42
+ return `- ${g}${id} ${at}${entry.by === 'agent' ? ' ·你摆的' : ''}${flags}`;
43
+ }
44
+
45
+ export function makeReadBoardTool({ projectId }) {
46
+ return tool(
47
+ 'read_board',
48
+ `Read the workbench canvas: an ASCII minimap, then GROUPS (things linked by lines or
49
+ sharing a #tag), then loose items row by row, then relation lines.
50
+
51
+ Use this BEFORE arranging (arrange_on_board), writing notes (create_on_board) or
52
+ sketching (sketch_on_board) — placement without looking is guessing. Coordinates are
53
+ world pixels. Only seated items appear (fresh artifacts get a seat within a second).
54
+ Items marked 〔草稿〕 are still staging (yours from this turn, half-transparent until
55
+ finish_sketch / end of turn). The user's current viewport (if known) is drawn as a box
56
+ on the minimap and listed with what is inside it.`,
57
+ {
58
+ layer: z.string().max(300).optional()
59
+ .describe("Folder path to read ('' or omitted = the root desktop plus a folder list)"),
60
+ tag: z.string().max(40).optional()
61
+ .describe('Only list items/lines carrying this #tag (one group, e.g. a sketch you made)'),
62
+ minimap: z.boolean().optional().describe('Also print an ASCII minimap (off by default — the relative-position summary is usually enough)'),
63
+ },
64
+ async ({ layer, tag, minimap }) => {
65
+ if (!projectId) {
66
+ return { content: [{ type: 'text', text: 'No project bound.' }], isError: true };
67
+ }
68
+ const board = await readBoard(projectId);
69
+ const known = new Set(Object.keys(board.zones || {}));
70
+ const want = typeof layer === 'string' ? layer : '';
71
+
72
+ // 分层
73
+ const byLayer = new Map();
74
+ for (const [id, entry] of Object.entries(board.objects || {})) {
75
+ if (!Number.isFinite(entry?.x) || !Number.isFinite(entry?.y)) continue;
76
+ const l = layerOf(id, entry, known);
77
+ if (!byLayer.has(l)) byLayer.set(l, []);
78
+ byLayer.get(l).push({ id, entry });
79
+ }
80
+
81
+ const lines = [];
82
+ const excerpts = await chalkExcerpts(getSharedDir(projectId), (byLayer.get(want) || []).map(it => it.id));
83
+ const items = (byLayer.get(want) || [])
84
+ .filter(({ entry }) => !tag || entry.tag === tag)
85
+ // 板书条目但文件已经没了 = 幽灵座位,别列给 agent(删文件那条路会清座位,这是兜底)
86
+ .filter(({ id }) => !id.startsWith(`${CHALK_DIR}/`) || excerpts.has(id))
87
+ .sort((a, b) => (a.entry.y - b.entry.y) || (a.entry.x - b.entry.x));
88
+ const entryOf = new Map(items.map(it => [it.id, it.entry]));
89
+
90
+ // 小地图(用户视口画框)
91
+ const vp = getViewpoint(projectId);
92
+ const vpRect = (vp && (vp.layer || '') === want && vp.camera) ? vp.camera : null;
93
+ const rects = items.map(({ id, entry }) => ({ id, x: entry.x, y: entry.y, ...estimateSizeOn(board, id, entry) }));
94
+ const mini = asciiMinimap(rects, { viewport: vpRect });
95
+ const glyphOf = new Map(mini ? mini.legend : []);
96
+
97
+ lines.push(want ? `文件夹「${want}」的座次${tag ? `(只看 #${tag})` : ''}:` : `桌面(根层)的座次${tag ? `(只看 #${tag})` : ''}:`);
98
+ if (!items.length) {
99
+ lines.push('(这一层还没有摆过的东西)');
100
+ } else {
101
+ if (mini && minimap) {
102
+ lines.push(`小地图(一格≈${mini.cell}px,左上=(${mini.bbox.x},${mini.bbox.y}),范围 ${mini.bbox.w}x${mini.bbox.h}${vpRect ? ',┌┐└┘ 框=用户视口' : ''}):`);
103
+ lines.push(mini.grid);
104
+ }
105
+ // 组:连通分量 + tag;≥2 件的才叫组,单件归「散件」按行列
106
+ const groups = groupObjects(items.map(it => it.id), board.bindings || {}, id => entryOf.get(id)?.tag || null);
107
+ const real = groups.filter(g => g.members.length >= 2);
108
+ const loose = groups.filter(g => g.members.length < 2).flatMap(g => g.members);
109
+ // 相对位置总览:每组的包围盒、列数、相对前一组/用户视口在哪(阅读顺序:先左后右、先上后下)
110
+ const rectOf = new Map(rects.map(r => [r.id, r]));
111
+ const boxes = real.map(g => bboxOfRects(g.members.map(id => rectOf.get(id)).filter(Boolean)));
112
+ const whole = bboxOfRects(rects);
113
+ if (whole) lines.push(`这一层内容范围:(${Math.round(whole.x)},${Math.round(whole.y)}) ${Math.round(whole.w)}x${Math.round(whole.h)}${vpRect ? `;用户视口 (${Math.round(vpRect.x)},${Math.round(vpRect.y)}) ${Math.round(vpRect.w)}x${Math.round(vpRect.h)}` : ''}`);
114
+ if (real.length) {
115
+ lines.push('各组位置(新东西默认排在已有内容的右侧或下方,顺着先左后右、先上后下的阅读顺序):');
116
+ real.forEach((g, i) => {
117
+ const b = boxes[i]; if (!b) return;
118
+ const tags = [...g.tags].map(t => `#${t}`).join(' ') || `组 ${i + 1}`;
119
+ const cols = columnsOf(g.members.map(id => rectOf.get(id)).filter(Boolean));
120
+ const bits = [`(${Math.round(b.x)},${Math.round(b.y)}) ${Math.round(b.w)}x${Math.round(b.h)}`, cols.length > 1 ? `${cols.length} 列(${cols.map(c => c.n).join('/')} 件)` : '单列'];
121
+ if (i > 0 && boxes[0]) bits.push(`在 ${[...real[0].tags].map(t => `#${t}`).join(' ') || '组 1'} 的${relationOf(boxes[0], b)}`);
122
+ const vr = viewportRelation(vpRect, b); if (vr) bits.push(vr);
123
+ lines.push(` ${tags}:${bits.join(';')}`);
124
+ });
125
+ }
126
+ real.forEach((g, i) => {
127
+ const tags = [...g.tags].map(t => `#${t}`).join(' ');
128
+ const staging = g.members.every(id => entryOf.get(id)?.staging);
129
+ lines.push('', `组 ${i + 1}${tags ? ` ${tags}` : ''}(${g.members.length} 件 ${g.edges.length} 线${staging ? ',草稿' : ''}):`);
130
+ const sorted = g.members.map(id => ({ id, entry: entryOf.get(id) }))
131
+ .sort((a, b) => (a.entry.y - b.entry.y) || (a.entry.x - b.entry.x));
132
+ for (const { id, entry } of sorted) lines.push(describeEntry(board, id, entry, glyphOf.get(id), excerpts));
133
+ for (const bid of g.edges.slice(0, 12)) lines.push(` ${bindingLine(board.bindings[bid], board)} (line id: ${bid})`);
134
+ if (g.edges.length > 12) lines.push(` …还有 ${g.edges.length - 12} 条线`);
135
+ });
136
+ if (groups.cross?.length) {
137
+ lines.push('', '组间线:');
138
+ for (const bid of groups.cross.slice(0, 12)) lines.push(` ${bindingLine(board.bindings[bid], board)} (line id: ${bid})`);
139
+ }
140
+ if (loose.length) {
141
+ lines.push('', real.length ? '散件:' : '');
142
+ let rowY = null;
143
+ const sorted = loose.map(id => ({ id, entry: entryOf.get(id) }))
144
+ .sort((a, b) => (a.entry.y - b.entry.y) || (a.entry.x - b.entry.x));
145
+ for (const { id, entry } of sorted) {
146
+ if (rowY === null || Math.abs(entry.y - rowY) > ROW_TOLERANCE) {
147
+ rowY = entry.y;
148
+ lines.push(`— 行 y≈${Math.round(rowY)} —`);
149
+ }
150
+ lines.push(describeEntry(board, id, entry, glyphOf.get(id), excerpts));
151
+ }
152
+ }
153
+ }
154
+ if (!want && !tag) {
155
+ const folders = Object.keys(board.zones || {}).sort();
156
+ if (folders.length) {
157
+ lines.push('', `文件夹卡:${folders.map(f => {
158
+ const zz = board.zones[f];
159
+ return `${f}@(${Math.round(zz.x)},${Math.round(zz.y)})`;
160
+ }).join('、')}`);
161
+ }
162
+ }
163
+ if (board.hero && !tag) lines.push('', `★ 显式主角:${board.hero}(arrange_on_board 的 feature/unfeature 管它)`);
164
+
165
+ // 用户视点(有上报才有)
166
+ if (vp && !tag) {
167
+ const inside = vpRect ? rects.filter(r =>
168
+ !(r.x + r.w < vpRect.x || r.x > vpRect.x + vpRect.w || r.y + r.h < vpRect.y || r.y > vpRect.y + vpRect.h))
169
+ .map(r => r.id) : [];
170
+ const bits = [];
171
+ if (vpRect) bits.push(`视口 (${Math.round(vpRect.x)},${Math.round(vpRect.y)}) ${Math.round(vpRect.w)}x${Math.round(vpRect.h)} 缩放 ${vp.zoom ?? '?'}`);
172
+ if (vp.openWindow) bits.push(`开着窗:${vp.openWindow}${vp.openPage ? `(${vp.openPage})` : ''}`);
173
+ if (vp.selected?.length) bits.push(`选中:${vp.selected.slice(0, 8).join('、')}`);
174
+ if (inside.length) bits.push(`视口里有:${inside.slice(0, 12).join('、')}${inside.length > 12 ? ' 等' : ''}`);
175
+ const age = Math.round((Date.now() - (vp.at || 0)) / 1000);
176
+ lines.push('', `用户此刻(${age}s 前上报):${bits.join(';') || '只知道在看这一层'}`);
177
+ }
178
+
179
+ if (!tag) {
180
+ try {
181
+ const digest = await relationsDigest(projectId, { limit: 16 });
182
+ if (digest) lines.push('', '关系线:', digest);
183
+ } catch { /* 关系读不到不挡座次 */ }
184
+ }
185
+
186
+ lines.push('', '(口径:稀疏表只列摆过的;层归属为服务端近似;尺寸为形态估算)');
187
+ return { content: [{ type: 'text', text: lines.join('\n') }] };
188
+ },
189
+ );
190
+ }
@@ -1,93 +1,93 @@
1
- /**
2
- * mcp/tools/read-document.js — read_document MCP tool(2026-08-07)
3
- *
4
- * Word / Excel / PowerPoint 三种格式的读取口。
5
- *
6
- * 为什么需要它:用户往上下文托盘里拖一份 .docx("照这份需求做一版海报"),
7
- * agent 拿 Read 去读得到的是二进制乱码 —— **而且不报错**,它会当成一份读不懂
8
- * 的文件继续往下干,交出一个跟需求无关的东西。这是最坏的一种失败:安静的。
9
- *
10
- * PDF 不走这里:SDK 的 Read 原生支持,2026-08-07 真跑验过。
11
- */
12
-
13
- import path from 'node:path';
14
- import fs from 'node:fs/promises';
15
- import { tool } from '@anthropic-ai/claude-agent-sdk';
16
- import { z } from 'zod';
17
- import { extractDocument, isExtractable, DOC_KINDS, MAX_CHARS } from '../../../lib/doc-extract.js';
18
-
19
- const EXT_LIST = Object.keys(DOC_KINDS).join(' / ');
20
-
21
- export function makeReadDocumentTool({ workspaceRoot, sharedRoot }) {
22
- return tool(
23
- 'read_document',
24
- `Read the text out of a Word / Excel / PowerPoint file (${EXT_LIST}).
25
-
26
- The plain Read tool CANNOT read these — they are zip archives, so Read returns
27
- binary garbage without failing, and you end up working from nothing. Whenever a
28
- user attaches or points at one of these files, use this tool instead.
29
-
30
- (PDF is different: the normal Read tool handles PDFs natively — use Read for those.)
31
-
32
- What you get back is text with structure markers, not layout:
33
- - .docx — the document text, headings/lists/tables flattened in reading order
34
- - .xlsx — one section per worksheet, rows as tab-separated cells
35
- - .pptx — one section per slide, in slide order
36
-
37
- Layout is deliberately not preserved. You are being asked what the document
38
- *says*; how it should look is your job to decide.
39
-
40
- Long files are cut at ${MAX_CHARS} characters and the result says so.`,
41
- {
42
- path: z.string().describe(
43
- 'Path to the file. Relative paths resolve against the session workspace, '
44
- + 'then against the shared project dir (e.g. "assets/需求.docx").',
45
- ),
46
- },
47
- async ({ path: rel }) => {
48
- const fail = (text) => ({ content: [{ type: 'text', text }], isError: true });
49
- try {
50
- const raw = String(rel || '').trim();
51
- if (!raw) return fail('read_document needs a path.');
52
-
53
- // 绝对路径直接用;相对路径先会话工作区、再项目共享目录
54
- const candidates = path.isAbsolute(raw)
55
- ? [raw]
56
- : [workspaceRoot && path.resolve(workspaceRoot, raw),
57
- sharedRoot && path.resolve(sharedRoot, raw)].filter(Boolean);
58
-
59
- let absPath = null;
60
- for (const c of candidates) {
61
- try { await fs.access(c); absPath = c; break; } catch { /* 下一个 */ }
62
- }
63
- if (!absPath) {
64
- return fail(`File not found: ${raw}\nLooked in:\n${candidates.map(c => ` ${c}`).join('\n')}`);
65
- }
66
-
67
- if (!isExtractable(absPath)) {
68
- const ext = path.extname(absPath).toLowerCase();
69
- return fail(
70
- ext === '.pdf'
71
- ? 'PDFs are handled by the normal Read tool — call Read on this path instead.'
72
- : `read_document only handles ${EXT_LIST}. For "${ext}" try the normal Read tool.`,
73
- );
74
- }
75
-
76
- const { kind, text, chars, truncated, note } = await extractDocument(absPath);
77
- if (!text) {
78
- return {
79
- content: [{ type: 'text', text: `${kind} ${path.basename(absPath)} 里没有可读的文字${note ? `(${note})` : ''}。` }],
80
- };
81
- }
82
- const head = [
83
- `${kind}:${path.basename(absPath)}`,
84
- `${chars} 字${truncated ? `,只给前 ${MAX_CHARS} 字` : ''}`,
85
- note,
86
- ].filter(Boolean).join(' · ');
87
- return { content: [{ type: 'text', text: `${head}\n\n${text}` }] };
88
- } catch (err) {
89
- return fail(`read_document failed: ${err?.message || String(err)}`);
90
- }
91
- },
92
- );
93
- }
1
+ /**
2
+ * mcp/tools/read-document.js — read_document MCP tool(2026-08-07)
3
+ *
4
+ * Word / Excel / PowerPoint 三种格式的读取口。
5
+ *
6
+ * 为什么需要它:用户往上下文托盘里拖一份 .docx("照这份需求做一版海报"),
7
+ * agent 拿 Read 去读得到的是二进制乱码 —— **而且不报错**,它会当成一份读不懂
8
+ * 的文件继续往下干,交出一个跟需求无关的东西。这是最坏的一种失败:安静的。
9
+ *
10
+ * PDF 不走这里:SDK 的 Read 原生支持,2026-08-07 真跑验过。
11
+ */
12
+
13
+ import path from 'node:path';
14
+ import fs from 'node:fs/promises';
15
+ import { tool } from '@anthropic-ai/claude-agent-sdk';
16
+ import { z } from 'zod';
17
+ import { extractDocument, isExtractable, DOC_KINDS, MAX_CHARS } from '../../../lib/doc-extract.js';
18
+
19
+ const EXT_LIST = Object.keys(DOC_KINDS).join(' / ');
20
+
21
+ export function makeReadDocumentTool({ workspaceRoot, sharedRoot }) {
22
+ return tool(
23
+ 'read_document',
24
+ `Read the text out of a Word / Excel / PowerPoint file (${EXT_LIST}).
25
+
26
+ The plain Read tool CANNOT read these — they are zip archives, so Read returns
27
+ binary garbage without failing, and you end up working from nothing. Whenever a
28
+ user attaches or points at one of these files, use this tool instead.
29
+
30
+ (PDF is different: the normal Read tool handles PDFs natively — use Read for those.)
31
+
32
+ What you get back is text with structure markers, not layout:
33
+ - .docx — the document text, headings/lists/tables flattened in reading order
34
+ - .xlsx — one section per worksheet, rows as tab-separated cells
35
+ - .pptx — one section per slide, in slide order
36
+
37
+ Layout is deliberately not preserved. You are being asked what the document
38
+ *says*; how it should look is your job to decide.
39
+
40
+ Long files are cut at ${MAX_CHARS} characters and the result says so.`,
41
+ {
42
+ path: z.string().describe(
43
+ 'Path to the file. Relative paths resolve against the session workspace, '
44
+ + 'then against the shared project dir (e.g. "assets/需求.docx").',
45
+ ),
46
+ },
47
+ async ({ path: rel }) => {
48
+ const fail = (text) => ({ content: [{ type: 'text', text }], isError: true });
49
+ try {
50
+ const raw = String(rel || '').trim();
51
+ if (!raw) return fail('read_document needs a path.');
52
+
53
+ // 绝对路径直接用;相对路径先会话工作区、再项目共享目录
54
+ const candidates = path.isAbsolute(raw)
55
+ ? [raw]
56
+ : [workspaceRoot && path.resolve(workspaceRoot, raw),
57
+ sharedRoot && path.resolve(sharedRoot, raw)].filter(Boolean);
58
+
59
+ let absPath = null;
60
+ for (const c of candidates) {
61
+ try { await fs.access(c); absPath = c; break; } catch { /* 下一个 */ }
62
+ }
63
+ if (!absPath) {
64
+ return fail(`File not found: ${raw}\nLooked in:\n${candidates.map(c => ` ${c}`).join('\n')}`);
65
+ }
66
+
67
+ if (!isExtractable(absPath)) {
68
+ const ext = path.extname(absPath).toLowerCase();
69
+ return fail(
70
+ ext === '.pdf'
71
+ ? 'PDFs are handled by the normal Read tool — call Read on this path instead.'
72
+ : `read_document only handles ${EXT_LIST}. For "${ext}" try the normal Read tool.`,
73
+ );
74
+ }
75
+
76
+ const { kind, text, chars, truncated, note } = await extractDocument(absPath);
77
+ if (!text) {
78
+ return {
79
+ content: [{ type: 'text', text: `${kind} ${path.basename(absPath)} 里没有可读的文字${note ? `(${note})` : ''}。` }],
80
+ };
81
+ }
82
+ const head = [
83
+ `${kind}:${path.basename(absPath)}`,
84
+ `${chars} 字${truncated ? `,只给前 ${MAX_CHARS} 字` : ''}`,
85
+ note,
86
+ ].filter(Boolean).join(' · ');
87
+ return { content: [{ type: 'text', text: `${head}\n\n${text}` }] };
88
+ } catch (err) {
89
+ return fail(`read_document failed: ${err?.message || String(err)}`);
90
+ }
91
+ },
92
+ );
93
+ }