@xiaobuyu/nodesign 0.0.7 → 0.0.8
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 +266 -79
- package/README.zh-CN.md +254 -67
- package/package.json +2 -2
- package/server/api/admin.js +7 -0
- package/server/api/assets/docx-page.js +5 -4
- package/server/api/assets/helpers.js +8 -0
- package/server/api/assets.js +21 -33
- package/server/api/board.js +29 -1
- package/server/api/browse.js +3 -2
- package/server/api/chatai.js +9 -8
- package/server/api/exports/cards.js +7 -6
- package/server/api/exports/docx-pdf.js +4 -1
- package/server/api/local.js +10 -8
- package/server/api/projects.js +19 -3
- package/server/api/publish.js +3 -2
- package/server/api/sessions-rewind.js +220 -0
- package/server/api/sessions.js +58 -194
- package/server/api/stage.js +35 -0
- package/server/api/turn-model-switch.js +109 -0
- package/server/api/turn.js +52 -88
- package/server/auth/middleware.js +29 -6
- package/server/auth/tier.js +27 -7
- package/server/auth/users-store.js +14 -1
- package/server/engine/agent/actor-trail.js +110 -0
- package/server/engine/agent/agent-shared.js +22 -52
- package/server/engine/agent/beat-state.js +61 -0
- package/server/engine/agent/cast.js +322 -0
- package/server/engine/agent/events.js +9 -2
- package/server/engine/agent/hooks/beat-gate.js +73 -0
- package/server/engine/agent/hooks/lifecycle.js +6 -2
- package/server/engine/agent/hooks/post-trim.js +1 -0
- package/server/engine/agent/hooks/pre-board-dirty.js +29 -0
- package/server/engine/agent/hooks/pre-defaults.js +206 -2
- package/server/engine/agent/hooks/pre-injectors.js +19 -2
- package/server/engine/agent/hooks/pre-peer-guard.js +88 -0
- package/server/engine/agent/hooks/pre-unknown-params.js +82 -0
- package/server/engine/agent/hooks/pre-workspace-scope-guard.js +33 -0
- package/server/engine/agent/hooks/resident-role-lifecycle.js +100 -0
- package/server/engine/agent/hooks/slot-alias.js +30 -0
- package/server/engine/agent/hooks/user-prompt-submit.js +197 -7
- package/server/engine/agent/hooks.js +76 -6
- package/server/engine/agent/isolation.js +25 -2
- package/server/engine/agent/memory-config.js +27 -1
- package/server/engine/agent/model-context.js +102 -18
- package/server/engine/agent/model-table.js +361 -58
- package/server/engine/agent/prompts/nodesign-prelude.md +566 -59
- package/server/engine/agent/prompts/tools/direct-edit-protocol.md +1 -1
- package/server/engine/agent/prompts/tools/generate-image-cookbook.md +2 -2
- package/server/engine/agent/prompts/tools/paint-still-cookbook.md +1 -1
- package/server/engine/agent/prompts/tools/site-reference.md +2 -1
- package/server/engine/agent/role-card.js +175 -0
- package/server/engine/agent/rp-mode.js +17 -0
- package/server/engine/agent/session-loop.js +26 -14
- package/server/engine/agent/stage-status.js +73 -0
- package/server/engine/agent/subagent-flight.js +73 -0
- package/server/engine/agent/system-prompts.js +41 -0
- package/server/engine/agent/tool-input-stream.js +165 -0
- package/server/engine/mcp/actor.js +59 -0
- package/server/engine/mcp/capability-gate.js +21 -2
- package/server/engine/mcp/index.js +116 -24
- package/server/engine/mcp/mode-profile.js +139 -0
- package/server/engine/mcp/server-name.js +19 -0
- package/server/engine/mcp/tools/artifact-session.js +2 -1
- package/server/engine/mcp/tools/browse-find-batch.js +76 -14
- package/server/engine/mcp/tools/browse.js +1 -1
- package/server/engine/mcp/tools/cast-role.js +167 -0
- package/server/engine/mcp/tools/draw-trend.js +104 -0
- package/server/engine/mcp/tools/edit-board-schema.js +56 -0
- package/server/engine/mcp/tools/edit-board.js +569 -0
- package/server/engine/mcp/tools/look-at-board.js +4 -3
- package/server/engine/mcp/tools/open-sheet.js +242 -0
- package/server/engine/mcp/tools/organize-board.js +19 -5
- package/server/engine/mcp/tools/paint-still.js +1 -1
- package/server/engine/mcp/tools/pin-to-board.js +78 -3
- package/server/engine/mcp/tools/read-board.js +109 -16
- package/server/engine/mcp/tools/read-tavern-json.js +87 -19
- package/server/engine/mcp/tools/report-issue.js +1 -0
- package/server/engine/mcp/tools/role-memory.js +83 -0
- package/server/engine/mcp/tools/roll-dice.js +82 -0
- package/server/engine/mcp/tools/roll-film.js +1 -1
- package/server/engine/mcp/tools/screenshot.js +2 -2
- package/server/engine/mcp/tools/set-vars.js +150 -0
- package/server/engine/mcp/tools/sheet-replan.js +46 -0
- package/server/engine/mcp/tools/web-search.js +1 -1
- package/server/engine/mcp/tools/write-on-board-flow.js +108 -0
- package/server/engine/mcp/tools/write-on-board-place.js +211 -0
- package/server/engine/mcp/tools/write-on-board-role-anchor.js +48 -0
- package/server/engine/mcp/tools/write-on-board-schema.js +125 -0
- package/server/engine/mcp/tools/write-on-board.js +567 -107
- package/server/engine/plugins/nodesign/skills/blackboard-rp/SKILL.md +215 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +11 -3
- package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +34 -4
- package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +20 -4
- package/server/engine/plugins/nodesign/skills/story-craft/SKILL.md +94 -0
- package/server/engine/plugins/nodesign/skills/story-import/SKILL.md +112 -0
- package/server/engine/plugins/nodesign/skills/story-intimacy/SKILL.md +72 -0
- package/server/engine/plugins/nodesign/skills/story-voice/SKILL.md +66 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//345/206/267/347/241/254.md +33 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//346/203/205/346/204/237/347/233/264/347/273/231.md +42 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//346/204/217/350/257/206/346/265/201.md +42 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//346/255/246/344/276/240/347/231/275/350/257/235.md +34 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//347/224/237/346/264/273/346/265/201.md +35 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//347/231/275/346/217/217.md +37 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//350/210/236/345/217/260/345/211/247.md +32 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//351/230/266/346/256/265/345/210/207/346/215/242.md +42 -0
- package/server/engine/runs/board-seater.js +272 -0
- package/server/engine/runs/board-tasklist.js +90 -48
- package/server/engine/runs/turn-relay.js +24 -2
- package/server/engine/runs/user-chalk-echo.js +44 -0
- package/server/index.js +2 -0
- package/server/lib/board-anchor.js +49 -0
- package/server/lib/board-dirty.js +93 -0
- package/server/lib/board-excerpt.js +80 -0
- package/server/lib/board-follow.js +111 -0
- package/server/lib/board-groups.js +1 -5
- package/server/lib/board-hero.js +3 -1
- package/server/lib/board-kind-sizes.js +36 -0
- package/server/lib/board-lanes.js +58 -0
- package/server/lib/board-obstacles.js +80 -0
- package/server/lib/board-relations.js +5 -1
- package/server/lib/board-sheets.js +467 -0
- package/server/lib/board-shelf.js +86 -0
- package/server/lib/board-transform.js +62 -0
- package/server/lib/canvas-id.js +18 -1
- package/server/lib/chalk-flow.js +88 -0
- package/server/lib/chalk-rewrite.js +63 -0
- package/server/lib/chalk-size-pref.js +48 -0
- package/server/lib/chalk.js +42 -3
- package/server/lib/ingress/forward-openai-chat.js +16 -4
- package/server/lib/ingress/openai-chat.js +23 -12
- package/server/lib/ingress/session-routes.js +3 -3
- package/server/lib/ingress/stream-watchdog.js +53 -0
- package/server/lib/ingress/upstream-billing.js +15 -0
- package/server/lib/ingress/upstream-error-hints.js +36 -0
- package/server/lib/json-preview.js +58 -0
- package/server/lib/model-ingress.js +26 -1
- package/server/lib/moderation.js +10 -5
- package/server/lib/quota.js +26 -6
- package/server/lib/rect.js +50 -0
- package/server/lib/screen.js +36 -0
- package/server/lib/sheet-state.js +19 -0
- package/server/lib/sketch-array.js +118 -0
- package/server/lib/sketch-hatch.js +99 -0
- package/server/lib/sketch-layout.js +269 -69
- package/server/lib/sketch-shapes.js +152 -0
- package/server/lib/sketch-stencils.js +170 -0
- package/server/lib/state-table.js +237 -0
- package/server/lib/state-trend.js +94 -0
- package/server/lib/state-triggers.js +199 -0
- package/server/lib/tavern-json.js +48 -0
- package/server/lib/user-uploads.js +88 -0
- package/server/projects/board-limits.js +5 -3
- package/server/projects/board-sanitize.js +220 -10
- package/server/projects/board-store.js +95 -6
- package/server/projects/session-jsonl.js +110 -0
- package/server/projects/store.js +56 -3
- package/server/projects/viewpoint-store.js +35 -3
- package/server/projects/workspace-slots.js +30 -0
- package/server/projects/workspace-templates.js +29 -0
- package/server/projects/workspace.js +16 -17
- package/server/runtime/capabilities.js +6 -2
- package/server/shared/locales.js +70 -0
- package/server/shared/messages-en.js +69 -0
- package/server/shared/messages.js +65 -0
- package/server/ws/broker.js +3 -0
- package/server/ws/index.js +43 -3
- package/web/README.md +1 -1
- package/web/dist/assets/{BrowserWindow-BoOBaIyz.js → BrowserWindow-CcaGRWz5.js} +1 -1
- package/web/dist/assets/DeckWindow-BIr2KGFT.js +6 -0
- package/web/dist/assets/{DocxWindow-B_u6aX9o.js → DocxWindow-B9KZmU5z.js} +2 -2
- package/web/dist/assets/{MermaidBlock-BO-46Du5.js → MermaidBlock-DrfRnz8E.js} +43 -43
- package/web/dist/assets/{SiteWindow-ConPmzdM.js → SiteWindow-CJOjCF7d.js} +6 -6
- package/web/dist/assets/{abnfDiagram-VCTEODGH-FCO9yf-n.js → abnfDiagram-VCTEODGH-CUZbQGIg.js} +1 -1
- package/web/dist/assets/{arc-BLcEOSRX.js → arc-C1-HSUny.js} +1 -1
- package/web/dist/assets/{architectureDiagram-5GKGNRK7-kxiX4TeN.js → architectureDiagram-5GKGNRK7-B5nwvu60.js} +2 -2
- package/web/dist/assets/{blockDiagram-NRAW4CY4-CptFksix.js → blockDiagram-NRAW4CY4-uPPuFLwR.js} +1 -1
- package/web/dist/assets/{c4Diagram-UCG6FXSJ-Dxmxj34F.js → c4Diagram-UCG6FXSJ-C0mKBFlz.js} +1 -1
- package/web/dist/assets/channel-BxpWNOGU.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-D9inx5oo.js → chunk-2Q5K7J3B-Cs0rlZ4q.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-DydfHlEc.js → chunk-5VM5RSS4-DtR63oYb.js} +1 -1
- package/web/dist/assets/{chunk-F27PBJKO-BO7GxhSv.js → chunk-F27PBJKO-B3MRdezJ.js} +1 -1
- package/web/dist/assets/{chunk-G27WJ6UU-BzP6YiQ_.js → chunk-G27WJ6UU-BtwntwpS.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-CHcgcd0s.js → chunk-JWPE2WC7-BHsTe5PD.js} +1 -1
- package/web/dist/assets/{chunk-LCL6LL3I-D2Wg5JtX.js → chunk-LCL6LL3I-knY15Qdm.js} +1 -1
- package/web/dist/assets/{chunk-POPQ4Y6H-B2fCRfpM.js → chunk-POPQ4Y6H-C51tgK_I.js} +1 -1
- package/web/dist/assets/{chunk-SVP7TREG-JSLcsRlZ.js → chunk-SVP7TREG-CnwRZmF_.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-BCjvaeHm.js → chunk-XXDRQBXY-BtXzVF-_.js} +1 -1
- package/web/dist/assets/classDiagram-DTDB5LWJ-DUEKmDfo.js +1 -0
- package/web/dist/assets/classDiagram-v2-JRS7N3AN-DUEKmDfo.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-0uKv6oJ7.js → cose-bilkent-JH36ORCC-CzWlGP6i.js} +1 -1
- package/web/dist/assets/{cynefin-OW5HDTMX-Bvt-gEmA.js → cynefin-OW5HDTMX-vK64EGSv.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-5FMLGOSQ-C9Ht02qC.js → cynefinDiagram-5FMLGOSQ-Bs3g1Gvh.js} +1 -1
- package/web/dist/assets/{dagre-3AP2YEHR-BfxZny4s.js → dagre-3AP2YEHR-BhgkYe8V.js} +1 -1
- package/web/dist/assets/{diagram-S7CK7UJ4-CrE0ZETE.js → diagram-S7CK7UJ4-C_6mvD2V.js} +1 -1
- package/web/dist/assets/{diagram-UQ7AKVKN-BO8VQ4Fx.js → diagram-UQ7AKVKN-qW7tssUW.js} +1 -1
- package/web/dist/assets/{diagram-VSXAHHWV-CHUZnR9F.js → diagram-VSXAHHWV-txJPHpvG.js} +1 -1
- package/web/dist/assets/{diagram-VX7I27RA-DYQAz1Vu.js → diagram-VX7I27RA-CQTMsCnL.js} +1 -1
- package/web/dist/assets/{diagram-Z3DM3KII-BDjivgLr.js → diagram-Z3DM3KII--cb1hoE6.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-PWID7BFC-Bpe_QVbH.js → ebnfDiagram-PWID7BFC-Da6lDU77.js} +1 -1
- package/web/dist/assets/{erDiagram-SSCWMZ5O-BxhwuAu1.js → erDiagram-SSCWMZ5O-DGCMrsQn.js} +1 -1
- package/web/dist/assets/{flowDiagram-A5DVABFB-CCw2H08-.js → flowDiagram-A5DVABFB-GdOL_qno.js} +1 -1
- package/web/dist/assets/{ganttDiagram-EL5Y4UJY-DtTc7MyI.js → ganttDiagram-EL5Y4UJY-Cx1F_jBK.js} +4 -4
- package/web/dist/assets/{gitGraphDiagram-WWUBYQGX-BvORxMd2.js → gitGraphDiagram-WWUBYQGX-B5_6JhR3.js} +1 -1
- package/web/dist/assets/hand-CuvZZQGW.webp +0 -0
- package/web/dist/assets/index-BYKfMnmv.js +2759 -0
- package/web/dist/assets/index-CW7VsrVg.css +1 -0
- package/web/dist/assets/{infoDiagram-RXCK75RN-DMcrkypD.js → infoDiagram-RXCK75RN-CjvKpSPA.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-5VMMS53U-CZkTIbe-.js → ishikawaDiagram-5VMMS53U-CxLoAPhx.js} +1 -1
- package/web/dist/assets/{journeyDiagram-EYS64GPL-DR0XazdE.js → journeyDiagram-EYS64GPL-BfvS1upJ.js} +1 -1
- package/web/dist/assets/{kanban-definition-3QL26DDD-DN8VbwVu.js → kanban-definition-3QL26DDD-DKPPu0w4.js} +1 -1
- package/web/dist/assets/{layout-9_paCbyt.js → layout-Dhgck-Zo.js} +1 -1
- package/web/dist/assets/{linear-g6Ct012e.js → linear-DMSQ-aUw.js} +1 -1
- package/web/dist/assets/lxgw-nd-app-bold-D1pKA1bR.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-app-regular-BDe3d26v.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-bold-Bufvlg03.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-regular-DQwoI4OP.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-100-DLPe7tEt.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-101-d3VsZKWT.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-102-ClzJuLq6.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-103-DuDnQ6K-.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-104-kPGW3Eem.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-105-BzD54h-0.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-106-BQ7fIzlx.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-107-D8tDfFHI.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-108-tGhkpwep.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-109-7SMVSrly.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-110-_uDKrFed.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-111-Bio825RE.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-112-D2ee0i-C.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-113-JPFVDrga.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-114-BcfxYA4w.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-115-B1HeeOsd.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-116-hY12tXpq.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-117-BvuZwmre.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-118-tzvs_H7j.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-119-J2DWuYDN.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-21-pDubCJV5.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-22-ChAPMpqp.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-23-BBx3oNpT.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-24-CYt-Oagk.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-25-Cx3j-ZUv.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-26-B9AfZ3LL.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-27-Bh2s9MNE.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-28-C02JPoKP.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-29-T_tbUGhk.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-30-DMfyNzhz.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-31-DFfdzbkg.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-32-Bs1fz2iW.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-33-cpB96OkZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-34-BhinPYWX.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-35-D_njRQqA.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-36-DqXlLE3t.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-37-j5LbvMph.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-38-oO4jWdKm.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-39-CabsX4rw.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-4-PatS3YHh.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-40-yrLg3C3e.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-41-DBXmCQhc.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-42-Bqij_UiQ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-43-D3kp0Ntc.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-44-B_BNGvK4.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-45-CZc0cQWs.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-46-CHQX5Kd9.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-47-Ci8gLrHA.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-48-run3ugD3.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-49-DgfiUb6u.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-5-B0CtGbX_.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-50-CdCG-POr.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-51-L2okeQZ2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-52-BZAqyGJW.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-53-B0xWqLeU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-54-BZVQX-EA.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-55-093mkIUJ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-56-CbhWG8G-.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-57-pLQuvo6c.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-58-BHhoY50x.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-59-D1_6DuIQ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-6-BnAelmYU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-60-HDBIuLTZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-61-C-c13SeU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-62-kHRVOhZ2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-63-C0MCsvFv.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-64-DZekguCj.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-65-CtP6c3IU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-66-DLVWoMSf.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-67-BVuOnayH.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-68-DpxQ6Mnx.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-69-qesBxLmC.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-70-BBt1xN7h.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-71-BWqZacwB.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-72-B1wP_Yst.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-73-C99T7jNZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-74-DrUTsWBe.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-75-C4M5XXQe.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-76-Bq8spVWF.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-77-D9vNupE2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-78-D6th7Gnu.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-79-bezM85cS.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-80-DD8pnhG2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-81-N6jtSyBs.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-82-D6c180bZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-83-em17AadM.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-84-CJ8gdrMK.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-85-9PPGO1eB.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-86-CjWPf-u5.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-87-8D_X_yOC.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-88-9vtKFPBx.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-89-BhE5jAKX.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-90-D2Npfm5E.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-91-C6Ft1-Eu.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-97-CaExyQP9.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-98-CPoVbsUd.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-99-ZUQZGVgH.woff2 +0 -0
- package/web/dist/assets/{mindmap-definition-FBJOCRG2-BGt4omYC.js → mindmap-definition-FBJOCRG2-CalfL_Kn.js} +1 -1
- package/web/dist/assets/{pegDiagram-XKGWAZYB-uSfS-PBE.js → pegDiagram-XKGWAZYB-D9mVWz7l.js} +1 -1
- package/web/dist/assets/pending-edit-apply-kwMFWsUe.js +31 -0
- package/web/dist/assets/{pieDiagram-E7YTZNPT-DpeI935H.js → pieDiagram-E7YTZNPT-DELzbZNS.js} +1 -1
- package/web/dist/assets/{quadrantDiagram-AXDQQJYC-B7IuNvWa.js → quadrantDiagram-AXDQQJYC-TZqp4mfA.js} +1 -1
- package/web/dist/assets/{railroadDiagram-O6MQD6OU-ARzYpPQI.js → railroadDiagram-O6MQD6OU-DxX59f0i.js} +1 -1
- package/web/dist/assets/{requirementDiagram-EFPCY7ZU-Cj4upkAo.js → requirementDiagram-EFPCY7ZU-DFdsJLIe.js} +1 -1
- package/web/dist/assets/{rotate-cw-CXJcyHWk.js → rotate-cw-62liz7LX.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-P5KCCOFB-OaVwSnLP.js → sankeyDiagram-P5KCCOFB-ByYcd3fG.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-WJ2MYXX4-1yrUbq4t.js → sequenceDiagram-WJ2MYXX4-Bj-ml1jS.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-nYH5p9D6.js → sizeCapture-X5ZJPWSS-KhsXThyD.js} +1 -1
- package/web/dist/assets/{square-dashed-mouse-pointer-CTtTVKoH.js → square-dashed-mouse-pointer-D_VrqVpy.js} +1 -1
- package/web/dist/assets/{stateDiagram-HBIQ2CUA-Dj5HvyNq.js → stateDiagram-HBIQ2CUA-BJlpHfWU.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-4QOOHH4V-CR2kYAH2.js +1 -0
- package/web/dist/assets/{swimlanes-XN3QIQJK-umeT0HpB.js → swimlanes-XN3QIQJK-DUB9OJVs.js} +1 -1
- package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BitOxnG2.js +8 -0
- package/web/dist/assets/{timeline-definition-24CTP7MA-MqnQfHKB.js → timeline-definition-24CTP7MA-C03NX82A.js} +1 -1
- package/web/dist/assets/{vennDiagram-4TSXK5OY-b2liyZra.js → vennDiagram-4TSXK5OY-B9pjzngp.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-VM6X3IG4-QMK-zNh1.js → wardleyDiagram-VM6X3IG4-UqWq_mcT.js} +1 -1
- package/web/dist/assets/{xychartDiagram-S5SC5T6Z-D5Mc6tfA.js → xychartDiagram-S5SC5T6Z-D2pDKtOv.js} +1 -1
- package/web/dist/index.html +2 -2
- package/server/engine/mcp/tools/arrange-on-board.js +0 -126
- package/server/engine/mcp/tools/create-on-board.js +0 -152
- package/server/engine/mcp/tools/edit-sketch.js +0 -167
- package/server/engine/mcp/tools/relate-on-board.js +0 -150
- package/server/engine/mcp/tools/sketch-on-board.js +0 -345
- package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +0 -235
- 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 +0 -78
- package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +0 -268
- package/web/dist/assets/DeckWindow-Bg3rgkvi.js +0 -11
- package/web/dist/assets/channel-DFyiuGL0.js +0 -1
- package/web/dist/assets/classDiagram-DTDB5LWJ-Aw1f8z-b.js +0 -1
- package/web/dist/assets/classDiagram-v2-JRS7N3AN-Aw1f8z-b.js +0 -1
- package/web/dist/assets/index-C0KUf6Wd.css +0 -1
- package/web/dist/assets/index-DGG5wIZs.js +0 -2118
- package/web/dist/assets/lxgw-nd-bold-DbRfiTgo.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-regular-CpMw9Ehg.woff2 +0 -0
- package/web/dist/assets/pending-edit-apply-uP4uvrOD.js +0 -31
- package/web/dist/assets/stateDiagram-v2-4QOOHH4V-kJ1m_zJT.js +0 -1
- package/web/dist/assets/swimlanesDiagram-VK2B7HYN-DPSkMbbl.js +0 -8
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* mcp/tools/edit-sketch.js —— edit_sketch(2026-08-23 黑板二期)
|
|
3
|
-
*
|
|
4
|
-
* 改已有的图,不重画。原则抄用户自己的 AI-Annotation-demo 里那条「引用,不吐坐标」:
|
|
5
|
-
* agent 只引用画布 id,位置用**相对关系**表达(在某件东西的右边/下面/上面/左边、
|
|
6
|
-
* 或网格位移),像素由服务端解析 —— 让模型吐绝对坐标必幻觉。
|
|
7
|
-
*
|
|
8
|
-
* 一次调用一批操作(按序执行,一条坏了其余照做并在返回里报):
|
|
9
|
-
* set_text 改字(文字节点:内容/格式/字号/颜色)
|
|
10
|
-
* move 挪一件:to {ref, side, gap?} 或 {dx, dy}(网格单位)
|
|
11
|
-
* move_group 挪一组(同 tag 的全部,保持相对位置):to 同上
|
|
12
|
-
* remove 删一件画布原生物件(text/scribble);产物卡不删(只能摘标签)
|
|
13
|
-
* add_node 加一个文字节点,位置 at {ref, side, gap?},归进 tag
|
|
14
|
-
* add_edge / set_edge / remove_edge 线的增改删(线 id 见 read_board)
|
|
15
|
-
* 改完前端按 board.updated 整份重拉;默认不进 staging(改的是已落定的图)。
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { tool } from '@anthropic-ai/claude-agent-sdk';
|
|
19
|
-
import { z } from 'zod';
|
|
20
|
-
import { readBoard, patchBoard, TEXT_FONTS } from '../../../projects/board-store.js';
|
|
21
|
-
import { estimateSizeOn } from '../../../lib/board-kind-sizes.js';
|
|
22
|
-
import { layerOf, normalizeCanvasId } from '../../../lib/canvas-id.js';
|
|
23
|
-
import { BINDING_TYPE_IDS, BINDING_MATERIALS } from '../../../lib/binding-types.js';
|
|
24
|
-
import { UNIT, textBox } from '../../../lib/sketch-layout.js';
|
|
25
|
-
|
|
26
|
-
const MAX_OPS = 40;
|
|
27
|
-
let seq = 0;
|
|
28
|
-
const stamp = () => `${Date.now().toString(36)}${(seq++ % 1000).toString(36)}`;
|
|
29
|
-
|
|
30
|
-
const REL = z.object({
|
|
31
|
-
ref: z.string().min(1).max(300).describe('canvas id to place relative to'),
|
|
32
|
-
side: z.enum(['right', 'left', 'above', 'below']),
|
|
33
|
-
gap: z.number().min(0).max(40).optional().describe('grid units (default 1)'),
|
|
34
|
-
});
|
|
35
|
-
const DELTA = z.object({ dx: z.number().min(-200).max(200), dy: z.number().min(-200).max(200) }).describe('grid units');
|
|
36
|
-
const TO = z.union([REL, DELTA]);
|
|
37
|
-
|
|
38
|
-
const OP = z.discriminatedUnion('op', [
|
|
39
|
-
z.object({ op: z.literal('set_text'), id: z.string().min(1).max(300), text: z.string().min(1).max(4000).optional(), format: z.enum(['plain', 'md']).optional(), size: z.enum(['sm', 'md', 'lg', 'xl']).optional(), color: z.enum(['ink', 'red', 'pencil', 'brass']).optional(), font: z.enum(['pen', 'kai', 'sans', 'serif', 'mono']).optional() }),
|
|
40
|
-
z.object({ op: z.literal('move'), id: z.string().min(1).max(300), to: TO }),
|
|
41
|
-
z.object({ op: z.literal('move_group'), tag: z.string().min(1).max(40), to: TO }),
|
|
42
|
-
z.object({ op: z.literal('remove'), id: z.string().min(1).max(300) }),
|
|
43
|
-
z.object({ op: z.literal('add_node'), id: z.string().regex(/^[A-Za-z0-9_-]{1,24}$/).optional().describe('local handle to reference in later ops of this call'), text: z.string().min(1).max(4000), format: z.enum(['plain', 'md']).optional(), size: z.enum(['sm', 'md', 'lg', 'xl']).optional(), font: z.enum(['pen', 'kai', 'sans', 'serif', 'mono']).optional(), color: z.enum(['ink', 'red', 'pencil', 'brass']).optional(), at: REL, tag: z.string().max(40).optional() }),
|
|
44
|
-
z.object({ op: z.literal('add_edge'), from: z.string().min(1).max(300), to: z.string().min(1).max(300), type: z.enum(BINDING_TYPE_IDS).optional(), material: z.enum(BINDING_MATERIALS).optional(), label: z.string().max(60).optional(), tag: z.string().max(40).optional() }),
|
|
45
|
-
z.object({ op: z.literal('set_edge'), id: z.string().min(1).max(300), label: z.string().max(60).optional(), type: z.enum(BINDING_TYPE_IDS).optional(), material: z.enum(BINDING_MATERIALS).optional() }),
|
|
46
|
-
z.object({ op: z.literal('remove_edge'), id: z.string().min(1).max(300) }),
|
|
47
|
-
]);
|
|
48
|
-
|
|
49
|
-
export function makeEditSketchTool({ projectId, ctx }) {
|
|
50
|
-
return tool(
|
|
51
|
-
'edit_sketch',
|
|
52
|
-
`Edit what is already on the board — by id, without redrawing. Positions are RELATIVE
|
|
53
|
-
(to {ref, side, gap} = beside/above/below another canvas id) or grid deltas ({dx,dy},
|
|
54
|
-
1 cell = ${UNIT}px); you never give absolute coordinates. ids come from read_board
|
|
55
|
-
(nodes: text:…/scribble:…, cards: deck:…/site:…/paths; lines: b:…).
|
|
56
|
-
ops (run in order; a failing op is reported, the rest still apply):
|
|
57
|
-
set_text{id,text?,format?,size?,color?,font?} · move{id,to} · move_group{tag,to} ·
|
|
58
|
-
remove{id} · add_node{id?,text,at:{ref,side,gap?},…} · add_edge{from,to,type?,material?,label?} ·
|
|
59
|
-
set_edge{id,label?,type?,material?} · remove_edge{id}
|
|
60
|
-
Use this for "move A under B", "rephrase that note", "add a branch", "delete the
|
|
61
|
-
wrong line". For a brand-new diagram use sketch_on_board.`,
|
|
62
|
-
{
|
|
63
|
-
tag: z.string().max(40).optional().describe('Default tag for add_node/add_edge (the sketch you are editing)'),
|
|
64
|
-
ops: z.array(OP).min(1).max(MAX_OPS),
|
|
65
|
-
},
|
|
66
|
-
async ({ tag: defaultTag, ops }) => {
|
|
67
|
-
const err = (t) => ({ content: [{ type: 'text', text: t }], isError: true });
|
|
68
|
-
if (!projectId) return err('No project bound.');
|
|
69
|
-
const board = await readBoard(projectId);
|
|
70
|
-
const known = new Set(Object.keys(board.zones || {}));
|
|
71
|
-
const objects = {}; const bindings = {}; // 增量 patch
|
|
72
|
-
const live = { ...board.objects }; // 本次调用内的"当前态"(让后一条 op 看见前一条的结果)
|
|
73
|
-
const local = new Map(); // add_node 的本地句柄 → canvas id
|
|
74
|
-
const rid = (raw) => { if (local.has(raw)) return local.get(raw); const c = normalizeCanvasId(raw); return c && live[c] ? c : null; };
|
|
75
|
-
const rectOf = (id) => { const e = live[id]; return e ? { x: e.x, y: e.y, ...estimateSizeOn(board, id, e) } : null; };
|
|
76
|
-
const placeRel = (box, rel) => {
|
|
77
|
-
const r = rectOf(rid(rel.ref));
|
|
78
|
-
if (!r) return null;
|
|
79
|
-
const g = (rel.gap ?? 1) * UNIT;
|
|
80
|
-
if (rel.side === 'right') return { x: r.x + r.w + g, y: r.y };
|
|
81
|
-
if (rel.side === 'left') return { x: r.x - g - box.w, y: r.y };
|
|
82
|
-
if (rel.side === 'below') return { x: r.x, y: r.y + r.h + g };
|
|
83
|
-
return { x: r.x, y: r.y - g - box.h };
|
|
84
|
-
};
|
|
85
|
-
const report = []; let ok = 0;
|
|
86
|
-
const setObj = (id, e) => { live[id] = e; objects[id] = e; };
|
|
87
|
-
|
|
88
|
-
for (let i = 0; i < ops.length; i += 1) {
|
|
89
|
-
const o = ops[i];
|
|
90
|
-
const fail = (why) => report.push(`✗ #${i + 1} ${o.op}: ${why}`);
|
|
91
|
-
try {
|
|
92
|
-
if (o.op === 'set_text') {
|
|
93
|
-
const id = rid(o.id); const e = id && live[id];
|
|
94
|
-
if (!e || e.kind !== 'text') { fail(`${o.id} 不是文字节点`); continue; }
|
|
95
|
-
const data = { ...e.data };
|
|
96
|
-
if (o.text) data.t = o.text;
|
|
97
|
-
if (o.format) { if (o.format === 'md') data.format = 'md'; else delete data.format; }
|
|
98
|
-
if (o.size) data.size = o.size; if (o.color) data.color = o.color; if (o.font && TEXT_FONTS.includes(o.font)) data.font = o.font;
|
|
99
|
-
const box = textBox(data.t, data.size || 'md', { md: data.format === 'md' });
|
|
100
|
-
setObj(id, { ...e, data, w: box.w, h: box.h }); ok += 1;
|
|
101
|
-
} else if (o.op === 'move') {
|
|
102
|
-
const id = rid(o.id); const e = id && live[id];
|
|
103
|
-
if (!e) { fail(`${o.id} 不在板上`); continue; }
|
|
104
|
-
const box = rectOf(id);
|
|
105
|
-
const p = 'ref' in o.to ? placeRel(box, o.to) : { x: e.x + o.to.dx * UNIT, y: e.y + o.to.dy * UNIT };
|
|
106
|
-
if (!p) { fail(`参照 ${o.to.ref} 不在板上`); continue; }
|
|
107
|
-
setObj(id, { ...e, x: Math.round(p.x), y: Math.round(p.y) }); ok += 1;
|
|
108
|
-
} else if (o.op === 'move_group') {
|
|
109
|
-
const members = Object.entries(live).filter(([, e]) => e.tag === o.tag && Number.isFinite(e?.x));
|
|
110
|
-
if (!members.length) { fail(`没有 #${o.tag} 的东西`); continue; }
|
|
111
|
-
const bb = { x: Math.min(...members.map(([, e]) => e.x)), y: Math.min(...members.map(([, e]) => e.y)) };
|
|
112
|
-
const rects = members.map(([id]) => rectOf(id));
|
|
113
|
-
const w = Math.max(...rects.map(r => r.x + r.w)) - bb.x; const h = Math.max(...rects.map(r => r.y + r.h)) - bb.y;
|
|
114
|
-
const p = 'ref' in o.to ? placeRel({ w, h }, o.to) : { x: bb.x + o.to.dx * UNIT, y: bb.y + o.to.dy * UNIT };
|
|
115
|
-
if (!p) { fail(`参照 ${o.to.ref} 不在板上`); continue; }
|
|
116
|
-
const dx = Math.round(p.x - bb.x); const dy = Math.round(p.y - bb.y);
|
|
117
|
-
for (const [id, e] of members) setObj(id, { ...e, x: e.x + dx, y: e.y + dy });
|
|
118
|
-
ok += 1;
|
|
119
|
-
} else if (o.op === 'remove') {
|
|
120
|
-
const id = rid(o.id); const e = id && live[id];
|
|
121
|
-
if (!e) { fail(`${o.id} 不在板上`); continue; }
|
|
122
|
-
if (!e.kind) { fail(`${id} 是产物卡,不能从黑板删;要摘标签用 finish_sketch erase`); continue; }
|
|
123
|
-
delete live[id]; objects[id] = null; ok += 1;
|
|
124
|
-
} else if (o.op === 'add_node') {
|
|
125
|
-
const size = (o.size === 'sm' && o.text.length > 40) ? 'md' : (o.size || 'md');
|
|
126
|
-
const box = textBox(o.text, size, { md: o.format === 'md' });
|
|
127
|
-
const p = placeRel(box, o.at);
|
|
128
|
-
if (!p) { fail(`参照 ${o.at.ref} 不在板上`); continue; }
|
|
129
|
-
const refId = rid(o.at.ref);
|
|
130
|
-
const zone = layerOf(refId, live[refId], known);
|
|
131
|
-
const id = `text:a${stamp()}`;
|
|
132
|
-
const tag = o.tag || defaultTag || live[refId]?.tag || null;
|
|
133
|
-
setObj(id, {
|
|
134
|
-
x: Math.round(p.x), y: Math.round(p.y), z: 1, w: box.w, h: box.h, kind: 'text',
|
|
135
|
-
data: { t: o.text, ...(o.format === 'md' ? { format: 'md' } : {}), font: TEXT_FONTS.includes(o.font) ? o.font : 'pen', size, color: o.color || 'ink' },
|
|
136
|
-
zone, by: 'agent', ...(tag ? { tag } : {}),
|
|
137
|
-
});
|
|
138
|
-
if (o.id) local.set(o.id, id);
|
|
139
|
-
report.push(`+ node ${o.id ? `${o.id}=` : ''}${id}`); ok += 1;
|
|
140
|
-
} else if (o.op === 'add_edge') {
|
|
141
|
-
const from = rid(o.from); const to = rid(o.to);
|
|
142
|
-
if (!from || !to || from === to) { fail(`端点不在板上:${o.from} → ${o.to}`); continue; }
|
|
143
|
-
const id = `b:a${stamp()}`;
|
|
144
|
-
const tag = o.tag || defaultTag || live[from]?.tag || live[to]?.tag || null;
|
|
145
|
-
bindings[id] = { type: o.type || 'link', from, to, by: 'agent', ...(o.material && o.material !== 'ink' ? { material: o.material } : {}), ...(o.label ? { label: o.label } : {}), ...(tag ? { tag } : {}) };
|
|
146
|
-
report.push(`+ edge ${id}`); ok += 1;
|
|
147
|
-
} else if (o.op === 'set_edge') {
|
|
148
|
-
const b = board.bindings?.[o.id];
|
|
149
|
-
if (!b) { fail(`线 ${o.id} 不存在`); continue; }
|
|
150
|
-
const nb = { ...b };
|
|
151
|
-
if (o.label !== undefined) { if (o.label) nb.label = o.label; else delete nb.label; }
|
|
152
|
-
if (o.type) nb.type = o.type;
|
|
153
|
-
if (o.material) { if (o.material === 'ink') delete nb.material; else nb.material = o.material; }
|
|
154
|
-
bindings[o.id] = nb; ok += 1;
|
|
155
|
-
} else if (o.op === 'remove_edge') {
|
|
156
|
-
if (!board.bindings?.[o.id]) { fail(`线 ${o.id} 不存在`); continue; }
|
|
157
|
-
bindings[o.id] = null; ok += 1;
|
|
158
|
-
}
|
|
159
|
-
} catch (e) { fail(String(e?.message || e).slice(0, 120)); }
|
|
160
|
-
}
|
|
161
|
-
if (!ok) return err(`没有一条操作成功:\n${report.join('\n')}`);
|
|
162
|
-
await patchBoard(projectId, { objects, bindings });
|
|
163
|
-
try { ctx?.emit?.({ type: 'board.updated', sessionId: null, summary: `改了黑板(${ok} 处)` }); } catch { /* */ }
|
|
164
|
-
return { content: [{ type: 'text', text: `Applied ${ok}/${ops.length} op(s).${report.length ? `\n${report.join('\n')}` : ''}` }] };
|
|
165
|
-
},
|
|
166
|
-
);
|
|
167
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* mcp/tools/relate-on-board.js — relate_on_board MCP tool(2026-08-07)
|
|
3
|
-
*
|
|
4
|
-
* 让 agent 在画布上**画出产物之间的关系**。
|
|
5
|
-
*
|
|
6
|
-
* ## 为什么这个工具重要
|
|
7
|
-
*
|
|
8
|
-
* 北极星是「任务文件夹能排出登录墙那种版面」。手摆之所以好看,是因为每张纸的
|
|
9
|
-
* 位置在回答「它和旁边那张什么关系」—— 而系统此前只知道每个产物的尺寸和
|
|
10
|
-
* mtime。**关系数据不到位,再好的布局算法也只能排出「错落有致的网格」。**
|
|
11
|
-
*
|
|
12
|
-
* 关键洞察是:**关系不需要推断,agent 手里本来就有**。它知道 proto-暖调 和
|
|
13
|
-
* proto-冷调 是同一拍的两个试作、知道哪张图是哪次批注的结果、知道这版改自那版。
|
|
14
|
-
* 这些信息现在全在会话里,一落到画布就只剩文件名。这个工具就是那条落差。
|
|
15
|
-
*
|
|
16
|
-
* 线不存坐标:端点是物件 id 或工作区 id,端点一移动线自己跟着走。
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import path from 'node:path';
|
|
20
|
-
import fs from 'node:fs/promises';
|
|
21
|
-
import { tool } from '@anthropic-ai/claude-agent-sdk';
|
|
22
|
-
import { z } from 'zod';
|
|
23
|
-
import { patchBoard, readBoard } from '../../../projects/board-store.js';
|
|
24
|
-
import { BINDING_TYPES, BINDING_TYPE_IDS, BINDING_MATERIALS } from '../../../lib/binding-types.js';
|
|
25
|
-
import { normalizeCanvasId } from '../../../lib/canvas-id.js';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 端点校验(08-23 案:不是 canvas id 的字符串照收,board.json 留悬空线)。
|
|
29
|
-
* 合法端点 = 板上有座位的 id,或者真实存在的工作区路径(带不带 kind 前缀都行)。
|
|
30
|
-
* write_on_board 的锚点早就这么查了,同一件事两个工具一个查一个不查是口径病。
|
|
31
|
-
*/
|
|
32
|
-
async function endpointExists(id, board, sharedRoot) {
|
|
33
|
-
if (board.objects?.[id]) return true;
|
|
34
|
-
if (/^doc:/.test(id)) return true; // 项目文档:固定名额
|
|
35
|
-
const bare = id.replace(/^(deck|site|docx|text|scribble):/, '');
|
|
36
|
-
if (!bare || bare.includes('..')) return false;
|
|
37
|
-
try { await fs.access(path.join(sharedRoot, bare)); return true; } catch { return false; }
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** 词汇表渲染成工具描述里的一段表,免得两处各写各的 */
|
|
41
|
-
const VOCAB = BINDING_TYPE_IDS
|
|
42
|
-
.map(id => `- ${id} (${BINDING_TYPES[id].label})${BINDING_TYPES[id].directed ? '' : ' — undirected'}`)
|
|
43
|
-
.join('\n');
|
|
44
|
-
|
|
45
|
-
let seq = 0;
|
|
46
|
-
const nextId = () => `b:a${Date.now().toString(36)}${(seq++ % 1000).toString(36)}`;
|
|
47
|
-
|
|
48
|
-
export function makeRelateOnBoardTool({ sharedRoot, projectId, ctx }) {
|
|
49
|
-
return tool(
|
|
50
|
-
'relate_on_board',
|
|
51
|
-
`Draw a relationship line between two things on the workbench canvas.
|
|
52
|
-
|
|
53
|
-
The canvas knows *what* each artifact is, but not *how they relate*. You are
|
|
54
|
-
the only one who knows that — you made them. Recording it lets the user see
|
|
55
|
-
lineage at a glance, and lets layout put related things near each other.
|
|
56
|
-
|
|
57
|
-
Relationship types:
|
|
58
|
-
${VOCAB}
|
|
59
|
-
|
|
60
|
-
Endpoints are canvas ids — a kind prefix plus the workspace-relative path,
|
|
61
|
-
exactly as the file sits on disk:
|
|
62
|
-
- a file: 'assets/generated/x.webp', 'notes/灵感.md'
|
|
63
|
-
- a deck: 'deck:海报/proto-暖调.html'
|
|
64
|
-
- a site: 'site:伊蕾娜手账研究站'
|
|
65
|
-
- a folder: just its path, '海报' or '海报/初稿'
|
|
66
|
-
|
|
67
|
-
Use it right after you produce something that relates to earlier work:
|
|
68
|
-
- made v2 of a poster → derives-from, from the new one to the old one
|
|
69
|
-
- produced two variants to compare → contrast between them
|
|
70
|
-
- storyboard panels in order → flow, each to the next
|
|
71
|
-
- used a reference image in a deck → ref, from the deck to the image
|
|
72
|
-
|
|
73
|
-
Do NOT use it for "these are all in the same folder" — that is what the
|
|
74
|
-
folder already says. Only record relationships that are not obvious from
|
|
75
|
-
where the files live.`,
|
|
76
|
-
{
|
|
77
|
-
type: z.enum(BINDING_TYPE_IDS).describe('Relationship kind (see list above)'),
|
|
78
|
-
from: z.string().min(1).max(300).describe('Source canvas id — see forms above'),
|
|
79
|
-
to: z.string().min(1).max(300).describe('Target canvas id — see forms above'),
|
|
80
|
-
label: z.string().max(60).optional()
|
|
81
|
-
.describe('Optional short words on the line. Omit to use the default for the type.'),
|
|
82
|
-
material: z.enum(BINDING_MATERIALS).optional()
|
|
83
|
-
.describe('How the line is drawn (orthogonal to type): ink = quiet archive line (default); pencil = hand-drawn; yarn = detective red string with pins, for hypotheses/evidence while reasoning. The whole canvas is a detective board — use yarn when the relation is still being argued, ink when it is settled.'),
|
|
84
|
-
},
|
|
85
|
-
async ({ type, from, to, label, material }) => {
|
|
86
|
-
if (!projectId) {
|
|
87
|
-
return { content: [{ type: 'text', text: 'No project bound; cannot draw relationships.' }], isError: true };
|
|
88
|
-
}
|
|
89
|
-
if (from === to) {
|
|
90
|
-
return {
|
|
91
|
-
content: [{ type: 'text', text: 'from and to are the same thing — a relationship needs two ends.' }],
|
|
92
|
-
isError: true,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
// 端点先归一再校验:不存在的端点会在 board.json 里留一条悬空线,
|
|
96
|
-
// 画布上凭空多出从无到有的线(08-23 录 GIF 时真踩)
|
|
97
|
-
const nFrom = normalizeCanvasId(from);
|
|
98
|
-
const nTo = normalizeCanvasId(to);
|
|
99
|
-
if (!nFrom || !nTo) {
|
|
100
|
-
return { content: [{ type: 'text', text: `Bad endpoint id: ${!nFrom ? from : to}` }], isError: true };
|
|
101
|
-
}
|
|
102
|
-
const board = await readBoard(projectId);
|
|
103
|
-
for (const [raw, nid] of [[from, nFrom], [to, nTo]]) {
|
|
104
|
-
if (!(await endpointExists(nid, board, sharedRoot))) {
|
|
105
|
-
const seats = Object.keys(board.objects || {}).slice(0, 20);
|
|
106
|
-
return {
|
|
107
|
-
content: [{
|
|
108
|
-
type: 'text',
|
|
109
|
-
text: `端点 ${raw} 不在板上,也不是存在的工作区路径 —— 线没画。`
|
|
110
|
-
+ `(read_board 可看全部;现有座位举例:${seats.join(', ') || '无'})`,
|
|
111
|
-
}],
|
|
112
|
-
isError: true,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
from = nFrom; to = nTo;
|
|
117
|
-
const id = nextId();
|
|
118
|
-
// by:'agent' 不只是记录出处 —— 用户画的线 agent 不该擅自删,反过来也一样
|
|
119
|
-
const binding = { type, from, to, by: 'agent', ...(label ? { label } : {}), ...(material && material !== 'ink' ? { material } : {}) };
|
|
120
|
-
const patched = await patchBoard(projectId, { bindings: { [id]: binding } });
|
|
121
|
-
|
|
122
|
-
// 服务端会丢掉非法的线(未知 type / 自环)。**丢了要说**,否则 agent 以为
|
|
123
|
-
// 画上了,用户看不到,两边都不知道发生了什么。
|
|
124
|
-
if (!patched.bindings?.[id]) {
|
|
125
|
-
return {
|
|
126
|
-
content: [{ type: 'text', text: `Relationship rejected by the board (bad type or endpoints): ${type} ${from} -> ${to}` }],
|
|
127
|
-
isError: true,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// 广播:前端整份重拉。**sessionId 必须显式给 null** —— ctx 会把自己的
|
|
132
|
-
// sessionId 补进事件,那样只有当前会话的连接收得到,别的 tab 看不见。
|
|
133
|
-
try {
|
|
134
|
-
ctx?.emit?.({
|
|
135
|
-
type: 'board.updated',
|
|
136
|
-
sessionId: null,
|
|
137
|
-
summary: `已画一条「${BINDING_TYPES[type].label}」关系`,
|
|
138
|
-
});
|
|
139
|
-
} catch { /* emit fail-safe:广播失败不该让工具报错,线已经落盘了 */ }
|
|
140
|
-
|
|
141
|
-
const arrow = BINDING_TYPES[type].directed ? '->' : '<->';
|
|
142
|
-
return {
|
|
143
|
-
content: [{
|
|
144
|
-
type: 'text',
|
|
145
|
-
text: `Drew ${type} (${BINDING_TYPES[type].label}): ${from} ${arrow} ${to}`,
|
|
146
|
-
}],
|
|
147
|
-
};
|
|
148
|
-
},
|
|
149
|
-
);
|
|
150
|
-
}
|