pmx-canvas 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/.github/extensions/pmx-canvas/runtime.json +1 -1
  2. package/.github/extensions/pmx-canvas/tools.json +601 -22
  3. package/CHANGELOG.md +49 -0
  4. package/Readme.md +131 -532
  5. package/dist/canvas/global.css +2724 -476
  6. package/dist/canvas/index.js +168 -107
  7. package/dist/canvas/mcp-app.html +2899 -596
  8. package/dist/json-render/index.js +20 -20
  9. package/dist/types/client/canvas/BarHint.d.ts +12 -2
  10. package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -1
  11. package/dist/types/client/canvas/BoardPinButton.d.ts +10 -0
  12. package/dist/types/client/canvas/BoardSwitcher.d.ts +1 -1
  13. package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
  14. package/dist/types/client/canvas/CommandBar.d.ts +3 -4
  15. package/dist/types/client/canvas/ConnectionBanner.d.ts +2 -1
  16. package/dist/types/client/canvas/ContextChip.d.ts +3 -0
  17. package/dist/types/client/canvas/CrossBoardReferences.d.ts +5 -0
  18. package/dist/types/client/canvas/EdgeLayer.d.ts +14 -3
  19. package/dist/types/client/canvas/EmptyState.d.ts +4 -4
  20. package/dist/types/client/canvas/HomeGraphView.d.ts +2 -0
  21. package/dist/types/client/canvas/HomeMapView.d.ts +1 -0
  22. package/dist/types/client/canvas/HomeReviewView.d.ts +6 -0
  23. package/dist/types/client/canvas/HomeView.d.ts +9 -7
  24. package/dist/types/client/canvas/KnowledgeDialog.d.ts +4 -0
  25. package/dist/types/client/canvas/LifecycleChrome.d.ts +5 -0
  26. package/dist/types/client/canvas/LifecycleFields.d.ts +7 -0
  27. package/dist/types/client/canvas/LifecyclePanel.d.ts +1 -0
  28. package/dist/types/client/canvas/MemoryInspector.d.ts +7 -0
  29. package/dist/types/client/canvas/NearPinLayer.d.ts +14 -0
  30. package/dist/types/client/canvas/NodeContextMark.d.ts +40 -7
  31. package/dist/types/client/canvas/RelationPresetsDialog.d.ts +4 -0
  32. package/dist/types/client/canvas/RelationsPanel.d.ts +4 -0
  33. package/dist/types/client/canvas/SessionPanel.d.ts +2 -0
  34. package/dist/types/client/canvas/canvas-area.d.ts +1 -1
  35. package/dist/types/client/canvas/graph-geometry.d.ts +27 -0
  36. package/dist/types/client/canvas/graph-model.d.ts +37 -0
  37. package/dist/types/client/canvas/memory-layout.d.ts +23 -0
  38. package/dist/types/client/canvas/use-chrome-height.d.ts +2 -0
  39. package/dist/types/client/canvas/use-focus-trap.d.ts +1 -0
  40. package/dist/types/client/canvas/use-memory-graph.d.ts +8 -0
  41. package/dist/types/client/canvas/use-pan-zoom.d.ts +4 -1
  42. package/dist/types/client/icons.d.ts +0 -10
  43. package/dist/types/client/nodes/AxStepControls.d.ts +1 -1
  44. package/dist/types/client/nodes/InlineMarkdownEditor.d.ts +4 -4
  45. package/dist/types/client/nodes/MarkdownNode.d.ts +5 -1
  46. package/dist/types/client/nodes/McpAppNode.d.ts +4 -4
  47. package/dist/types/client/nodes/viewer-runtime-state.d.ts +1 -1
  48. package/dist/types/client/state/boards-store.d.ts +81 -9
  49. package/dist/types/client/state/canvas-store.d.ts +14 -1
  50. package/dist/types/client/state/context-chip-store.d.ts +22 -0
  51. package/dist/types/client/state/context-status-store.d.ts +35 -0
  52. package/dist/types/client/state/intent-bridge.d.ts +11 -8
  53. package/dist/types/client/state/knowledge-store.d.ts +22 -0
  54. package/dist/types/client/state/near-pin-store.d.ts +44 -1
  55. package/dist/types/client/state/presence-store.d.ts +4 -4
  56. package/dist/types/client/state/presentation.d.ts +28 -0
  57. package/dist/types/client/state/relation-store.d.ts +10 -0
  58. package/dist/types/client/state/session-store.d.ts +41 -2
  59. package/dist/types/client/state/workspace-settings.d.ts +7 -0
  60. package/dist/types/client/types.d.ts +13 -1
  61. package/dist/types/client/utils/keyboard.d.ts +2 -0
  62. package/dist/types/client/utils/time-ago.d.ts +4 -0
  63. package/dist/types/mcp/context-uri-template.d.ts +3 -1
  64. package/dist/types/server/agent-presence.d.ts +10 -3
  65. package/dist/types/server/ax-context.d.ts +1 -1
  66. package/dist/types/server/canvas-db.d.ts +30 -7
  67. package/dist/types/server/canvas-operations.d.ts +24 -2
  68. package/dist/types/server/canvas-provenance.d.ts +22 -2
  69. package/dist/types/server/canvas-serialization.d.ts +2 -0
  70. package/dist/types/server/canvas-state.d.ts +93 -15
  71. package/dist/types/server/canvas-validation.d.ts +1 -1
  72. package/dist/types/server/context-brief.d.ts +94 -6
  73. package/dist/types/server/context-reads.d.ts +33 -7
  74. package/dist/types/server/edit-summary.d.ts +20 -0
  75. package/dist/types/server/index.d.ts +71 -10
  76. package/dist/types/server/knowledge-lifecycle.d.ts +221 -0
  77. package/dist/types/server/lifecycle-status.d.ts +10 -0
  78. package/dist/types/server/memory-graph.d.ts +9 -0
  79. package/dist/types/server/mutation-history.d.ts +2 -0
  80. package/dist/types/server/operations/ops/ax-shared.d.ts +1 -1
  81. package/dist/types/server/operations/ops/boards.d.ts +2 -1
  82. package/dist/types/server/operations/ops/lifecycle.d.ts +2 -0
  83. package/dist/types/server/operations/ops/memory-graph.d.ts +2 -0
  84. package/dist/types/server/operations/ops/nodes.d.ts +5 -0
  85. package/dist/types/server/operations/ops/query.d.ts +23 -0
  86. package/dist/types/server/operations/registry.d.ts +1 -0
  87. package/dist/types/server/placement.d.ts +7 -0
  88. package/dist/types/server/scope-fence.d.ts +2 -1
  89. package/dist/types/server/spatial-analysis.d.ts +6 -3
  90. package/dist/types/server/tour-control.d.ts +32 -0
  91. package/dist/types/server/wiki-links.d.ts +14 -0
  92. package/dist/types/server/workbench-auth.d.ts +2 -0
  93. package/dist/types/shared/agent-context.d.ts +2 -0
  94. package/dist/types/shared/agent-presence.d.ts +15 -12
  95. package/dist/types/shared/auto-arrange.d.ts +1 -0
  96. package/dist/types/shared/boards.d.ts +4 -4
  97. package/dist/types/shared/knowledge-lifecycle.d.ts +41 -0
  98. package/dist/types/shared/knowledge-presets.d.ts +6 -0
  99. package/dist/types/shared/lifecycle-status.d.ts +24 -0
  100. package/dist/types/shared/memory-graph.d.ts +53 -0
  101. package/dist/types/shared/relations.d.ts +68 -0
  102. package/dist/types/shared/semantic-attention.d.ts +2 -0
  103. package/dist/types/shared/tour.d.ts +38 -0
  104. package/dist/types/shared/workspace-settings.d.ts +11 -0
  105. package/docs/RELEASE.md +21 -3
  106. package/docs/adr-001-bun-only-runtime.md +7 -1
  107. package/docs/adr-002-shared-product-separate-runtimes.md +93 -0
  108. package/docs/api-reference.md +109 -0
  109. package/docs/ax-host-adapter-contract.md +1 -1
  110. package/docs/ax-state-contract.md +5 -4
  111. package/docs/cli.md +153 -15
  112. package/docs/demos/pmx-canvas-demo.mp4 +0 -0
  113. package/docs/design/AgentContext.dc.html +3 -3
  114. package/docs/design/BoardPins.dc.html +37 -22
  115. package/docs/design/Categories.dc.html +208 -0
  116. package/docs/design/CategoryEditor.dc.html +33 -0
  117. package/docs/design/Chrome.dc.html +52 -9
  118. package/docs/design/ContextChip.dc.html +224 -0
  119. package/docs/design/ContextChip600.dc.html +136 -0
  120. package/docs/design/EmptyState.dc.html +45 -0
  121. package/docs/design/Flows.dc.html +23 -1
  122. package/docs/design/HeaderMarks.dc.html +183 -0
  123. package/docs/design/Home.dc.html +19 -1
  124. package/docs/design/HomeGraph.dc.html +86 -202
  125. package/docs/design/HomeMap.dc.html +23 -1
  126. package/docs/design/HomeTeam.dc.html +32 -14
  127. package/docs/design/Lifecycle.dc.html +22 -0
  128. package/docs/design/Lifecycle600.dc.html +18 -0
  129. package/docs/design/LifecycleVariants.dc.html +157 -0
  130. package/docs/design/LinkAuthorship.dc.html +139 -0
  131. package/docs/design/LinksChanged.dc.html +203 -0
  132. package/docs/design/LinksOptions.dc.html +137 -0
  133. package/docs/design/LinksOptions600.dc.html +134 -0
  134. package/docs/design/NearPin.dc.html +9 -10
  135. package/docs/design/Pane600.dc.html +4 -3
  136. package/docs/design/Relations.dc.html +3 -2
  137. package/docs/design/Restore.dc.html +152 -0
  138. package/docs/design/SeeChange.dc.html +267 -0
  139. package/docs/design/Uses.dc.html +12 -6
  140. package/docs/design/canvas.json +146 -19
  141. package/docs/design.md +449 -16
  142. package/docs/environment.md +0 -1
  143. package/docs/evals/curation-effect.md +128 -0
  144. package/docs/http-api.md +459 -37
  145. package/docs/mcp.md +146 -18
  146. package/docs/node-types.md +58 -3
  147. package/docs/plans/design-gaps-2026-10-08.md +53 -0
  148. package/docs/plans/plan-009-tech-debt-backlog.md +379 -0
  149. package/docs/plans/plan-016-context-made-visible-wave-1.md +150 -5
  150. package/docs/plans/plan-017-knowledge-categories-and-lifecycle.md +175 -0
  151. package/docs/plans/plan-018-connected-memory-wave-2.md +594 -0
  152. package/docs/plans/plan-019-post-0.9-roadmap.md +198 -0
  153. package/docs/product-context-vision-2026-09.md +17 -1
  154. package/docs/product-vision-2026-09.md +1082 -43
  155. package/docs/product-vision-review-2026-09.md +13 -1
  156. package/docs/screenshots/workbench-live-dark.png +0 -0
  157. package/docs/screenshots/workbench-live-light.png +0 -0
  158. package/docs/sdk.md +62 -2
  159. package/package.json +1 -1
  160. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
  161. package/plugins/pmx-canvas/plugin.json +1 -1
  162. package/plugins/pmx-canvas/runtime.json +1 -1
  163. package/plugins/pmx-canvas/tools.json +601 -22
  164. package/skills/pmx-canvas/SKILL.md +216 -42
  165. package/skills/pmx-canvas/references/full-reference.md +120 -21
  166. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +2 -2
  167. package/skills/pmx-canvas/references/importing-context.md +132 -0
  168. package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
  169. package/skills/pmx-canvas/references/tours-and-recording.md +16 -1
  170. package/skills/pmx-canvas-testing/SKILL.md +20 -0
  171. package/src/cli/agent.ts +2 -0
  172. package/src/cli/commands/ax.ts +12 -0
  173. package/src/cli/commands/boards.ts +87 -11
  174. package/src/cli/commands/edges.ts +28 -0
  175. package/src/cli/commands/groups.ts +7 -0
  176. package/src/cli/commands/nodes.ts +1 -0
  177. package/src/cli/commands/query.ts +34 -2
  178. package/src/cli/commands/record.ts +199 -48
  179. package/src/cli/index.ts +6 -1
  180. package/src/client/App.tsx +177 -70
  181. package/src/client/canvas/AttentionHistory.tsx +0 -2
  182. package/src/client/canvas/BarHint.tsx +40 -2
  183. package/src/client/canvas/BoardCopyDialog.tsx +1 -1
  184. package/src/client/canvas/BoardFolderDialog.tsx +7 -7
  185. package/src/client/canvas/BoardLinkDialog.tsx +138 -33
  186. package/src/client/canvas/BoardPinButton.tsx +33 -0
  187. package/src/client/canvas/BoardSwitcher.tsx +26 -40
  188. package/src/client/canvas/CanvasNode.tsx +360 -237
  189. package/src/client/canvas/CanvasViewport.tsx +45 -7
  190. package/src/client/canvas/CommandBar.tsx +29 -23
  191. package/src/client/canvas/CommandPalette.tsx +5 -12
  192. package/src/client/canvas/ConnectionBanner.tsx +3 -3
  193. package/src/client/canvas/ContextChip.tsx +296 -0
  194. package/src/client/canvas/ContextMenu.tsx +63 -5
  195. package/src/client/canvas/CrossBoardReferences.tsx +60 -0
  196. package/src/client/canvas/EdgeLayer.tsx +339 -76
  197. package/src/client/canvas/EmptyState.tsx +33 -18
  198. package/src/client/canvas/ExpandedNodeOverlay.tsx +72 -40
  199. package/src/client/canvas/ExternalWriters.tsx +57 -42
  200. package/src/client/canvas/HomeGraphView.tsx +942 -0
  201. package/src/client/canvas/HomeMapView.tsx +641 -0
  202. package/src/client/canvas/HomeReviewView.tsx +167 -0
  203. package/src/client/canvas/HomeView.tsx +687 -160
  204. package/src/client/canvas/IntentLayer.tsx +19 -12
  205. package/src/client/canvas/KnowledgeDialog.tsx +488 -0
  206. package/src/client/canvas/LifecycleChrome.tsx +133 -0
  207. package/src/client/canvas/LifecycleFields.tsx +91 -0
  208. package/src/client/canvas/LifecyclePanel.tsx +533 -0
  209. package/src/client/canvas/MemoryInspector.tsx +104 -0
  210. package/src/client/canvas/Minimap.tsx +3 -3
  211. package/src/client/canvas/NearPinLayer.tsx +187 -0
  212. package/src/client/canvas/NodeContextMark.tsx +175 -97
  213. package/src/client/canvas/Presentation.tsx +23 -57
  214. package/src/client/canvas/RelationPresetsDialog.tsx +413 -0
  215. package/src/client/canvas/RelationsPanel.tsx +82 -0
  216. package/src/client/canvas/SelectionBar.tsx +3 -6
  217. package/src/client/canvas/SessionPanel.tsx +90 -50
  218. package/src/client/canvas/SessionReceipt.tsx +311 -39
  219. package/src/client/canvas/ShortcutOverlay.tsx +1 -1
  220. package/src/client/canvas/SnapshotPanel.tsx +27 -36
  221. package/src/client/canvas/ToolRail.tsx +367 -192
  222. package/src/client/canvas/TopBar.tsx +354 -124
  223. package/src/client/canvas/canvas-area.ts +10 -3
  224. package/src/client/canvas/graph-geometry.ts +195 -0
  225. package/src/client/canvas/graph-model.ts +254 -0
  226. package/src/client/canvas/memory-layout.ts +106 -0
  227. package/src/client/canvas/use-chrome-height.ts +20 -0
  228. package/src/client/canvas/use-focus-trap.ts +31 -5
  229. package/src/client/canvas/use-memory-graph.ts +58 -0
  230. package/src/client/canvas/use-node-drag.ts +2 -1
  231. package/src/client/canvas/use-pan-zoom.ts +64 -7
  232. package/src/client/icons.tsx +0 -63
  233. package/src/client/mcp-app/style.css +0 -6
  234. package/src/client/nodes/AxStepControls.tsx +74 -50
  235. package/src/client/nodes/BoardNode.tsx +5 -1
  236. package/src/client/nodes/ContextNode.tsx +18 -11
  237. package/src/client/nodes/ExtAppFrame.tsx +0 -1
  238. package/src/client/nodes/FileNode.tsx +46 -42
  239. package/src/client/nodes/HtmlNode.tsx +32 -0
  240. package/src/client/nodes/ImageNode.tsx +1 -1
  241. package/src/client/nodes/InlineFormatBar.tsx +6 -9
  242. package/src/client/nodes/InlineMarkdownEditor.tsx +56 -17
  243. package/src/client/nodes/MarkdownNode.tsx +377 -109
  244. package/src/client/nodes/McpAppNode.tsx +50 -20
  245. package/src/client/nodes/MdFormatBar.tsx +21 -18
  246. package/src/client/nodes/PromptNode.tsx +1 -1
  247. package/src/client/nodes/ResponseNode.tsx +1 -1
  248. package/src/client/nodes/viewer-runtime-state.ts +54 -22
  249. package/src/client/state/attention-bridge.ts +64 -15
  250. package/src/client/state/boards-store.ts +193 -39
  251. package/src/client/state/canvas-store.ts +99 -12
  252. package/src/client/state/context-chip-store.ts +70 -0
  253. package/src/client/state/context-status-store.ts +53 -0
  254. package/src/client/state/intent-bridge.ts +51 -30
  255. package/src/client/state/knowledge-store.ts +43 -0
  256. package/src/client/state/near-pin-store.ts +115 -6
  257. package/src/client/state/presence-store.ts +7 -10
  258. package/src/client/state/presentation.ts +91 -0
  259. package/src/client/state/relation-store.ts +81 -0
  260. package/src/client/state/session-store.ts +144 -18
  261. package/src/client/state/sse-bridge.ts +206 -29
  262. package/src/client/state/workspace-settings.ts +11 -0
  263. package/src/client/theme/global.css +2724 -476
  264. package/src/client/types.ts +12 -0
  265. package/src/client/utils/keyboard.ts +10 -0
  266. package/src/client/utils/time-ago.ts +14 -0
  267. package/src/json-render/renderer/index.tsx +31 -0
  268. package/src/mcp/canvas-app.ts +12 -2
  269. package/src/mcp/context-uri-template.ts +7 -4
  270. package/src/mcp/server.ts +63 -41
  271. package/src/mcp/workbench-app.ts +39 -6
  272. package/src/server/agent-presence.ts +128 -28
  273. package/src/server/ax-context.ts +27 -13
  274. package/src/server/board-export.ts +12 -1
  275. package/src/server/canvas-db.ts +331 -42
  276. package/src/server/canvas-operations.ts +195 -83
  277. package/src/server/canvas-provenance.ts +53 -22
  278. package/src/server/canvas-serialization.ts +2 -0
  279. package/src/server/canvas-state.ts +950 -122
  280. package/src/server/canvas-validation.ts +9 -2
  281. package/src/server/context-brief.ts +345 -38
  282. package/src/server/context-reads.ts +178 -30
  283. package/src/server/demo-state.json +355 -24
  284. package/src/server/edit-summary.ts +74 -0
  285. package/src/server/html-surface.ts +8 -1
  286. package/src/server/index.ts +181 -11
  287. package/src/server/knowledge-lifecycle.ts +304 -0
  288. package/src/server/lifecycle-status.ts +232 -0
  289. package/src/server/memory-graph.ts +166 -0
  290. package/src/server/mutation-history.ts +18 -6
  291. package/src/server/operations/composites.ts +32 -11
  292. package/src/server/operations/http.ts +7 -2
  293. package/src/server/operations/index.ts +4 -0
  294. package/src/server/operations/mcp.ts +26 -1
  295. package/src/server/operations/ops/ax-presence.ts +1 -1
  296. package/src/server/operations/ops/ax-read.ts +57 -6
  297. package/src/server/operations/ops/ax-timeline.ts +28 -2
  298. package/src/server/operations/ops/backup.ts +5 -3
  299. package/src/server/operations/ops/batch.ts +35 -4
  300. package/src/server/operations/ops/boards.ts +188 -47
  301. package/src/server/operations/ops/canvas-wire.ts +31 -1
  302. package/src/server/operations/ops/edges.ts +36 -2
  303. package/src/server/operations/ops/groups.ts +6 -1
  304. package/src/server/operations/ops/lifecycle.ts +262 -0
  305. package/src/server/operations/ops/memory-graph.ts +45 -0
  306. package/src/server/operations/ops/nodes.ts +107 -24
  307. package/src/server/operations/ops/query.ts +281 -68
  308. package/src/server/operations/ops/snapshots.ts +73 -1
  309. package/src/server/operations/ops/validate.ts +5 -1
  310. package/src/server/operations/ops/viewport.ts +97 -3
  311. package/src/server/operations/registry.ts +74 -25
  312. package/src/server/placement.ts +29 -0
  313. package/src/server/scope-fence.ts +78 -6
  314. package/src/server/server.ts +115 -16
  315. package/src/server/spatial-analysis.ts +48 -23
  316. package/src/server/tour-control.ts +82 -0
  317. package/src/server/wiki-links.ts +137 -0
  318. package/src/server/workbench-auth.ts +11 -0
  319. package/src/shared/agent-context.ts +3 -0
  320. package/src/shared/agent-presence.ts +15 -15
  321. package/src/shared/auto-arrange.ts +2 -0
  322. package/src/shared/boards.ts +4 -4
  323. package/src/shared/knowledge-lifecycle.ts +22 -0
  324. package/src/shared/knowledge-presets.ts +80 -0
  325. package/src/shared/lifecycle-status.ts +26 -0
  326. package/src/shared/memory-graph.ts +74 -0
  327. package/src/shared/relations.ts +98 -0
  328. package/src/shared/semantic-attention.ts +15 -1
  329. package/src/shared/tour.ts +23 -5
  330. package/src/shared/workspace-settings.ts +13 -0
  331. package/dist/types/client/canvas/ContextPinBar.d.ts +0 -1
  332. package/dist/types/client/canvas/FocusFieldLayer.d.ts +0 -1
  333. package/dist/types/client/canvas/kind-colors.d.ts +0 -3
  334. package/dist/types/server/agent-context.d.ts +0 -22
  335. package/src/client/canvas/ContextPinBar.tsx +0 -25
