@xiaobuyu/nodesign 0.0.6 → 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 +26 -0
- package/server/api/assets/notes.js +50 -0
- package/server/api/assets.js +31 -38
- 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/instruction.js +3 -3
- 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 +18 -1
- package/server/engine/agent/hooks/beat-gate.js +73 -0
- package/server/engine/agent/hooks/failure.js +11 -1
- package/server/engine/agent/hooks/lifecycle.js +16 -41
- package/server/engine/agent/hooks/post-guidance.js +4 -38
- 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 +208 -47
- package/server/engine/agent/hooks.js +80 -14
- package/server/engine/agent/isolation.js +25 -2
- package/server/engine/agent/memory-config.js +87 -0
- 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 +579 -70
- package/server/engine/agent/prompts/tools/direct-edit-protocol.md +1 -1
- package/server/engine/agent/prompts/tools/generate-image-cookbook.md +26 -9
- package/server/engine/agent/prompts/tools/paint-still-cookbook.md +1 -1
- package/server/engine/agent/prompts/tools/site-reference.md +15 -4
- 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 +55 -32
- 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/agents/explorer.md +1 -1
- package/server/engine/agents/index.js +1 -1
- package/server/engine/mcp/actor.js +59 -0
- package/server/engine/mcp/capability-gate.js +21 -2
- package/server/engine/mcp/index.js +118 -33
- 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-computer.js +2 -1
- package/server/engine/mcp/tools/browse-find-batch.js +95 -16
- package/server/engine/mcp/tools/browse.js +25 -3
- package/server/engine/mcp/tools/cast-role.js +167 -0
- package/server/engine/mcp/tools/crystallize-skill.js +1 -1
- 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/generate-image.js +50 -70
- package/server/engine/mcp/tools/helpers/codex-imagegen.js +139 -0
- package/server/engine/mcp/tools/helpers/rembg.js +7 -2
- package/server/engine/mcp/tools/helpers/shot-pipeline.js +109 -0
- package/server/engine/mcp/tools/list-pages.js +11 -5
- 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 +45 -6
- package/server/engine/mcp/tools/paint-still.js +1 -1
- package/server/engine/mcp/tools/pin-to-board.js +103 -19
- package/server/engine/mcp/tools/read-board.js +123 -16
- package/server/engine/mcp/tools/read-tavern-json.js +87 -19
- package/server/engine/mcp/tools/report-issue.js +15 -7
- 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 +19 -9
- 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 +12 -16
- 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 +568 -99
- package/server/engine/plugins/nodesign/skills/blackboard-rp/SKILL.md +215 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +14 -7
- package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +44 -5
- package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +16 -0
- package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +24 -8
- 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 -2
- package/server/lib/artifact-target.js +31 -14
- 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 +38 -3
- 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 +46 -4
- 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/export-collect.js +3 -1
- 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/issues-store.js +3 -1
- package/server/lib/json-preview.js +58 -0
- package/server/lib/kinds/docx.js +11 -4
- package/server/lib/kinds/index.js +6 -16
- package/server/lib/kinds/site.js +40 -7
- 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/rewrite-refs.js +110 -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/ssrf-guard.js +9 -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/task-scan.js +18 -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 +137 -7
- package/server/projects/memory-migration.js +85 -0
- package/server/projects/move-entry.js +11 -1
- package/server/projects/session-jsonl.js +110 -0
- package/server/projects/store.js +56 -3
- package/server/projects/ui-config.js +2 -1
- package/server/projects/viewpoint-store.js +35 -3
- package/server/projects/workspace-slots.js +30 -0
- package/server/projects/workspace-templates.js +42 -10
- package/server/projects/workspace.js +25 -26
- package/server/runtime/capabilities.js +6 -2
- package/server/services/rembg-launcher.js +5 -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-P_vIdh-G.js → BrowserWindow-CcaGRWz5.js} +1 -1
- package/web/dist/assets/DeckWindow-BIr2KGFT.js +6 -0
- package/web/dist/assets/{DocxWindow-DSAU1-tD.js → DocxWindow-B9KZmU5z.js} +2 -2
- package/web/dist/assets/{MermaidBlock-DR1GikCv.js → MermaidBlock-DrfRnz8E.js} +43 -43
- package/web/dist/assets/{SiteWindow-92uBuRRR.js → SiteWindow-CJOjCF7d.js} +6 -6
- package/web/dist/assets/{abnfDiagram-VCTEODGH-uA8Esix4.js → abnfDiagram-VCTEODGH-CUZbQGIg.js} +1 -1
- package/web/dist/assets/{arc-D_khH_xN.js → arc-C1-HSUny.js} +1 -1
- package/web/dist/assets/{architectureDiagram-5GKGNRK7-DydJHqOt.js → architectureDiagram-5GKGNRK7-B5nwvu60.js} +2 -2
- package/web/dist/assets/{blockDiagram-NRAW4CY4-B7wBbKiW.js → blockDiagram-NRAW4CY4-uPPuFLwR.js} +1 -1
- package/web/dist/assets/{c4Diagram-UCG6FXSJ-6mrH0x7Y.js → c4Diagram-UCG6FXSJ-C0mKBFlz.js} +1 -1
- package/web/dist/assets/channel-BxpWNOGU.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-Cuikho5Q.js → chunk-2Q5K7J3B-Cs0rlZ4q.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-3-KZ5AUz.js → chunk-5VM5RSS4-DtR63oYb.js} +1 -1
- package/web/dist/assets/{chunk-F27PBJKO-Bkwq2gGU.js → chunk-F27PBJKO-B3MRdezJ.js} +1 -1
- package/web/dist/assets/{chunk-G27WJ6UU-BLyQzdGE.js → chunk-G27WJ6UU-BtwntwpS.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-B7HxxQds.js → chunk-JWPE2WC7-BHsTe5PD.js} +1 -1
- package/web/dist/assets/{chunk-LCL6LL3I-CtXrnUwF.js → chunk-LCL6LL3I-knY15Qdm.js} +1 -1
- package/web/dist/assets/{chunk-POPQ4Y6H-dxddZlXt.js → chunk-POPQ4Y6H-C51tgK_I.js} +1 -1
- package/web/dist/assets/{chunk-SVP7TREG-DpkmrgHn.js → chunk-SVP7TREG-CnwRZmF_.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-BwZ7RbpQ.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-DGFOsMUt.js → cose-bilkent-JH36ORCC-CzWlGP6i.js} +1 -1
- package/web/dist/assets/{cynefin-OW5HDTMX-DUwn3ehS.js → cynefin-OW5HDTMX-vK64EGSv.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-5FMLGOSQ-DlLAOZ9x.js → cynefinDiagram-5FMLGOSQ-Bs3g1Gvh.js} +1 -1
- package/web/dist/assets/{dagre-3AP2YEHR-CyourLfz.js → dagre-3AP2YEHR-BhgkYe8V.js} +1 -1
- package/web/dist/assets/{diagram-S7CK7UJ4-BTMbx2np.js → diagram-S7CK7UJ4-C_6mvD2V.js} +1 -1
- package/web/dist/assets/{diagram-UQ7AKVKN-UK94_1-P.js → diagram-UQ7AKVKN-qW7tssUW.js} +1 -1
- package/web/dist/assets/{diagram-VSXAHHWV-C7zS-v9v.js → diagram-VSXAHHWV-txJPHpvG.js} +1 -1
- package/web/dist/assets/{diagram-VX7I27RA-DKJRa6CK.js → diagram-VX7I27RA-CQTMsCnL.js} +1 -1
- package/web/dist/assets/{diagram-Z3DM3KII-kUbAfGAp.js → diagram-Z3DM3KII--cb1hoE6.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-PWID7BFC-CVW16_vb.js → ebnfDiagram-PWID7BFC-Da6lDU77.js} +1 -1
- package/web/dist/assets/{erDiagram-SSCWMZ5O-BRQMFQ5y.js → erDiagram-SSCWMZ5O-DGCMrsQn.js} +1 -1
- package/web/dist/assets/{flowDiagram-A5DVABFB-S34LpLtK.js → flowDiagram-A5DVABFB-GdOL_qno.js} +1 -1
- package/web/dist/assets/{ganttDiagram-EL5Y4UJY-BFiqk1-G.js → ganttDiagram-EL5Y4UJY-Cx1F_jBK.js} +4 -4
- package/web/dist/assets/{gitGraphDiagram-WWUBYQGX-LZsrdyyv.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-0DvN6KIp.js → infoDiagram-RXCK75RN-CjvKpSPA.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-5VMMS53U-CaY3A-Bx.js → ishikawaDiagram-5VMMS53U-CxLoAPhx.js} +1 -1
- package/web/dist/assets/{journeyDiagram-EYS64GPL-BBeiaCrj.js → journeyDiagram-EYS64GPL-BfvS1upJ.js} +1 -1
- package/web/dist/assets/{kanban-definition-3QL26DDD-ux42DRXQ.js → kanban-definition-3QL26DDD-DKPPu0w4.js} +1 -1
- package/web/dist/assets/{layout-CnXiF_NB.js → layout-Dhgck-Zo.js} +1 -1
- package/web/dist/assets/{linear-BMW0u6G6.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-hUxSpdKn.js → mindmap-definition-FBJOCRG2-CalfL_Kn.js} +1 -1
- package/web/dist/assets/{pegDiagram-XKGWAZYB-aCaQPH0w.js → pegDiagram-XKGWAZYB-D9mVWz7l.js} +1 -1
- package/web/dist/assets/pending-edit-apply-kwMFWsUe.js +31 -0
- package/web/dist/assets/{pieDiagram-E7YTZNPT-CmOEZ8zg.js → pieDiagram-E7YTZNPT-DELzbZNS.js} +1 -1
- package/web/dist/assets/{quadrantDiagram-AXDQQJYC-VW6fAhsV.js → quadrantDiagram-AXDQQJYC-TZqp4mfA.js} +1 -1
- package/web/dist/assets/{railroadDiagram-O6MQD6OU-CKJ7Ba5K.js → railroadDiagram-O6MQD6OU-DxX59f0i.js} +1 -1
- package/web/dist/assets/{requirementDiagram-EFPCY7ZU-pNGkq9Vw.js → requirementDiagram-EFPCY7ZU-DFdsJLIe.js} +1 -1
- package/web/dist/assets/{rotate-cw-KAfChbGV.js → rotate-cw-62liz7LX.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-P5KCCOFB-BIMmDTfZ.js → sankeyDiagram-P5KCCOFB-ByYcd3fG.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-WJ2MYXX4-BBW7wzKY.js → sequenceDiagram-WJ2MYXX4-Bj-ml1jS.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-DdkXmLYR.js → sizeCapture-X5ZJPWSS-KhsXThyD.js} +1 -1
- package/web/dist/assets/{square-dashed-mouse-pointer-CDUlhUP7.js → square-dashed-mouse-pointer-D_VrqVpy.js} +1 -1
- package/web/dist/assets/{stateDiagram-HBIQ2CUA-W313Uz16.js → stateDiagram-HBIQ2CUA-BJlpHfWU.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-4QOOHH4V-CR2kYAH2.js +1 -0
- package/web/dist/assets/{swimlanes-XN3QIQJK-C29TL26K.js → swimlanes-XN3QIQJK-DUB9OJVs.js} +1 -1
- package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BitOxnG2.js +8 -0
- package/web/dist/assets/{timeline-definition-24CTP7MA-CF0JMObX.js → timeline-definition-24CTP7MA-C03NX82A.js} +1 -1
- package/web/dist/assets/{vennDiagram-4TSXK5OY-DrDFsaH3.js → vennDiagram-4TSXK5OY-B9pjzngp.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-VM6X3IG4-CVrnRIS_.js → wardleyDiagram-VM6X3IG4-UqWq_mcT.js} +1 -1
- package/web/dist/assets/{xychartDiagram-S5SC5T6Z-BvkN1zsT.js → xychartDiagram-S5SC5T6Z-D2pDKtOv.js} +1 -1
- package/web/dist/index.html +2 -2
- package/server/api/memory.js +0 -185
- package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +0 -583
- package/server/engine/mcp/tools/arrange-on-board.js +0 -124
- 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/record-decision.js +0 -140
- package/server/engine/mcp/tools/relate-on-board.js +0 -112
- package/server/engine/mcp/tools/sketch-on-board.js +0 -337
- 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-BpLv5082.js +0 -16
- package/web/dist/assets/channel-DGkccMO1.js +0 -1
- package/web/dist/assets/classDiagram-DTDB5LWJ-BGscw3OD.js +0 -1
- package/web/dist/assets/classDiagram-v2-JRS7N3AN-BGscw3OD.js +0 -1
- package/web/dist/assets/index-C0KUf6Wd.css +0 -1
- package/web/dist/assets/index-GAamh1p4.js +0 -2152
- 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-AcdOQ9Ij.js +0 -31
- package/web/dist/assets/stateDiagram-v2-4QOOHH4V-DDhJBZzf.js +0 -1
- package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BfN-Xdmu.js +0 -8
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* mcp/tools/arrange-on-board.js —— arrange_on_board(2026-08-14,agent 摆位批·写侧)
|
|
3
|
-
*
|
|
4
|
-
* 语义摆位,**不给裸坐标**(用户拍板):agent 说的是"把 A 摆到 B 旁边"这种
|
|
5
|
-
* 人话,坐标由这里算出来、写进跟用户拖拽**同一个 layout 权威**(board.objects
|
|
6
|
-
* 的 x/y)—— 布局引擎、用户、agent 三方从此不打架。撞上已有的卡就往下让
|
|
7
|
-
* (跟入座避让同一个精神),落盘带 by:'agent' 出处。
|
|
8
|
-
*
|
|
9
|
-
* feature/unfeature 立/撤主角:写 board.hero,前端 pickHero 的推断被它压过。
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { tool } from '@anthropic-ai/claude-agent-sdk';
|
|
13
|
-
import { z } from 'zod';
|
|
14
|
-
import { readBoard, patchBoard } from '../../../projects/board-store.js';
|
|
15
|
-
import { estimateSizeOn } from '../../../lib/board-kind-sizes.js';
|
|
16
|
-
import { normalizeCanvasId, layerOf } from '../../../lib/canvas-id.js';
|
|
17
|
-
|
|
18
|
-
const GAP_X = 24;
|
|
19
|
-
const GAP_Y = 16;
|
|
20
|
-
|
|
21
|
-
const hit = (a, b) => !(a.x + a.w <= b.x || b.x + b.w <= a.x || a.y + a.h <= b.y || b.y + b.h <= a.y);
|
|
22
|
-
|
|
23
|
-
/** 撞卡往下让:同一层的既有条目当障碍(subject 自己除外) */
|
|
24
|
-
function nudgeDown(pos, size, obstacles) {
|
|
25
|
-
const r = { ...pos, ...size };
|
|
26
|
-
for (let guard = 0; guard < 60; guard += 1) {
|
|
27
|
-
const blocker = obstacles.find(o => hit(r, o));
|
|
28
|
-
if (!blocker) return { x: r.x, y: r.y };
|
|
29
|
-
r.y = blocker.y + blocker.h + GAP_Y;
|
|
30
|
-
}
|
|
31
|
-
return { x: r.x, y: r.y };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function makeArrangeOnBoardTool({ projectId, ctx }) {
|
|
35
|
-
return tool(
|
|
36
|
-
'arrange_on_board',
|
|
37
|
-
`Move things on the canvas by MEANING, not pixels. Read the chart first (read_board).
|
|
38
|
-
|
|
39
|
-
Actions:
|
|
40
|
-
- beside: put subject immediately right of anchor (same row)
|
|
41
|
-
- below: put subject under anchor (same column)
|
|
42
|
-
- feature: make subject THE hero of the desktop (rendered one size up); no anchor
|
|
43
|
-
- unfeature: drop the explicit hero, back to automatic judgment; no subject needed
|
|
44
|
-
|
|
45
|
-
Rules the tool enforces: subject and anchor must live in the same folder layer
|
|
46
|
-
(move files with mv first); collisions push the subject downward; your placements
|
|
47
|
-
are marked by:'agent' so the user can tell who seated what. The user can always
|
|
48
|
-
drag things afterwards — their move wins, do not fight it.`,
|
|
49
|
-
{
|
|
50
|
-
action: z.enum(['beside', 'below', 'feature', 'unfeature'])
|
|
51
|
-
.describe('What to do (see list above)'),
|
|
52
|
-
subject: z.string().min(1).max(300).optional()
|
|
53
|
-
.describe('Canvas id of the thing to move / feature (required except for unfeature)'),
|
|
54
|
-
anchor: z.string().min(1).max(300).optional()
|
|
55
|
-
.describe('Canvas id to place the subject relative to (required for beside/below)'),
|
|
56
|
-
},
|
|
57
|
-
async ({ action, subject: rawSubject, anchor: rawAnchor }) => {
|
|
58
|
-
if (!projectId) {
|
|
59
|
-
return { content: [{ type: 'text', text: 'No project bound.' }], isError: true };
|
|
60
|
-
}
|
|
61
|
-
const err = (t) => ({ content: [{ type: 'text', text: t }], isError: true });
|
|
62
|
-
|
|
63
|
-
if (action === 'unfeature') {
|
|
64
|
-
await patchBoard(projectId, { hero: null });
|
|
65
|
-
try { ctx?.emit?.({ type: 'board.updated', sessionId: null, summary: '撤了显式主角' }); } catch { /* fail-soft */ }
|
|
66
|
-
return { content: [{ type: 'text', text: 'Hero override cleared — automatic judgment resumes.' }] };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const subject = normalizeCanvasId(rawSubject);
|
|
70
|
-
if (!subject) return err('subject id 不合法。');
|
|
71
|
-
|
|
72
|
-
if (action === 'feature') {
|
|
73
|
-
await patchBoard(projectId, { hero: subject });
|
|
74
|
-
try { ctx?.emit?.({ type: 'board.updated', sessionId: null, summary: `立了主角:${subject}` }); } catch { /* fail-soft */ }
|
|
75
|
-
return { content: [{ type: 'text', text: `Featured: ${subject} now renders as the hero.` }] };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const anchor = normalizeCanvasId(rawAnchor);
|
|
79
|
-
if (!anchor) return err('beside/below 需要 anchor。');
|
|
80
|
-
if (anchor === subject) return err('subject 和 anchor 是同一件东西。');
|
|
81
|
-
|
|
82
|
-
const board = await readBoard(projectId);
|
|
83
|
-
const anchorEntry = board.objects?.[anchor];
|
|
84
|
-
if (!anchorEntry || !Number.isFinite(anchorEntry.x)) {
|
|
85
|
-
return err(`锚点 ${anchor} 还没有座位(read_board 里看不到的东西没法当锚)。`);
|
|
86
|
-
}
|
|
87
|
-
const known = new Set(Object.keys(board.zones || {}));
|
|
88
|
-
const subjectEntry = board.objects?.[subject] || null;
|
|
89
|
-
const anchorLayer = layerOf(anchor, anchorEntry, known);
|
|
90
|
-
if (subjectEntry) {
|
|
91
|
-
const subjectLayer = layerOf(subject, subjectEntry, known);
|
|
92
|
-
if (subjectLayer !== anchorLayer) {
|
|
93
|
-
return err(`不同层(${subjectLayer || '根'} vs ${anchorLayer || '根'}):先 mv 文件到同一个文件夹,画布跟着走。`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const aSize = estimateSizeOn(board, anchor, anchorEntry);
|
|
98
|
-
const sSize = estimateSizeOn(board, subject, subjectEntry);
|
|
99
|
-
const want = action === 'beside'
|
|
100
|
-
? { x: Math.round(anchorEntry.x + aSize.w + GAP_X), y: Math.round(anchorEntry.y) }
|
|
101
|
-
: { x: Math.round(anchorEntry.x), y: Math.round(anchorEntry.y + aSize.h + GAP_Y) };
|
|
102
|
-
|
|
103
|
-
const obstacles = Object.entries(board.objects || {})
|
|
104
|
-
.filter(([id, e]) => id !== subject && Number.isFinite(e?.x)
|
|
105
|
-
&& layerOf(id, e, known) === anchorLayer)
|
|
106
|
-
.map(([id, e]) => ({ ...estimateSizeOn(board, id, e), x: e.x, y: e.y }));
|
|
107
|
-
const pos = nudgeDown(want, sSize, obstacles);
|
|
108
|
-
|
|
109
|
-
await patchBoard(projectId, {
|
|
110
|
-
objects: { [subject]: { ...(subjectEntry || {}), x: pos.x, y: pos.y, by: 'agent' } },
|
|
111
|
-
});
|
|
112
|
-
try {
|
|
113
|
-
ctx?.emit?.({ type: 'board.updated', sessionId: null, summary: `摆了 ${subject}` });
|
|
114
|
-
} catch { /* fail-soft */ }
|
|
115
|
-
return {
|
|
116
|
-
content: [{
|
|
117
|
-
type: 'text',
|
|
118
|
-
text: `Placed ${subject} ${action} ${anchor} at (${pos.x},${pos.y})`
|
|
119
|
-
+ (pos.y !== want.y ? ' (nudged down to avoid overlap)' : '') + '.',
|
|
120
|
-
}],
|
|
121
|
-
};
|
|
122
|
-
},
|
|
123
|
-
);
|
|
124
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* mcp/tools/create-on-board.js —— create_on_board(2026-08-14,agent 建元素批)
|
|
3
|
-
*
|
|
4
|
-
* 让 agent 落**用户级的画布原生物件**。v1 只开手写字(text):便签是留档不是
|
|
5
|
-
* 聊天,agent 值得写下来的是"这一版为什么这么改"这类一句话,不是回复的搬运。
|
|
6
|
-
* 涂鸦不开 —— agent 画矢量笔迹没有意义,它要画图走生图。
|
|
7
|
-
*
|
|
8
|
-
* 设计对齐用户侧:
|
|
9
|
-
* - near 锚定 = 用户"标注"的同构(落在锚右侧空白,关系线可选一步带上)——
|
|
10
|
-
* 一次调用 = 一段字 + 一条线,跟 keepAnnotation 落的东西一模一样
|
|
11
|
-
* - 字体/字号走 board-store 同一份白名单(TEXT_FONTS)
|
|
12
|
-
* - 尺寸估算抄前端 handleCreateText 同款公式(约 26 全角字/行)
|
|
13
|
-
* - by:'agent' 出处,用户一眼分得出谁写的
|
|
14
|
-
*
|
|
15
|
-
* 护栏:每回合上限 08-23 按用户意思撤了(只记数);画布不是弹幕区这条写在 prelude 里。
|
|
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 { normalizeCanvasId, layerOf } from '../../../lib/canvas-id.js';
|
|
23
|
-
|
|
24
|
-
const SIZE_PX = { sm: 13, md: 16, lg: 22, xl: 30 };
|
|
25
|
-
|
|
26
|
-
let seq = 0;
|
|
27
|
-
const nextTextId = () => `text:a${Date.now().toString(36)}${(seq++ % 1000).toString(36)}`;
|
|
28
|
-
const nextBindingId = () => `b:a${Date.now().toString(36)}n${(seq++ % 1000).toString(36)}`;
|
|
29
|
-
|
|
30
|
-
/** 前端 handleCreateText 同款身位估算(约 26 全角字/行,行高 1.6) */
|
|
31
|
-
function textBox(t, sizeKey) {
|
|
32
|
-
const px = SIZE_PX[sizeKey] || 16;
|
|
33
|
-
const cols = Math.min(26, Math.max(6, t.length));
|
|
34
|
-
const lines = Math.ceil(t.length / cols) + (t.match(/\n/g)?.length || 0);
|
|
35
|
-
return { w: Math.round(cols * px * 1.05) + 12, h: Math.round(lines * px * 1.6) + 10 };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function makeCreateOnBoardTool({ projectId, ctx }) {
|
|
39
|
-
const turnStamp = { turn: -1, count: 0 };
|
|
40
|
-
return tool(
|
|
41
|
-
'create_on_board',
|
|
42
|
-
`Write a handwritten note directly on the canvas — the same kind the user writes.
|
|
43
|
-
|
|
44
|
-
Use it for remarks worth KEEPING on the board: why a version went this way, what
|
|
45
|
-
to compare, a caption for a group. Not for conversation (that goes in your reply)
|
|
46
|
-
and not for documents (write a .md instead).
|
|
47
|
-
|
|
48
|
-
- near: anchor it beside a thing (and usually pass relation to draw the line —
|
|
49
|
-
that is exactly the user's "annotation" gesture: a note + an annotates edge)
|
|
50
|
-
- omit near: the note lands under the desktop's current content`,
|
|
51
|
-
{
|
|
52
|
-
text: z.string().min(1).max(200).describe('The note text (≤200 chars, one thought)'),
|
|
53
|
-
near: z.string().max(300).optional()
|
|
54
|
-
.describe('Canvas id to place the note next to (right side, auto-avoids overlap)'),
|
|
55
|
-
relation: z.object({
|
|
56
|
-
type: z.enum(['annotates', 'link']).describe('annotates = the note is ABOUT it; link = loosely related'),
|
|
57
|
-
to: z.string().min(1).max(300).describe('Canvas id the line connects to (usually = near)'),
|
|
58
|
-
label: z.string().max(60).optional().describe('Optional words on the line (link only usually)'),
|
|
59
|
-
}).optional().describe('Draw a relation line from the note in the same call'),
|
|
60
|
-
font: z.enum(['pen', 'kai', 'sans', 'serif', 'mono']).optional()
|
|
61
|
-
.describe('Handwriting style (default pen — same as the user\'s default)'),
|
|
62
|
-
size: z.enum(['sm', 'md', 'lg', 'xl']).optional().describe('Text size (default md)'),
|
|
63
|
-
},
|
|
64
|
-
async ({ text, near: rawNear, relation, font, size }) => {
|
|
65
|
-
if (!projectId) {
|
|
66
|
-
return { content: [{ type: 'text', text: 'No project bound.' }], isError: true };
|
|
67
|
-
}
|
|
68
|
-
const err = (t) => ({ content: [{ type: 'text', text: t }], isError: true });
|
|
69
|
-
|
|
70
|
-
// 回合闸 08-23 用户拍板先不设:只记数不拦
|
|
71
|
-
const turn = ctx?.runId ?? -1;
|
|
72
|
-
if (turnStamp.turn !== turn) { turnStamp.turn = turn; turnStamp.count = 0; }
|
|
73
|
-
|
|
74
|
-
const t = String(text).trim();
|
|
75
|
-
if (!t) return err('空字不落板。');
|
|
76
|
-
const sizeKey = SIZE_PX[size] ? size : 'md';
|
|
77
|
-
const box = textBox(t, sizeKey);
|
|
78
|
-
|
|
79
|
-
const board = await readBoard(projectId);
|
|
80
|
-
const known = new Set(Object.keys(board.zones || {}));
|
|
81
|
-
let pos; let zone = '';
|
|
82
|
-
if (rawNear) {
|
|
83
|
-
const near = normalizeCanvasId(rawNear);
|
|
84
|
-
const anchorEntry = near ? board.objects?.[near] : null;
|
|
85
|
-
if (!anchorEntry || !Number.isFinite(anchorEntry.x)) {
|
|
86
|
-
return err(`锚点 ${rawNear} 还没有座位(read_board 里看不到就锚不上)。`);
|
|
87
|
-
}
|
|
88
|
-
zone = layerOf(near, anchorEntry, known);
|
|
89
|
-
const aSize = estimateSizeOn(board, near, anchorEntry);
|
|
90
|
-
pos = { x: Math.round(anchorEntry.x + aSize.w + 24), y: Math.round(anchorEntry.y) };
|
|
91
|
-
// 撞卡往下让(同 arrange 的避让)
|
|
92
|
-
const obstacles = Object.entries(board.objects || {})
|
|
93
|
-
.filter(([id, e]) => Number.isFinite(e?.x) && layerOf(id, e, known) === zone)
|
|
94
|
-
.map(([id, e]) => ({ ...estimateSizeOn(board, id, e), x: e.x, y: e.y }));
|
|
95
|
-
for (let g = 0; g < 40; g += 1) {
|
|
96
|
-
const blocker = obstacles.find(o =>
|
|
97
|
-
!(pos.x + box.w <= o.x || o.x + o.w <= pos.x || pos.y + box.h <= o.y || o.y + o.h <= pos.y));
|
|
98
|
-
if (!blocker) break;
|
|
99
|
-
pos.y = blocker.y + blocker.h + 16;
|
|
100
|
-
}
|
|
101
|
-
} else {
|
|
102
|
-
// 桌面根层内容最低边下面(跟新物件入座同一条起排线精神)
|
|
103
|
-
let bottom = 0;
|
|
104
|
-
for (const [id, e] of Object.entries(board.objects || {})) {
|
|
105
|
-
if (!Number.isFinite(e?.y) || layerOf(id, e, known) !== '') continue;
|
|
106
|
-
bottom = Math.max(bottom, e.y + estimateSizeOn(board, id, e).h);
|
|
107
|
-
}
|
|
108
|
-
for (const zz of Object.values(board.zones || {})) {
|
|
109
|
-
if (Number.isFinite(zz?.y)) bottom = Math.max(bottom, zz.y + 150);
|
|
110
|
-
}
|
|
111
|
-
pos = { x: 10, y: Math.round(bottom) + 16 };
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const textId = nextTextId();
|
|
115
|
-
const patch = {
|
|
116
|
-
objects: {
|
|
117
|
-
[textId]: {
|
|
118
|
-
x: pos.x, y: pos.y, z: 1, w: box.w, h: box.h,
|
|
119
|
-
kind: 'text',
|
|
120
|
-
data: { t, font: TEXT_FONTS.includes(font) ? font : 'pen', size: sizeKey, color: 'ink' },
|
|
121
|
-
zone, by: 'agent',
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
if (relation) {
|
|
126
|
-
const to = normalizeCanvasId(relation.to);
|
|
127
|
-
if (to && to !== textId) {
|
|
128
|
-
patch.bindings = {
|
|
129
|
-
[nextBindingId()]: {
|
|
130
|
-
type: relation.type, from: textId, to, by: 'agent',
|
|
131
|
-
...(relation.label ? { label: relation.label } : {}),
|
|
132
|
-
},
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const saved = await patchBoard(projectId, patch);
|
|
138
|
-
if (!saved.objects?.[textId]) return err('便签被 board 拒了(内容或字段不合法)。');
|
|
139
|
-
turnStamp.count += 1;
|
|
140
|
-
try {
|
|
141
|
-
ctx?.emit?.({ type: 'board.updated', sessionId: null, summary: '写了一条画布便签' });
|
|
142
|
-
} catch { /* fail-soft */ }
|
|
143
|
-
return {
|
|
144
|
-
content: [{
|
|
145
|
-
type: 'text',
|
|
146
|
-
text: `Note on board at (${pos.x},${pos.y})${relation ? ' with a line' : ''} (id: ${textId}).`
|
|
147
|
-
+ '',
|
|
148
|
-
}],
|
|
149
|
-
};
|
|
150
|
-
},
|
|
151
|
-
);
|
|
152
|
-
}
|
|
@@ -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,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* mcp/tools/record-decision.js — record_decision MCP tool
|
|
3
|
-
*
|
|
4
|
-
* agent 在做关键设计决策时调,把"做了什么 + 为什么"写成共享便利贴
|
|
5
|
-
* `notes/决策.md` 的一面(`---` 分面)。2026-07-30 起决策
|
|
6
|
-
* 不再进 spec.json.decisions[]:便利贴是 agent 和用户的**共享层**——
|
|
7
|
-
* 用户在画布上直接看到、能翻、能改;spec.json 只是 agent 自己的暗档案,
|
|
8
|
-
* 用户看不见,头脑风暴根本进不来。旧会话已有的 spec.json decisions[]
|
|
9
|
-
* 仍被 hooks 注入(只读遗产),新决策一律走这里。
|
|
10
|
-
*
|
|
11
|
-
* 便利贴格式约定(前端 NoteSticker 按此渲染):
|
|
12
|
-
* - 一个 .md = 一张贴;`\n---\n` 分面,翻页看
|
|
13
|
-
* - 每面第一行 `# 标题`
|
|
14
|
-
*
|
|
15
|
-
* 落点:工作区 `notes/`(扁平模型,2026-08-08 起)—— 没有"任务归属"了,
|
|
16
|
-
* 便利贴直接住项目桌面。
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { tool } from '@anthropic-ai/claude-agent-sdk';
|
|
20
|
-
import { z } from 'zod';
|
|
21
|
-
import { promises as fs } from 'node:fs';
|
|
22
|
-
import path from 'node:path';
|
|
23
|
-
import { Events } from '../../agent/events.js';
|
|
24
|
-
|
|
25
|
-
const NOTE_FILE = '决策.md';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @param {object} deps
|
|
29
|
-
* @param {string} deps.workspaceRoot
|
|
30
|
-
* @param {string} [deps.sessionId]
|
|
31
|
-
* @param {import('../../agent/context.js').AgentContext} [deps.ctx]
|
|
32
|
-
*/
|
|
33
|
-
export function makeRecordDecisionTool({ workspaceRoot, sessionId, ctx }) {
|
|
34
|
-
return tool(
|
|
35
|
-
'record_decision',
|
|
36
|
-
`Record a key design decision as a face of the project's decision sticky note
|
|
37
|
-
(notes/决策.md). The note is SHARED with the user — it renders as
|
|
38
|
-
a flippable sticky on their canvas, they can read and edit it. Use this to
|
|
39
|
-
capture WHY you made a particular choice — color, type scale, layout metaphor,
|
|
40
|
-
copy strategy — so the intent survives across sessions and the user can push
|
|
41
|
-
back on it.
|
|
42
|
-
|
|
43
|
-
Use this tool when:
|
|
44
|
-
- You make a non-trivial choice that has multiple defensible alternatives
|
|
45
|
-
(e.g., picking a primary color, deciding deck length, choosing a layout)
|
|
46
|
-
- The user gives feedback that changes a previous decision (record both)
|
|
47
|
-
- You want to document a constraint discovered mid-work
|
|
48
|
-
|
|
49
|
-
Do NOT use this tool for:
|
|
50
|
-
- Trivial implementation details (CSS class names, file structure)
|
|
51
|
-
- Things obvious from the canvas itself (the visible design speaks for itself)
|
|
52
|
-
- Every change — over-recording bloats the note and dilutes signal
|
|
53
|
-
|
|
54
|
-
For free-form shared notes (brainstorm material, reference digests, handoff
|
|
55
|
-
context), just Write notes/<slug>.md directly — same sticky-note
|
|
56
|
-
rendering, no tool needed.`,
|
|
57
|
-
{
|
|
58
|
-
title: z
|
|
59
|
-
.string()
|
|
60
|
-
.min(2)
|
|
61
|
-
.max(200)
|
|
62
|
-
.describe('Short decision title (e.g., "Primary color = #3366FF")'),
|
|
63
|
-
rationale: z
|
|
64
|
-
.string()
|
|
65
|
-
.min(2)
|
|
66
|
-
.max(2000)
|
|
67
|
-
.describe('Why this choice — connect to brief / user feedback / design principle'),
|
|
68
|
-
scope: z
|
|
69
|
-
.string()
|
|
70
|
-
.optional()
|
|
71
|
-
.describe('Where it applies (e.g., "Global", "Cover page", "All H1 headings")'),
|
|
72
|
-
alternatives: z
|
|
73
|
-
.array(z.string())
|
|
74
|
-
.optional()
|
|
75
|
-
.describe('Alternatives considered, briefly noting why rejected'),
|
|
76
|
-
},
|
|
77
|
-
async ({ title, rationale, scope, alternatives }) => {
|
|
78
|
-
try {
|
|
79
|
-
if (!workspaceRoot) {
|
|
80
|
-
return {
|
|
81
|
-
content: [{ type: 'text', text: 'No workspace bound; cannot record decision.' }],
|
|
82
|
-
isError: true,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
const alts = Array.isArray(alternatives)
|
|
86
|
-
? alternatives.map(s => String(s).trim()).filter(Boolean) : [];
|
|
87
|
-
const face = [
|
|
88
|
-
`# ${title.trim()}`,
|
|
89
|
-
'',
|
|
90
|
-
rationale.trim(),
|
|
91
|
-
...(scope ? ['', `- 范围:${scope.trim()}`] : []),
|
|
92
|
-
...(alts.length ? [`- 备选:${alts.join(' / ')}`] : []),
|
|
93
|
-
`- ${new Date().toISOString().slice(0, 10)}`,
|
|
94
|
-
].join('\n');
|
|
95
|
-
|
|
96
|
-
const notesDir = path.join(workspaceRoot, 'notes');
|
|
97
|
-
await fs.mkdir(notesDir, { recursive: true });
|
|
98
|
-
const noteFile = path.join(notesDir, NOTE_FILE);
|
|
99
|
-
let prev = '';
|
|
100
|
-
try { prev = await fs.readFile(noteFile, 'utf8'); } catch { /* 首条 */ }
|
|
101
|
-
const next = prev.trim() ? `${prev.replace(/\s+$/, '')}\n\n---\n\n${face}\n` : `${face}\n`;
|
|
102
|
-
await fs.writeFile(noteFile, next, 'utf8');
|
|
103
|
-
|
|
104
|
-
const faceCount = next.split(/\n---\n/).length;
|
|
105
|
-
try {
|
|
106
|
-
// MCP 工具写盘不走 PostToolUse(Write/Edit) 那条 file_changed 直发,
|
|
107
|
-
// 自己补一发,前端便利贴才会当场刷新
|
|
108
|
-
// 工作区相对路径(fileChanged 正字法;曾发绝对路径=前端只吃到清单重拉,
|
|
109
|
-
// 寻址和版本 key 全失配。2026-08-14 普查改)
|
|
110
|
-
ctx?.emit?.(Events.fileChanged(`notes/${NOTE_FILE}`, 'change'));
|
|
111
|
-
ctx?.emit?.({
|
|
112
|
-
type: 'run.decision_recorded',
|
|
113
|
-
title: title.trim(),
|
|
114
|
-
scope: scope?.trim() || null,
|
|
115
|
-
decisionsCount: faceCount,
|
|
116
|
-
});
|
|
117
|
-
} catch { /* emit fail-safe */ }
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
content: [{
|
|
121
|
-
type: 'text',
|
|
122
|
-
// ⚠️ 这里曾写 `tasks/${task}/...` —— 扁平化删掉 task 变量时漏了这行,
|
|
123
|
-
// `task` 成悬空引用:决策照写、事件照发,**返回时 ReferenceError 被
|
|
124
|
-
// 外层 catch 接住**,agent 每次调用都收到报错以为失败(还可能重试出
|
|
125
|
-
// 重复面)。悬空引用潜伏族第二案(第一案 reloadToken,2026-08-08)。
|
|
126
|
-
text: `Decision recorded as face ${faceCount} of notes/${NOTE_FILE}: "${title.trim()}"`,
|
|
127
|
-
}],
|
|
128
|
-
};
|
|
129
|
-
} catch (err) {
|
|
130
|
-
return {
|
|
131
|
-
content: [{
|
|
132
|
-
type: 'text',
|
|
133
|
-
text: `Record decision failed: ${err?.message || String(err)}`,
|
|
134
|
-
}],
|
|
135
|
-
isError: true,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
);
|
|
140
|
-
}
|