@agent-canvas/cli 0.10.0 → 0.12.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 (131) hide show
  1. package/dist/commands/__tests__/create-folder.test.d.ts +1 -0
  2. package/dist/commands/__tests__/create-folder.test.js +82 -0
  3. package/dist/commands/__tests__/delete-folder.test.d.ts +1 -0
  4. package/dist/commands/__tests__/delete-folder.test.js +79 -0
  5. package/dist/commands/__tests__/list.test.js +90 -0
  6. package/dist/commands/__tests__/move-to-folder.test.d.ts +1 -0
  7. package/dist/commands/__tests__/move-to-folder.test.js +99 -0
  8. package/dist/commands/add-arrow.d.ts +1 -0
  9. package/dist/commands/add-arrow.js +1 -0
  10. package/dist/commands/add-line.d.ts +1 -0
  11. package/dist/commands/add-line.js +1 -0
  12. package/dist/commands/add-polygon.d.ts +1 -0
  13. package/dist/commands/add-polygon.js +1 -0
  14. package/dist/commands/add-shape.d.ts +1 -0
  15. package/dist/commands/add-shape.js +1 -0
  16. package/dist/commands/add-text.d.ts +1 -0
  17. package/dist/commands/add-text.js +1 -0
  18. package/dist/commands/create-folder.d.ts +20 -0
  19. package/dist/commands/create-folder.js +24 -0
  20. package/dist/commands/delete-folder.d.ts +20 -0
  21. package/dist/commands/delete-folder.js +24 -0
  22. package/dist/commands/list.d.ts +3 -1
  23. package/dist/commands/list.js +38 -12
  24. package/dist/commands/move-to-folder.d.ts +21 -0
  25. package/dist/commands/move-to-folder.js +29 -0
  26. package/dist/commands/start.d.ts +3 -1
  27. package/dist/commands/start.js +65 -2
  28. package/dist/index.js +58 -3
  29. package/dist/lib/protocol.d.ts +36 -0
  30. package/dist/server/index.d.ts +3 -1
  31. package/dist/server/index.js +52 -38
  32. package/dist/static/assets/{ar-SA-G6X2FPQ2-XEzZ0MM8.js → ar-SA-G6X2FPQ2-DBNnjE12.js} +1 -1
  33. package/dist/static/assets/{arc-DrFnHjo9.js → arc-6RZ6YUY1.js} +1 -1
  34. package/dist/static/assets/{az-AZ-76LH7QW2-miPGYjNv.js → az-AZ-76LH7QW2-CEisZhHx.js} +1 -1
  35. package/dist/static/assets/{bg-BG-XCXSNQG7-FKYgNI-z.js → bg-BG-XCXSNQG7-DTKl-Qhp.js} +1 -1
  36. package/dist/static/assets/{blockDiagram-38ab4fdb-DDVs3kx8.js → blockDiagram-38ab4fdb-BOQCxa-J.js} +1 -1
  37. package/dist/static/assets/{bn-BD-2XOGV67Q-DxZpSMDT.js → bn-BD-2XOGV67Q-IHdqEEiU.js} +1 -1
  38. package/dist/static/assets/{c4Diagram-3d4e48cf-z1ySwFoq.js → c4Diagram-3d4e48cf-BsfS6bbA.js} +1 -1
  39. package/dist/static/assets/{ca-ES-6MX7JW3Y-BAULR9eC.js → ca-ES-6MX7JW3Y-BvXW96-a.js} +1 -1
  40. package/dist/static/assets/channel-CxlHPNx-.js +1 -0
  41. package/dist/static/assets/{classDiagram-70f12bd4-CcflZaLB.js → classDiagram-70f12bd4-DtEBciUO.js} +1 -1
  42. package/dist/static/assets/{classDiagram-v2-f2320105-4s1YEUJ7.js → classDiagram-v2-f2320105-DhstM95e.js} +1 -1
  43. package/dist/static/assets/clone-CLXPdWxD.js +1 -0
  44. package/dist/static/assets/{createText-2e5e7dd3-CG8XFIhs.js → createText-2e5e7dd3-CMwfBpkf.js} +1 -1
  45. package/dist/static/assets/{cs-CZ-2BRQDIVT-CtyhBcGZ.js → cs-CZ-2BRQDIVT-CEO7-ad3.js} +1 -1
  46. package/dist/static/assets/{da-DK-5WZEPLOC-BRuHhKp8.js → da-DK-5WZEPLOC--SyHMiIr.js} +1 -1
  47. package/dist/static/assets/{de-DE-XR44H4JA-CLsnZ_4A.js → de-DE-XR44H4JA-DFhdbiqr.js} +1 -1
  48. package/dist/static/assets/{edges-e0da2a9e-zcuQ77XI.js → edges-e0da2a9e-Dt6wNpzA.js} +1 -1
  49. package/dist/static/assets/{el-GR-BZB4AONW-DsoaT9EY.js → el-GR-BZB4AONW-BtqKxae0.js} +1 -1
  50. package/dist/static/assets/{erDiagram-9861fffd-Bgs9cpyD.js → erDiagram-9861fffd-D8dTYLmG.js} +1 -1
  51. package/dist/static/assets/{es-ES-U4NZUMDT-Bw2E0XBb.js → es-ES-U4NZUMDT-CQECcRJK.js} +1 -1
  52. package/dist/static/assets/{eu-ES-A7QVB2H4-CQ1kuGle.js → eu-ES-A7QVB2H4-DMCSZcU_.js} +1 -1
  53. package/dist/static/assets/{fa-IR-HGAKTJCU-DS5nNDFX.js → fa-IR-HGAKTJCU-CuWGTo8O.js} +1 -1
  54. package/dist/static/assets/{fi-FI-Z5N7JZ37-DDV9MnZL.js → fi-FI-Z5N7JZ37-B48P0yVO.js} +1 -1
  55. package/dist/static/assets/{flowDb-956e92f1-BD6rTiXG.js → flowDb-956e92f1-DjuJ2RsZ.js} +1 -1
  56. package/dist/static/assets/{flowDiagram-66a62f08-D-fj1twH.js → flowDiagram-66a62f08-CGxdWcr1.js} +1 -1
  57. package/dist/static/assets/flowDiagram-v2-96b9c2cf-AVP-y8Wc.js +1 -0
  58. package/dist/static/assets/{flowchart-elk-definition-4a651766-DQwlXyNj.js → flowchart-elk-definition-4a651766-CwrQg8bQ.js} +1 -1
  59. package/dist/static/assets/{fr-FR-RHASNOE6-CX7gK2yH.js → fr-FR-RHASNOE6-kC-2LYF7.js} +1 -1
  60. package/dist/static/assets/{ganttDiagram-c361ad54-CcT8r-Dv.js → ganttDiagram-c361ad54-BKeezv9l.js} +1 -1
  61. package/dist/static/assets/{gitGraphDiagram-72cf32ee-B-fu77nl.js → gitGraphDiagram-72cf32ee-Dj-thy84.js} +1 -1
  62. package/dist/static/assets/{gl-ES-HMX3MZ6V-DmSRu7KL.js → gl-ES-HMX3MZ6V-Dqb5t32o.js} +1 -1
  63. package/dist/static/assets/{graph-Xozq0y74.js → graph-BVXcMiT7.js} +1 -1
  64. package/dist/static/assets/{he-IL-6SHJWFNN-ioUktrzK.js → he-IL-6SHJWFNN-sQ-x56Z1.js} +1 -1
  65. package/dist/static/assets/{hi-IN-IWLTKZ5I-DSd_IEtU.js → hi-IN-IWLTKZ5I-D3YHTvwR.js} +1 -1
  66. package/dist/static/assets/{hu-HU-A5ZG7DT2-vfe7InOJ.js → hu-HU-A5ZG7DT2-C7N2rM7-.js} +1 -1
  67. package/dist/static/assets/{id-ID-SAP4L64H-drkdhw09.js → id-ID-SAP4L64H-CquleGuo.js} +1 -1
  68. package/dist/static/assets/{index-3862675e-BsSoycVK.js → index-3862675e-C20q-vC5.js} +1 -1
  69. package/dist/static/assets/{index-DxNzWq2L.js → index-CPaVz_o7.js} +73 -73
  70. package/dist/static/assets/{index-CD2XG8u2.js → index-n9PxF2n7.js} +4 -4
  71. package/dist/static/assets/{infoDiagram-f8f76790-Db3XQGPS.js → infoDiagram-f8f76790-DTzqBsX9.js} +1 -1
  72. package/dist/static/assets/{it-IT-JPQ66NNP-Dfrm9f2z.js → it-IT-JPQ66NNP-DgQ1I_rS.js} +1 -1
  73. package/dist/static/assets/{ja-JP-DBVTYXUO-d3XoxutP.js → ja-JP-DBVTYXUO-BDDHsohK.js} +1 -1
  74. package/dist/static/assets/{journeyDiagram-49397b02-ChsXwRn8.js → journeyDiagram-49397b02-DDWDe9VT.js} +1 -1
  75. package/dist/static/assets/{kaa-6HZHGXH3-Dv5PRO2z.js → kaa-6HZHGXH3-7kU2MZDG.js} +1 -1
  76. package/dist/static/assets/{kab-KAB-ZGHBKWFO-B2Vj6PNf.js → kab-KAB-ZGHBKWFO-DxLZR8Xc.js} +1 -1
  77. package/dist/static/assets/{kk-KZ-P5N5QNE5-Dv70gIrO.js → kk-KZ-P5N5QNE5-CrjN6SZo.js} +1 -1
  78. package/dist/static/assets/{km-KH-HSX4SM5Z-D1adujaO.js → km-KH-HSX4SM5Z-fXBCY1PI.js} +1 -1
  79. package/dist/static/assets/{ko-KR-MTYHY66A-MZXJ1wNa.js → ko-KR-MTYHY66A-r0fEGl_8.js} +1 -1
  80. package/dist/static/assets/{ku-TR-6OUDTVRD-DaWBrq1c.js → ku-TR-6OUDTVRD-QiwK8NVD.js} +1 -1
  81. package/dist/static/assets/{layout-BTPFVak9.js → layout-DDTwsdmP.js} +1 -1
  82. package/dist/static/assets/{line-t41n6zRi.js → line-vXc1x1Ab.js} +1 -1
  83. package/dist/static/assets/{linear-DkELI7NI.js → linear-BjjGEUdO.js} +1 -1
  84. package/dist/static/assets/{lt-LT-XHIRWOB4-WJQQKy4Q.js → lt-LT-XHIRWOB4-BRXBv4jU.js} +1 -1
  85. package/dist/static/assets/{lv-LV-5QDEKY6T-N3YNfIM0.js → lv-LV-5QDEKY6T-B_Md2FV-.js} +1 -1
  86. package/dist/static/assets/{mindmap-definition-fc14e90a-CVim-b_D.js → mindmap-definition-fc14e90a-C_Rv7XkC.js} +1 -1
  87. package/dist/static/assets/{mr-IN-CRQNXWMA-c_mnxx8Z.js → mr-IN-CRQNXWMA-BBosvXx_.js} +1 -1
  88. package/dist/static/assets/{my-MM-5M5IBNSE--joRx6Yq.js → my-MM-5M5IBNSE-bOGsKksC.js} +1 -1
  89. package/dist/static/assets/{nb-NO-T6EIAALU-DmXOFlIn.js → nb-NO-T6EIAALU-Sq_cBGBN.js} +1 -1
  90. package/dist/static/assets/{nl-NL-IS3SIHDZ-ITIWpYJK.js → nl-NL-IS3SIHDZ-D2C0R6Ta.js} +1 -1
  91. package/dist/static/assets/{nn-NO-6E72VCQL-C3OADWCu.js → nn-NO-6E72VCQL-gS3iXfeO.js} +1 -1
  92. package/dist/static/assets/{oc-FR-POXYY2M6-DdHJcrgt.js → oc-FR-POXYY2M6-CExAQbrv.js} +1 -1
  93. package/dist/static/assets/{pa-IN-N4M65BXN-DDAJ9pij.js → pa-IN-N4M65BXN-8dS4bEav.js} +1 -1
  94. package/dist/static/assets/{pica-B_6ofXXN.js → pica-DmOZUjq-.js} +1 -1
  95. package/dist/static/assets/{pieDiagram-8a3498a8-BYt2x9MC.js → pieDiagram-8a3498a8-NdIgX8Qu.js} +1 -1
  96. package/dist/static/assets/{pl-PL-T2D74RX3-DshGI3Tz.js → pl-PL-T2D74RX3-CyaePQAI.js} +1 -1
  97. package/dist/static/assets/{pt-BR-5N22H2LF-HGo88nQV.js → pt-BR-5N22H2LF-DCElnIPA.js} +1 -1
  98. package/dist/static/assets/{pt-PT-UZXXM6DQ-cTNSPqAn.js → pt-PT-UZXXM6DQ-fiz3tL4V.js} +1 -1
  99. package/dist/static/assets/{quadrantDiagram-120e2f19-2mxX1_wl.js → quadrantDiagram-120e2f19-SzZ20uL9.js} +1 -1
  100. package/dist/static/assets/{requirementDiagram-deff3bca-BNfiqDe7.js → requirementDiagram-deff3bca-BkESiM5P.js} +1 -1
  101. package/dist/static/assets/{ro-RO-JPDTUUEW-Bf70y6xU.js → ro-RO-JPDTUUEW-B7Ks_bu3.js} +1 -1
  102. package/dist/static/assets/{ru-RU-B4JR7IUQ-C7f4z-21.js → ru-RU-B4JR7IUQ-BCkgHOqG.js} +1 -1
  103. package/dist/static/assets/{sankeyDiagram-04a897e0-BgjE-O06.js → sankeyDiagram-04a897e0-DO-VaUSa.js} +1 -1
  104. package/dist/static/assets/{sequenceDiagram-704730f1-B_79WsQL.js → sequenceDiagram-704730f1-BvITw4ii.js} +1 -1
  105. package/dist/static/assets/{si-LK-N5RQ5JYF-CkDp-HPU.js → si-LK-N5RQ5JYF-DXMX6jRF.js} +1 -1
  106. package/dist/static/assets/{sk-SK-C5VTKIMK-CMUQ8-wE.js → sk-SK-C5VTKIMK-mVkvwInm.js} +1 -1
  107. package/dist/static/assets/{sl-SI-NN7IZMDC-BGSJyP5v.js → sl-SI-NN7IZMDC-yqb-5JGK.js} +1 -1
  108. package/dist/static/assets/{stateDiagram-587899a1-B2EiD02Y.js → stateDiagram-587899a1-Cb555UUm.js} +1 -1
  109. package/dist/static/assets/{stateDiagram-v2-d93cdb3a-BVKX7Q0H.js → stateDiagram-v2-d93cdb3a-BnSTRfdW.js} +1 -1
  110. package/dist/static/assets/{styles-6aaf32cf-DX6VLmBC.js → styles-6aaf32cf-DmM-xNbN.js} +1 -1
  111. package/dist/static/assets/{styles-9a916d00-ZObg1EjO.js → styles-9a916d00-CQp9r-zI.js} +1 -1
  112. package/dist/static/assets/{styles-c10674c1-B2QdbJXH.js → styles-c10674c1-DukJEX99.js} +1 -1
  113. package/dist/static/assets/{subset-shared.chunk-CFNHXLul.js → subset-shared.chunk-DxGqiA-P.js} +1 -1
  114. package/dist/static/assets/{subset-worker.chunk-BdzGxI7D.js → subset-worker.chunk-CgfLr-3s.js} +1 -1
  115. package/dist/static/assets/{sv-SE-XGPEYMSR-CBqB_zcB.js → sv-SE-XGPEYMSR-D4OA9xsC.js} +1 -1
  116. package/dist/static/assets/{svgDrawCommon-08f97a94-zaLFrg17.js → svgDrawCommon-08f97a94-CKUw9D3R.js} +1 -1
  117. package/dist/static/assets/{ta-IN-2NMHFXQM-BngjSe7_.js → ta-IN-2NMHFXQM-CGYGb5ad.js} +1 -1
  118. package/dist/static/assets/{th-TH-HPSO5L25-hC8JL2xm.js → th-TH-HPSO5L25-C4-hGx1K.js} +1 -1
  119. package/dist/static/assets/{timeline-definition-85554ec2-zp_zvxKY.js → timeline-definition-85554ec2-BGkqW2cy.js} +1 -1
  120. package/dist/static/assets/{tr-TR-DEFEU3FU-CgMVUbEX.js → tr-TR-DEFEU3FU-qfQO0kyM.js} +1 -1
  121. package/dist/static/assets/{uk-UA-QMV73CPH-C7YkH2lC.js → uk-UA-QMV73CPH-Dim8QSj6.js} +1 -1
  122. package/dist/static/assets/{vi-VN-M7AON7JQ-B3j43PFt.js → vi-VN-M7AON7JQ-DI9wHHg_.js} +1 -1
  123. package/dist/static/assets/{xychartDiagram-e933f94c-Cf364fl1.js → xychartDiagram-e933f94c-C9PQpTDy.js} +1 -1
  124. package/dist/static/assets/{zh-CN-LNUGB5OW-CB5Bs8hi.js → zh-CN-LNUGB5OW-Y-ZGqBc2.js} +1 -1
  125. package/dist/static/assets/{zh-HK-E62DVLB3-Cq_xqGWq.js → zh-HK-E62DVLB3-D56waEAR.js} +1 -1
  126. package/dist/static/assets/{zh-TW-RAJ6MFWO-B2_BLWCE.js → zh-TW-RAJ6MFWO-B1JfV8AH.js} +1 -1
  127. package/dist/static/index.html +1 -1
  128. package/package.json +2 -4
  129. package/dist/static/assets/channel-DEbp799J.js +0 -1
  130. package/dist/static/assets/clone-C5amchTr.js +0 -1
  131. package/dist/static/assets/flowDiagram-v2-96b9c2cf-DvE5NWu3.js +0 -1
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,82 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { createFolder } from '../create-folder';
3
+ function createMockClient(sendFn) {
4
+ return {
5
+ send: sendFn ?? vi.fn(() => Promise.resolve({ success: true, category: { id: 'cat-1', name: 'Test', isCollapsed: false, order: 0 } })),
6
+ close: vi.fn(),
7
+ };
8
+ }
9
+ function createMockDeps(overrides) {
10
+ return {
11
+ connectToCanvas: vi.fn(() => Promise.resolve(createMockClient())),
12
+ generateId: vi.fn(() => 'test-id'),
13
+ log: vi.fn(),
14
+ error: vi.fn(),
15
+ exit: vi.fn(),
16
+ ...overrides,
17
+ };
18
+ }
19
+ describe('createFolder', () => {
20
+ describe('parameter assembly', () => {
21
+ it('should send correct params with folder name', async () => {
22
+ const mockSend = vi.fn(() => Promise.resolve({ success: true, category: { id: 'cat-1', name: 'My Folder', isCollapsed: false, order: 0 } }));
23
+ const mockClient = createMockClient(mockSend);
24
+ const deps = createMockDeps({
25
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
26
+ });
27
+ await createFolder({ name: 'My Folder' }, deps);
28
+ expect(mockSend).toHaveBeenCalledWith(expect.objectContaining({
29
+ type: 'createFolder',
30
+ id: 'test-id',
31
+ params: { name: 'My Folder' },
32
+ }));
33
+ });
34
+ });
35
+ describe('response handling', () => {
36
+ it('should log success message on success', async () => {
37
+ const mockSend = vi.fn(() => Promise.resolve({
38
+ success: true,
39
+ category: { id: 'cat-1', name: 'My Folder', isCollapsed: false, order: 0 },
40
+ }));
41
+ const mockClient = createMockClient(mockSend);
42
+ const deps = createMockDeps({
43
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
44
+ });
45
+ await createFolder({ name: 'My Folder' }, deps);
46
+ expect(deps.log).toHaveBeenCalledWith('Folder "My Folder" created');
47
+ });
48
+ it('should error and exit on failure response', async () => {
49
+ const mockSend = vi.fn(() => Promise.resolve({
50
+ success: false,
51
+ error: 'Folder already exists',
52
+ }));
53
+ const mockClient = createMockClient(mockSend);
54
+ const deps = createMockDeps({
55
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
56
+ });
57
+ await createFolder({ name: 'Test' }, deps);
58
+ expect(deps.error).toHaveBeenCalledWith('Failed: Folder already exists');
59
+ expect(deps.exit).toHaveBeenCalledWith(1);
60
+ });
61
+ it('should close client after success', async () => {
62
+ const mockClose = vi.fn();
63
+ const mockSend = vi.fn(() => Promise.resolve({ success: true, category: { id: 'cat-1', name: 'Test', isCollapsed: false, order: 0 } }));
64
+ const mockClient = { send: mockSend, close: mockClose };
65
+ const deps = createMockDeps({
66
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
67
+ });
68
+ await createFolder({ name: 'Test' }, deps);
69
+ expect(mockClose).toHaveBeenCalled();
70
+ });
71
+ it('should close client after failure', async () => {
72
+ const mockClose = vi.fn();
73
+ const mockSend = vi.fn(() => Promise.resolve({ success: false, error: 'error' }));
74
+ const mockClient = { send: mockSend, close: mockClose };
75
+ const deps = createMockDeps({
76
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
77
+ });
78
+ await createFolder({ name: 'Test' }, deps);
79
+ expect(mockClose).toHaveBeenCalled();
80
+ });
81
+ });
82
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,79 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { deleteFolder } from '../delete-folder';
3
+ function createMockClient(sendFn) {
4
+ return {
5
+ send: sendFn ?? vi.fn(() => Promise.resolve({ success: true })),
6
+ close: vi.fn(),
7
+ };
8
+ }
9
+ function createMockDeps(overrides) {
10
+ return {
11
+ connectToCanvas: vi.fn(() => Promise.resolve(createMockClient())),
12
+ generateId: vi.fn(() => 'test-id'),
13
+ log: vi.fn(),
14
+ error: vi.fn(),
15
+ exit: vi.fn(),
16
+ ...overrides,
17
+ };
18
+ }
19
+ describe('deleteFolder', () => {
20
+ describe('parameter assembly', () => {
21
+ it('should send correct params with folder name', async () => {
22
+ const mockSend = vi.fn(() => Promise.resolve({ success: true }));
23
+ const mockClient = createMockClient(mockSend);
24
+ const deps = createMockDeps({
25
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
26
+ });
27
+ await deleteFolder({ name: 'My Folder' }, deps);
28
+ expect(mockSend).toHaveBeenCalledWith(expect.objectContaining({
29
+ type: 'deleteFolder',
30
+ id: 'test-id',
31
+ params: { name: 'My Folder' },
32
+ }));
33
+ });
34
+ });
35
+ describe('response handling', () => {
36
+ it('should log success message on success', async () => {
37
+ const mockSend = vi.fn(() => Promise.resolve({ success: true }));
38
+ const mockClient = createMockClient(mockSend);
39
+ const deps = createMockDeps({
40
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
41
+ });
42
+ await deleteFolder({ name: 'My Folder' }, deps);
43
+ expect(deps.log).toHaveBeenCalledWith('Folder "My Folder" deleted');
44
+ });
45
+ it('should error and exit on failure response', async () => {
46
+ const mockSend = vi.fn(() => Promise.resolve({
47
+ success: false,
48
+ error: 'Folder not found',
49
+ }));
50
+ const mockClient = createMockClient(mockSend);
51
+ const deps = createMockDeps({
52
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
53
+ });
54
+ await deleteFolder({ name: 'Test' }, deps);
55
+ expect(deps.error).toHaveBeenCalledWith('Failed: Folder not found');
56
+ expect(deps.exit).toHaveBeenCalledWith(1);
57
+ });
58
+ it('should close client after success', async () => {
59
+ const mockClose = vi.fn();
60
+ const mockSend = vi.fn(() => Promise.resolve({ success: true }));
61
+ const mockClient = { send: mockSend, close: mockClose };
62
+ const deps = createMockDeps({
63
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
64
+ });
65
+ await deleteFolder({ name: 'Test' }, deps);
66
+ expect(mockClose).toHaveBeenCalled();
67
+ });
68
+ it('should close client after failure', async () => {
69
+ const mockClose = vi.fn();
70
+ const mockSend = vi.fn(() => Promise.resolve({ success: false, error: 'error' }));
71
+ const mockClient = { send: mockSend, close: mockClose };
72
+ const deps = createMockDeps({
73
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
74
+ });
75
+ await deleteFolder({ name: 'Test' }, deps);
76
+ expect(mockClose).toHaveBeenCalled();
77
+ });
78
+ });
79
+ });
@@ -76,6 +76,77 @@ describe('list', () => {
76
76
  expect(lines[1]).toContain('(updated:');
77
77
  });
78
78
  });