@@ -47,8 +47,8 @@ panel.
47
47
 
48
48
  The extension also reports Copilot's real context window: the SDK's `session.usage_info`
49
49
  event (`currentTokens` / `tokenLimit`, root agent only) is posted to the presence as
50
- `contextUsage`, so the board's top-bar meter reads **Context** with the live numbers rather
51
- than the pinned-context estimate (**Pins**).
50
+ `contextUsage`, so the board's context panel names how many tokens Copilot's whole context holds,
51
+ next to what the brief costs.
52
52
 
53
53
  The extension attaches a Copilot session to the canvas when it joins the host session, flips the
54
54
  phase to `thinking` on every submitted prompt, and detaches on exit. While attached the workbench
@@ -0,0 +1,132 @@
1
+ # Importing context
2
+
3
+ Use this workflow when bringing existing context into PMX Canvas from folders, repositories,
4
+ documents, notes, backlogs, structured exports or knowledge bases. It guides organization and
5
+ verification, not a universal file converter or live sync. PDF/Office attachments still require
6
+ the human-consented extraction and review workflow; this guide does not bypass it.
7
+
8
+ ## Inspect the source before choosing the structure
9
+
10
+ Inventory the requested scope, file types, existing categories, references and assets. Read
11
+ representative content before mapping it: folder names alone do not establish board boundaries,
12
+ and files may contain several independent items or one argument that should stay together.
13
+ Identify duplicates, historical material, unsupported formats and inaccessible sources. Report
14
+ gaps rather than silently skipping them or claiming extraction succeeded. Keep private context
15
+ within the authorized destination; a folder import is not permission to include credentials.
16
+
17
+ ## Choose boards and cards by purpose
18
+
19
+ Categories organize boards; boards hold coherent topics or workflows; cards hold useful content.
20
+ Preserve useful categories and folder context without mechanically copying the directory tree.
21
+ A project folder may become an overview board, a backlog with item cards, and a research board.
22
+ Several small files may belong on one board; a single export may need several boards. Choose
23
+ cards by useful units of meaning, not automatically one per file, heading or data row. Retain
24
+ section context, nested details and useful source ordering when splitting content. These are
25
+ examples, not a fixed taxonomy. Explain consequential grouping choices and make them easy to revise.
26
+ Several boards in one category are fine. Keep an argument together when splitting would lose
27
+ meaning. Group related cards and use the main skill's README and focus guidance for a readable start.
28
+
29
+ Lead each card with the task, item, decision or document section the reader came to find.
30
+ Retain ancestor headings, folder/repository context and source details below that content,
31
+ not as repeated preambles that push every useful item below the card's visible area. Preserve
32
+ meaning and source hierarchy without making the reader expand every card to identify it.
33
+
34
+ ## Preserve sources and stable targets
35
+
36
+ Work in an isolated workspace for a test import. Preserve an existing import before replacing
37
+ it, and leave source material unchanged unless editing it was requested. Record source paths,
38
+ URLs or record IDs, revisions where available, and the source-item/section → board/card ID
39
+ mapping so interruptions and retries can reuse created objects instead of duplicating them.
40
+ Distinguish faithful imports from summaries; keep a path back to the original. Capture the active
41
+ board before writes, separately from any explicit destination. Create destination boards and
42
+ cards before rewriting internal references, use returned IDs rather than guessed titles, and
43
+ preserve external links.
44
+
45
+ Create supported static cards, groups and edges on an inactive board with `destinationBoardId`
46
+ without switching the human's view. Static nodes are Markdown, status, context, ledger, trace,
47
+ diff, Mermaid and plain HTML; images, runtime-backed nodes, generated primitives and intents
48
+ are not supported by this path. Updates/removals still require the active board. In a named
49
+ `canvas_batch`, each operation's `args` carries its destination and retry fields; group children
50
+ and edge endpoints can use the existing named references. `toBoardId` independently chooses a
51
+ remote edge endpoint, not the board that owns its source.
52
+
53
+ Assign a stable `requestId` to each logical static node/group/edge creation and retain it in
54
+ the source-to-canvas mapping. Retry with the same input and destination: the durable receipt
55
+ returns the original result after interruption or restart, even if the object was subsequently
56
+ edited or deleted. It does not restore deleted objects. Changed-input reuse conflicts; a new
57
+ intentional creation gets a new ID even when its content is identical. Batch operations commit
58
+ in sequence, not as one all-or-nothing import. Persist the mapping as receipts arrive.
59
+
60
+ `expectedBoardId` guards the active board even with an explicit destination. A stale guard
61
+ means refresh context, not substitute the destination ID or switch the human's view to retry.
62
+ For active-only work, explicitly open the intended board and verify its returned ID first.
63
+ Scope fences and human edit locks remain enforced. If Home is active, list/open a suitable
64
+ board before reading board context; library discovery and background static creation can
65
+ stay on Home. For retrieval, `canvas_query` search accepts intersecting `boardId` and `folder`
66
+ filters. Folder includes slash-boundary descendants; `folder: ""` selects unfiled boards.
67
+ Filters default omitted scope to library, while explicit active scope never widens. Search
68
+ never changes the active board.
69
+
70
+ Use native `references` edges for explicit source links and qualified remote endpoints for
71
+ cross-board cards. Add semantic relations only when the source supports them; record the
72
+ supporting passage or source reference in `reason`. Similar subjects alone do not establish
73
+ dependency, causation or supersession. Follow the main skill's direction rules for dependency kinds.
74
+
75
+ Preserve historical/draft status and source evidence dates. Importing, reading or rendering
76
+ content does not revalidate it. Flag source contradictions separately from conversion errors
77
+ and product bugs; do not silently rewrite uncertain facts or invent validation dates.
78
+
79
+ ## Markdown link conversion
80
+
81
+ For content represented as Markdown, use returned IDs in `[[board-id#card-id|label]]` links
82
+ and keep code spans/fences unchanged. In table cells, escape the alias pipe:
83
+ `[[board-id#card-id\|label]]`. Otherwise the table parser can split the link before the wiki
84
+ link parser sees it. Zero unresolved links is not sufficient: compare expected source link
85
+ occurrences with rendered anchors. This is a Markdown rendering concern, not a source-format requirement.
86
+
87
+ ## Keep picture bytes out of long Markdown
88
+
89
+ Prefer local asset references over large base64 image strings embedded in Markdown. The
90
+ embedded bytes enlarge layout/render requests; inline pictures remain supported. Choosing
91
+ file-backed assets is a representation choice, not proof that a parser or loading bug is fixed.
92
+ Keep private pictures in the authorized workspace; do not upload them to a public image host.
93
+
94
+ One supported approach is a file-backed image card in the document's board:
95
+
96
+ ```json
97
+ {"action":"add","type":"image","content":"assets/diagram.png","title":"System diagram"}
98
+ ```
99
+
100
+ Call `canvas_node` with that payload, then use the returned ID in the document:
101
+ `![System diagram](/api/canvas/image/<image-node-id>)`. Keep the original alt text and connect
102
+ the document to its figure card when useful. This route resolves nodes on the active board:
103
+ keep the image card in the same board, and recheck references after copying or moving cards.
104
+
105
+ Relative image creation paths resolve against the canvas workspace root, not the server's
106
+ launch directory. An absolute path inside that workspace also works. Image creation requires
107
+ opening its destination board; do not send `destinationBoardId` or `requestId` for image nodes.
108
+ The database stores the path, not the file bytes: retain the assets alongside it, update paths
109
+ if relocating the workspace, and verify images in exports rather than assuming the live URL
110
+ is portable.
111
+
112
+ ## Verify the imported workspace, not just the API receipts
113
+
114
+ 1. Account for each in-scope source item as imported, summarized, intentionally excluded or
115
+ unresolved. Reconcile split items, references and assets with the mapping. Check edge endpoints,
116
+ group containment and collisions with `canvas_query { action: "validate" }`.
117
+ 2. Open representative boards for the content actually imported: for example a dense backlog,
118
+ long document, linked table or picture-heavy board. Wait for the correct board and populated
119
+ card bodies, not only titles or HTTP success. Images must load with nonzero natural dimensions.
120
+ 3. Wait for the intended card/group bounding rectangle after a camera update. Two animation
121
+ frames do not prove an asynchronous layout update arrived. Inspect screenshots at the
122
+ intended viewing zoom for readable cards, relations, labels and unobscured controls.
123
+ 4. Click local and cross-board links; verify the intended card becomes visible. On a disposable
124
+ test service, restart and verify persistence, including pictures. Compare asset bytes when
125
+ changing image representation to confirm originals were preserved.
126
+ 5. If loading is slow, measure board-open to rendered-content readiness on repeated equivalent
127
+ workloads. Include a text-only control; record payload sizes, cache conditions and whether
128
+ measurements used a local connection or the user's host. Separate observations from causes.
129
+
130
+ Report coverage, unresolved issues and storage requirements. Distinguish an import workaround
131
+ from a product fix, and a source snapshot from live synchronization. Keep private source paths,
132
+ names and content out of reusable skills and public issue reports.
@@ -4,7 +4,7 @@ Use this reference when the `pmx-canvas` skill is installed but the `pmx-canvas`
4
4
 
