@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.
- package/README.md +156 -106
- package/bin/nodesign.js +0 -0
- package/package.json +1 -1
- package/server/api/README.md +45 -45
- package/server/api/_guard.js +75 -75
- package/server/api/admin.js +187 -187
- package/server/api/assets/docx-page.js +103 -103
- package/server/api/assets/helpers.js +42 -22
- package/server/api/assets/notes.js +194 -144
- package/server/api/assets.js +891 -896
- package/server/api/board.js +134 -57
- package/server/api/browse.js +138 -138
- package/server/api/canvas.js +444 -444
- package/server/api/chatai.js +324 -324
- package/server/api/exports/build-standalone.js +980 -980
- package/server/api/exports/cards.js +118 -118
- package/server/api/exports/docx-pdf.js +46 -46
- package/server/api/exports/export-page.js +142 -142
- package/server/api/exports/handoff.js +147 -147
- package/server/api/exports.js +690 -690
- package/server/api/instruction.js +75 -75
- package/server/api/me.js +116 -116
- package/server/api/memory.js +185 -185
- package/server/api/pending-changes.js +366 -366
- package/server/api/plugins.js +164 -164
- package/server/api/projects.js +220 -220
- package/server/api/publish.js +64 -64
- package/server/api/recent.js +73 -73
- package/server/api/sessions.js +589 -589
- package/server/api/skills.js +77 -77
- package/server/api/standalone-fit.js +168 -168
- package/server/api/turn-compose.js +180 -180
- package/server/api/turn-inflight.js +44 -44
- package/server/auth/README.md +17 -17
- package/server/auth/middleware.js +138 -138
- package/server/auth/origin-guard.js +71 -71
- package/server/auth/session.js +108 -108
- package/server/auth/tier.js +113 -113
- package/server/auth/users-store.js +348 -348
- package/server/edit/README.md +29 -29
- package/server/engine/README.md +113 -113
- package/server/engine/agent/agent-shared.js +550 -550
- package/server/engine/agent/auto-mode-default-hard-deny.txt +4 -4
- package/server/engine/agent/auto-mode-rules.js +78 -78
- package/server/engine/agent/context.js +418 -418
- package/server/engine/agent/events.js +465 -464
- package/server/engine/agent/hooks/canvas-validate.js +199 -199
- package/server/engine/agent/hooks/failure.js +120 -120
- package/server/engine/agent/hooks/file-events.js +75 -75
- package/server/engine/agent/hooks/lifecycle.js +233 -233
- package/server/engine/agent/hooks/post-canvas-focus.js +80 -80
- package/server/engine/agent/hooks/post-guidance.js +206 -206
- package/server/engine/agent/hooks/post-subagent-report.js +104 -104
- package/server/engine/agent/hooks/post-trim.js +58 -58
- package/server/engine/agent/hooks/pre-board-neighborhood.js +34 -34
- package/server/engine/agent/hooks/pre-defaults.js +84 -84
- package/server/engine/agent/hooks/pre-injectors.js +277 -277
- package/server/engine/agent/hooks/pre-performance-log-guard.js +78 -78
- package/server/engine/agent/hooks/pre-starter-files.js +61 -61
- package/server/engine/agent/hooks/pre-workspace-scope-guard.js +78 -78
- package/server/engine/agent/hooks/site-validate.js +94 -94
- package/server/engine/agent/hooks/tool-prompts.js +31 -31
- package/server/engine/agent/hooks/turn-state-memory.js +56 -56
- package/server/engine/agent/hooks/user-prompt-submit.js +258 -215
- package/server/engine/agent/hooks.js +354 -354
- package/server/engine/agent/init-contract.js +69 -69
- package/server/engine/agent/isolation.js +159 -159
- package/server/engine/agent/model-table.js +292 -292
- package/server/engine/agent/plugin-loader.js +266 -266
- package/server/engine/agent/prompts/nodesign-prelude.md +388 -336
- package/server/engine/agent/prompts/tools/ask-user-question-protocol.md +71 -71
- package/server/engine/agent/prompts/tools/direct-edit-protocol.md +98 -98
- package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +583 -583
- package/server/engine/agent/prompts/tools/generate-image-cookbook.md +483 -483
- package/server/engine/agent/prompts/tools/hybrid-reference.md +100 -100
- package/server/engine/agent/prompts/tools/paint-still-cookbook.md +270 -270
- package/server/engine/agent/prompts/tools/roll-film-cookbook.md +70 -70
- package/server/engine/agent/prompts/tools/site-reference.md +106 -106
- package/server/engine/agent/prompts/tools/tweaks-syntax.md +125 -125
- package/server/engine/agent/prompts/tools/vision-checker-dispatch.md +48 -48
- package/server/engine/agent/session-loop.js +1035 -1024
- package/server/engine/agent/session-model.js +164 -164
- package/server/engine/agent/skill.js +204 -204
- package/server/engine/agent/task-events.js +78 -78
- package/server/engine/agents/ds-extractor.md +132 -132
- package/server/engine/agents/explorer.md +190 -190
- package/server/engine/agents/index.js +230 -230
- package/server/engine/agents/schemas/design-system.json +156 -156
- package/server/engine/agents/schemas/tweak-schema.json +113 -113
- package/server/engine/agents/tweak-proposer.md +127 -127
- package/server/engine/agents/vision-checker.md +254 -254
- package/server/engine/browse/capture.js +422 -422
- package/server/engine/browse/card.js +137 -137
- package/server/engine/browse/handover.js +59 -59
- package/server/engine/browse/page-digest.js +119 -119
- package/server/engine/browse/refs.js +196 -196
- package/server/engine/browse/registry.js +307 -307
- package/server/engine/browse/screencast.js +170 -170
- package/server/engine/browse/state.js +121 -121
- package/server/engine/chatai/chat-log.js +79 -79
- package/server/engine/chatai/index.js +193 -193
- package/server/engine/chatai/openai-compat.js +152 -152
- package/server/engine/chatai/orchestrate.js +309 -309
- package/server/engine/chatai/perform.js +40 -40
- package/server/engine/chatai/summarize.js +79 -79
- package/server/engine/mcp/capability-gate.js +52 -52
- package/server/engine/mcp/index.js +351 -339
- package/server/engine/mcp/param-sanitizer.js +142 -142
- package/server/engine/mcp/tools/arrange-on-board.js +124 -124
- package/server/engine/mcp/tools/artifact-session.js +278 -278
- package/server/engine/mcp/tools/browse-computer.js +421 -421
- package/server/engine/mcp/tools/browse-find-batch.js +183 -183
- package/server/engine/mcp/tools/browse-screenshot.js +157 -157
- package/server/engine/mcp/tools/browse.js +480 -480
- package/server/engine/mcp/tools/build-docx.js +101 -101
- package/server/engine/mcp/tools/clear-pending-changes.js +99 -99
- package/server/engine/mcp/tools/create-on-board.js +152 -155
- package/server/engine/mcp/tools/crystallize-skill.js +168 -168
- package/server/engine/mcp/tools/edit-sketch.js +167 -0
- package/server/engine/mcp/tools/explain-style.js +237 -237
- package/server/engine/mcp/tools/export-handoff.js +133 -133
- package/server/engine/mcp/tools/expose-tweaks.js +149 -149
- package/server/engine/mcp/tools/generate-image.js +842 -842
- package/server/engine/mcp/tools/get-computed-styles.js +164 -164
- package/server/engine/mcp/tools/get-pending-changes.js +205 -205
- package/server/engine/mcp/tools/h3box-ssh.js +84 -84
- package/server/engine/mcp/tools/helpers/acquire-page.js +82 -82
- package/server/engine/mcp/tools/helpers/motion-lab.js +554 -554
- package/server/engine/mcp/tools/helpers/motion-scroll.js +112 -112
- package/server/engine/mcp/tools/helpers/perception-page.js +215 -215
- package/server/engine/mcp/tools/helpers/reference-download.js +103 -103
- package/server/engine/mcp/tools/helpers/rembg-bridge.py +80 -80
- package/server/engine/mcp/tools/helpers/shot-pipeline.js +280 -280
- package/server/engine/mcp/tools/highlight.js +68 -68
- package/server/engine/mcp/tools/list-pages.js +222 -222
- package/server/engine/mcp/tools/look-at-board.js +124 -0
- package/server/engine/mcp/tools/lookup-tags.js +59 -59
- package/server/engine/mcp/tools/navigate-to-page.js +60 -60
- package/server/engine/mcp/tools/organize-board.js +64 -64
- package/server/engine/mcp/tools/paint-still.js +434 -434
- package/server/engine/mcp/tools/pin-to-board.js +130 -130
- package/server/engine/mcp/tools/preview-deck.js +130 -130
- package/server/engine/mcp/tools/profile-scroll.js +305 -305
- package/server/engine/mcp/tools/publish-site.js +102 -102
- package/server/engine/mcp/tools/query-elements.js +195 -195
- package/server/engine/mcp/tools/read-board.js +190 -103
- package/server/engine/mcp/tools/read-document.js +93 -93
- package/server/engine/mcp/tools/read-page.js +264 -264
- package/server/engine/mcp/tools/read-tavern-json.js +143 -143
- package/server/engine/mcp/tools/read-user-view.js +59 -0
- package/server/engine/mcp/tools/record-decision.js +140 -140
- package/server/engine/mcp/tools/relate-on-board.js +112 -110
- package/server/engine/mcp/tools/report-issue.js +115 -115
- package/server/engine/mcp/tools/roll-film.js +266 -266
- package/server/engine/mcp/tools/screenshot-docx.js +138 -138
- package/server/engine/mcp/tools/screenshot-url.js +195 -195
- package/server/engine/mcp/tools/screenshot.js +583 -583
- package/server/engine/mcp/tools/sketch-on-board.js +337 -0
- package/server/engine/mcp/tools/tier-gate.js +96 -96
- package/server/engine/mcp/tools/trace-motion.js +215 -215
- package/server/engine/mcp/tools/web-search.js +548 -548
- package/server/engine/mcp/tools/write-on-board.js +130 -0
- package/server/engine/motion/inventory.js +349 -349
- package/server/engine/perception/session.js +235 -235
- package/server/engine/plugins/nodesign/.claude-plugin/plugin.json +5 -5
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +199 -192
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/canvas.template.html +667 -667
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/hybrid-grid.md +22 -22
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/image-led-cover.md +25 -25
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/portrait.md +23 -23
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/quote-backdrop.md +21 -21
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/section-divider.md +23 -23
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/text-led.md +21 -21
- package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +169 -162
- package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +407 -407
- package/server/engine/plugins/nodesign/skills/docx-craft//346/226/207/346/241/243.template.json +80 -80
- package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +235 -228
- 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
- package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +268 -268
- package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +330 -323
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/build-lane.md +134 -134
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/cutout-collage.md +61 -61
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/mock-app.md +163 -163
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/page-transitions.md +86 -86
- package/server/engine/runs/active-runs.js +698 -698
- package/server/engine/runs/board-tasklist.js +130 -0
- package/server/engine/runs/live-turn.js +252 -252
- package/server/engine/runs/store.js +364 -364
- package/server/engine/runs/turn-relay.js +217 -217
- package/server/engine/runtime/workspace.js +164 -164
- package/server/index.js +3 -0
- package/server/lib/artifact-file-path.js +80 -80
- package/server/lib/artifact-target.js +284 -284
- package/server/lib/asset-refs.js +239 -239
- package/server/lib/async-queue.js +104 -104
- package/server/lib/auto-relations.js +130 -130
- package/server/lib/binding-types.js +72 -60
- package/server/lib/board-graph-export.js +191 -0
- package/server/lib/board-groups.js +167 -0
- package/server/lib/board-hero.js +76 -0
- package/server/lib/board-kind-sizes.js +85 -62
- package/server/lib/board-relations.js +133 -133
- package/server/lib/browse-proxy.js +180 -180
- package/server/lib/canvas-id.js +36 -36
- package/server/lib/chalk.js +121 -0
- package/server/lib/cover.js +227 -227
- package/server/lib/danbooru-tags.js +291 -291
- package/server/lib/doc-extract.js +156 -156
- package/server/lib/docx/build-from-source.js +260 -260
- package/server/lib/docx/build.js +490 -490
- package/server/lib/docx/dump-styles.js +287 -287
- package/server/lib/docx/fonts/nodesign-cjk.conf +164 -164
- package/server/lib/docx/merge-runs.js +129 -129
- package/server/lib/docx/numbering.js +218 -218
- package/server/lib/docx/order.js +152 -152
- package/server/lib/docx/rawzip.js +172 -172
- package/server/lib/docx/render.js +92 -92
- package/server/lib/docx/text-lint.js +225 -225
- package/server/lib/docx/tokens.js +335 -335
- package/server/lib/docx/units.js +29 -29
- package/server/lib/docx/xml.js +291 -291
- package/server/lib/docx-pages.js +187 -187
- package/server/lib/export-collect.js +289 -289
- package/server/lib/export-package.js +210 -210
- package/server/lib/html-srcset.js +145 -145
- package/server/lib/image-variant.js +460 -460
- package/server/lib/ingress/forward-openai-chat.js +305 -305
- package/server/lib/ingress/openai-chat.js +491 -491
- package/server/lib/ingress/session-notice.js +66 -66
- package/server/lib/ingress/session-routes.js +68 -68
- package/server/lib/ingress/slot-probe.js +130 -130
- package/server/lib/ingress/upstream-billing.js +52 -52
- package/server/lib/ingress/upstream-fail-streak.js +72 -72
- package/server/lib/ingress/upstream-truncation.js +48 -48
- package/server/lib/issues-store.js +182 -182
- package/server/lib/kinds/deck.js +72 -72
- package/server/lib/kinds/docx.js +283 -283
- package/server/lib/kinds/file-kinds.js +113 -113
- package/server/lib/kinds/index.js +202 -202
- package/server/lib/kinds/site.js +161 -161
- package/server/lib/model-ingress.js +543 -543
- package/server/lib/moderation.js +255 -255
- package/server/lib/notice-store.js +103 -103
- package/server/lib/plugin-install.js +104 -104
- package/server/lib/plugin-validator.js +721 -721
- package/server/lib/publish-store.js +115 -115
- package/server/lib/quick-summary.js +52 -52
- package/server/lib/quota.js +270 -270
- package/server/lib/rate-window.js +29 -29
- package/server/lib/reference-assets.js +92 -92
- package/server/lib/region-shot.js +135 -135
- package/server/lib/safe-path.js +73 -73
- package/server/lib/sdk-session.js +33 -33
- package/server/lib/showcase-store.js +92 -92
- package/server/lib/site-publish.js +465 -465
- package/server/lib/sketch-layout.js +277 -0
- package/server/lib/ssrf-guard.js +432 -432
- package/server/lib/task-scan.js +158 -158
- package/server/lib/tavern-json.js +154 -154
- package/server/lib/video-variant.js +237 -237
- package/server/lib/workspace-path.js +33 -33
- package/server/ops/fix-sdk-musl.mjs +40 -40
- package/server/ops/install-macos-fonts.sh +114 -114
- package/server/ops/macos-fonts.conf +336 -336
- package/server/ops/sandbox-shim/bwrap +57 -57
- package/server/projects/assets-summary.js +119 -119
- package/server/projects/auto-name.js +53 -53
- package/server/projects/board-limits.js +12 -0
- package/server/projects/board-sanitize.js +252 -0
- package/server/projects/board-store.js +507 -640
- package/server/projects/move-entry.js +103 -103
- package/server/projects/store.js +270 -270
- package/server/projects/ui-config.js +60 -54
- package/server/projects/viewpoint-store.js +78 -0
- package/server/projects/workspace-templates.js +53 -53
- package/server/projects/workspace.js +1025 -1025
- package/server/runtime/local-config.js +177 -177
- package/server/runtime/profile.js +72 -72
- package/server/services/rembg-service.py +334 -334
- package/server/shared/README.md +25 -25
- package/server/shared/deck.js +54 -54
- package/server/shared/time.js +49 -49
- package/server/style-pipeline/README.md +53 -53
- package/server/ws/broker.js +39 -36
- package/server/ws/browse-channel.js +177 -177
- package/server/ws/index.js +386 -386
- package/web/README.md +89 -89
- package/web/dist/assets/BrowserWindow-P_vIdh-G.js +11 -0
- package/web/dist/assets/{DeckWindow-4G0xX7P3.js → DeckWindow-BpLv5082.js} +3 -3
- package/web/dist/assets/{DocxWindow-BXQsoPkh.js → DocxWindow-DSAU1-tD.js} +4 -4
- package/web/dist/assets/MermaidBlock-DR1GikCv.js +313 -0
- package/web/dist/assets/SiteWindow-92uBuRRR.js +18 -0
- package/web/dist/assets/abnfDiagram-VCTEODGH-uA8Esix4.js +1 -0
- package/web/dist/assets/arc-D_khH_xN.js +1 -0
- package/web/dist/assets/architectureDiagram-5GKGNRK7-DydJHqOt.js +36 -0
- package/web/dist/assets/blockDiagram-NRAW4CY4-B7wBbKiW.js +129 -0
- package/web/dist/assets/c4Diagram-UCG6FXSJ-6mrH0x7Y.js +38 -0
- package/web/dist/assets/channel-DGkccMO1.js +1 -0
- package/web/dist/assets/chunk-2Q5K7J3B-Cuikho5Q.js +1 -0
- package/web/dist/assets/chunk-5VM5RSS4-3-KZ5AUz.js +15 -0
- package/web/dist/assets/chunk-F27PBJKO-Bkwq2gGU.js +1 -0
- package/web/dist/assets/chunk-G27WJ6UU-BLyQzdGE.js +231 -0
- package/web/dist/assets/chunk-JWPE2WC7-B7HxxQds.js +1 -0
- package/web/dist/assets/chunk-LCL6LL3I-CtXrnUwF.js +206 -0
- package/web/dist/assets/chunk-POPQ4Y6H-dxddZlXt.js +1 -0
- package/web/dist/assets/chunk-SVP7TREG-DpkmrgHn.js +88 -0
- package/web/dist/assets/chunk-XXDRQBXY-BwZ7RbpQ.js +1 -0
- package/web/dist/assets/classDiagram-DTDB5LWJ-BGscw3OD.js +1 -0
- package/web/dist/assets/classDiagram-v2-JRS7N3AN-BGscw3OD.js +1 -0
- package/web/dist/assets/cose-bilkent-JH36ORCC-DGFOsMUt.js +1 -0
- package/web/dist/assets/cynefin-OW5HDTMX-DUwn3ehS.js +178 -0
- package/web/dist/assets/cynefinDiagram-5FMLGOSQ-DlLAOZ9x.js +62 -0
- package/web/dist/assets/cytoscape.esm-BjAbmLdb.js +331 -0
- package/web/dist/assets/dagre-3AP2YEHR-CyourLfz.js +4 -0
- package/web/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/web/dist/assets/diagram-S7CK7UJ4-BTMbx2np.js +30 -0
- package/web/dist/assets/diagram-UQ7AKVKN-UK94_1-P.js +41 -0
- package/web/dist/assets/diagram-VSXAHHWV-C7zS-v9v.js +3 -0
- package/web/dist/assets/diagram-VX7I27RA-DKJRa6CK.js +24 -0
- package/web/dist/assets/diagram-Z3DM3KII-kUbAfGAp.js +24 -0
- package/web/dist/assets/ebnfDiagram-PWID7BFC-CVW16_vb.js +1 -0
- package/web/dist/assets/erDiagram-SSCWMZ5O-BRQMFQ5y.js +99 -0
- package/web/dist/assets/flowDiagram-A5DVABFB-S34LpLtK.js +168 -0
- package/web/dist/assets/ganttDiagram-EL5Y4UJY-BFiqk1-G.js +292 -0
- package/web/dist/assets/gitGraphDiagram-WWUBYQGX-LZsrdyyv.js +106 -0
- package/web/dist/assets/index-GAamh1p4.js +2152 -0
- package/web/dist/assets/infoDiagram-RXCK75RN-0DvN6KIp.js +2 -0
- package/web/dist/assets/init-Gi6I4Gst.js +1 -0
- package/web/dist/assets/ishikawaDiagram-5VMMS53U-CaY3A-Bx.js +70 -0
- package/web/dist/assets/journeyDiagram-EYS64GPL-BBeiaCrj.js +139 -0
- package/web/dist/assets/kanban-definition-3QL26DDD-ux42DRXQ.js +89 -0
- package/web/dist/assets/layout-CnXiF_NB.js +1 -0
- package/web/dist/assets/linear-BMW0u6G6.js +1 -0
- package/web/dist/assets/mindmap-definition-FBJOCRG2-hUxSpdKn.js +96 -0
- package/web/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/web/dist/assets/pegDiagram-XKGWAZYB-aCaQPH0w.js +1 -0
- package/web/dist/assets/{pending-edit-apply-DTjo2geW.js → pending-edit-apply-AcdOQ9Ij.js} +6 -6
- package/web/dist/assets/pieDiagram-E7YTZNPT-CmOEZ8zg.js +39 -0
- package/web/dist/assets/quadrantDiagram-AXDQQJYC-VW6fAhsV.js +7 -0
- package/web/dist/assets/railroadDiagram-O6MQD6OU-CKJ7Ba5K.js +1 -0
- package/web/dist/assets/requirementDiagram-EFPCY7ZU-pNGkq9Vw.js +84 -0
- package/web/dist/assets/{rotate-cw-LMT3x1Zj.js → rotate-cw-KAfChbGV.js} +1 -1
- package/web/dist/assets/sankeyDiagram-P5KCCOFB-BIMmDTfZ.js +40 -0
- package/web/dist/assets/sequenceDiagram-WJ2MYXX4-BBW7wzKY.js +162 -0
- package/web/dist/assets/sizeCapture-X5ZJPWSS-DdkXmLYR.js +1 -0
- package/web/dist/assets/{square-dashed-mouse-pointer-CBoAkfhi.js → square-dashed-mouse-pointer-CDUlhUP7.js} +1 -1
- package/web/dist/assets/stateDiagram-HBIQ2CUA-W313Uz16.js +1 -0
- package/web/dist/assets/stateDiagram-v2-4QOOHH4V-DDhJBZzf.js +1 -0
- package/web/dist/assets/swimlanes-XN3QIQJK-C29TL26K.js +1 -0
- package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BfN-Xdmu.js +8 -0
- package/web/dist/assets/timeline-definition-24CTP7MA-CF0JMObX.js +120 -0
- package/web/dist/assets/vennDiagram-4TSXK5OY-DrDFsaH3.js +34 -0
- package/web/dist/assets/wardleyDiagram-VM6X3IG4-CVrnRIS_.js +78 -0
- package/web/dist/assets/xychartDiagram-S5SC5T6Z-BvkN1zsT.js +7 -0
- package/web/dist/index.html +17 -17
- package/web/dist/assets/BrowserWindow-Dc7J7CYc.js +0 -11
- package/web/dist/assets/SiteWindow-D6eS-2gM.js +0 -18
- 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 {
|
|
19
|
-
import { layerOf } from '../../../lib/canvas-id.js';
|
|
20
|
-
import { relationsDigest } from '../../../lib/board-relations.js';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
'
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
lines
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
+
}
|