79
+ describe('folder display', () => {
80
+ it('should display canvases grouped by folder when categories exist', () => {
81
+ const lines = formatCanvasList({
82
+ canvases: [
83
+ { id: 'c1', name: 'Canvas A', createdAt: 1000, updatedAt: 2000 },
84
+ { id: 'c2', name: 'Canvas B', createdAt: 1000, updatedAt: 3000 },
85
+ ],
86
+ categories: [
87
+ { id: 'cat-1', name: 'Work', isCollapsed: false, order: 0 },
88
+ ],
89
+ canvasCategoryMap: { 'c1': 'cat-1' },
90
+ });
91
+ expect(lines).toContain(' [F] Work (1)');
92
+ const ungroupedLine = lines.find(l => l.includes('Ungrouped'));
93
+ expect(ungroupedLine).toContain('(1)');
94
+ });
95
+ it('should sort folders by order', () => {
96
+ const lines = formatCanvasList({
97
+ canvases: [
98
+ { id: 'c1', name: 'Canvas A', createdAt: 1000, updatedAt: 2000 },
99
+ { id: 'c2', name: 'Canvas B', createdAt: 1000, updatedAt: 3000 },
100
+ ],
101
+ categories: [
102
+ { id: 'cat-2', name: 'Zebra', isCollapsed: false, order: 1 },
103
+ { id: 'cat-1', name: 'Alpha', isCollapsed: false, order: 0 },
104
+ ],
105
+ canvasCategoryMap: { 'c1': 'cat-1', 'c2': 'cat-2' },
106
+ });
107
+ const alphaIdx = lines.findIndex(l => l.includes('Alpha'));
108
+ const zebraIdx = lines.findIndex(l => l.includes('Zebra'));
109
+ expect(alphaIdx).toBeLessThan(zebraIdx);
110
+ });
111
+ it('should indent canvases inside folders', () => {
112
+ const lines = formatCanvasList({
113
+ canvases: [
114
+ { id: 'c1', name: 'Canvas A', createdAt: 1000, updatedAt: 2000 },
115
+ ],
116
+ categories: [
117
+ { id: 'cat-1', name: 'Work', isCollapsed: false, order: 0 },
118
+ ],
119
+ canvasCategoryMap: { 'c1': 'cat-1' },
120
+ });
121
+ const canvasLine = lines.find(l => l.includes('Canvas A'));
122
+ expect(canvasLine).toMatch(/^ /);
123
+ });
124
+ it('should not show ungrouped section when all canvases are categorized', () => {
125
+ const lines = formatCanvasList({
126
+ canvases: [
127
+ { id: 'c1', name: 'Canvas A', createdAt: 1000, updatedAt: 2000 },
128
+ ],
129
+ categories: [
130
+ { id: 'cat-1', name: 'Work', isCollapsed: false, order: 0 },
131
+ ],
132
+ canvasCategoryMap: { 'c1': 'cat-1' },
133
+ });
134
+ const ungroupedLine = lines.find(l => l.includes('Ungrouped'));
135
+ expect(ungroupedLine).toBeUndefined();
136
+ });
137
+ it('should fall back to flat list when no categories', () => {
138
+ const lines = formatCanvasList({
139
+ canvases: [
140
+ { id: 'c1', name: 'Canvas A', createdAt: 1000, updatedAt: 2000 },
141
+ { id: 'c2', name: 'Canvas B', createdAt: 1000, updatedAt: 3000 },
142
+ ],
143
+ categories: [],
144
+ canvasCategoryMap: {},
145
+ });
146
+ expect(lines).toHaveLength(3);
147
+ expect(lines.find(l => l.includes('[F]'))).toBeUndefined();
148
+ });
149
+ });
79
150
  // ==================== Response Handling Tests ====================