5
5
  ## Prerequisites and version
6
6
 
7
- The examples pin **PMX Canvas 0.8.2**, which requires **Bun >=1.4.2** even when
7
+ The examples pin **PMX Canvas 0.9.0**, which requires **Bun >=1.4.2** even when
8
8
  installed through npm: the CLI runs through Bun, not Node. Check `bun --version`
9
9
  first. If missing or older, follow the [Bun installation instructions](https://bun.sh/docs/installation)
10
10
  with the user's authorization. Ensure Bun is on the MCP host's PATH as well as the
@@ -17,11 +17,11 @@ in `SKILL.md`. Do not pair newer checkout guidance with an older runtime unknowi
17
17
  ## Install from npm
18
18
 
19
19
  ```bash
20
- npm install -g pmx-canvas@0.8.2
20
+ npm install -g pmx-canvas@0.9.0
21
21
  pmx-canvas --version
22
22
  ```
23
23
 
24
- Without a global install, use `bunx pmx-canvas@0.8.2` in place of `pmx-canvas` in
24
+ Without a global install, use `bunx pmx-canvas@0.9.0` in place of `pmx-canvas` in
25
25
  the commands below. Do not use unpinned `bunx pmx-canvas` for a version-specific check.
26
26
 
27
27
  ## Choose the workspace and target
@@ -91,7 +91,7 @@ discards empty env values, remove `PMX_CANVAS_URL` from its launch environment i
91
91
  "mcpServers": {
92
92
  "canvas": {
93
93
  "command": "bunx",
94
- "args": ["pmx-canvas@0.8.2", "--mcp"],
94
+ "args": ["pmx-canvas@0.9.0", "--mcp"],
95
95
  "env": {
96
96
  "PMX_CANVAS_WORKSPACE_ROOT": "/absolute/path/to/project",
97
97
  "PMX_CANVAS_PORT": "14313",
@@ -135,7 +135,7 @@ Amp executes, not only on the user's laptop. Merge this into that workspace's
135
135
  "amp.mcpServers": {
136
136
  "canvas": {
137
137
  "command": "bunx",
138
- "args": ["pmx-canvas@0.8.2", "--mcp"],
138
+ "args": ["pmx-canvas@0.9.0", "--mcp"],
139
139
  "includeTools": ["canvas_*"],
140
140
  "env": {
141
141
  "PMX_CANVAS_WORKSPACE_ROOT": "/absolute/path/to/project",
@@ -169,7 +169,7 @@ The agent must not run this approval itself or move the command to global settin
169
169
  `--mcp-config` to bypass workspace trust. Approval authorizes execution of this MCP
170
170
  command; it is not a new Canvas import consent or permission to mutate arbitrary boards.
171
171
  After approval, reload MCP connections (`reload_mcp` in Amp) and require `connected`.
172
- Discover `canvas` tools with `tool_search`; for the full unfiltered 0.8.2 server there
172
+ Discover `canvas` tools with `tool_search`; for the full unfiltered 0.9.0 server there
173
173
  are 24 tools. If still absent, inspect the server status, command/PATH and target instead
174
174
  of reinstalling blindly. See [Amp MCP documentation](https://ampcode.com/docs/customize/mcp).
175
175
 
@@ -19,10 +19,11 @@ Persist a tour with `canvas_view { action: "set-tour", tour: { stops: [...] } }`
19
19
  ```
20
20
 
21
21
  - `nodeId` may identify a card or group. Its persisted position and size determine the camera.
22
+ - `rect` is a world-space box `{ x, y, width, height }` (top-left corner plus size).
22
23
  - An explicit viewport is `{ x, y, scale }`, with positive `scale`. Translation is screen-space:
23
24
  `screen = world * scale + offset`.
24
25
  - `duration` is transition time in seconds (default 1; zero jumps). Valid values are 0–3600.
25
- - `padding` is screen pixels around a node target (default 40).
26
+ - `padding` is screen pixels around a node or rect target (default 40).
26
27
  - `easing` is `linear`, `ease-in-out` (default), or `ease-out`.
27
28
  - `pullback` is 0–4 (default 0). It zooms out during the move without changing either endpoint.
28
29
  - A tour contains at most 1000 stops.
@@ -39,6 +40,20 @@ ID. A saved `{ "stops": [] }` intentionally has no stops; it does not enable gro
39
40
  Preserve the tour of each board when working across boards and target the intended board before
40
41
  setting or reading it.
41
42
 
43
+ ## Drive a tour
44
+
45
+ The server holds one in-memory tour cursor, tied to the board it was started on (a tour on another board replaces it). `canvas_view { action: "tour-go", stop: 0 }`
46
+ starts presenting in open workbenches; `step: "next" | "previous"` moves relative to wherever the
47
+ audience is (a human's arrow keys move the same cursor) and clamps at the ends. Pass
48
+ `present: false` to move only viewers already presenting. `canvas_view { action: "tour-exit" }`
49
+ leaves presentation everywhere. `get-tour` reports `position` (the current stop, or null).
50
+
51
+ `canvas_view { action: "move", nodeId | rect | viewport, duration, easing, padding, pullback }`
52
+ eases the camera without a tour. Outside a presentation the end camera becomes the board
53
+ viewport. Tour steps and moves never enter undo history, never count as agent writes, and are
54
+ allowed under a scope fence. Viewers with reduced motion jump instead of animating; a
55
+ deterministic capture ignores both.
56
+
42
57
  ## Capture prerequisites and behavior
43
58
 
44
59
  Start and verify the matching PMX daemon first: its `/health` workspace and version must match the
@@ -53,6 +53,10 @@ and check `$?`, then read the file.
53
53
  - Test-only changes: run `bun run test` and `bun run test:coverage` if coverage matters
54
54
  - Client/UI/browser interaction changes: run `bun run test:web-canvas`
55
55
  - Cross-stack or non-trivial changes: run `bun run test:all`
56
+ - Bundled skill/documentation-only changes: run
57
+ `bun test tests/unit/bundled-skills.test.ts tests/unit/no-native-tooltips.test.ts`
58
+ and `git diff --check`; inspect public-contract prose against source. No client rebuild or
59
+ full browser suite is needed when executable behavior has not changed.
56
60
  - Before changing browser-visible behavior under `src/client/`: rebuild with `bun run build`
57
61
  Manual browser validation also requires a fresh client bundle. `bun run test:web-canvas`
58
62
  already does this for you.
@@ -146,6 +150,13 @@ Four ways a test silently stops discriminating in this repo:
146
150
  between those assertions. Separately verify a real spec revision resets to authored state,
147
151
  and that local drafts never entered the server spec. See `tests/e2e/json-render-forms.pw.ts`.
148
152
  Eval definitions are not evidence of executing this browser regression or a model benchmark.
153
+ - For viewer refreshes, run `tests/e2e/viewer-refresh-frames.pw.ts`: it delays the runtime-state
154
+ restore beyond document load and checks every captured frame for blank/white flashes in
155
+ Harbor with light/dark OS settings and Daylight with dark OS settings. Keep the frame assertions;
156
+ a settled DOM check cannot prove the handoff stayed painted. Reset canvas-bound work as well as
157
+ nodes between cases. The inert replacement renders behind the active iframe, so select the
158
+ foreground with `iframe:not([inert])`, not `iframe:visible`. Keep capturing until the last
159
+ update is actually in the foreground; a momentary one-iframe count may precede the next poll.
149
160
  - When checking embedded frame fit manually, start from a clean seeded state, rebuild stale bundles,
150
161
  and inspect the actual iframe document in a browser. Server dimensions can look correct while the
151
162
  embedded content is still clipped.
@@ -158,6 +169,15 @@ Four ways a test silently stops discriminating in this repo:
158
169
  - User-facing creation flows must end with the new nodes visible: after the create (plus the
159
170
  skill-mandated focus/fit), a screenshot must show them without any manual pan. A `panned: true`
160
171
  API result alone is not proof — verify the frame is on-screen and unobscured.
172
+ - Layout validation proves node non-overlap, not relation readability. After content settles,
173
+ inspect edge lines, labels, arrowheads and authorship marks at the intended viewing zoom,
174
+ including after auto-arrange; do not prescribe one universal gap.
175
+ - Context/receipt checks must distinguish map delivery (seen links and board read status) from
176
+ full card-content reads. Check one-open stacked See change, clipped-excerpt captions and
177
+ whole-card Undo with a latest-human-edit negative control. Retain a receipt across a board
178
+ switch: snapshot comparison must reopen its owning board, Undo/Unpin must not affect the
179
+ foreign board, and lens marks must use only active-board touches. Check the narrow agent
180
+ initial's counts/lens/panel interaction separately. Use disposable boards for these checks.
161
181
 
162
182
  ## Failure Handling
163
183
 
package/src/cli/agent.ts CHANGED
@@ -52,6 +52,8 @@ Usage:
52
52
 
53
53
  Presentation and capture:
54
54
  pmx-canvas tour get|set Read or persist ordered camera stops
55
+ pmx-canvas tour go <n|next|previous> Present a stop; tour exit leaves presentation
56
+ pmx-canvas camera move --node <id> Ease the camera to a node, --rect or --viewport
55
57
  pmx-canvas record --help Optionally capture the same board tour or live presentation
56
58
 
57
59
  Server:
@@ -397,6 +397,18 @@ cmd(
397
397
  },
398
398
  );
399
399
 
400
+ cmd(
401
+ 'ax reads status',
402
+ 'Read status for a board: its last agent read, per-node reads and revisions, and pin metadata',
403
+ ['pmx-canvas ax reads status', 'pmx-canvas ax reads status --board <board-id>'],
404
+ async (args) => {
405
+ const { flags } = parseFlags(args);
406
+ if (flags.help || flags.h) return showCommandHelp('ax reads status');
407
+
408
+ output(await invokeOperation('ax.reads.status', typeof flags.board === 'string' ? { board: flags.board } : {}));
409
+ },
410
+ );
411
+
400
412
  cmd(
401
413
  'ax work add',
402
414
  'Add a canvas-bound AX work item',
@@ -2,6 +2,55 @@
2
2
 
3
3
  import { cmd, die, getStringFlag, invokeOperation, output, parseFlags, showCommandHelp } from '../shared.js';
4
4
 
5
+ cmd(
6
+ 'board vocabulary',
7
+ 'Set a board relation vocabulary from JSON',
8
+ ['pmx-canvas board vocabulary <id> \'[{"kind":"supports","ink":"supports","directed":true}]\''],
9
+ async (args) => {
10
+ const [id, json] = args;
11
+ if (!id || !json) die('Expected board id and vocabulary JSON.');
12
+ output(await invokeOperation('board.update', { id, relationVocabulary: JSON.parse(json) }));
13
+ },
14
+ );
15
+
16
+ cmd(
17
+ 'board knowledge',
18
+ 'Workspace knowledge actions with a JSON payload',
19
+ [
20
+ 'pmx-canvas board knowledge list',
21
+ 'pmx-canvas board knowledge category-remove \'{"id":"unused-category"}\'',
22
+ 'pmx-canvas board knowledge folder-set \'{"path":"A","categoryId":null,"lifecycleOverride":null}\'',
23
+ ],
24
+ async (args) => {
25
+ const [action, payload = '{}'] = args;
26
+ if (
27
+ ![
28
+ 'list',
29
+ 'category-set',
30
+ 'category-remove',
31
+ 'folder-set',
32
+ 'relation-preset-set',
33
+ 'relation-preset-remove',
34
+ ].includes(action ?? '')
35
+ )
36
+ die('Expected list, category-set, category-remove, folder-set, relation-preset-set or relation-preset-remove.');
37
+ output(await invokeOperation(`knowledge.${action}`, JSON.parse(payload)));
38
+ },
39
+ );
40
+
41
+ cmd(
42
+ 'board lifecycle',
43
+ 'Card lifecycle and audited reviews with a JSON payload',
44
+ ['pmx-canvas board lifecycle get \'{"id":"card"}\''],
45
+ async (args) => {
46
+ const [action, payload = '{}'] = args;
47
+ if (!['get', 'set', 'review', 'restore'].includes(action ?? '')) die('Expected get, set, review or restore.');
48
+ output(
49
+ await invokeOperation(action === 'review' ? 'node.review' : `node.lifecycle-${action}`, JSON.parse(payload)),
50
+ );
51
+ },
52
+ );
53
+
5
54
  cmd('board list', 'List boards, most recently opened first', ['pmx-canvas board list'], async (args) => {
6
55
  const { flags } = parseFlags(args);
7
56
  if (flags.help || flags.h) return showCommandHelp('board list');
@@ -49,14 +98,41 @@ cmd(
49
98
  cmd(
50
99
  'board create',
51
100
  'Create an empty board (use board open with its returned id before writing)',
52
- ['pmx-canvas board create "Q4 planning"', 'pmx-canvas board create "Q4 planning" --category Planning'],
101
+ ['pmx-canvas board create "Q4 planning"', 'pmx-canvas board create "Q4 planning" --folder Planning'],
53
102
  async (args) => {
54
103
  const { positional, flags } = parseFlags(args);
55
104
  if (flags.help || flags.h) return showCommandHelp('board create');
56
105
  const name = positional.join(' ').trim();
57
- if (!name) die('Usage: pmx-canvas board create <name> [--category <category>]');
58
- const category = getStringFlag(flags, 'category');
59
- output(await invokeOperation('board.create', { name, ...(category ? { category } : {}) }));
106
+ if (!name) die('Usage: pmx-canvas board create <name> [--folder <folder>]');
107
+ const folder = getStringFlag(flags, 'folder');
108
+ output(await invokeOperation('board.create', { name, ...(folder ? { folder } : {}) }));
109
+ },
110
+ );
111
+
112
+ cmd(
113
+ 'board pin',
114
+ "Pin a board into the agent's working set (its README and pinned cards reach every brief)",
115
+ ['pmx-canvas board pin board-abc123', 'pmx-canvas board pin board-abc123 --reason "Q4 rests on it"'],
116
+ async (args) => {
117
+ const { positional, flags } = parseFlags(args);
118
+ if (flags.help || flags.h) return showCommandHelp('board pin');
119
+ const [id] = positional;
120
+ if (!id || positional.length > 1) die('Usage: pmx-canvas board pin <board-id> [--reason <why>]');
121
+ const reason = getStringFlag(flags, 'reason');
122
+ output(await invokeOperation('board.pin', { id, ...(reason ? { reason } : {}) }));
123
+ },
124
+ );
125
+
126
+ cmd(
127
+ 'board unpin',
128
+ "Remove a board from the agent's working set",
129
+ ['pmx-canvas board unpin board-abc123'],
130
+ async (args) => {
131
+ const { positional, flags } = parseFlags(args);
132
+ if (flags.help || flags.h) return showCommandHelp('board unpin');
133
+ const [id] = positional;
134
+ if (!id || positional.length > 1) die('Usage: pmx-canvas board unpin <board-id>');
135
+ output(await invokeOperation('board.unpin', { id }));
60
136
  },
61
137
  );
62
138
 
@@ -70,15 +146,15 @@ cmd('board rename', 'Rename a board', ['pmx-canvas board rename board-abc123 "Q4
70
146
  });
71
147
 
72
148
  cmd(
73
- 'board category',
74
- 'File a board under a category on Home (--clear removes it)',
75
- ['pmx-canvas board category board-abc123 "Planning"', 'pmx-canvas board category board-abc123 --clear'],
149
+ 'board folder',
150
+ 'File a board under a folder on Home (--clear removes it)',
151
+ ['pmx-canvas board folder board-abc123 "Planning"', 'pmx-canvas board folder board-abc123 --clear'],
76
152
  async (args) => {
77
153
  const { positional, flags } = parseFlags(args);
78
- if (flags.help || flags.h) return showCommandHelp('board category');
154
+ if (flags.help || flags.h) return showCommandHelp('board folder');
79
155
  const [id, ...rest] = positional;
80
- const category = flags.clear ? '' : rest.join(' ').trim();
81
- if (!id || (!category && !flags.clear)) die('Usage: pmx-canvas board category <board-id> <category> | --clear');
82
- output(await invokeOperation('board.update', { id, category }));
156
+ const folder = flags.clear ? '' : rest.join(' ').trim();
157
+ if (!id || (!folder && !flags.clear)) die('Usage: pmx-canvas board folder <board-id> <folder> | --clear');
158
+ output(await invokeOperation('board.update', { id, folder }));
83
159
  },
84
160
  );
@@ -39,18 +39,46 @@ cmd(
39
39
  type,
40
40
  ...(from ? { from } : {}),
41
41
  ...(to ? { to } : {}),
42
+ ...(typeof flags['to-board'] === 'string' ? { toBoardId: flags['to-board'] } : {}),
42
43
  ...(fromSearch ? { fromSearch } : {}),
43
44
  ...(toSearch ? { toSearch } : {}),
44
45
  };
45
46
  if (flags.label && flags.label !== true) body.label = flags.label;
47
+ if (typeof flags.kind === 'string') body.kind = flags.kind;
48
+ if (typeof flags.reason === 'string') body.reason = flags.reason;
46
49
  if (typeof flags.style === 'string') body.style = flags.style;
47
50
  if (flags.animated) body.animated = true;
51
+ for (const [flag, field] of [
52
+ ['destination-board', 'destinationBoardId'],
53
+ ['request-id', 'requestId'],
54
+ ['expected-board', 'expectedBoardId'],
55
+ ] as const) {
56
+ if (typeof flags[flag] === 'string') body[field] = flags[flag];
57
+ }
48
58
 
49
59
  const result = await invokeOperation('edge.add', body);
50
60
  output(result);
51
61
  },
52
62
  );
53
63
 
64
+ cmd(
65
+ 'edge update',
66
+ 'Edit a relation kind, reason, label or appearance',
67
+ ['pmx-canvas edge update <id> --kind supports --reason "Evidence from interviews"'],
68
+ async (args) => {
69
+ const { positional, flags } = parseFlags(args);
70
+ if (flags.help || flags.h) return showCommandHelp('edge update');
71
+ if (!positional[0]) die('Missing edge ID', 'pmx-canvas edge update <id> --kind supports');
72
+ const body: Record<string, unknown> = { id: positional[0] };
73
+ for (const key of ['kind', 'reason', 'label', 'type', 'style']) {
74
+ if (typeof flags[key] === 'string') body[key] = flags[key];
75
+ }
76
+ if (flags['clear-kind']) body.kind = null;
77
+ if (flags['clear-reason']) body.reason = null;
78
+ output(await invokeOperation('edge.update', body));
79
+ },
80
+ );
81
+
54
82
  // ── edge list ────────────────────────────────────────────────
55
83
  cmd('edge list', 'List all edges on the canvas', ['pmx-canvas edge list'], async (args) => {
56
84
  const { flags } = parseFlags(args);
@@ -38,6 +38,13 @@ cmd(
38
38
  if (height !== undefined) body.height = height;
39
39
  if (typeof flags['child-layout'] === 'string') body.childLayout = flags['child-layout'];
40
40
  if (positional.length > 0) body.childIds = positional;
41
+ for (const [flag, field] of [
42
+ ['destination-board', 'destinationBoardId'],
43
+ ['request-id', 'requestId'],
44
+ ['expected-board', 'expectedBoardId'],
45
+ ] as const) {
46
+ if (typeof flags[flag] === 'string') body[field] = flags[flag];
47
+ }
41
48
 
42
49
  const result = await invokeOperation('group.create', body);
43
50
  output(result);
@@ -133,6 +133,7 @@ function summarizeNodeResult(node: Record<string, unknown>): Record<string, unkn
133
133
  ...(node.path !== undefined ? { path: node.path } : {}),
134
134
  ...(node.url !== undefined ? { url: node.url } : {}),
135
135
  ...(node.provenance !== undefined ? { provenance: node.provenance } : {}),
136
+ ...(node.qualification !== undefined ? { qualification: node.qualification } : {}),
136
137
  ...(typeof data.mode === 'string' ? { mode: data.mode } : {}),
137
138
  ...(typeof data.viewerType === 'string' ? { viewerType: data.viewerType } : {}),
138
139
  ...(typeof data.serverName === 'string' ? { serverName: data.serverName } : {}),
@@ -28,7 +28,7 @@ import {
28
28
  cmd(
29
29
  'search',
30
30
  'Search nodes by title or content',
31
- ['pmx-canvas search "design doc"', 'pmx-canvas search --query "TODO"'],
31
+ ['pmx-canvas search "design doc"', 'pmx-canvas search --query "TODO" --folder "Research" --board-id <id>'],
32
32
  async (args) => {
33
33
  const { positional, flags } = parseFlags(args);
34
34
  if (flags.help || flags.h) return showCommandHelp('search');
@@ -36,11 +36,43 @@ cmd(
36
36
  const query = positional[0] || (typeof flags.query === 'string' ? flags.query : '');
37
37
  if (!query) die('Missing search query', 'pmx-canvas search "query"');
38
38
 
39
- const result = await invokeOperation('search', { q: query, ...(flags.library ? { scope: 'library' } : {}) });
39
+ if (flags.scope !== undefined && flags.scope !== 'active' && flags.scope !== 'library')
40
+ die('--scope must be active or library.');
41
+ const result = await invokeOperation('search', {
42
+ q: query,
43
+ ...(flags.scope ? { scope: flags.scope } : flags.library ? { scope: 'library' } : {}),
44
+ ...(typeof flags['board-id'] === 'string' ? { boardId: flags['board-id'] } : {}),
45
+ ...(typeof flags.folder === 'string' ? { folder: flags.folder } : {}),
46
+ ...(typeof flags.limit === 'string' ? { limit: Number(flags.limit) } : {}),
47
+ });
40
48
  output(result);
41
49
  },
42
50
  );
43
51
 
52
+ cmd(
53
+ 'memory-graph',
54
+ 'Explore the board/card relation graph (summary-only)',
55
+ [
56
+ 'pmx-canvas memory-graph',
57
+ 'pmx-canvas memory-graph --focus pins --depth 2',
58
+ 'pmx-canvas memory-graph --projection boards',
59
+ ],
60
+ async (args) => {
61
+ const { flags } = parseFlags(args);
62
+ if (flags.help || flags.h) return showCommandHelp('memory-graph');
63
+ if (flags.depth !== undefined && (typeof flags.depth !== 'string' || !flags.depth.trim())) {
64
+ die('--depth requires an integer from 0 to 4.');
65
+ }
66
+ output(
67
+ await invokeOperation('memory-graph.get', {
68
+ ...(typeof flags.focus === 'string' ? { focus: flags.focus } : {}),
69
+ ...(flags.depth !== undefined ? { depth: Number(flags.depth) } : {}),
70
+ ...(flags.projection !== undefined ? { projection: flags.projection } : {}),
71
+ }),
72
+ );
73
+ },
74
+ );
75
+
44
76
  cmd(
45
77
  'context',
46
78
  'Read the budgeted cross-board context brief',