@hienlh/ppm 0.9.0-beta.9 → 0.9.1

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 (262) hide show
  1. package/CHANGELOG.md +233 -0
  2. package/bun.lock +17 -0
  3. package/dist/web/assets/{_basePickBy-3Xe18azI.js → _basePickBy-5PGDJbfF.js} +1 -1
  4. package/dist/web/assets/{_baseUniq-Yy35llnn.js → _baseUniq-BT4Ow4Kk.js} +1 -1
  5. package/dist/web/assets/api-settings-BUvk6Saw.js +1 -0
  6. package/dist/web/assets/{arc-B9n1Gvb5.js → arc-BAOivWpI.js} +1 -1
  7. package/dist/web/assets/architecture-PBZL5I3N-DEO2f3VD.js +1 -0
  8. package/dist/web/assets/{architectureDiagram-2XIMDMQ5-DqAZP_F6.js → architectureDiagram-2XIMDMQ5-Z-4eN4za.js} +1 -1
  9. package/dist/web/assets/arrow-up-BYhx9ckd.js +1 -0
  10. package/dist/web/assets/{blockDiagram-WCTKOSBZ-h3cDF2vI.js → blockDiagram-WCTKOSBZ-BCLqzhuZ.js} +1 -1
  11. package/dist/web/assets/browser-tab-CrkhFCaw.js +1 -0
  12. package/dist/web/assets/{c4Diagram-IC4MRINW--pF1r5lr.js → c4Diagram-IC4MRINW-0Vp0Jeas.js} +1 -1
  13. package/dist/web/assets/channel-By7bn0Yq.js +1 -0
  14. package/dist/web/assets/chat-tab-C6jpiwh7.js +8 -0
  15. package/dist/web/assets/chevron-right-5HgK6l7K.js +1 -0
  16. package/dist/web/assets/{chunk-4BX2VUAB-C3aZvW7B.js → chunk-4BX2VUAB-D4tOov49.js} +1 -1
  17. package/dist/web/assets/{chunk-55IACEB6-D5cABeB9.js → chunk-55IACEB6-DJ6BynZ4.js} +1 -1
  18. package/dist/web/assets/{chunk-7E7YKBS2-CkFGv6Zs.js → chunk-7E7YKBS2-CiyUJxNI.js} +1 -1
  19. package/dist/web/assets/{chunk-7R4GIKGN-Dvbyu4Zw.js → chunk-7R4GIKGN-Dv-4cAYn.js} +2 -2
  20. package/dist/web/assets/{chunk-C72U2L5F-CtqKiH4q.js → chunk-C72U2L5F-D21mS_6G.js} +1 -1
  21. package/dist/web/assets/{chunk-EGIJ26TM-Cpr87sBR.js → chunk-EGIJ26TM-DzqmU2Z7.js} +1 -1
  22. package/dist/web/assets/{chunk-FMBD7UC4-D23YVTOU.js → chunk-FMBD7UC4-DXncblvW.js} +1 -1
  23. package/dist/web/assets/{chunk-GEFDOKGD-tDjHsAUs.js → chunk-GEFDOKGD-D-pKjlVd.js} +1 -1
  24. package/dist/web/assets/chunk-GLR3WWYH-DKikpoJM.js +2 -0
  25. package/dist/web/assets/chunk-HHEYEP7N-C7vxA5i9.js +1 -0
  26. package/dist/web/assets/{chunk-JSJVCQXG-BBmymCjA.js → chunk-JSJVCQXG-99JzIdPr.js} +1 -1
  27. package/dist/web/assets/{chunk-KX2RTZJC-DP36BDiU.js → chunk-KX2RTZJC-CRq1OBZv.js} +1 -1
  28. package/dist/web/assets/{chunk-KYZI473N-Djw13C-3.js → chunk-KYZI473N-Bb0MCaIO.js} +1 -1
  29. package/dist/web/assets/{chunk-L3YUKLVL-HG_eMj_C.js → chunk-L3YUKLVL-C7qGJrfV.js} +1 -1
  30. package/dist/web/assets/{chunk-MX3YWQON-C2UEioMs.js → chunk-MX3YWQON-BpS_PtKp.js} +1 -1
  31. package/dist/web/assets/{chunk-NQ4KR5QH-DXUTQ-BL.js → chunk-NQ4KR5QH-z_blpjxi.js} +1 -1
  32. package/dist/web/assets/{chunk-O4XLMI2P-BsUWb9d0.js → chunk-O4XLMI2P-nDhi_cVu.js} +1 -1
  33. package/dist/web/assets/{chunk-OZEHJAEY-rG0P22U9.js → chunk-OZEHJAEY-BXhYx3nO.js} +1 -1
  34. package/dist/web/assets/{chunk-PQ6SQG4A-DX0xW7kO.js → chunk-PQ6SQG4A-TF58UVMU.js} +1 -1
  35. package/dist/web/assets/{chunk-PU5JKC2W-C7Gry6md.js → chunk-PU5JKC2W-ek7k4QVB.js} +1 -1
  36. package/dist/web/assets/chunk-QZHKN3VN-CYaTbeZf.js +1 -0
  37. package/dist/web/assets/{chunk-R5LLSJPH-CMY0PkRK.js → chunk-R5LLSJPH-CFwSJijQ.js} +1 -1
  38. package/dist/web/assets/{chunk-WL4C6EOR-CXuQvlyu.js → chunk-WL4C6EOR-ByUrSRin.js} +1 -1
  39. package/dist/web/assets/{chunk-XIRO2GV7-DRJEb7Zb.js → chunk-XIRO2GV7-Djlmrely.js} +1 -1
  40. package/dist/web/assets/{chunk-XPW4576I-BPEX8KhL.js → chunk-XPW4576I-BPQQBakK.js} +1 -1
  41. package/dist/web/assets/{chunk-XZSTWKYB-Cb0iqycX.js → chunk-XZSTWKYB-DxAOx4hG.js} +1 -1
  42. package/dist/web/assets/{chunk-YBOYWFTD-av5aeHLq.js → chunk-YBOYWFTD-rQG3QH5s.js} +1 -1
  43. package/dist/web/assets/classDiagram-VBA2DB6C-BA8Nj-_C.js +1 -0
  44. package/dist/web/assets/classDiagram-v2-RAHNMMFH-DjYu-6mn.js +1 -0
  45. package/dist/web/assets/clone-LRxlvnMj.js +1 -0
  46. package/dist/web/assets/code-editor-CBIPzlP2.js +2 -0
  47. package/dist/web/assets/columns-2-cEVJHYd7.js +1 -0
  48. package/dist/web/assets/{cose-bilkent-S5V4N54A-qudEiMCT.js → cose-bilkent-S5V4N54A-B_AWZsOP.js} +1 -1
  49. package/dist/web/assets/createLucideIcon-PuMiQgHl.js +1 -0
  50. package/dist/web/assets/{csv-preview-DUbHtTAS.js → csv-preview-ncSOnJSC.js} +2 -2
  51. package/dist/web/assets/{dagre-BFcnKyBF.js → dagre-DHq9bhnd.js} +1 -1
  52. package/dist/web/assets/{dagre-KLK3FWXG-C3O-MTLf.js → dagre-KLK3FWXG-BdJr7Byp.js} +1 -1
  53. package/dist/web/assets/database-viewer-BqOJR_zi.js +1 -0
  54. package/dist/web/assets/{diagram-E7M64L7V-DxPjK7_c.js → diagram-E7M64L7V-_db4pBVA.js} +1 -1
  55. package/dist/web/assets/{diagram-IFDJBPK2-sqTog_XV.js → diagram-IFDJBPK2-xKoeuiJx.js} +1 -1
  56. package/dist/web/assets/{diagram-P4PSJMXO-hzmp0GHK.js → diagram-P4PSJMXO-C8tjJsev.js} +1 -1
  57. package/dist/web/assets/diff-viewer-CcLyp4eY.js +4 -0
  58. package/dist/web/assets/{dist-CALwEtco.js → dist-DIV6WgAG.js} +1 -1
  59. package/dist/web/assets/{dist-DGDPTxs1.js → dist-ovWkrgO-.js} +1 -1
  60. package/dist/web/assets/{erDiagram-INFDFZHY-DLeYhAAT.js → erDiagram-INFDFZHY-BSh2z9Df.js} +1 -1
  61. package/dist/web/assets/extension-webview-NiZ7Ybvv.js +3 -0
  62. package/dist/web/assets/{flowDiagram-PKNHOUZH-CRxlE9Sr.js → flowDiagram-PKNHOUZH-oYaovqyp.js} +1 -1
  63. package/dist/web/assets/{ganttDiagram-A5KZAMGK-BdjmoMLS.js → ganttDiagram-A5KZAMGK-DmL26q2P.js} +1 -1
  64. package/dist/web/assets/git-graph-CoTvMrIo.js +1 -0
  65. package/dist/web/assets/gitGraph-HDMCJU4V-Bwna3and.js +1 -0
  66. package/dist/web/assets/{gitGraphDiagram-K3NZZRJ6-BeHSX7kk.js → gitGraphDiagram-K3NZZRJ6-CMoukSrY.js} +1 -1
  67. package/dist/web/assets/{graphlib-Duh_bWLa.js → graphlib-BcsNnGcW.js} +1 -1
  68. package/dist/web/assets/index-C8byznLO.js +37 -0
  69. package/dist/web/assets/index-KwC2YrG4.css +2 -0
  70. package/dist/web/assets/info-3K5VOQVL-_vRxVNUm.js +1 -0
  71. package/dist/web/assets/infoDiagram-LFFYTUFH-DWwumDkq.js +2 -0
  72. package/dist/web/assets/{isEmpty-B9L-Ge-H.js → isEmpty-bnrF3Qbc.js} +1 -1
  73. package/dist/web/assets/{ishikawaDiagram-PHBUUO56-Cu0Rt1Ok.js → ishikawaDiagram-PHBUUO56-D05_LyL7.js} +1 -1
  74. package/dist/web/assets/{journeyDiagram-4ABVD52K-CgDI-UG4.js → journeyDiagram-4ABVD52K-B_L20qMe.js} +1 -1
  75. package/dist/web/assets/jsx-runtime-kMwlnEGE.js +1 -0
  76. package/dist/web/assets/{kanban-definition-K7BYSVSG-h4g10UHL.js → kanban-definition-K7BYSVSG-CZ535BbZ.js} +1 -1
  77. package/dist/web/assets/keybindings-store-DPYzBe_M.js +1 -0
  78. package/dist/web/assets/{line-B75-Rx70.js → line-CVvo3dRu.js} +1 -1
  79. package/dist/web/assets/{linear-Bcjv9FQt.js → linear-DP4mkX3m.js} +1 -1
  80. package/dist/web/assets/{markdown-renderer-VIZB1GXE.js → markdown-renderer-DPLdR9xc.js} +5 -5
  81. package/dist/web/assets/{mermaid-parser.core-8u2leTXI.js → mermaid-parser.core-C7UwoIh6.js} +2 -2
  82. package/dist/web/assets/{mindmap-definition-YRQLILUH-BaOBwb-W.js → mindmap-definition-YRQLILUH-x0MTutJp.js} +1 -1
  83. package/dist/web/assets/{ordinal-LFEjVtwQ.js → ordinal-_K3x1fkz.js} +1 -1
  84. package/dist/web/assets/packet-RMMSAZCW-DY5PNnZU.js +1 -0
  85. package/dist/web/assets/pie-UPGHQEXC-BHncZutv.js +1 -0
  86. package/dist/web/assets/{pieDiagram-SKSYHLDU-At5Kz0KK.js → pieDiagram-SKSYHLDU-C1Gjrtzy.js} +1 -1
  87. package/dist/web/assets/postgres-viewer-BeiK4lCa.js +1 -0
  88. package/dist/web/assets/{quadrantDiagram-337W2JSQ-CdjGIDfw.js → quadrantDiagram-337W2JSQ-C8bzJCjQ.js} +1 -1
  89. package/dist/web/assets/radar-KQ55EAFF-DH0AOkUy.js +1 -0
  90. package/dist/web/assets/{requirementDiagram-Z7DCOOCP-B9F_Cx_p.js → requirementDiagram-Z7DCOOCP-pQyah6WB.js} +1 -1
  91. package/dist/web/assets/{sankeyDiagram-WA2Y5GQK-RolPi8bU.js → sankeyDiagram-WA2Y5GQK-T6RgG-N8.js} +1 -1
  92. package/dist/web/assets/{sequenceDiagram-2WXFIKYE-DM-tMAhx.js → sequenceDiagram-2WXFIKYE-BQDJ4CVs.js} +1 -1
  93. package/dist/web/assets/settings-tab-D3AvU4lu.js +1 -0
  94. package/dist/web/assets/sqlite-viewer-nA2sD4Yv.js +1 -0
  95. package/dist/web/assets/{stateDiagram-RAJIS63D-C4EMl6jf.js → stateDiagram-RAJIS63D-66vhiIuk.js} +1 -1
  96. package/dist/web/assets/stateDiagram-v2-FVOUBMTO-BGVqj_g9.js +1 -0
  97. package/dist/web/assets/tab-store-BOgTrqRr.js +1 -0
  98. package/dist/web/assets/table-DFevCOMd.js +1 -0
  99. package/dist/web/assets/tag-CXMT0QB6.js +1 -0
  100. package/dist/web/assets/{terminal-tab-XhKfb4ei.js → terminal-tab-BBi0pEji.js} +1 -1
  101. package/dist/web/assets/{timeline-definition-YZTLITO2-A4PN_Efm.js → timeline-definition-YZTLITO2-DwZqB3nn.js} +1 -1
  102. package/dist/web/assets/treemap-KZPCXAKY-B2Xkyv-K.js +1 -0
  103. package/dist/web/assets/{use-monaco-theme-0p0-84jJ.js → use-monaco-theme-B5pG2d1w.js} +1 -1
  104. package/dist/web/assets/{vennDiagram-LZ73GAT5-ywK7LMaH.js → vennDiagram-LZ73GAT5-s9Z71fz-.js} +1 -1
  105. package/dist/web/assets/{xychartDiagram-JWTSCODW-DylHYNtJ.js → xychartDiagram-JWTSCODW-DRa_TH4B.js} +1 -1
  106. package/dist/web/index.html +10 -9
  107. package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
  108. package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
  109. package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
  110. package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
  111. package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
  112. package/dist/web/sw.js +1 -1
  113. package/docs/code-standards.md +128 -1
  114. package/docs/codebase-summary.md +79 -12
  115. package/docs/extension-development-guide.md +532 -0
  116. package/docs/project-changelog.md +51 -1
  117. package/docs/project-roadmap.md +9 -3
  118. package/docs/streaming-input-guide.md +267 -0
  119. package/docs/system-architecture.md +432 -3
  120. package/package.json +6 -3
  121. package/packages/ext-database/package.json +41 -0
  122. package/packages/ext-database/src/connection-tree.ts +142 -0
  123. package/packages/ext-database/src/extension.ts +346 -0
  124. package/packages/ext-database/src/query-panel.ts +120 -0
  125. package/packages/ext-database/src/table-viewer-panel.ts +410 -0
  126. package/packages/ext-database/tsconfig.json +8 -0
  127. package/packages/vscode-compat/package.json +16 -0
  128. package/packages/vscode-compat/src/commands.ts +39 -0
  129. package/packages/vscode-compat/src/context.ts +65 -0
  130. package/packages/vscode-compat/src/disposable.ts +21 -0
  131. package/packages/vscode-compat/src/env.ts +20 -0
  132. package/packages/vscode-compat/src/event-emitter.ts +28 -0
  133. package/packages/vscode-compat/src/index.ts +93 -0
  134. package/packages/vscode-compat/src/not-supported.ts +15 -0
  135. package/packages/vscode-compat/src/types.ts +167 -0
  136. package/packages/vscode-compat/src/uri.ts +65 -0
  137. package/packages/vscode-compat/src/window.ts +229 -0
  138. package/packages/vscode-compat/src/workspace.ts +76 -0
  139. package/packages/vscode-compat/tsconfig.json +10 -0
  140. package/snapshot-state.md +1526 -0
  141. package/src/cli/commands/autostart.ts +1 -1
  142. package/src/cli/commands/ext-cmd.ts +121 -0
  143. package/src/cli/commands/restart.ts +9 -1
  144. package/src/cli/commands/status.ts +19 -0
  145. package/src/index.ts +5 -3
  146. package/src/providers/claude-agent-sdk.ts +221 -17
  147. package/src/providers/cli-provider-base.ts +6 -0
  148. package/src/server/index.ts +55 -155
  149. package/src/server/routes/chat.ts +81 -11
  150. package/src/server/routes/extensions.ts +81 -0
  151. package/src/server/routes/project-scoped.ts +2 -0
  152. package/src/server/routes/settings.ts +27 -0
  153. package/src/server/routes/workspace.ts +35 -0
  154. package/src/server/ws/chat.ts +9 -3
  155. package/src/server/ws/extensions.ts +175 -0
  156. package/src/services/account-selector.service.ts +14 -5
  157. package/src/services/account.service.ts +7 -7
  158. package/src/services/claude-usage.service.ts +11 -11
  159. package/src/services/cloud-ws.service.ts +228 -0
  160. package/src/services/cloud.service.ts +1 -0
  161. package/src/services/contribution-registry.ts +110 -0
  162. package/src/services/db.service.ts +181 -4
  163. package/src/services/extension-host-worker.ts +160 -0
  164. package/src/services/extension-installer.ts +112 -0
  165. package/src/services/extension-manifest.ts +65 -0
  166. package/src/services/extension-rpc-handlers.ts +235 -0
  167. package/src/services/extension-rpc.ts +105 -0
  168. package/src/services/extension.service.ts +228 -0
  169. package/src/services/mcp-config.service.ts +15 -6
  170. package/src/services/supervisor.ts +271 -25
  171. package/src/types/api.ts +1 -0
  172. package/src/types/chat.ts +4 -0
  173. package/src/types/extension-messages.ts +64 -0
  174. package/src/types/extension.ts +131 -0
  175. package/src/web/app.tsx +69 -48
  176. package/src/web/components/chat/account-rotation-settings.tsx +163 -0
  177. package/src/web/components/chat/chat-history-bar.tsx +106 -10
  178. package/src/web/components/chat/chat-tab.tsx +15 -10
  179. package/src/web/components/chat/chat-welcome.tsx +148 -0
  180. package/src/web/components/chat/message-list.tsx +19 -6
  181. package/src/web/components/chat/session-picker.tsx +80 -32
  182. package/src/web/components/chat/usage-badge.tsx +68 -8
  183. package/src/web/components/extensions/extension-inputbox.tsx +92 -0
  184. package/src/web/components/extensions/extension-quickpick.tsx +194 -0
  185. package/src/web/components/extensions/extension-tree-view.tsx +240 -0
  186. package/src/web/components/extensions/extension-webview.tsx +83 -0
  187. package/src/web/components/layout/command-palette.tsx +22 -2
  188. package/src/web/components/layout/editor-panel.tsx +163 -18
  189. package/src/web/components/layout/mobile-nav.tsx +2 -1
  190. package/src/web/components/layout/sidebar.tsx +21 -3
  191. package/src/web/components/layout/status-bar.tsx +64 -0
  192. package/src/web/components/layout/tab-bar.tsx +2 -0
  193. package/src/web/components/layout/tab-content.tsx +5 -0
  194. package/src/web/components/layout/upgrade-banner.tsx +15 -5
  195. package/src/web/components/settings/change-password-section.tsx +128 -0
  196. package/src/web/components/settings/extension-manager-section.tsx +214 -0
  197. package/src/web/components/settings/settings-tab.tsx +9 -2
  198. package/src/web/components/shared/connection-lost-overlay.tsx +89 -0
  199. package/src/web/hooks/use-chat.ts +28 -0
  200. package/src/web/hooks/use-extension-ws.ts +181 -0
  201. package/src/web/hooks/use-global-keybindings.ts +18 -2
  202. package/src/web/hooks/use-server-reload.ts +9 -0
  203. package/src/web/hooks/use-url-sync.ts +173 -21
  204. package/src/web/stores/connection-store.ts +39 -0
  205. package/src/web/stores/extension-store.ts +204 -0
  206. package/src/web/stores/panel-store.ts +63 -9
  207. package/src/web/stores/panel-utils.ts +145 -3
  208. package/src/web/stores/settings-store.ts +7 -2
  209. package/src/web/stores/tab-store.ts +2 -1
  210. package/test-session-ops.mjs +444 -0
  211. package/test-tokens.mjs +212 -0
  212. package/tsconfig.json +3 -1
  213. package/dist/web/assets/api-settings-CEMxVMCV.js +0 -1
  214. package/dist/web/assets/architecture-PBZL5I3N-CFzkFKEL.js +0 -1
  215. package/dist/web/assets/arrow-up--LjUXLEt.js +0 -1
  216. package/dist/web/assets/browser-tab-D1Zua62g.js +0 -1
  217. package/dist/web/assets/channel-C2fMafck.js +0 -1
  218. package/dist/web/assets/chat-tab-BnD27Vp9.js +0 -7
  219. package/dist/web/assets/chevron-right-CHnjJt4E.js +0 -1
  220. package/dist/web/assets/chunk-GLR3WWYH-DBdWQ3zy.js +0 -2
  221. package/dist/web/assets/chunk-HHEYEP7N-BBw_z0fW.js +0 -1
  222. package/dist/web/assets/chunk-QZHKN3VN-DFKFM_C1.js +0 -1
  223. package/dist/web/assets/classDiagram-VBA2DB6C-Dp4Kk3Yb.js +0 -1
  224. package/dist/web/assets/classDiagram-v2-RAHNMMFH-D8IvcV_B.js +0 -1
  225. package/dist/web/assets/clone-B2hUek6n.js +0 -1
  226. package/dist/web/assets/code-editor-DGRg8stf.js +0 -2
  227. package/dist/web/assets/columns-2-DbesTfa7.js +0 -1
  228. package/dist/web/assets/database-viewer-DxCXZQcE.js +0 -1
  229. package/dist/web/assets/diff-viewer-C1sDJG35.js +0 -4
  230. package/dist/web/assets/git-graph-BDn-EiGE.js +0 -1
  231. package/dist/web/assets/gitGraph-HDMCJU4V-CNlas3Rz.js +0 -1
  232. package/dist/web/assets/index-Bun94AK3.js +0 -37
  233. package/dist/web/assets/index-Db8uky1a.css +0 -2
  234. package/dist/web/assets/info-3K5VOQVL-BDzTLc11.js +0 -1
  235. package/dist/web/assets/infoDiagram-LFFYTUFH-ZZmpgc6t.js +0 -2
  236. package/dist/web/assets/jsx-runtime-BRW_vwa9.js +0 -1
  237. package/dist/web/assets/keybindings-store-COmK4Dte.js +0 -1
  238. package/dist/web/assets/packet-RMMSAZCW-IVa5F-go.js +0 -1
  239. package/dist/web/assets/pie-UPGHQEXC-CvXHKAzp.js +0 -1
  240. package/dist/web/assets/postgres-viewer-CvQZ8gkh.js +0 -1
  241. package/dist/web/assets/radar-KQ55EAFF-Z-Tr5wtS.js +0 -1
  242. package/dist/web/assets/settings-tab-RCnvZ29H.js +0 -1
  243. package/dist/web/assets/sqlite-viewer-CEEm2W4C.js +0 -1
  244. package/dist/web/assets/stateDiagram-v2-FVOUBMTO-B-UjZch3.js +0 -1
  245. package/dist/web/assets/tab-store-Bjh6bXFP.js +0 -1
  246. package/dist/web/assets/table-CQVQM2SB.js +0 -1
  247. package/dist/web/assets/tag-Q2dZiSPX.js +0 -1
  248. package/dist/web/assets/treemap-KZPCXAKY-C9TYRE0k.js +0 -1
  249. /package/dist/web/assets/{api-client-BKIT_Qeg.js → api-client-BfBM3I7n.js} +0 -0
  250. /package/dist/web/assets/{array-DqLCdDFv.js → array-B9UHiPd-.js} +0 -0
  251. /package/dist/web/assets/{cytoscape.esm-CWPXKqbJ.js → cytoscape.esm-BW-DbntU.js} +0 -0
  252. /package/dist/web/assets/{defaultLocale-CrJzLgRD.js → defaultLocale-5eAKkKJC.js} +0 -0
  253. /package/dist/web/assets/{dist-Cep75xXf.js → dist-CSJdAyA9.js} +0 -0
  254. /package/dist/web/assets/{init-C0r9Gk5G.js → init-DlZdxViB.js} +0 -0
  255. /package/dist/web/assets/{isArrayLikeObject-CGBoxvCD.js → isArrayLikeObject-B_v2FtYn.js} +0 -0
  256. /package/dist/web/assets/{katex-DzXRfQ_m.js → katex-Bqvo_ZG0.js} +0 -0
  257. /package/dist/web/assets/{lib-BeaDXEkP.js → lib-BQ34Db2e.js} +0 -0
  258. /package/dist/web/assets/{math-y9zN1W-N.js → math-069Z4SuC.js} +0 -0
  259. /package/dist/web/assets/{path-DIKpVbHL.js → path-6uRLdFF7.js} +0 -0
  260. /package/dist/web/assets/{rough.esm-nHaDi0Kw.js → rough.esm-JX0wREDd.js} +0 -0
  261. /package/dist/web/assets/{src-Dw4QhedI.js → src-BqX54PbV.js} +0 -0
  262. /package/dist/web/assets/{utils-DMiycH3O.js → utils-BNytJOb1.js} +0 -0
