@hienlh/ppm 0.9.0-beta.9 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude.bak/agent-memory/tester/MEMORY.md +3 -0
- package/.claude.bak/agent-memory/tester/project-ppm-test-conventions.md +32 -0
- package/CHANGELOG.md +240 -0
- package/bun.lock +17 -0
- package/dist/web/assets/{_basePickBy-3Xe18azI.js → _basePickBy-5PGDJbfF.js} +1 -1
- package/dist/web/assets/{_baseUniq-Yy35llnn.js → _baseUniq-BT4Ow4Kk.js} +1 -1
- package/dist/web/assets/api-settings-BUvk6Saw.js +1 -0
- package/dist/web/assets/{arc-B9n1Gvb5.js → arc-BAOivWpI.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-DEO2f3VD.js +1 -0
- package/dist/web/assets/{architectureDiagram-2XIMDMQ5-DqAZP_F6.js → architectureDiagram-2XIMDMQ5-Z-4eN4za.js} +1 -1
- package/dist/web/assets/arrow-up-BYhx9ckd.js +1 -0
- package/dist/web/assets/{blockDiagram-WCTKOSBZ-h3cDF2vI.js → blockDiagram-WCTKOSBZ-BCLqzhuZ.js} +1 -1
- package/dist/web/assets/browser-tab-CjUzlPYv.js +1 -0
- package/dist/web/assets/{c4Diagram-IC4MRINW--pF1r5lr.js → c4Diagram-IC4MRINW-0Vp0Jeas.js} +1 -1
- package/dist/web/assets/channel-By7bn0Yq.js +1 -0
- package/dist/web/assets/chat-tab-moB4W7-w.js +8 -0
- package/dist/web/assets/chevron-right-5HgK6l7K.js +1 -0
- package/dist/web/assets/{chunk-4BX2VUAB-C3aZvW7B.js → chunk-4BX2VUAB-D4tOov49.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-D5cABeB9.js → chunk-55IACEB6-DJ6BynZ4.js} +1 -1
- package/dist/web/assets/{chunk-7E7YKBS2-CkFGv6Zs.js → chunk-7E7YKBS2-CiyUJxNI.js} +1 -1
- package/dist/web/assets/{chunk-7R4GIKGN-Dvbyu4Zw.js → chunk-7R4GIKGN-Dv-4cAYn.js} +2 -2
- package/dist/web/assets/{chunk-C72U2L5F-CtqKiH4q.js → chunk-C72U2L5F-D21mS_6G.js} +1 -1
- package/dist/web/assets/{chunk-EGIJ26TM-Cpr87sBR.js → chunk-EGIJ26TM-DzqmU2Z7.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-D23YVTOU.js → chunk-FMBD7UC4-DXncblvW.js} +1 -1
- package/dist/web/assets/{chunk-GEFDOKGD-tDjHsAUs.js → chunk-GEFDOKGD-D-pKjlVd.js} +1 -1
- package/dist/web/assets/chunk-GLR3WWYH-DKikpoJM.js +2 -0
- package/dist/web/assets/chunk-HHEYEP7N-C7vxA5i9.js +1 -0
- package/dist/web/assets/{chunk-JSJVCQXG-BBmymCjA.js → chunk-JSJVCQXG-99JzIdPr.js} +1 -1
- package/dist/web/assets/{chunk-KX2RTZJC-DP36BDiU.js → chunk-KX2RTZJC-CRq1OBZv.js} +1 -1
- package/dist/web/assets/{chunk-KYZI473N-Djw13C-3.js → chunk-KYZI473N-Bb0MCaIO.js} +1 -1
- package/dist/web/assets/{chunk-L3YUKLVL-HG_eMj_C.js → chunk-L3YUKLVL-C7qGJrfV.js} +1 -1
- package/dist/web/assets/{chunk-MX3YWQON-C2UEioMs.js → chunk-MX3YWQON-BpS_PtKp.js} +1 -1
- package/dist/web/assets/{chunk-NQ4KR5QH-DXUTQ-BL.js → chunk-NQ4KR5QH-z_blpjxi.js} +1 -1
- package/dist/web/assets/{chunk-O4XLMI2P-BsUWb9d0.js → chunk-O4XLMI2P-nDhi_cVu.js} +1 -1
- package/dist/web/assets/{chunk-OZEHJAEY-rG0P22U9.js → chunk-OZEHJAEY-BXhYx3nO.js} +1 -1
- package/dist/web/assets/{chunk-PQ6SQG4A-DX0xW7kO.js → chunk-PQ6SQG4A-TF58UVMU.js} +1 -1
- package/dist/web/assets/{chunk-PU5JKC2W-C7Gry6md.js → chunk-PU5JKC2W-ek7k4QVB.js} +1 -1
- package/dist/web/assets/chunk-QZHKN3VN-CYaTbeZf.js +1 -0
- package/dist/web/assets/{chunk-R5LLSJPH-CMY0PkRK.js → chunk-R5LLSJPH-CFwSJijQ.js} +1 -1
- package/dist/web/assets/{chunk-WL4C6EOR-CXuQvlyu.js → chunk-WL4C6EOR-ByUrSRin.js} +1 -1
- package/dist/web/assets/{chunk-XIRO2GV7-DRJEb7Zb.js → chunk-XIRO2GV7-Djlmrely.js} +1 -1
- package/dist/web/assets/{chunk-XPW4576I-BPEX8KhL.js → chunk-XPW4576I-BPQQBakK.js} +1 -1
- package/dist/web/assets/{chunk-XZSTWKYB-Cb0iqycX.js → chunk-XZSTWKYB-DxAOx4hG.js} +1 -1
- package/dist/web/assets/{chunk-YBOYWFTD-av5aeHLq.js → chunk-YBOYWFTD-rQG3QH5s.js} +1 -1
- package/dist/web/assets/classDiagram-VBA2DB6C-BA8Nj-_C.js +1 -0
- package/dist/web/assets/classDiagram-v2-RAHNMMFH-DjYu-6mn.js +1 -0
- package/dist/web/assets/clone-LRxlvnMj.js +1 -0
- package/dist/web/assets/code-editor-aQQZUc2m.js +2 -0
- package/dist/web/assets/columns-2-cEVJHYd7.js +1 -0
- package/dist/web/assets/{cose-bilkent-S5V4N54A-qudEiMCT.js → cose-bilkent-S5V4N54A-B_AWZsOP.js} +1 -1
- package/dist/web/assets/createLucideIcon-PuMiQgHl.js +1 -0
- package/dist/web/assets/{csv-preview-DUbHtTAS.js → csv-preview-ncSOnJSC.js} +2 -2
- package/dist/web/assets/{dagre-BFcnKyBF.js → dagre-DHq9bhnd.js} +1 -1
- package/dist/web/assets/{dagre-KLK3FWXG-C3O-MTLf.js → dagre-KLK3FWXG-BdJr7Byp.js} +1 -1
- package/dist/web/assets/database-viewer-ChyP1N3c.js +1 -0
- package/dist/web/assets/{diagram-E7M64L7V-DxPjK7_c.js → diagram-E7M64L7V-_db4pBVA.js} +1 -1
- package/dist/web/assets/{diagram-IFDJBPK2-sqTog_XV.js → diagram-IFDJBPK2-xKoeuiJx.js} +1 -1
- package/dist/web/assets/{diagram-P4PSJMXO-hzmp0GHK.js → diagram-P4PSJMXO-C8tjJsev.js} +1 -1
- package/dist/web/assets/diff-viewer-ktwO5JbX.js +4 -0
- package/dist/web/assets/{dist-CALwEtco.js → dist-DIV6WgAG.js} +1 -1
- package/dist/web/assets/{dist-DGDPTxs1.js → dist-ovWkrgO-.js} +1 -1
- package/dist/web/assets/{erDiagram-INFDFZHY-DLeYhAAT.js → erDiagram-INFDFZHY-BSh2z9Df.js} +1 -1
- package/dist/web/assets/extension-webview-Bx1TlP6q.js +3 -0
- package/dist/web/assets/{flowDiagram-PKNHOUZH-CRxlE9Sr.js → flowDiagram-PKNHOUZH-oYaovqyp.js} +1 -1
- package/dist/web/assets/{ganttDiagram-A5KZAMGK-BdjmoMLS.js → ganttDiagram-A5KZAMGK-DmL26q2P.js} +1 -1
- package/dist/web/assets/git-graph-BIrGMX6e.js +1 -0
- package/dist/web/assets/gitGraph-HDMCJU4V-Bwna3and.js +1 -0
- package/dist/web/assets/{gitGraphDiagram-K3NZZRJ6-BeHSX7kk.js → gitGraphDiagram-K3NZZRJ6-CMoukSrY.js} +1 -1
- package/dist/web/assets/{graphlib-Duh_bWLa.js → graphlib-BcsNnGcW.js} +1 -1
- package/dist/web/assets/index-C6KLr58u.js +37 -0
- package/dist/web/assets/index-DpBKDbIW.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-_vRxVNUm.js +1 -0
- package/dist/web/assets/infoDiagram-LFFYTUFH-DWwumDkq.js +2 -0
- package/dist/web/assets/{isEmpty-B9L-Ge-H.js → isEmpty-bnrF3Qbc.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-PHBUUO56-Cu0Rt1Ok.js → ishikawaDiagram-PHBUUO56-D05_LyL7.js} +1 -1
- package/dist/web/assets/{journeyDiagram-4ABVD52K-CgDI-UG4.js → journeyDiagram-4ABVD52K-B_L20qMe.js} +1 -1
- package/dist/web/assets/jsx-runtime-kMwlnEGE.js +1 -0
- package/dist/web/assets/{kanban-definition-K7BYSVSG-h4g10UHL.js → kanban-definition-K7BYSVSG-CZ535BbZ.js} +1 -1
- package/dist/web/assets/keybindings-store-D3Y5c5uS.js +1 -0
- package/dist/web/assets/{line-B75-Rx70.js → line-CVvo3dRu.js} +1 -1
- package/dist/web/assets/{linear-Bcjv9FQt.js → linear-DP4mkX3m.js} +1 -1
- package/dist/web/assets/{markdown-renderer-VIZB1GXE.js → markdown-renderer-A7J2gdKT.js} +5 -5
- package/dist/web/assets/{mermaid-parser.core-8u2leTXI.js → mermaid-parser.core-C7UwoIh6.js} +2 -2
- package/dist/web/assets/{mindmap-definition-YRQLILUH-BaOBwb-W.js → mindmap-definition-YRQLILUH-x0MTutJp.js} +1 -1
- package/dist/web/assets/{ordinal-LFEjVtwQ.js → ordinal-_K3x1fkz.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-DY5PNnZU.js +1 -0
- package/dist/web/assets/pie-UPGHQEXC-BHncZutv.js +1 -0
- package/dist/web/assets/{pieDiagram-SKSYHLDU-At5Kz0KK.js → pieDiagram-SKSYHLDU-C1Gjrtzy.js} +1 -1
- package/dist/web/assets/postgres-viewer-C9-Acry_.js +1 -0
- package/dist/web/assets/{quadrantDiagram-337W2JSQ-CdjGIDfw.js → quadrantDiagram-337W2JSQ-C8bzJCjQ.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-DH0AOkUy.js +1 -0
- package/dist/web/assets/{requirementDiagram-Z7DCOOCP-B9F_Cx_p.js → requirementDiagram-Z7DCOOCP-pQyah6WB.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-WA2Y5GQK-RolPi8bU.js → sankeyDiagram-WA2Y5GQK-T6RgG-N8.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-2WXFIKYE-DM-tMAhx.js → sequenceDiagram-2WXFIKYE-BQDJ4CVs.js} +1 -1
- package/dist/web/assets/settings-tab-C17exmRv.js +1 -0
- package/dist/web/assets/sqlite-viewer-Dr5oWCWA.js +1 -0
- package/dist/web/assets/{stateDiagram-RAJIS63D-C4EMl6jf.js → stateDiagram-RAJIS63D-66vhiIuk.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-FVOUBMTO-BGVqj_g9.js +1 -0
- package/dist/web/assets/tab-store-BOgTrqRr.js +1 -0
- package/dist/web/assets/table-DFevCOMd.js +1 -0
- package/dist/web/assets/tag-CXMT0QB6.js +1 -0
- package/dist/web/assets/{terminal-tab-XhKfb4ei.js → terminal-tab-CpyKvyfC.js} +1 -1
- package/dist/web/assets/{timeline-definition-YZTLITO2-A4PN_Efm.js → timeline-definition-YZTLITO2-DwZqB3nn.js} +1 -1
- package/dist/web/assets/treemap-KZPCXAKY-B2Xkyv-K.js +1 -0
- package/dist/web/assets/{use-monaco-theme-0p0-84jJ.js → use-monaco-theme-BjPAik5w.js} +1 -1
- package/dist/web/assets/{vennDiagram-LZ73GAT5-ywK7LMaH.js → vennDiagram-LZ73GAT5-s9Z71fz-.js} +1 -1
- package/dist/web/assets/{xychartDiagram-JWTSCODW-DylHYNtJ.js → xychartDiagram-JWTSCODW-DRa_TH4B.js} +1 -1
- package/dist/web/index.html +10 -9
- package/dist/web/sw.js +1 -1
- package/docs/code-standards.md +128 -1
- package/docs/codebase-summary.md +79 -12
- package/docs/extension-development-guide.md +532 -0
- package/docs/project-changelog.md +51 -1
- package/docs/project-roadmap.md +9 -3
- package/docs/system-architecture.md +432 -3
- package/package.json +6 -3
- package/packages/ext-database/package.json +41 -0
- package/packages/ext-database/src/connection-tree.ts +142 -0
- package/packages/ext-database/src/extension.ts +346 -0
- package/packages/ext-database/src/query-panel.ts +120 -0
- package/packages/ext-database/src/table-viewer-panel.ts +410 -0
- package/packages/ext-database/tsconfig.json +8 -0
- package/packages/vscode-compat/package.json +16 -0
- package/packages/vscode-compat/src/commands.ts +39 -0
- package/packages/vscode-compat/src/context.ts +65 -0
- package/packages/vscode-compat/src/disposable.ts +21 -0
- package/packages/vscode-compat/src/env.ts +20 -0
- package/packages/vscode-compat/src/event-emitter.ts +28 -0
- package/packages/vscode-compat/src/index.ts +93 -0
- package/packages/vscode-compat/src/not-supported.ts +15 -0
- package/packages/vscode-compat/src/types.ts +167 -0
- package/packages/vscode-compat/src/uri.ts +65 -0
- package/packages/vscode-compat/src/window.ts +229 -0
- package/packages/vscode-compat/src/workspace.ts +76 -0
- package/packages/vscode-compat/tsconfig.json +10 -0
- package/src/cli/commands/autostart.ts +1 -1
- package/src/cli/commands/ext-cmd.ts +121 -0
- package/src/cli/commands/restart.ts +9 -1
- package/src/cli/commands/status.ts +19 -0
- package/src/index.ts +5 -3
- package/src/providers/claude-agent-sdk.ts +221 -17
- package/src/providers/cli-provider-base.ts +6 -0
- package/src/server/index.ts +55 -155
- package/src/server/routes/chat.ts +81 -11
- package/src/server/routes/extensions.ts +81 -0
- package/src/server/routes/project-scoped.ts +2 -0
- package/src/server/routes/settings.ts +27 -0
- package/src/server/routes/workspace.ts +35 -0
- package/src/server/ws/chat.ts +9 -3
- package/src/server/ws/extensions.ts +175 -0
- package/src/services/account-selector.service.ts +14 -5
- package/src/services/account.service.ts +7 -7
- package/src/services/claude-usage.service.ts +11 -11
- package/src/services/cloud-ws.service.ts +228 -0
- package/src/services/cloud.service.ts +1 -0
- package/src/services/contribution-registry.ts +110 -0
- package/src/services/db.service.ts +181 -4
- package/src/services/extension-host-worker.ts +160 -0
- package/src/services/extension-installer.ts +112 -0
- package/src/services/extension-manifest.ts +65 -0
- package/src/services/extension-rpc-handlers.ts +235 -0
- package/src/services/extension-rpc.ts +105 -0
- package/src/services/extension.service.ts +228 -0
- package/src/services/mcp-config.service.ts +15 -6
- package/src/services/supervisor.ts +271 -25
- package/src/types/api.ts +1 -0
- package/src/types/chat.ts +4 -0
- package/src/types/extension-messages.ts +64 -0
- package/src/types/extension.ts +131 -0
- package/src/web/app.tsx +69 -48
- package/src/web/components/chat/account-rotation-settings.tsx +163 -0
- package/src/web/components/chat/chat-history-bar.tsx +106 -10
- package/src/web/components/chat/chat-tab.tsx +15 -10
- package/src/web/components/chat/chat-welcome.tsx +148 -0
- package/src/web/components/chat/message-input.tsx +29 -29
- package/src/web/components/chat/message-list.tsx +19 -6
- package/src/web/components/chat/session-picker.tsx +80 -32
- package/src/web/components/chat/usage-badge.tsx +83 -10
- package/src/web/components/extensions/extension-inputbox.tsx +92 -0
- package/src/web/components/extensions/extension-quickpick.tsx +194 -0
- package/src/web/components/extensions/extension-tree-view.tsx +240 -0
- package/src/web/components/extensions/extension-webview.tsx +83 -0
- package/src/web/components/layout/command-palette.tsx +22 -2
- package/src/web/components/layout/editor-panel.tsx +163 -18
- package/src/web/components/layout/mobile-nav.tsx +2 -1
- package/src/web/components/layout/sidebar.tsx +21 -3
- package/src/web/components/layout/status-bar.tsx +64 -0
- package/src/web/components/layout/tab-bar.tsx +2 -0
- package/src/web/components/layout/tab-content.tsx +5 -0
- package/src/web/components/layout/upgrade-banner.tsx +15 -5
- package/src/web/components/settings/change-password-section.tsx +128 -0
- package/src/web/components/settings/extension-manager-section.tsx +214 -0
- package/src/web/components/settings/settings-tab.tsx +9 -2
- package/src/web/components/shared/connection-lost-overlay.tsx +89 -0
- package/src/web/hooks/use-chat.ts +28 -0
- package/src/web/hooks/use-extension-ws.ts +181 -0
- package/src/web/hooks/use-global-keybindings.ts +18 -2
- package/src/web/hooks/use-server-reload.ts +9 -0
- package/src/web/hooks/use-url-sync.ts +173 -21
- package/src/web/stores/connection-store.ts +39 -0
- package/src/web/stores/extension-store.ts +204 -0
- package/src/web/stores/panel-store.ts +63 -9
- package/src/web/stores/panel-utils.ts +145 -3
- package/src/web/stores/settings-store.ts +7 -2
- package/src/web/stores/tab-store.ts +2 -1
- package/tsconfig.json +3 -1
- package/dist/web/assets/api-settings-CEMxVMCV.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-CFzkFKEL.js +0 -1
- package/dist/web/assets/arrow-up--LjUXLEt.js +0 -1
- package/dist/web/assets/browser-tab-D1Zua62g.js +0 -1
- package/dist/web/assets/channel-C2fMafck.js +0 -1
- package/dist/web/assets/chat-tab-BnD27Vp9.js +0 -7
- package/dist/web/assets/chevron-right-CHnjJt4E.js +0 -1
- package/dist/web/assets/chunk-GLR3WWYH-DBdWQ3zy.js +0 -2
- package/dist/web/assets/chunk-HHEYEP7N-BBw_z0fW.js +0 -1
- package/dist/web/assets/chunk-QZHKN3VN-DFKFM_C1.js +0 -1
- package/dist/web/assets/classDiagram-VBA2DB6C-Dp4Kk3Yb.js +0 -1
- package/dist/web/assets/classDiagram-v2-RAHNMMFH-D8IvcV_B.js +0 -1
- package/dist/web/assets/clone-B2hUek6n.js +0 -1
- package/dist/web/assets/code-editor-DGRg8stf.js +0 -2
- package/dist/web/assets/columns-2-DbesTfa7.js +0 -1
- package/dist/web/assets/database-viewer-DxCXZQcE.js +0 -1
- package/dist/web/assets/diff-viewer-C1sDJG35.js +0 -4
- package/dist/web/assets/git-graph-BDn-EiGE.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-CNlas3Rz.js +0 -1
- package/dist/web/assets/index-Bun94AK3.js +0 -37
- package/dist/web/assets/index-Db8uky1a.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-BDzTLc11.js +0 -1
- package/dist/web/assets/infoDiagram-LFFYTUFH-ZZmpgc6t.js +0 -2
- package/dist/web/assets/jsx-runtime-BRW_vwa9.js +0 -1
- package/dist/web/assets/keybindings-store-COmK4Dte.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-IVa5F-go.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-CvXHKAzp.js +0 -1
- package/dist/web/assets/postgres-viewer-CvQZ8gkh.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-Z-Tr5wtS.js +0 -1
- package/dist/web/assets/settings-tab-RCnvZ29H.js +0 -1
- package/dist/web/assets/sqlite-viewer-CEEm2W4C.js +0 -1
- package/dist/web/assets/stateDiagram-v2-FVOUBMTO-B-UjZch3.js +0 -1
- package/dist/web/assets/tab-store-Bjh6bXFP.js +0 -1
- package/dist/web/assets/table-CQVQM2SB.js +0 -1
- package/dist/web/assets/tag-Q2dZiSPX.js +0 -1
- package/dist/web/assets/treemap-KZPCXAKY-C9TYRE0k.js +0 -1
- /package/dist/web/assets/{api-client-BKIT_Qeg.js → api-client-BfBM3I7n.js} +0 -0
- /package/dist/web/assets/{array-DqLCdDFv.js → array-B9UHiPd-.js} +0 -0
- /package/dist/web/assets/{cytoscape.esm-CWPXKqbJ.js → cytoscape.esm-BW-DbntU.js} +0 -0
- /package/dist/web/assets/{defaultLocale-CrJzLgRD.js → defaultLocale-5eAKkKJC.js} +0 -0
- /package/dist/web/assets/{dist-Cep75xXf.js → dist-CSJdAyA9.js} +0 -0
- /package/dist/web/assets/{init-C0r9Gk5G.js → init-DlZdxViB.js} +0 -0
- /package/dist/web/assets/{isArrayLikeObject-CGBoxvCD.js → isArrayLikeObject-B_v2FtYn.js} +0 -0
- /package/dist/web/assets/{katex-DzXRfQ_m.js → katex-Bqvo_ZG0.js} +0 -0
- /package/dist/web/assets/{lib-BeaDXEkP.js → lib-BQ34Db2e.js} +0 -0
- /package/dist/web/assets/{math-y9zN1W-N.js → math-069Z4SuC.js} +0 -0
- /package/dist/web/assets/{path-DIKpVbHL.js → path-6uRLdFF7.js} +0 -0
- /package/dist/web/assets/{rough.esm-nHaDi0Kw.js → rough.esm-JX0wREDd.js} +0 -0
- /package/dist/web/assets/{src-Dw4QhedI.js → src-BqX54PbV.js} +0 -0
- /package/dist/web/assets/{utils-DMiycH3O.js → utils-BNytJOb1.js} +0 -0
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
# PPM Extension Development Guide
|
|
2
|
+
|
|
3
|
+
Build extensions that add features to PPM. Extensions are TypeScript packages that hook into PPM's UI, commands, file system, and configuration.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Extension Basics
|
|
8
|
+
|
|
9
|
+
### Structure
|
|
10
|
+
|
|
11
|
+
A PPM extension is a directory with `package.json` manifest and TypeScript entry point:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
my-extension/
|
|
15
|
+
├── package.json
|
|
16
|
+
├── tsconfig.json
|
|
17
|
+
├── src/
|
|
18
|
+
│ └── extension.ts # Entry point with activate() & deactivate()
|
|
19
|
+
├── webview/ # Optional: webview HTML/CSS/JS
|
|
20
|
+
│ └── index.html
|
|
21
|
+
└── README.md
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Manifest (package.json)
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"name": "@org/ext-myfeature",
|
|
29
|
+
"version": "1.0.0",
|
|
30
|
+
"main": "src/extension.ts",
|
|
31
|
+
"engines": { "ppm": ">=0.9.0" },
|
|
32
|
+
"activationEvents": [
|
|
33
|
+
"onCommand:myext.doSomething",
|
|
34
|
+
"onView:myext.sidebar"
|
|
35
|
+
],
|
|
36
|
+
"contributes": {
|
|
37
|
+
"commands": [
|
|
38
|
+
{ "command": "myext.doSomething", "title": "My Feature: Do Something" }
|
|
39
|
+
],
|
|
40
|
+
"views": {
|
|
41
|
+
"sidebar": [
|
|
42
|
+
{ "id": "myext.sidebar", "name": "My Sidebar", "type": "tree" }
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"menus": {
|
|
46
|
+
"commandPalette": [
|
|
47
|
+
{ "command": "myext.doSomething" }
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
"configuration": {
|
|
51
|
+
"properties": {
|
|
52
|
+
"myext.setting": { "type": "boolean", "default": true }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"ppm": {
|
|
57
|
+
"displayName": "My Feature",
|
|
58
|
+
"icon": "star",
|
|
59
|
+
"webviewDir": "webview"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Key fields:**
|
|
65
|
+
- `main` — TypeScript file with `export function activate(context, vscode) {}`
|
|
66
|
+
- `engines.ppm` — minimum PPM version (not `vscode`)
|
|
67
|
+
- `activationEvents` — when extension loads (commands, views, or startup)
|
|
68
|
+
- `contributes` — UI elements: commands, views, menus, config
|
|
69
|
+
- `ppm.displayName` — human-readable name in extension UI
|
|
70
|
+
- `ppm.icon` — icon name (standard VSCode icon set)
|
|
71
|
+
- `ppm.webviewDir` — directory containing webview HTML
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Activation & Lifecycle
|
|
76
|
+
|
|
77
|
+
Extensions run in an isolated Bun Worker thread. The Worker receives `context` and `vscode` API:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
/** Runs when activation event fires */
|
|
81
|
+
export function activate(context: ExtensionContext, vscode: VscodeApi): void {
|
|
82
|
+
console.log("Extension activated");
|
|
83
|
+
|
|
84
|
+
// Register commands, listeners, views, etc.
|
|
85
|
+
context.subscriptions.push(
|
|
86
|
+
vscode.commands.registerCommand("myext.foo", () => {
|
|
87
|
+
// ...
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Cleanup on disable/uninstall */
|
|
93
|
+
export function deactivate(): void {
|
|
94
|
+
console.log("Extension deactivated");
|
|
95
|
+
// Browser/streams are auto-closed; use this for final logging
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Important:** Push ALL disposables (commands, listeners, panels, views) to `context.subscriptions` for automatic cleanup.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## API Surface
|
|
104
|
+
|
|
105
|
+
### Commands
|
|
106
|
+
|
|
107
|
+
Register and execute commands:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Register
|
|
111
|
+
const cmd = vscode.commands.registerCommand("myext.foo", (arg1, arg2) => {
|
|
112
|
+
return "result";
|
|
113
|
+
});
|
|
114
|
+
context.subscriptions.push(cmd);
|
|
115
|
+
|
|
116
|
+
// Execute (from command palette or button click)
|
|
117
|
+
// vscode.commands.executeCommand("myext.foo", arg1, arg2);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Messages (Dialogs)
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// Information
|
|
124
|
+
await vscode.window.showInformationMessage("Done!", "OK", "Cancel");
|
|
125
|
+
|
|
126
|
+
// Warning
|
|
127
|
+
await vscode.window.showWarningMessage("Are you sure?", "Yes", "No");
|
|
128
|
+
|
|
129
|
+
// Error
|
|
130
|
+
await vscode.window.showErrorMessage("Failed!", "Retry");
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Quick Pick
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const selected = await vscode.window.showQuickPick(
|
|
137
|
+
[
|
|
138
|
+
{ label: "Option A", description: "First choice" },
|
|
139
|
+
{ label: "Option B", description: "Second choice" }
|
|
140
|
+
],
|
|
141
|
+
{
|
|
142
|
+
title: "Choose one",
|
|
143
|
+
placeHolder: "Start typing to filter...",
|
|
144
|
+
canPickMany: false,
|
|
145
|
+
matchOnDescription: true
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Input Box
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
const input = await vscode.window.showInputBox({
|
|
154
|
+
title: "Enter name",
|
|
155
|
+
prompt: "What is your name?",
|
|
156
|
+
placeHolder: "John Doe",
|
|
157
|
+
password: false
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Status Bar
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const item = vscode.window.createStatusBarItem(
|
|
165
|
+
vscode.StatusBarAlignment.Right,
|
|
166
|
+
100 // priority
|
|
167
|
+
);
|
|
168
|
+
item.text = "⚙️ Status";
|
|
169
|
+
item.tooltip = "Click for menu";
|
|
170
|
+
item.command = "myext.openMenu";
|
|
171
|
+
item.show();
|
|
172
|
+
context.subscriptions.push(item);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Tree View
|
|
176
|
+
|
|
177
|
+
Sidebar tree for displaying hierarchical data:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
class MyTreeProvider {
|
|
181
|
+
getChildren(element?: NodeData): Promise<NodeData[]> {
|
|
182
|
+
if (!element) {
|
|
183
|
+
// Root items
|
|
184
|
+
return [
|
|
185
|
+
{ id: "1", label: "Item 1" },
|
|
186
|
+
{ id: "2", label: "Item 2" }
|
|
187
|
+
];
|
|
188
|
+
}
|
|
189
|
+
// Children of element
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
getTreeItem(element: NodeData) {
|
|
194
|
+
return {
|
|
195
|
+
id: element.id,
|
|
196
|
+
label: element.label,
|
|
197
|
+
collapsibleState: element.children?.length ?
|
|
198
|
+
vscode.TreeItemCollapsibleState.Collapsed :
|
|
199
|
+
vscode.TreeItemCollapsibleState.None,
|
|
200
|
+
command: { command: "myext.selectNode", arguments: [element.id] }
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const provider = new MyTreeProvider();
|
|
206
|
+
const tree = vscode.window.createTreeView("myext.tree", {
|
|
207
|
+
treeDataProvider: provider
|
|
208
|
+
});
|
|
209
|
+
context.subscriptions.push(tree);
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**viewId** must match the `contributes.views[].id` in package.json.
|
|
213
|
+
|
|
214
|
+
### Webview Panel
|
|
215
|
+
|
|
216
|
+
Sandboxed iframe for custom UI:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
const panel = vscode.window.createWebviewPanel(
|
|
220
|
+
"myext.panel", // viewType (unique)
|
|
221
|
+
"My Panel", // title (shown in tab)
|
|
222
|
+
vscode.ViewColumn.Active
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// Set HTML (scripts auto-sandboxed, allow-scripts only)
|
|
226
|
+
panel.webview.html = `
|
|
227
|
+
<!DOCTYPE html>
|
|
228
|
+
<html>
|
|
229
|
+
<body>
|
|
230
|
+
<button id="btn">Click me</button>
|
|
231
|
+
<script>
|
|
232
|
+
const vscode = acquireVsCodeApi();
|
|
233
|
+
document.getElementById("btn").onclick = () => {
|
|
234
|
+
vscode.postMessage({ type: "buttonClicked" });
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
window.addEventListener("message", (e) => {
|
|
238
|
+
console.log("From extension:", e.data);
|
|
239
|
+
});
|
|
240
|
+
</script>
|
|
241
|
+
</body>
|
|
242
|
+
</html>
|
|
243
|
+
`;
|
|
244
|
+
|
|
245
|
+
// Receive messages from webview
|
|
246
|
+
panel.webview.onDidReceiveMessage(async (msg) => {
|
|
247
|
+
if (msg.type === "buttonClicked") {
|
|
248
|
+
await vscode.window.showInformationMessage("Button clicked!");
|
|
249
|
+
await panel.webview.postMessage({ type: "response", data: "Hello from extension" });
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
context.subscriptions.push(panel.onDidDispose(() => {
|
|
254
|
+
console.log("Panel closed");
|
|
255
|
+
}));
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Configuration
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// Read
|
|
262
|
+
const config = vscode.workspace.getConfiguration("myext");
|
|
263
|
+
const timeout = config.get<number>("timeout", 5000);
|
|
264
|
+
|
|
265
|
+
// Update
|
|
266
|
+
await config.update("timeout", 10000, vscode.ConfigurationTarget.Global);
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Storage (Memento)
|
|
270
|
+
|
|
271
|
+
Persist data across sessions:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
// Global (user-wide)
|
|
275
|
+
await context.globalState.update("key", { foo: "bar" });
|
|
276
|
+
const value = context.globalState.get("key");
|
|
277
|
+
|
|
278
|
+
// Workspace-scoped
|
|
279
|
+
await context.workspaceState.update("project", "data");
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Utilities
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
// URI
|
|
286
|
+
const uri = vscode.Uri.file("/path/to/file");
|
|
287
|
+
const webUri = panel.webview.asWebviewUri(uri);
|
|
288
|
+
|
|
289
|
+
// Disposable
|
|
290
|
+
const disposable = vscode.Disposable.from(
|
|
291
|
+
vscode.commands.registerCommand("a", () => {}),
|
|
292
|
+
vscode.commands.registerCommand("b", () => {})
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
// EventEmitter
|
|
296
|
+
const emitter = new vscode.EventEmitter<string>();
|
|
297
|
+
emitter.fire("event data"); // Notify listeners
|
|
298
|
+
// const unsub = emitter.event((data) => {});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## API Support Matrix
|
|
304
|
+
|
|
305
|
+
| Feature | Status | Notes |
|
|
306
|
+
|---------|--------|-------|
|
|
307
|
+
| **Commands** | ✅ Supported | `registerCommand`, `executeCommand` |
|
|
308
|
+
| **Messages** | ✅ Supported | Info, warning, error dialogs |
|
|
309
|
+
| **Quick Pick** | ✅ Supported | Single/multi-select, filtering |
|
|
310
|
+
| **Input Box** | ✅ Supported | Text input with validation (client-side) |
|
|
311
|
+
| **Status Bar** | ✅ Supported | Left/right alignment, priority, commands |
|
|
312
|
+
| **Tree View** | ✅ Supported | Hierarchical sidebar trees |
|
|
313
|
+
| **Webview Panel** | ✅ Supported | Sandboxed iframe, 2-way messaging |
|
|
314
|
+
| **Workspace Config** | ✅ Supported | Read/write user & workspace settings |
|
|
315
|
+
| **Workspace FS** | ⚠️ Partial | Read, write, stat, readDirectory (no watch) |
|
|
316
|
+
| **Storage (Memento)** | ✅ Supported | Global & workspace state, auto-persisted |
|
|
317
|
+
| **Uri** | ✅ Supported | File/webview URI utilities |
|
|
318
|
+
| **EventEmitter** | ✅ Supported | Custom event streams |
|
|
319
|
+
| **Disposable** | ✅ Supported | Cleanup & resource management |
|
|
320
|
+
| **Environment** | ✅ Supported | App name, machine ID |
|
|
321
|
+
| **Languages** | ❌ Not supported | Syntax highlighting, language services |
|
|
322
|
+
| **Debug** | ❌ Not supported | Debugger integration |
|
|
323
|
+
| **Tasks** | ❌ Not supported | Task provider, task execution |
|
|
324
|
+
| **SCM** | ❌ Not supported | Source control providers |
|
|
325
|
+
| **Notebooks** | ❌ Not supported | Notebook editing |
|
|
326
|
+
| **Authentication** | ❌ Not supported | Auth provider, sessions |
|
|
327
|
+
| **Tests** | ❌ Not supported | Test controller |
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Webview Details
|
|
332
|
+
|
|
333
|
+
### Sandbox
|
|
334
|
+
|
|
335
|
+
Webviews run in a sandboxed iframe with **`allow-scripts` only** — no inline styles, event handlers, or external scripts. Use a bundler or inline `<style>` tags.
|
|
336
|
+
|
|
337
|
+
### Communication
|
|
338
|
+
|
|
339
|
+
**From webview to extension:**
|
|
340
|
+
```typescript
|
|
341
|
+
const vscode = acquireVsCodeApi();
|
|
342
|
+
vscode.postMessage({ type: "action", data: "..." });
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**From extension to webview:**
|
|
346
|
+
```typescript
|
|
347
|
+
panel.webview.postMessage({ type: "update", data: "..." });
|
|
348
|
+
panel.webview.onDidReceiveMessage((msg) => {
|
|
349
|
+
console.log("From webview:", msg);
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### HTML Setup
|
|
354
|
+
|
|
355
|
+
```html
|
|
356
|
+
<!DOCTYPE html>
|
|
357
|
+
<html>
|
|
358
|
+
<head>
|
|
359
|
+
<style>
|
|
360
|
+
body { font-family: sans-serif; padding: 20px; }
|
|
361
|
+
button { padding: 8px 16px; cursor: pointer; }
|
|
362
|
+
</style>
|
|
363
|
+
</head>
|
|
364
|
+
<body>
|
|
365
|
+
<h1>My Webview</h1>
|
|
366
|
+
<button id="btn">Send Message</button>
|
|
367
|
+
|
|
368
|
+
<script>
|
|
369
|
+
const vscode = acquireVsCodeApi();
|
|
370
|
+
|
|
371
|
+
document.getElementById("btn").onclick = () => {
|
|
372
|
+
vscode.postMessage({ type: "click", time: Date.now() });
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
window.addEventListener("message", (e) => {
|
|
376
|
+
const { type, data } = e.data;
|
|
377
|
+
if (type === "update") {
|
|
378
|
+
document.body.innerHTML += `<p>${data}</p>`;
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
</script>
|
|
382
|
+
</body>
|
|
383
|
+
</html>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Porting from VSCode
|
|
389
|
+
|
|
390
|
+
PPM uses the same API shape as VSCode. Porting is straightforward:
|
|
391
|
+
|
|
392
|
+
### 1. Update imports
|
|
393
|
+
|
|
394
|
+
**Before (VSCode):**
|
|
395
|
+
```typescript
|
|
396
|
+
import * as vscode from "vscode";
|
|
397
|
+
|
|
398
|
+
export function activate(context: vscode.ExtensionContext) {
|
|
399
|
+
// ...
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**After (PPM):**
|
|
404
|
+
```typescript
|
|
405
|
+
import type { ExtensionContext } from "@ppm/vscode-compat";
|
|
406
|
+
|
|
407
|
+
export function activate(context: ExtensionContext, vscode: any) {
|
|
408
|
+
// vscode is passed as 2nd argument
|
|
409
|
+
// ...
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Or import for type checking:
|
|
414
|
+
```typescript
|
|
415
|
+
import * as vscode from "@ppm/vscode-compat";
|
|
416
|
+
|
|
417
|
+
export function activate(context: ExtensionContext, _vscode: typeof vscode) {
|
|
418
|
+
// ...
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### 2. Update package.json
|
|
423
|
+
|
|
424
|
+
```json
|
|
425
|
+
{
|
|
426
|
+
"engines": { "ppm": ">=0.9.0" } // was vscode
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### 3. Check API support
|
|
431
|
+
|
|
432
|
+
Verify unsupported features aren't used (see API Support Matrix above).
|
|
433
|
+
|
|
434
|
+
### 4. Test
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
ppm ext dev ./path/to/extension
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## Example: Database Viewer
|
|
443
|
+
|
|
444
|
+
See `packages/ext-database/` in the PPM repo for a complete reference extension:
|
|
445
|
+
|
|
446
|
+
- **Tree view** of database connections
|
|
447
|
+
- **Commands** to open query panel
|
|
448
|
+
- **Webview panel** for SQL editor
|
|
449
|
+
- **Status bar** icon
|
|
450
|
+
- **Configuration** (maxRows, autoConnect)
|
|
451
|
+
- **RPC to backend** for queries
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
export function activate(context: ExtensionContext, vscode: any) {
|
|
455
|
+
// Tree view
|
|
456
|
+
const provider = new ConnectionTreeProvider();
|
|
457
|
+
const tree = vscode.window.createTreeView("ppm-db.connections", {
|
|
458
|
+
treeDataProvider: provider
|
|
459
|
+
});
|
|
460
|
+
context.subscriptions.push(tree);
|
|
461
|
+
|
|
462
|
+
// Command
|
|
463
|
+
context.subscriptions.push(
|
|
464
|
+
vscode.commands.registerCommand("ppm-db.openViewer", (connId) => {
|
|
465
|
+
const panel = vscode.window.createWebviewPanel(
|
|
466
|
+
"ppm-db.query",
|
|
467
|
+
"Query",
|
|
468
|
+
vscode.ViewColumn.Active
|
|
469
|
+
);
|
|
470
|
+
panel.webview.html = `...`;
|
|
471
|
+
panel.webview.onDidReceiveMessage(async (msg) => {
|
|
472
|
+
if (msg.type === "query") {
|
|
473
|
+
const res = await fetch(`/api/db/${connId}/query`, {
|
|
474
|
+
method: "POST",
|
|
475
|
+
body: JSON.stringify({ sql: msg.sql })
|
|
476
|
+
});
|
|
477
|
+
await panel.webview.postMessage(await res.json());
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
})
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
// Status bar
|
|
484
|
+
const status = vscode.window.createStatusBarItem(
|
|
485
|
+
vscode.StatusBarAlignment.Right
|
|
486
|
+
);
|
|
487
|
+
status.text = "DB";
|
|
488
|
+
status.show();
|
|
489
|
+
context.subscriptions.push(status);
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## Best Practices
|
|
496
|
+
|
|
497
|
+
1. **Cleanup**: Always push disposables to `context.subscriptions`.
|
|
498
|
+
2. **Errors**: Catch and show errors via `showErrorMessage()`.
|
|
499
|
+
3. **Async**: Use `await` for RPC calls; block UI during long operations.
|
|
500
|
+
4. **Storage**: Use `Memento` for persistence, not file system (unless granted access).
|
|
501
|
+
5. **Webview Security**: Never use `eval()`, `innerHTML` with user input, or external CDNs.
|
|
502
|
+
6. **Activation**: Lazy-load expensive code via activation events, not on require.
|
|
503
|
+
7. **Testing**: Use `ppm ext dev` to load locally before publishing.
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## Publishing
|
|
508
|
+
|
|
509
|
+
Register your extension with PPM's extension registry (coming in v0.9). For now, distribute via npm or GitHub.
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## Troubleshooting
|
|
514
|
+
|
|
515
|
+
| Issue | Solution |
|
|
516
|
+
|-------|----------|
|
|
517
|
+
| Extension doesn't activate | Check `activationEvents` in package.json matches your commands/views |
|
|
518
|
+
| Command not found | Verify `contributes.commands[].command` id |
|
|
519
|
+
| Tree view empty | Implement `getChildren()` and `getTreeItem()` in provider |
|
|
520
|
+
| Webview blank | Check `panel.webview.html` is valid HTML; test in browser first |
|
|
521
|
+
| RPC timeout | Network issue or long-running request; add retry logic |
|
|
522
|
+
| Storage not persisted | Use `Memento.update()`, not local variables |
|
|
523
|
+
| Unsupported API error | Check API Support Matrix; file issue if critical |
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## Learn More
|
|
528
|
+
|
|
529
|
+
- [VSCode Extension API](https://code.visualstudio.com/api) — reference (PPM subset)
|
|
530
|
+
- `packages/ext-database/` — working example
|
|
531
|
+
- `packages/vscode-compat/src/` — API implementation
|
|
532
|
+
- GitHub issues — feature requests & bugs
|
|
@@ -2,7 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to PPM are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|
4
4
|
|
|
5
|
-
**Current Version:** v0.
|
|
5
|
+
**Current Version:** v0.9.0
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## [0.9.0] — 2026-04-03
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Extension System (Phase 1-6)** — VSCode-compatible npm extensions with Bun Worker isolation, RPC protocol, @ppm/vscode-compat API shim, UI components (TreeView, WebviewPanel, StatusBar, QuickPick, InputBox), WS bridge, ext-database demo extension, Extension Manager UI, CLI support
|
|
13
|
+
- **Multi-Provider AI** — ProviderInterface, CliProvider base, CursorCliProvider with NDJSON streaming, provider selector UI
|
|
14
|
+
- **MCP Server Management** — REST API CRUD, SQLite storage, Settings UI, auto-import from ~/.claude.json, SDK integration
|
|
15
|
+
- **ext-database extension** — Connection tree with color dots/badges/actions, table viewer webview with data grid/inline editing/pagination/SQL panel, add connection via QuickPick+InputBox flow
|
|
16
|
+
|
|
17
|
+
### Technical Details
|
|
18
|
+
- Extension architecture: npm packages isolated in Bun Workers, communicate via RPC, manifest-driven contribution points (commands, views, menus, configuration)
|
|
19
|
+
- Multi-provider: Tier 1 (Claude Agent SDK, full agentic), Tier 2 (Cursor CLI, agentic via own tool system)
|
|
20
|
+
- MCP: Servers passed to SDK as `mcpServers`, tools auto-allowed via `mcp__*` wildcard
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## [0.8.77] — 2026-04-01
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- **Deterministic Tab URLs + Backend Workspace Sync**
|
|
28
|
+
- Tab IDs now deterministic ({type}:{identifier}) instead of random (tab-xxxx)
|
|
29
|
+
- URL format changed: `/project/{name}/{tabType}/{identifier}` (e.g., `/project/ppm/editor/src/index.ts`)
|
|
30
|
+
- New `workspace_state` database table (schema v10) persists tab layout per project
|
|
31
|
+
- GET/PUT `/api/project/:name/workspace` endpoints for server-side persistence
|
|
32
|
+
- Frontend syncs workspace layout to server with 1.5s debounce
|
|
33
|
+
- Deep linking from URL auto-creates tabs if missing
|
|
34
|
+
- Latest-wins conflict resolution: server timestamp > client localStorage
|
|
35
|
+
- Tab ID patterns standardized: `editor:path`, `chat:provider/sessionId`, `terminal:index`, etc.
|
|
36
|
+
- Migration from random IDs to deterministic IDs on first load
|
|
37
|
+
|
|
38
|
+
### Technical Details
|
|
39
|
+
- **Files Created:**
|
|
40
|
+
- `src/server/routes/workspace.ts` — Workspace GET/PUT endpoints
|
|
41
|
+
- `src/web/hooks/use-workspace-sync.ts` — Server sync orchestration with debounce
|
|
42
|
+
- **Files Modified:**
|
|
43
|
+
- `src/services/db.service.ts` — Added workspace_state table (schema v10), helper functions
|
|
44
|
+
- `src/web/stores/panel-utils.ts` — Deterministic tab ID derivation logic
|
|
45
|
+
- `src/web/hooks/use-url-sync.ts` — URL parsing/building with new format
|
|
46
|
+
- `src/web/stores/panel-store.ts` — Load workspace from server on project switch
|
|
47
|
+
- `src/web/stores/tab-store.ts` — Tab interface updated with metadata
|
|
48
|
+
- **Breaking Changes:** URLs changed; old `/project/{name}/tab/{id}` URLs redirected or ignored (fallback to project root)
|
|
49
|
+
- **Migration:** Client-side: old random tab IDs migrated to deterministic format on first load
|
|
50
|
+
|
|
51
|
+
### Benefits
|
|
52
|
+
- **Shareable deep links** — URLs point to specific files/chats (e.g., `/project/ppm/editor/src/index.ts`)
|
|
53
|
+
- **Cross-device persistence** — Workspace layout saved on server, restored on any device
|
|
54
|
+
- **URL-driven navigation** — Paste URL to recreate workspace state
|
|
55
|
+
- **Conflict-free sync** — Latest timestamp wins; no manual merge dialogs
|
|
6
56
|
|
|
7
57
|
---
|
|
8
58
|
|
package/docs/project-roadmap.md
CHANGED
|
@@ -58,25 +58,31 @@ PPM is the **lightest path from phone to code** — a self-hosted, BYOK, multi-d
|
|
|
58
58
|
|
|
59
59
|
**Theme:** Multi-provider AI (Claude + Cursor) + extension system. Ship a focused release, expand providers later.
|
|
60
60
|
|
|
61
|
-
**Overall progress:
|
|
61
|
+
**Overall progress: 100%** (All 3 features complete)
|
|
62
62
|
|
|
63
63
|
| Feature | Priority | Status | Description |
|
|
64
64
|
|---------|----------|--------|-------------|
|
|
65
65
|
| **Multi-provider AI** | Critical | ✅ Done | ProviderInterface, registry, Cursor CLI, CLI provider base, UI provider/model selector, permission mode selector, system prompt customization, comprehensive tests — all on beta branch. |
|
|
66
66
|
| **MCP Management** | Medium | ✅ Done | REST API (CRUD + import), SQLite storage, Settings UI, auto-import from `~/.claude.json`, validation, SDK integration. |
|
|
67
|
-
| **Extension architecture** | High |
|
|
67
|
+
| **Extension architecture (Phase 1-6)** | High | ✅ Done | VSCode-compatible npm extensions, Bun Worker isolation, RPC protocol, state persistence, contribution registry, CLI support, dev mode. @ppm/vscode-compat API shim (commands, window, workspace). UI components (StatusBar, TreeView, WebviewPanel, QuickPick, InputBox). WS bridge for real-time ext↔browser communication. First extension: ext-database with tree view + SQL query panel. Unit tests + extension dev guide. |
|
|
68
68
|
|
|
69
69
|
**Multi-provider — v0.9 scope (reduced):**
|
|
70
70
|
- Tier 1 (full agentic): Claude Agent SDK — file edit, terminal, git, full autonomy
|
|
71
71
|
- Tier 2 (agentic CLI): Cursor — agentic via its own tool system
|
|
72
72
|
- Provider interface is clean enough to add more providers later without refactor
|
|
73
73
|
|
|
74
|
-
**Deferred to v0.9.5
|
|
74
|
+
**Deferred to v0.9.5+ (Multi-Provider Phase 2):**
|
|
75
75
|
- Gemini CLI (Tier 2)
|
|
76
76
|
- OpenAI Codex (Tier 2)
|
|
77
77
|
- Tier 3 (chat-only): Any OpenAI-compatible API
|
|
78
78
|
- Chinese providers (DeepSeek, Qwen) — v1.0+
|
|
79
79
|
|
|
80
|
+
**Extension System — remaining for v0.10+:**
|
|
81
|
+
- Settings UI auto-generation from manifest
|
|
82
|
+
- Hot reload during dev (`ppm ext dev --watch`)
|
|
83
|
+
- Extension template scaffold (`ppm ext create <name>`)
|
|
84
|
+
- Extension marketplace (v1.0)
|
|
85
|
+
|
|
80
86
|
**Extension architecture — design principles:**
|
|
81
87
|
- Extensions are npm packages: `ppm ext install @ppm/ext-database`
|
|
82
88
|
- Extension manifest defines: skills, UI panels, sidebar tabs, routes, settings schema
|