80
151
  describe('response handling', () => {
81
152
  it('should log each formatted line on success', async () => {
@@ -130,5 +201,24 @@ describe('list', () => {
130
201
  await list(deps);
131
202
  expect(mockClose).toHaveBeenCalled();
132
203
  });
204
+ it('should pass categories and canvasCategoryMap from response', async () => {
205
+ const mockSend = vi.fn(() => Promise.resolve({
206
+ success: true,
207
+ activeCanvasId: 'c1',
208
+ canvases: [
209
+ { id: 'c1', name: 'Canvas A', createdAt: 1000, updatedAt: 2000 },
210
+ ],
211
+ categories: [
212
+ { id: 'cat-1', name: 'Work', isCollapsed: false, order: 0 },
213
+ ],
214
+ canvasCategoryMap: { 'c1': 'cat-1' },
215
+ }));
216
+ const mockClient = createMockClient(mockSend);
217
+ const deps = createMockDeps({
218
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
219
+ });
220
+ await list(deps);
221
+ expect(deps.log).toHaveBeenCalledWith(expect.stringContaining('[F] Work'));
222
+ });
133
223
  });
134
224
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,99 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { moveToFolder } from '../move-to-folder';
3
+ function createMockClient(sendFn) {
4
+ return {
5
+ send: sendFn ?? vi.fn(() => Promise.resolve({ success: true })),
6
+ close: vi.fn(),
7
+ };
8
+ }
9
+ function createMockDeps(overrides) {
10
+ return {
11
+ connectToCanvas: vi.fn(() => Promise.resolve(createMockClient())),
12
+ generateId: vi.fn(() => 'test-id'),
13
+ log: vi.fn(),
14
+ error: vi.fn(),
15
+ exit: vi.fn(),
16
+ ...overrides,
17
+ };
18
+ }
19
+ describe('moveToFolder', () => {
20
+ describe('parameter assembly', () => {
21
+ it('should send correct params when moving to a folder', async () => {
22
+ const mockSend = vi.fn(() => Promise.resolve({ success: true }));
23
+ const mockClient = createMockClient(mockSend);
24
+ const deps = createMockDeps({
25
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
26
+ });
27
+ await moveToFolder({ canvasName: 'My Canvas', folderName: 'My Folder' }, deps);
28
+ expect(mockSend).toHaveBeenCalledWith(expect.objectContaining({
29
+ type: 'moveCanvasToFolder',
30
+ id: 'test-id',
31
+ params: { canvasName: 'My Canvas', folderName: 'My Folder' },
32
+ }));
33
+ });
34
+ it('should send null folderName when moving to ungrouped', async () => {
35
+ const mockSend = vi.fn(() => Promise.resolve({ success: true }));
36
+ const mockClient = createMockClient(mockSend);
37
+ const deps = createMockDeps({
38
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
39
+ });
40
+ await moveToFolder({ canvasName: 'My Canvas', folderName: null }, deps);
41
+ expect(mockSend).toHaveBeenCalledWith(expect.objectContaining({
42
+ params: { canvasName: 'My Canvas', folderName: null },
43
+ }));
44
+ });
45
+ });
46
+ describe('response handling', () => {
47
+ it('should log success message when moved to folder', async () => {
48
+ const mockSend = vi.fn(() => Promise.resolve({ success: true }));
49
+ const mockClient = createMockClient(mockSend);
50
+ const deps = createMockDeps({
51
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
52
+ });
53
+ await moveToFolder({ canvasName: 'My Canvas', folderName: 'My Folder' }, deps);
54
+ expect(deps.log).toHaveBeenCalledWith('Canvas "My Canvas" moved to folder "My Folder"');
55
+ });
56
+ it('should log ungrouped message when folderName is null', async () => {
57
+ const mockSend = vi.fn(() => Promise.resolve({ success: true }));
58
+ const mockClient = createMockClient(mockSend);
59
+ const deps = createMockDeps({
60
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
61
+ });
62
+ await moveToFolder({ canvasName: 'My Canvas', folderName: null }, deps);
63
+ expect(deps.log).toHaveBeenCalledWith('Canvas "My Canvas" moved to ungrouped');
64
+ });
65
+ it('should error and exit on failure response', async () => {
66
+ const mockSend = vi.fn(() => Promise.resolve({
67
+ success: false,
68
+ error: 'Canvas not found',
69
+ }));
70
+ const mockClient = createMockClient(mockSend);
71
+ const deps = createMockDeps({
72
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
73
+ });
74
+ await moveToFolder({ canvasName: 'Test', folderName: 'Folder' }, deps);
75
+ expect(deps.error).toHaveBeenCalledWith('Failed: Canvas not found');
76
+ expect(deps.exit).toHaveBeenCalledWith(1);
77
+ });
78
+ it('should close client after success', async () => {
79
+ const mockClose = vi.fn();
80
+ const mockSend = vi.fn(() => Promise.resolve({ success: true }));
81
+ const mockClient = { send: mockSend, close: mockClose };
82
+ const deps = createMockDeps({
83
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
84
+ });
85
+ await moveToFolder({ canvasName: 'Test', folderName: 'Folder' }, deps);
86
+ expect(mockClose).toHaveBeenCalled();
87
+ });
88
+ it('should close client after failure', async () => {
89
+ const mockClose = vi.fn();
90
+ const mockSend = vi.fn(() => Promise.resolve({ success: false, error: 'error' }));
91
+ const mockClient = { send: mockSend, close: mockClose };
92
+ const deps = createMockDeps({
93
+ connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
94
+ });
95
+ await moveToFolder({ canvasName: 'Test', folderName: 'Folder' }, deps);
96
+ expect(mockClose).toHaveBeenCalled();
97
+ });
98
+ });
99
+ });
@@ -11,6 +11,7 @@ export interface AddArrowOptions {
11
11
  arrowType?: 'sharp' | 'round' | 'elbow';
12
12
  via?: string;
13
13
  note?: string;
14
+ animated?: boolean;
14
15
  }
15
16
  export interface ArrowClient {
16
17
  send: <T>(request: {
@@ -35,6 +35,7 @@ export async function addArrow(options, deps = defaultDeps) {
35
35
  arrowType: options.arrowType,
36
36
  midpoints,
37
37
  customData: options.note ? { note: options.note } : undefined,
38
+ animated: options.animated,
38
39
  };
39
40
  const result = await client.send({
40
41
  type: 'addArrow',
@@ -7,6 +7,7 @@ export interface AddLineOptions {
7
7
  strokeWidth?: number;
8
8
  strokeStyle?: 'solid' | 'dashed' | 'dotted';
9
9
  note?: string;
10
+ animated?: boolean;
10
11
  }
11
12
  export interface LineClient {
12
13
  send: <T>(request: {
@@ -18,6 +18,7 @@ export async function addLine(options, deps = defaultDeps) {
18
18
  strokeWidth: options.strokeWidth,
19
19
  strokeStyle: options.strokeStyle,
20
20
  customData: options.note ? { note: options.note } : undefined,
21
+ animated: options.animated,
21
22
  };
22
23
  const result = await client.send({
23
24
  type: 'addLine',
@@ -6,6 +6,7 @@ export interface AddPolygonOptions {
6
6
  strokeStyle?: 'solid' | 'dashed' | 'dotted';
7
7
  fillStyle?: 'hachure' | 'cross-hatch' | 'solid' | 'zigzag';
8
8
  note?: string;
9
+ animated?: boolean;
9
10
  }
10
11
  export interface PolygonClient {
11
12
  send: <T>(request: {
@@ -27,6 +27,7 @@ export async function addPolygon(options, deps = defaultDeps) {
27
27
  strokeStyle: options.strokeStyle,
28
28
  fillStyle: options.fillStyle,
29
29
  customData: options.note ? { note: options.note } : undefined,
30
+ animated: options.animated,
30
31
  };
31
32
  const result = await client.send({
32
33
  type: 'addPolygon',
@@ -13,6 +13,7 @@ export interface AddShapeOptions {
13
13
  labelFontSize?: number;
14
14
  labelStrokeColor?: string;
15
15
  note?: string;
16
+ animated?: boolean;
16
17
  }
17
18
  export interface ShapeClient {
18
19
  send: <T>(request: {
@@ -21,6 +21,7 @@ export async function addShape(options, deps = defaultDeps) {
21
21
  strokeStyle: options.strokeStyle,
22
22
  fillStyle: options.fillStyle,
23
23
  customData: options.note ? { note: options.note } : undefined,
24
+ animated: options.animated,
24
25
  };
25
26
  if (options.label) {
26
27
  params.label = {
@@ -8,6 +8,7 @@ export interface AddTextOptions {
8
8
  anchor?: TextAnchor;
9
9
  strokeColor?: string;
10
10
  note?: string;
11
+ animated?: boolean;
11
12
  }
12
13
  export interface TextClient {
13
14
  send: <T>(request: {
@@ -18,6 +18,7 @@ export async function addText(options, deps = defaultDeps) {
18
18
  anchor: options.anchor,
19
19
  strokeColor: options.strokeColor,
20
20
  customData: options.note ? { note: options.note } : undefined,
21
+ animated: options.animated,
21
22
  };
22
23
  const result = await client.send({
23
24
  type: 'addText',
@@ -0,0 +1,20 @@
1
+ export interface CreateFolderOptions {
2
+ name: string;
3
+ }
4
+ export interface CreateFolderClient {
5
+ send: <T>(request: {
6
+ type: string;
7
+ id: string;
8
+ params?: unknown;
9
+ }) => Promise<T>;
10
+ close: () => void;
11
+ }
12
+ export interface CreateFolderDeps {
13
+ connectToCanvas: () => Promise<CreateFolderClient>;
14
+ generateId: () => string;
15
+ log: (msg: string) => void;
16
+ error: (msg: string) => void;
17
+ exit: (code: number) => never | void;
18
+ }
19
+ export declare const defaultDeps: CreateFolderDeps;
20
+ export declare function createFolder(options: CreateFolderOptions, deps?: CreateFolderDeps): Promise<void>;
@@ -0,0 +1,24 @@
1
+ import { connectToCanvas, generateId } from '../lib/ws-client.js';
2
+ export const defaultDeps = {
3
+ connectToCanvas,
4
+ generateId,
5
+ log: console.log,
6
+ error: console.error,
7
+ exit: process.exit,
8
+ };
9
+ export async function createFolder(options, deps = defaultDeps) {
10
+ const client = await deps.connectToCanvas();
11
+ const result = await client.send({
12
+ type: 'createFolder',
13
+ id: deps.generateId(),
14
+ params: { name: options.name },
15
+ });
16
+ if (result.success && result.category) {
17
+ deps.log(`Folder "${result.category.name}" created`);
18
+ }
19
+ else {
20
+ deps.error(`Failed: ${result.error}`);
21
+ deps.exit(1);
22
+ }
23
+ client.close();
24
+ }
@@ -0,0 +1,20 @@
1
+ export interface DeleteFolderOptions {
2
+ name: string;
3
+ }
4
+ export interface DeleteFolderClient {
5
+ send: <T>(request: {
6
+ type: string;
7
+ id: string;
8
+ params?: unknown;
9
+ }) => Promise<T>;
10
+ close: () => void;
11
+ }
12
+ export interface DeleteFolderDeps {
13
+ connectToCanvas: () => Promise<DeleteFolderClient>;
14
+ generateId: () => string;
15
+ log: (msg: string) => void;
16
+ error: (msg: string) => void;
17
+ exit: (code: number) => never | void;
18
+ }
19
+ export declare const defaultDeps: DeleteFolderDeps;
20
+ export declare function deleteFolder(options: DeleteFolderOptions, deps?: DeleteFolderDeps): Promise<void>;
@@ -0,0 +1,24 @@
1
+ import { connectToCanvas, generateId } from '../lib/ws-client.js';
2
+ export const defaultDeps = {
3
+ connectToCanvas,
4
+ generateId,
5
+ log: console.log,
6
+ error: console.error,
7
+ exit: process.exit,
8
+ };
9
+ export async function deleteFolder(options, deps = defaultDeps) {
10
+ const client = await deps.connectToCanvas();
11
+ const result = await client.send({
12
+ type: 'deleteFolder',
13
+ id: deps.generateId(),
14
+ params: { name: options.name },
15
+ });
16
+ if (result.success) {
17
+ deps.log(`Folder "${options.name}" deleted`);
18
+ }
19
+ else {
20
+ deps.error(`Failed: ${result.error}`);
21
+ deps.exit(1);
22
+ }
23
+ client.close();
24
+ }
@@ -1,4 +1,4 @@
1
- import type { CanvasMetadata } from '../lib/protocol.js';
1
+ import type { CanvasMetadata, CanvasCategory } from '../lib/protocol.js';
2
2
  export interface ListClient {
3
3
  send: <T>(request: {
4
4
  type: string;
@@ -18,6 +18,8 @@ export interface ListResult {
18
18
  activeCanvasId?: string;
19
19
  agentActiveCanvasId?: string;
20
20
  canvases: CanvasMetadata[];
21
+ categories?: CanvasCategory[];
22
+ canvasCategoryMap?: Record<string, string>;
21
23
  }
22
24
  export declare function formatCanvasList(result: ListResult): string[];
23
25
  export declare function list(deps?: ListDeps): Promise<void>;
@@ -6,23 +6,47 @@ export const defaultDeps = {
6
6
  error: console.error,
7
7
  exit: process.exit,
8
8
  };
9
+ function getMarker(canvas, activeCanvasId, agentActiveCanvasId) {
10
+ const isUser = canvas.id === activeCanvasId;
11
+ const isAgent = canvas.id === agentActiveCanvasId;
12
+ if (isUser && isAgent)
13
+ return '[UA]';
14
+ if (isUser)
15
+ return '[U] ';
16
+ if (isAgent)
17
+ return '[A] ';
18
+ return ' ';
19
+ }
20
+ function formatCanvasLine(canvas, activeCanvasId, agentActiveCanvasId, indent = '') {
21
+ const marker = getMarker(canvas, activeCanvasId, agentActiveCanvasId);
22
+ const date = new Date(canvas.updatedAt).toLocaleString();
23
+ return `${indent}${marker} ${canvas.name} (updated: ${date})`;
24
+ }
9
25
  export function formatCanvasList(result) {
10
26
  const lines = ['Canvases: ([U]=User [A]=Agent)'];
11
- for (const canvas of result.canvases) {
12
- const isUser = canvas.id === result.activeCanvasId;
13
- const isAgent = canvas.id === result.agentActiveCanvasId;
14
- let marker = ' ';
15
- if (isUser && isAgent) {
16
- marker = '[UA]';
27
+ const categories = result.categories || [];
28
+ const categoryMap = result.canvasCategoryMap || {};
29
+ if (categories.length === 0) {
30
+ for (const canvas of result.canvases) {
31
+ lines.push(formatCanvasLine(canvas, result.activeCanvasId, result.agentActiveCanvasId));
17
32
  }
18
- else if (isUser) {
19
- marker = '[U] ';
33
+ return lines;
34
+ }
35
+ const categorizedCanvasIds = new Set(Object.keys(categoryMap));
36
+ const sorted = [...categories].sort((a, b) => a.order - b.order);
37
+ for (const cat of sorted) {
38
+ const catCanvases = result.canvases.filter(c => categoryMap[c.id] === cat.id);
39
+ lines.push(` [F] ${cat.name} (${catCanvases.length})`);
40
+ for (const canvas of catCanvases) {
41
+ lines.push(formatCanvasLine(canvas, result.activeCanvasId, result.agentActiveCanvasId, ' '));
20
42
  }
21
- else if (isAgent) {
22
- marker = '[A] ';
43
+ }
44
+ const ungrouped = result.canvases.filter(c => !categorizedCanvasIds.has(c.id));
45
+ if (ungrouped.length > 0) {
46
+ lines.push(` Ungrouped (${ungrouped.length})`);
47
+ for (const canvas of ungrouped) {
48
+ lines.push(formatCanvasLine(canvas, result.activeCanvasId, result.agentActiveCanvasId, ' '));
23
49
  }
24
- const date = new Date(canvas.updatedAt).toLocaleString();
25
- lines.push(`${marker} ${canvas.name} (updated: ${date})`);
26
50
  }
27
51
  return lines;
28
52
  }
@@ -37,6 +61,8 @@ export async function list(deps = defaultDeps) {
37
61
  activeCanvasId: result.activeCanvasId,
38
62
  agentActiveCanvasId: result.agentActiveCanvasId,
39
63
  canvases: result.canvases,
64
+ categories: result.categories,
65
+ canvasCategoryMap: result.canvasCategoryMap,
40
66
  });
41
67
  for (const line of lines) {
42
68
  deps.log(line);
@@ -0,0 +1,21 @@
1
+ export interface MoveToFolderOptions {
2
+ canvasName: string;
3
+ folderName: string | null;
4
+ }
5
+ export interface MoveToFolderClient {
6
+ send: <T>(request: {
7
+ type: string;
8
+ id: string;
9
+ params?: unknown;
10
+ }) => Promise<T>;
11
+ close: () => void;
12
+ }
13
+ export interface MoveToFolderDeps {
14
+ connectToCanvas: () => Promise<MoveToFolderClient>;
15
+ generateId: () => string;
16
+ log: (msg: string) => void;
17
+ error: (msg: string) => void;
18
+ exit: (code: number) => never | void;
19
+ }
20
+ export declare const defaultDeps: MoveToFolderDeps;
21
+ export declare function moveToFolder(options: MoveToFolderOptions, deps?: MoveToFolderDeps): Promise<void>;