@@ -0,0 +1,194 @@
1
+ import { useState, useEffect, useRef, useMemo, useCallback } from "react";
2
+ import { Check, Search } from "lucide-react";
3
+ import { useExtensionStore, type QuickPickItemUI } from "@/stores/extension-store";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ /** Modal overlay for extension-driven QuickPick selection */
7
+ export function ExtensionQuickPick() {
8
+ const quickPick = useExtensionStore((s) => s.quickPick);
9
+ const resolveQuickPick = useExtensionStore((s) => s.resolveQuickPick);
10
+
11
+ if (!quickPick) return null;
12
+
13
+ return (
14
+ <QuickPickModal
15
+ items={quickPick.items}
16
+ options={quickPick.options}
17
+ onSelect={(selected) => resolveQuickPick(selected)}
18
+ onCancel={() => resolveQuickPick(undefined)}
19
+ />
20
+ );
21
+ }
22
+
23
+ function QuickPickModal({
24
+ items,
25
+ options,
26
+ onSelect,
27
+ onCancel,
28
+ }: {
29
+ items: QuickPickItemUI[];
30
+ options: { placeholder?: string; canPickMany?: boolean };
31
+ onSelect: (selected: QuickPickItemUI[]) => void;
32
+ onCancel: () => void;
33
+ }) {
34
+ const [query, setQuery] = useState("");
35
+ const [selectedIdx, setSelectedIdx] = useState(0);
36
+ const [picked, setPicked] = useState<Set<number>>(() => {
37
+ const initial = new Set<number>();
38
+ items.forEach((item, i) => { if (item.picked) initial.add(i); });
39
+ return initial;
40
+ });
41
+ const inputRef = useRef<HTMLInputElement>(null);
42
+ const listRef = useRef<HTMLDivElement>(null);
43
+ const canPickMany = options.canPickMany ?? false;
44
+
45
+ useEffect(() => {
46
+ requestAnimationFrame(() => inputRef.current?.focus());
47
+ }, []);
48
+
49
+ const filtered = useMemo(() => {
50
+ if (!query.trim()) return items.map((item, i) => ({ item, originalIdx: i }));
51
+ const q = query.toLowerCase();
52
+ return items
53
+ .map((item, i) => ({ item, originalIdx: i }))
54
+ .filter(({ item }) =>
55
+ item.label.toLowerCase().includes(q) ||
56
+ item.description?.toLowerCase().includes(q) ||
57
+ item.detail?.toLowerCase().includes(q),
58
+ );
59
+ }, [items, query]);
60
+
61
+ // Clamp selection when filter changes
62
+ useEffect(() => {
63
+ setSelectedIdx((prev) => Math.min(prev, Math.max(filtered.length - 1, 0)));
64
+ }, [filtered.length]);
65
+
66
+ // Scroll selected into view
67
+ useEffect(() => {
68
+ const el = listRef.current?.children[selectedIdx] as HTMLElement | undefined;
69
+ el?.scrollIntoView({ block: "nearest" });
70
+ }, [selectedIdx]);
71
+
72
+ const confirmSelection = useCallback(() => {
73
+ if (canPickMany) {
74
+ const selected = items.filter((_, i) => picked.has(i));
75
+ onSelect(selected);
76
+ } else if (filtered[selectedIdx]) {
77
+ onSelect([filtered[selectedIdx].item]);
78
+ }
79
+ }, [canPickMany, picked, items, filtered, selectedIdx, onSelect]);
80
+
81
+ function handleKeyDown(e: React.KeyboardEvent) {
82
+ switch (e.key) {
83
+ case "ArrowDown":
84
+ e.preventDefault();
85
+ if (filtered.length > 0) setSelectedIdx((i) => (i + 1) % filtered.length);
86
+ break;
87
+ case "ArrowUp":
88
+ e.preventDefault();
89
+ if (filtered.length > 0) setSelectedIdx((i) => (i - 1 + filtered.length) % filtered.length);
90
+ break;
91
+ case " ":
92
+ if (canPickMany && filtered[selectedIdx]) {
93
+ e.preventDefault();
94
+ const origIdx = filtered[selectedIdx].originalIdx;
95
+ setPicked((prev) => {
96
+ const next = new Set(prev);
97
+ if (next.has(origIdx)) next.delete(origIdx); else next.add(origIdx);
98
+ return next;
99
+ });
100
+ }
101
+ break;
102
+ case "Enter":
103
+ e.preventDefault();
104
+ confirmSelection();
105
+ break;
106
+ case "Escape":
107
+ e.preventDefault();
108
+ onCancel();
109
+ break;
110
+ }
111
+ }
112
+
113
+ return (
114
+ <div className="fixed inset-0 z-50 flex items-end md:items-start justify-center md:pt-[20vh]" onClick={onCancel}>
115
+ <div className="fixed inset-0 bg-black/50" />
116
+ <div
117
+ className="relative z-10 w-full max-w-md rounded-t-xl md:rounded-xl border border-border bg-background shadow-2xl overflow-hidden max-h-[80vh] md:max-h-none"
118
+ onClick={(e) => e.stopPropagation()}
119
+ onKeyDown={handleKeyDown}
120
+ >
121
+ {/* Search input */}
122
+ <div className="flex items-center gap-2 border-b border-border px-3 py-2.5">
123
+ <Search className="size-4 text-text-subtle shrink-0" />
124
+ <input
125
+ ref={inputRef}
126
+ type="text"
127
+ value={query}
128
+ onChange={(e) => setQuery(e.target.value)}
129
+ placeholder={options.placeholder ?? "Select an item..."}
130
+ className="flex-1 bg-transparent text-sm text-text-primary outline-none placeholder:text-text-subtle"
131
+ />
132
+ <kbd className="hidden sm:inline-flex items-center rounded border border-border bg-surface px-1.5 py-0.5 text-[10px] text-text-subtle font-mono">
133
+ ESC
134
+ </kbd>
135
+ </div>
136
+
137
+ {/* Results */}
138
+ <div ref={listRef} className="max-h-72 overflow-y-auto py-1">
139
+ {filtered.length === 0 ? (
140
+ <p className="px-3 py-4 text-sm text-text-subtle text-center">No matching items</p>
141
+ ) : (
142
+ filtered.map(({ item, originalIdx }, i) => (
143
+ <button
144
+ key={originalIdx}
145
+ onClick={() => {
146
+ if (canPickMany) {
147
+ setPicked((prev) => {
148
+ const next = new Set(prev);
149
+ if (next.has(originalIdx)) next.delete(originalIdx); else next.add(originalIdx);
150
+ return next;
151
+ });
152
+ } else {
153
+ onSelect([item]);
154
+ }
155
+ }}
156
+ className={cn(
157
+ "flex items-center gap-3 w-full px-3 py-2 text-sm text-left transition-colors",
158
+ i === selectedIdx ? "bg-accent/15 text-text-primary" : "text-text-secondary hover:bg-surface-elevated",
159
+ )}
160
+ >
161
+ {canPickMany && (
162
+ <span className={cn(
163
+ "size-4 shrink-0 rounded border flex items-center justify-center",
164
+ picked.has(originalIdx) ? "bg-primary border-primary text-primary-foreground" : "border-border",
165
+ )}>
166
+ {picked.has(originalIdx) && <Check className="size-3" />}
167
+ </span>
168
+ )}
169
+ <div className="flex-1 min-w-0">
170
+ <div className="truncate">{item.label}</div>
171
+ {item.description && <span className="text-xs text-text-subtle ml-2">{item.description}</span>}
172
+ {item.detail && <div className="text-xs text-text-subtle truncate mt-0.5">{item.detail}</div>}
173
+ </div>
174
+ </button>
175
+ ))
176
+ )}
177
+ </div>
178
+
179
+ {/* Footer hint for multi-select */}
180
+ {canPickMany && (
181
+ <div className="flex items-center justify-between border-t border-border px-3 py-1.5">
182
+ <span className="text-[10px] text-text-subtle">{picked.size} selected</span>
183
+ <button
184
+ onClick={confirmSelection}
185
+ className="text-xs text-primary hover:text-primary/80 font-medium"
186
+ >
187
+ Confirm
188
+ </button>
189
+ </div>
190
+ )}
191
+ </div>
192
+ </div>
193
+ );
194
+ }
@@ -0,0 +1,240 @@
1
+ import { useCallback, useState, useRef, useMemo } from "react";
2
+ import { ChevronRight, ChevronDown, RefreshCw, Pencil, Trash2, Plus, Search } from "lucide-react";
3
+ import { useExtensionStore, type TreeItemUI, type TreeItemAction } from "@/stores/extension-store";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ interface ExtensionTreeViewProps {
7
+ viewId: string;
8
+ className?: string;
9
+ }
10
+
11
+ /** Dispatch a tree:expand request to fetch children from the server */
12
+ function requestTreeExpand(viewId: string, itemId: string) {
13
+ window.dispatchEvent(new CustomEvent("ext:tree:expand", {
14
+ detail: { viewId, itemId },
15
+ }));
16
+ }
17
+
18
+ /** Dispatch a command:execute request via the WS bridge */
19
+ function executeCommand(command: string, args?: unknown[]) {
20
+ window.dispatchEvent(new CustomEvent("ext:command:execute", {
21
+ detail: { command, args },
22
+ }));
23
+ }
24
+
25
+ const ACTION_ICONS: Record<string, React.ElementType> = {
26
+ refresh: RefreshCw,
27
+ edit: Pencil,
28
+ trash: Trash2,
29
+ plus: Plus,
30
+ search: Search,
31
+ };
32
+
33
+ /** Generic TreeView renderer for extension-contributed tree data */
34
+ export function ExtensionTreeView({ viewId, className }: ExtensionTreeViewProps) {
35
+ const items = useExtensionStore((s) => s.treeViews[viewId]) ?? [];
36
+ const contributions = useExtensionStore((s) => s.contributions);
37
+
38
+ // Find view name & header actions from contributions
39
+ const viewMeta = useMemo(() => {
40
+ if (!contributions) return { name: viewId, headerActions: [] };
41
+ // Find view name
42
+ let name = viewId;
43
+ const views = contributions.views;
44
+ if (views) {
45
+ for (const group of Object.values(views)) {
46
+ const found = group.find((v) => v.id === viewId);
47
+ if (found) { name = found.name; break; }
48
+ }
49
+ }
50
+ // Find header actions from menus.view/title
51
+ const headerActions: { command: string; title: string; icon?: string }[] = [];
52
+ const viewTitleMenus = contributions.menus?.["view/title"];
53
+ if (viewTitleMenus) {
54
+ for (const menu of viewTitleMenus) {
55
+ // Check "when" clause: view == viewId
56
+ if (menu.when) {
57
+ const match = menu.when.match(/view\s*==\s*(\S+)/);
58
+ if (match && match[1] !== viewId) continue;
59
+ }
60
+ // Find command title
61
+ const cmd = contributions.commands?.find((c) => c.command === menu.command);
62
+ if (cmd) headerActions.push({ command: cmd.command, title: cmd.title, icon: cmd.icon });
63
+ }
64
+ }
65
+ return { name, headerActions };
66
+ }, [contributions, viewId]);
67
+
68
+ return (
69
+ <div className={cn("flex flex-col h-full", className)}>
70
+ {/* Header — matches built-in DatabaseSidebar header */}
71
+ <div className="flex items-center justify-between px-3 py-2 border-b border-border shrink-0">
72
+ <span className="text-[10px] font-semibold text-text-subtle uppercase tracking-wider">
73
+ {viewMeta.name}
74
+ </span>
75
+ <div className="flex items-center gap-0.5">
76
+ {viewMeta.headerActions.map((action) => {
77
+ const iconName = action.icon ?? inferIcon(action.command);
78
+ const Icon = ACTION_ICONS[iconName] ?? RefreshCw;
79
+ return (
80
+ <button
81
+ key={action.command}
82
+ onClick={() => executeCommand(action.command)}
83
+ className="flex items-center justify-center size-5 rounded hover:bg-surface-elevated transition-colors text-text-subtle hover:text-foreground"
84
+ title={action.title}
85
+ >
86
+ <Icon className="size-3.5" />
87
+ </button>
88
+ );
89
+ })}
90
+ </div>
91
+ </div>
92
+
93
+ {/* Tree content */}
94
+ <div className="flex-1 overflow-y-auto min-h-0 py-1" role="tree" aria-label={viewId}>
95
+ {items.length === 0 ? (
96
+ <p className="px-4 py-6 text-xs text-text-subtle text-center">No items</p>
97
+ ) : (
98
+ items.map((item) => (
99
+ <TreeNode key={item.id} item={item} depth={0} viewId={viewId} />
100
+ ))
101
+ )}
102
+ </div>
103
+ </div>
104
+ );
105
+ }
106
+
107
+ /** Infer icon name from command ID */
108
+ function inferIcon(command: string): string {
109
+ if (command.includes("refresh")) return "refresh";
110
+ if (command.includes("add") || command.includes("create") || command.includes("new")) return "plus";
111
+ if (command.includes("delete") || command.includes("remove")) return "trash";
112
+ if (command.includes("edit") || command.includes("update")) return "edit";
113
+ if (command.includes("search") || command.includes("find")) return "search";
114
+ return "refresh";
115
+ }
116
+
117
+ function TreeNode({ item, depth, viewId }: { item: TreeItemUI; depth: number; viewId: string }) {
118
+ const [expanded, setExpanded] = useState(false);
119
+ const hasChildren = item.collapsibleState !== "none";
120
+ const childrenLoaded = useRef(false);
121
+
122
+ // When children arrive from server, auto-expand once
123
+ if (item.children && item.children.length > 0 && !childrenLoaded.current) {
124
+ childrenLoaded.current = true;
125
+ if (!expanded) setExpanded(true);
126
+ }
127
+
128
+ const handleToggle = useCallback(() => {
129
+ if (!hasChildren) return;
130
+ const willExpand = !expanded;
131
+ setExpanded(willExpand);
132
+ if (willExpand && (!item.children || item.children.length === 0)) {
133
+ requestTreeExpand(viewId, item.id);
134
+ }
135
+ }, [hasChildren, expanded, item.id, item.children, viewId]);
136
+
137
+ const handleClick = useCallback(() => {
138
+ handleToggle();
139
+ if (item.command) {
140
+ executeCommand(item.command, item.commandArgs);
141
+ }
142
+ }, [handleToggle, item.command, item.commandArgs]);
143
+
144
+ const paddingLeft = 8 + depth * 16;
145
+
146
+ return (
147
+ <div role="treeitem" aria-expanded={hasChildren ? expanded : undefined}>
148
+ <div
149
+ className={cn("group/node flex items-center gap-1 py-1 hover:bg-surface-elevated transition-colors")}
150
+ style={{ paddingLeft, paddingRight: 8 }}
151
+ >
152
+ {/* Expand chevron */}
153
+ <button
154
+ onClick={handleToggle}
155
+ className="shrink-0 text-text-subtle hover:text-foreground transition-colors"
156
+ >
157
+ {hasChildren ? (
158
+ expanded ? <ChevronDown className="size-3" /> : <ChevronRight className="size-3" />
159
+ ) : (
160
+ <span className="size-3" />
161
+ )}
162
+ </button>
163
+
164
+ {/* Color dot */}
165
+ {item.color && (
166
+ <span
167
+ className="shrink-0 size-2 rounded-full border border-border"
168
+ style={{ backgroundColor: item.color }}
169
+ />
170
+ )}
171
+
172
+ {/* Label — click to toggle or execute command */}
173
+ <button
174
+ className="flex-1 text-left text-xs truncate hover:text-primary transition-colors"
175
+ onClick={handleClick}
176
+ >
177
+ {item.label}
178
+ </button>
179
+
180
+ {/* Description (column type info) */}
181
+ {item.description && (
182
+ <span className="shrink-0 ml-1 text-text-subtle text-[10px]">{item.description}</span>
183
+ )}
184
+
185
+ {/* Badge (PG/DB) */}
186
+ {item.badge && (
187
+ <span className="shrink-0 text-[9px] text-text-subtle uppercase px-1 rounded bg-surface-elevated">
188
+ {item.badge}
189
+ </span>
190
+ )}
191
+
192
+ {/* Action buttons (visible on hover) */}
193
+ {item.actions && item.actions.length > 0 && (
194
+ <div className="hidden group-hover/node:flex items-center gap-0.5 shrink-0">
195
+ {item.actions.map((action) => (
196
+ <ActionButton key={action.command} action={action} />
197
+ ))}
198
+ </div>
199
+ )}
200
+ </div>
201
+
202
+ {/* Children */}
203
+ {hasChildren && expanded && item.children && (
204
+ <div role="group">
205
+ {item.children.map((child) => (
206
+ <TreeNode key={child.id} item={child} depth={depth + 1} viewId={viewId} />
207
+ ))}
208
+ </div>
209
+ )}
210
+ </div>
211
+ );
212
+ }
213
+
214
+ function ActionButton({ action }: { action: TreeItemAction }) {
215
+ const [spinning, setSpinning] = useState(false);
216
+ const Icon = ACTION_ICONS[action.icon] ?? RefreshCw;
217
+ const isTrash = action.icon === "trash";
218
+
219
+ const handleClick = useCallback((e: React.MouseEvent) => {
220
+ e.stopPropagation();
221
+ if (action.icon === "refresh") {
222
+ setSpinning(true);
223
+ setTimeout(() => setSpinning(false), 1000);
224
+ }
225
+ executeCommand(action.command, action.commandArgs);
226
+ }, [action]);
227
+
228
+ return (
229
+ <button
230
+ onClick={handleClick}
231
+ className={cn(
232
+ "p-0.5 text-text-subtle transition-colors",
233
+ isTrash ? "hover:text-red-500" : "hover:text-foreground",
234
+ )}
235
+ title={action.tooltip}
236
+ >
237
+ <Icon className={cn("size-3", spinning && "animate-spin")} />
238
+ </button>
239
+ );
240
+ }
@@ -0,0 +1,83 @@
1
+ import { useRef, useEffect, useCallback } from "react";
2
+ import { useExtensionStore } from "@/stores/extension-store";
3
+
4
+ /** Inject acquireVsCodeApi() shim so extension webviews can postMessage to parent */
5
+ const VSCODE_API_SHIM = `<script>
6
+ function acquireVsCodeApi(){return{postMessage:function(m){window.parent.postMessage(m,"*")},getState:function(){try{return JSON.parse(sessionStorage.getItem("vscode-state")||"null")}catch{return null}},setState:function(s){sessionStorage.setItem("vscode-state",JSON.stringify(s));return s}}}
7
+ </script>`;
8
+
9
+ function injectVscodeApiShim(html: string): string {
10
+ if (!html) return html;
11
+ // Insert shim right after <head> tag (or at start if no <head>)
12
+ const headIdx = html.indexOf("<head>");
13
+ if (headIdx !== -1) {
14
+ return html.slice(0, headIdx + 6) + VSCODE_API_SHIM + html.slice(headIdx + 6);
15
+ }
16
+ return VSCODE_API_SHIM + html;
17
+ }
18
+
19
+ interface ExtensionWebviewProps {
20
+ metadata?: Record<string, unknown>;
21
+ }
22
+
23
+ /**
24
+ * iframe-based webview container for extension-contributed webview panels.
25
+ * Renders as a tab component in the editor panel system.
26
+ */
27
+ export function ExtensionWebview({ metadata }: ExtensionWebviewProps) {
28
+ const panelId = metadata?.panelId as string | undefined;
29
+ const panel = useExtensionStore((s) => panelId ? s.webviewPanels[panelId] : undefined);
30
+ const iframeRef = useRef<HTMLIFrameElement>(null);
31
+
32
+ // Inject acquireVsCodeApi shim + write HTML into iframe via srcdoc
33
+ const rawHtml = panel?.html ?? "";
34
+ const html = injectVscodeApiShim(rawHtml);
35
+
36
+ // Listen for postMessage from iframe → forward to extension via WS bridge
37
+ useEffect(() => {
38
+ const handler = (event: MessageEvent) => {
39
+ if (iframeRef.current && event.source === iframeRef.current.contentWindow) {
40
+ // Forward to server via custom event → picked up by useExtensionWs
41
+ window.dispatchEvent(new CustomEvent("ext:webview:send", {
42
+ detail: { panelId, message: event.data },
43
+ }));
44
+ }
45
+ };
46
+ window.addEventListener("message", handler);
47
+ return () => window.removeEventListener("message", handler);
48
+ }, [panelId]);
49
+
50
+ // Listen for server→webview messages (dispatched by useExtensionWs)
51
+ // targetOrigin "*" is safe here because sandbox omits allow-same-origin,
52
+ // so iframe origin is opaque "null". MUST restrict if allow-same-origin is ever added.
53
+ useEffect(() => {
54
+ const handler = (e: Event) => {
55
+ const { panelId: targetId, message } = (e as CustomEvent).detail;
56
+ if (targetId === panelId) {
57
+ iframeRef.current?.contentWindow?.postMessage(message, "*");
58
+ }
59
+ };
60
+ window.addEventListener("ext:webview:message", handler);
61
+ return () => window.removeEventListener("ext:webview:message", handler);
62
+ }, [panelId]);
63
+
64
+ if (!panel) {
65
+ return (
66
+ <div className="flex items-center justify-center h-full text-sm text-text-subtle">
67
+ Webview panel not found
68
+ </div>
69
+ );
70
+ }
71
+
72
+ return (
73
+ <div className="h-full w-full relative">
74
+ <iframe
75
+ ref={iframeRef}
76
+ srcDoc={html}
77
+ sandbox="allow-scripts"
78
+ className="w-full h-full border-0 bg-white dark:bg-zinc-900"
79
+ title={panel.title}
80
+ />
81
+ </div>
82
+ );
83
+ }
@@ -12,12 +12,14 @@ import {
12
12
  Loader2,
13
13
  Globe,
14
14
  Mic,
15
+ Puzzle,
15
16
  } from "lucide-react";
16
17
  import { useTabStore, type TabType } from "@/stores/tab-store";
17
18
  import { useProjectStore } from "@/stores/project-store";
18
19
  import { useSettingsStore } from "@/stores/settings-store";
19
20
  import { useKeybindingsStore } from "@/stores/keybindings-store";
20
21
  import { useFileStore, type FileNode } from "@/stores/file-store";
22
+ import { useExtensionStore } from "@/stores/extension-store";
21
23
  import { api } from "@/lib/api-client";
22
24
  import { basename } from "@/lib/utils";
23
25
 
@@ -103,6 +105,7 @@ export function CommandPalette({ open, onClose, initialQuery = "" }: { open: boo
103
105
  const sidebarCollapsed = useSettingsStore((s) => s.sidebarCollapsed);
104
106
  const toggleSidebar = useSettingsStore((s) => s.toggleSidebar);
105
107
  const getBinding = useKeybindingsStore((s) => s.getBinding);
108
+ const extContributions = useExtensionStore((s) => s.contributions);
106
109
 
107
110
  // Fetch filesystem files when path query changes directory
108
111
  const fetchFsFiles = useCallback(async (dir: string) => {
@@ -154,7 +157,7 @@ export function CommandPalette({ open, onClose, initialQuery = "" }: { open: boo
154
157
  onClose();
155
158
  };
156
159
 
157
- return [
160
+ const builtIn: CommandItem[] = [
158
161
  { id: "chat", label: "New AI Chat", icon: MessageSquare, action: openNewTab("chat", "AI Chat"), keywords: "ai assistant claude", group: "action", shortcut: formatShortcut(getBinding("open-chat")) },
159
162
  { id: "terminal", label: "New Terminal", icon: Terminal, action: openNewTab("terminal", "Terminal"), keywords: "bash shell console", group: "action", shortcut: formatShortcut(getBinding("open-terminal")) },
160
163
  { id: "git-graph", label: "Git Graph", icon: GitBranch, action: openNewTab("git-graph", "Git Graph"), keywords: "branch history log", group: "action", shortcut: formatShortcut(getBinding("open-git-graph")) },
@@ -174,7 +177,24 @@ export function CommandPalette({ open, onClose, initialQuery = "" }: { open: boo
174
177
  shortcut: formatShortcut(getBinding("open-settings")),
175
178
  },
176
179
  ];
177
- }, [activeProject, openTab, onClose, setSidebarActiveTab, sidebarCollapsed, toggleSidebar, getBinding]);
180
+
181
+ // Append extension-contributed commands
182
+ const extCmds: CommandItem[] = (extContributions?.commands ?? []).map((cmd) => ({
183
+ id: `ext:${cmd.command}`,
184
+ label: cmd.title,
185
+ hint: cmd.category,
186
+ icon: Puzzle,
187
+ group: "action" as const,
188
+ keywords: `extension ${cmd.command} ${cmd.category ?? ""}`,
189
+ action: () => {
190
+ // Phase 4: execute via WS bridge
191
+ console.log("[CmdPalette] ext command:", cmd.command);
192
+ onClose();
193
+ },
194
+ }));
195
+
196
+ return [...builtIn, ...extCmds];
197
+ }, [activeProject, openTab, onClose, setSidebarActiveTab, sidebarCollapsed, toggleSidebar, getBinding, extContributions]);
178
198
 
179
199
  // File commands — derived from file store tree (project files)
180
200
  const fileCommands = useMemo<CommandItem[]>(() => {