@hienlh/ppm 0.8.86 → 0.8.87

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 (219) hide show
  1. package/CHANGELOG.md +193 -5
  2. package/bun.lock +5 -0
  3. package/dist/web/assets/{_basePickBy-5eBmZ_lt.js → _basePickBy-5PGDJbfF.js} +1 -1
  4. package/dist/web/assets/{_baseUniq-DimLlN0y.js → _baseUniq-BT4Ow4Kk.js} +1 -1
  5. package/dist/web/assets/api-settings-Bx1GaNmQ.js +1 -0
  6. package/dist/web/assets/{arc-D4SasZrA.js → arc-BAOivWpI.js} +1 -1
  7. package/dist/web/assets/architecture-PBZL5I3N-DEO2f3VD.js +1 -0
  8. package/dist/web/assets/{architectureDiagram-2XIMDMQ5-nv0WbM7d.js → architectureDiagram-2XIMDMQ5-DWBCPMLF.js} +1 -1
  9. package/dist/web/assets/arrow-up--LjUXLEt.js +1 -0
  10. package/dist/web/assets/{blockDiagram-WCTKOSBZ-C1XvYrb8.js → blockDiagram-WCTKOSBZ-TEF8Ally.js} +1 -1
  11. package/dist/web/assets/browser-tab-DaHGm_0i.js +1 -0
  12. package/dist/web/assets/{c4Diagram-IC4MRINW-CygDrbWJ.js → c4Diagram-IC4MRINW-dV22iAsY.js} +1 -1
  13. package/dist/web/assets/channel-wrd-NHWf.js +1 -0
  14. package/dist/web/assets/chat-tab-BDYE0KHF.js +8 -0
  15. package/dist/web/assets/chevron-right-DeV0ehiG.js +1 -0
  16. package/dist/web/assets/{chunk-4BX2VUAB-C2FDgsgT.js → chunk-4BX2VUAB-D4tOov49.js} +1 -1
  17. package/dist/web/assets/{chunk-55IACEB6-jF4w6cat.js → chunk-55IACEB6-DJ6BynZ4.js} +1 -1
  18. package/dist/web/assets/{chunk-7E7YKBS2-BVCECZFi.js → chunk-7E7YKBS2-CiyUJxNI.js} +1 -1
  19. package/dist/web/assets/{chunk-7R4GIKGN-DXTbeu5d.js → chunk-7R4GIKGN-BbIFzsIv.js} +2 -2
  20. package/dist/web/assets/{chunk-C72U2L5F-BaZqOsTs.js → chunk-C72U2L5F-D21mS_6G.js} +1 -1
  21. package/dist/web/assets/{chunk-EGIJ26TM-Bky2tcH7.js → chunk-EGIJ26TM-DzqmU2Z7.js} +1 -1
  22. package/dist/web/assets/{chunk-FMBD7UC4-Cp4BK9A8.js → chunk-FMBD7UC4-DXncblvW.js} +1 -1
  23. package/dist/web/assets/{chunk-GEFDOKGD-BosFEH7G.js → chunk-GEFDOKGD-BbQkJu8C.js} +1 -1
  24. package/dist/web/assets/chunk-GLR3WWYH-CzYx4w-r.js +2 -0
  25. package/dist/web/assets/chunk-HHEYEP7N-HRhYy3kG.js +1 -0
  26. package/dist/web/assets/{chunk-JSJVCQXG-H5Gbjsbr.js → chunk-JSJVCQXG-23tyvw8k.js} +1 -1
  27. package/dist/web/assets/{chunk-KX2RTZJC-CWerSUwS.js → chunk-KX2RTZJC-sQ0o-39C.js} +1 -1
  28. package/dist/web/assets/{chunk-KYZI473N-FvwP7jUy.js → chunk-KYZI473N-BcUZNnwd.js} +1 -1
  29. package/dist/web/assets/{chunk-L3YUKLVL-D1PI_ORP.js → chunk-L3YUKLVL-C7qGJrfV.js} +1 -1
  30. package/dist/web/assets/{chunk-MX3YWQON-C7Vzk_AI.js → chunk-MX3YWQON-BpS_PtKp.js} +1 -1
  31. package/dist/web/assets/{chunk-NQ4KR5QH-BceYBGYX.js → chunk-NQ4KR5QH-wMgTlP7f.js} +1 -1
  32. package/dist/web/assets/{chunk-O4XLMI2P-WPtzgxql.js → chunk-O4XLMI2P-JC6EGoUz.js} +1 -1
  33. package/dist/web/assets/{chunk-OZEHJAEY-DlHXDeLY.js → chunk-OZEHJAEY-BXhYx3nO.js} +1 -1
  34. package/dist/web/assets/{chunk-PQ6SQG4A-Ci_Prygb.js → chunk-PQ6SQG4A-D6BTbCQw.js} +1 -1
  35. package/dist/web/assets/{chunk-PU5JKC2W-CO0zMN-z.js → chunk-PU5JKC2W-Dw8ClWch.js} +1 -1
  36. package/dist/web/assets/chunk-QZHKN3VN-CYaTbeZf.js +1 -0
  37. package/dist/web/assets/{chunk-R5LLSJPH-IAEEzfpM.js → chunk-R5LLSJPH-CFwSJijQ.js} +1 -1
  38. package/dist/web/assets/{chunk-WL4C6EOR-BLXalOgc.js → chunk-WL4C6EOR-DfofndiH.js} +1 -1
  39. package/dist/web/assets/{chunk-XIRO2GV7-Dx1Ri_p2.js → chunk-XIRO2GV7-Djlmrely.js} +1 -1
  40. package/dist/web/assets/{chunk-XPW4576I-m9pPGKn7.js → chunk-XPW4576I-BPQQBakK.js} +1 -1
  41. package/dist/web/assets/{chunk-XZSTWKYB-B_08ExbI.js → chunk-XZSTWKYB-DxAOx4hG.js} +1 -1
  42. package/dist/web/assets/{chunk-YBOYWFTD-DqSOVcYe.js → chunk-YBOYWFTD-CeU4Q-xC.js} +1 -1
  43. package/dist/web/assets/classDiagram-VBA2DB6C-lse8oZoJ.js +1 -0
  44. package/dist/web/assets/classDiagram-v2-RAHNMMFH-CxkwuInd.js +1 -0
  45. package/dist/web/assets/clone-LRxlvnMj.js +1 -0
  46. package/dist/web/assets/code-editor-DTA3c9Y8.js +2 -0
  47. package/dist/web/assets/{cose-bilkent-S5V4N54A-DlL82QHu.js → cose-bilkent-S5V4N54A-B_AWZsOP.js} +1 -1
  48. package/dist/web/assets/csv-preview-DLqYtXxt.js +10 -0
  49. package/dist/web/assets/{dagre-BmVoh2At.js → dagre-Dbb5k38K.js} +1 -1
  50. package/dist/web/assets/{dagre-KLK3FWXG-sDrRW9MQ.js → dagre-KLK3FWXG-BH7aWGRP.js} +1 -1
  51. package/dist/web/assets/database-viewer-DXk79Nel.js +1 -0
  52. package/dist/web/assets/{diagram-E7M64L7V-ChnAhgni.js → diagram-E7M64L7V-B1Qz70Do.js} +1 -1
  53. package/dist/web/assets/{diagram-IFDJBPK2-DW1J1uJd.js → diagram-IFDJBPK2-k55eVqVU.js} +1 -1
  54. package/dist/web/assets/{diagram-P4PSJMXO-CQ32hyG_.js → diagram-P4PSJMXO-BkfNRc9U.js} +1 -1
  55. package/dist/web/assets/diff-viewer-HhIcsOQE.js +4 -0
  56. package/dist/web/assets/dist-DylI9XxN.js +13 -0
  57. package/dist/web/assets/dist-lF8CoYII.js +41 -0
  58. package/dist/web/assets/{erDiagram-INFDFZHY-6CHo6nOw.js → erDiagram-INFDFZHY-CKzVujYI.js} +1 -1
  59. package/dist/web/assets/{flowDiagram-PKNHOUZH-DroDiNT0.js → flowDiagram-PKNHOUZH-DIqcTrDV.js} +1 -1
  60. package/dist/web/assets/{ganttDiagram-A5KZAMGK-DP0QBh8w.js → ganttDiagram-A5KZAMGK-D4v7ZbVE.js} +1 -1
  61. package/dist/web/assets/git-graph-CQtWu8yE.js +1 -0
  62. package/dist/web/assets/gitGraph-HDMCJU4V-Bwna3and.js +1 -0
  63. package/dist/web/assets/{gitGraphDiagram-K3NZZRJ6-DvU3JGZn.js → gitGraphDiagram-K3NZZRJ6-BTXo57mF.js} +1 -1
  64. package/dist/web/assets/{graphlib-CQBb2thr.js → graphlib-BcsNnGcW.js} +1 -1
  65. package/dist/web/assets/index-CgQXpBb_.css +2 -0
  66. package/dist/web/assets/index-DEeeRoka.js +37 -0
  67. package/dist/web/assets/info-3K5VOQVL-_vRxVNUm.js +1 -0
  68. package/dist/web/assets/infoDiagram-LFFYTUFH-B1CX0pbC.js +2 -0
  69. package/dist/web/assets/input-BglMT33g.js +1 -0
  70. package/dist/web/assets/{isEmpty-B4kqZBtn.js → isEmpty-bnrF3Qbc.js} +1 -1
  71. package/dist/web/assets/{ishikawaDiagram-PHBUUO56-46yibrV5.js → ishikawaDiagram-PHBUUO56-BOyvKMmB.js} +1 -1
  72. package/dist/web/assets/{journeyDiagram-4ABVD52K-BcmRwjK-.js → journeyDiagram-4ABVD52K-ufoasAy6.js} +1 -1
  73. package/dist/web/assets/{kanban-definition-K7BYSVSG-B619K53y.js → kanban-definition-K7BYSVSG-Bi0UTUeN.js} +1 -1
  74. package/dist/web/assets/keybindings-store-1CJ7VX57.js +1 -0
  75. package/dist/web/assets/lib-BQ34Db2e.js +4 -0
  76. package/dist/web/assets/{line-1gcO63_w.js → line-B78g-52T.js} +1 -1
  77. package/dist/web/assets/{linear-DfRqDoVd.js → linear-DP4mkX3m.js} +1 -1
  78. package/dist/web/assets/markdown-renderer-Brj8_LQM.js +69 -0
  79. package/dist/web/assets/{mermaid-parser.core-XtjZQOeM.js → mermaid-parser.core-DMIWdgEW.js} +2 -2
  80. package/dist/web/assets/{mindmap-definition-YRQLILUH-CifOFo_q.js → mindmap-definition-YRQLILUH-BsfWvIoO.js} +1 -1
  81. package/dist/web/assets/{ordinal-BJYw-iDX.js → ordinal-_K3x1fkz.js} +1 -1
  82. package/dist/web/assets/packet-RMMSAZCW-DY5PNnZU.js +1 -0
  83. package/dist/web/assets/pie-UPGHQEXC-BHncZutv.js +1 -0
  84. package/dist/web/assets/{pieDiagram-SKSYHLDU-BuHUh_fO.js → pieDiagram-SKSYHLDU-WP0XXw51.js} +1 -1
  85. package/dist/web/assets/postgres-viewer-CwkTGmqy.js +1 -0
  86. package/dist/web/assets/{quadrantDiagram-337W2JSQ-Bau_hj6Z.js → quadrantDiagram-337W2JSQ-FHMogtsh.js} +1 -1
  87. package/dist/web/assets/radar-KQ55EAFF-DH0AOkUy.js +1 -0
  88. package/dist/web/assets/react-dom-Bpkvzu3U.js +1 -0
  89. package/dist/web/assets/{requirementDiagram-Z7DCOOCP-Cq2b-uwp.js → requirementDiagram-Z7DCOOCP-BatTxyWb.js} +1 -1
  90. package/dist/web/assets/{sankeyDiagram-WA2Y5GQK-DrdGQxWQ.js → sankeyDiagram-WA2Y5GQK-ClJuW3Hv.js} +1 -1
  91. package/dist/web/assets/{sequenceDiagram-2WXFIKYE-qPxiTUcS.js → sequenceDiagram-2WXFIKYE-ByxQqGgs.js} +1 -1
  92. package/dist/web/assets/settings-tab-BDE1MsIh.js +1 -0
  93. package/dist/web/assets/sqlite-viewer-CFYTwgA8.js +1 -0
  94. package/dist/web/assets/{stateDiagram-RAJIS63D-Dulj2oa8.js → stateDiagram-RAJIS63D-f8opcZNY.js} +1 -1
  95. package/dist/web/assets/stateDiagram-v2-FVOUBMTO-DrxVDY9q.js +1 -0
  96. package/dist/web/assets/tab-store-BJw7OCmy.js +1 -0
  97. package/dist/web/assets/{terminal-tab-wKgpSPAT.js → terminal-tab-CCDLZA5Y.js} +2 -2
  98. package/dist/web/assets/{timeline-definition-YZTLITO2-BWyDnCYq.js → timeline-definition-YZTLITO2-58BlOSf9.js} +1 -1
  99. package/dist/web/assets/treemap-KZPCXAKY-B2Xkyv-K.js +1 -0
  100. package/dist/web/assets/use-monaco-theme-CNzekTN3.js +11 -0
  101. package/dist/web/assets/{vennDiagram-LZ73GAT5-B9Iv2bNV.js → vennDiagram-LZ73GAT5-BOSy9ma9.js} +1 -1
  102. package/dist/web/assets/{xychartDiagram-JWTSCODW-ChXcMzBQ.js → xychartDiagram-JWTSCODW-z5MVJauZ.js} +1 -1
  103. package/dist/web/index.html +12 -11
  104. package/dist/web/sw.js +1 -1
  105. package/docs/code-standards.md +232 -7
  106. package/docs/codebase-summary.md +9 -3
  107. package/docs/design-guidelines.md +21 -0
  108. package/docs/project-changelog.md +115 -1
  109. package/docs/project-roadmap.md +41 -19
  110. package/docs/system-architecture.md +212 -15
  111. package/package.json +3 -2
  112. package/src/cli/commands/autostart.ts +1 -1
  113. package/src/cli/commands/restart.ts +9 -1
  114. package/src/cli/commands/status.ts +19 -0
  115. package/src/index.ts +2 -3
  116. package/src/providers/claude-agent-sdk.ts +92 -25
  117. package/src/providers/mock-provider.ts +6 -1
  118. package/src/server/index.ts +38 -166
  119. package/src/server/routes/browser-preview.ts +159 -0
  120. package/src/server/routes/chat.ts +52 -3
  121. package/src/server/routes/project-scoped.ts +2 -0
  122. package/src/server/routes/proxy.ts +46 -53
  123. package/src/server/routes/tunnel.ts +0 -32
  124. package/src/server/routes/workspace.ts +35 -0
  125. package/src/server/ws/chat.ts +207 -146
  126. package/src/services/account-selector.service.ts +16 -8
  127. package/src/services/account.service.ts +19 -13
  128. package/src/services/claude-usage.service.ts +48 -11
  129. package/src/services/cloud-ws.service.ts +227 -0
  130. package/src/services/cloud.service.ts +10 -6
  131. package/src/services/db.service.ts +111 -6
  132. package/src/services/proxy.service.ts +4 -19
  133. package/src/services/supervisor.ts +285 -25
  134. package/src/types/api.ts +9 -1
  135. package/src/types/chat.ts +3 -1
  136. package/src/web/app.tsx +41 -35
  137. package/src/web/components/browser/browser-tab.tsx +106 -97
  138. package/src/web/components/chat/account-rotation-settings.tsx +163 -0
  139. package/src/web/components/chat/chat-history-bar.tsx +72 -6
  140. package/src/web/components/chat/chat-tab.tsx +32 -16
  141. package/src/web/components/chat/chat-welcome.tsx +148 -0
  142. package/src/web/components/chat/message-input.tsx +107 -13
  143. package/src/web/components/chat/message-list.tsx +27 -15
  144. package/src/web/components/chat/session-picker.tsx +78 -31
  145. package/src/web/components/chat/usage-badge.tsx +11 -1
  146. package/src/web/components/editor/code-editor.tsx +36 -26
  147. package/src/web/components/editor/csv-preview.tsx +228 -0
  148. package/src/web/components/editor/editor-breadcrumb.tsx +216 -0
  149. package/src/web/components/editor/editor-toolbar.tsx +74 -0
  150. package/src/web/components/layout/command-palette.tsx +3 -1
  151. package/src/web/components/layout/editor-panel.tsx +162 -18
  152. package/src/web/components/layout/panel-layout.tsx +17 -1
  153. package/src/web/components/settings/proxy-settings-section.tsx +40 -42
  154. package/src/web/components/shared/connection-lost-overlay.tsx +89 -0
  155. package/src/web/hooks/use-chat.ts +211 -201
  156. package/src/web/hooks/use-global-keybindings.ts +25 -2
  157. package/src/web/hooks/use-server-reload.ts +9 -0
  158. package/src/web/hooks/use-url-sync.ts +173 -21
  159. package/src/web/hooks/use-voice-input.ts +111 -0
  160. package/src/web/lib/csv-parser.ts +134 -0
  161. package/src/web/stores/connection-store.ts +39 -0
  162. package/src/web/stores/keybindings-store.ts +1 -0
  163. package/src/web/stores/panel-store.ts +73 -19
  164. package/src/web/stores/panel-utils.ts +145 -3
  165. package/dist/web/assets/api-settings-CFw-lh5k.js +0 -1
  166. package/dist/web/assets/architecture-PBZL5I3N-CJupe6q_.js +0 -1
  167. package/dist/web/assets/browser-tab-CmsL5eny.js +0 -1
  168. package/dist/web/assets/channel-DmKoFTd_.js +0 -1
  169. package/dist/web/assets/chat-tab-CFWsf13Z.js +0 -7
  170. package/dist/web/assets/chunk-GLR3WWYH-BnP-hOp6.js +0 -2
  171. package/dist/web/assets/chunk-HHEYEP7N-DKDPTPEZ.js +0 -1
  172. package/dist/web/assets/chunk-QZHKN3VN-C_wpI9wz.js +0 -1
  173. package/dist/web/assets/classDiagram-VBA2DB6C-B1T5uY-F.js +0 -1
  174. package/dist/web/assets/classDiagram-v2-RAHNMMFH-xs5vI3xC.js +0 -1
  175. package/dist/web/assets/clone-CijCFRT5.js +0 -1
  176. package/dist/web/assets/code-editor-H_dAh_fJ.js +0 -1
  177. package/dist/web/assets/database-viewer-DBzsgEJ8.js +0 -1
  178. package/dist/web/assets/diff-viewer-DzS-OnAR.js +0 -4
  179. package/dist/web/assets/dist-0Va_2L7G.js +0 -16
  180. package/dist/web/assets/dist-D9irYETY.js +0 -41
  181. package/dist/web/assets/git-graph-D3C7F8o3.js +0 -1
  182. package/dist/web/assets/gitGraph-HDMCJU4V-B0KvGQG8.js +0 -1
  183. package/dist/web/assets/index-CIkjfera.js +0 -31
  184. package/dist/web/assets/index-WKLuYsBY.css +0 -2
  185. package/dist/web/assets/info-3K5VOQVL-1uJ6_hCm.js +0 -1
  186. package/dist/web/assets/infoDiagram-LFFYTUFH-DLA5Q-3y.js +0 -2
  187. package/dist/web/assets/input-CGp1nFIg.js +0 -1
  188. package/dist/web/assets/keybindings-store-BdaoLwSo.js +0 -1
  189. package/dist/web/assets/markdown-renderer-DH49Zag7.js +0 -69
  190. package/dist/web/assets/packet-RMMSAZCW-34C4o9yj.js +0 -1
  191. package/dist/web/assets/pie-UPGHQEXC-D9ekKlh9.js +0 -1
  192. package/dist/web/assets/postgres-viewer-B9FYk8sD.js +0 -1
  193. package/dist/web/assets/radar-KQ55EAFF-DEuXOXSD.js +0 -1
  194. package/dist/web/assets/settings-store-DWXGVHsE.js +0 -2
  195. package/dist/web/assets/settings-tab-D-q8pd-5.js +0 -1
  196. package/dist/web/assets/sqlite-viewer-CDqcTePw.js +0 -1
  197. package/dist/web/assets/stateDiagram-v2-FVOUBMTO-CAkzLlhk.js +0 -1
  198. package/dist/web/assets/tab-store-BPeiymiH.js +0 -1
  199. package/dist/web/assets/treemap-KZPCXAKY-nc7a1Ia1.js +0 -1
  200. package/dist/web/assets/use-monaco-theme-CCBTQ0S3.js +0 -11
  201. package/src/services/port-tunnel.service.ts +0 -97
  202. /package/dist/web/assets/{api-client-DOElml5u.js → api-client-BfBM3I7n.js} +0 -0
  203. /package/dist/web/assets/{array-CYkMkqnU.js → array-B9UHiPd-.js} +0 -0
  204. /package/dist/web/assets/{columns-2-ChOTgl3e.js → columns-2-DpsNbZOc.js} +0 -0
  205. /package/dist/web/assets/{cytoscape.esm-HeHO0VhB.js → cytoscape.esm-BW-DbntU.js} +0 -0
  206. /package/dist/web/assets/{defaultLocale-Beh6XjaL.js → defaultLocale-5eAKkKJC.js} +0 -0
  207. /package/dist/web/assets/{dist-BUYzeuKe.js → dist-CSJdAyA9.js} +0 -0
  208. /package/dist/web/assets/{init-Rr1s_RiX.js → init-DlZdxViB.js} +0 -0
  209. /package/dist/web/assets/{isArrayLikeObject-BB-mzMLb.js → isArrayLikeObject-B_v2FtYn.js} +0 -0
  210. /package/dist/web/assets/{katex-CKoArbIw.js → katex-Bqvo_ZG0.js} +0 -0
  211. /package/dist/web/assets/{math-B7b0HgJF.js → math-069Z4SuC.js} +0 -0
  212. /package/dist/web/assets/{path-BAQ3hXlG.js → path-6uRLdFF7.js} +0 -0
  213. /package/dist/web/assets/{preload-helper-DeiOTZKJ.js → preload-helper-uTix4PVD.js} +0 -0
  214. /package/dist/web/assets/{react-Dev-wu-s.js → react-ER-4DN55.js} +0 -0
  215. /package/dist/web/assets/{rough.esm-Dwml_la6.js → rough.esm-JX0wREDd.js} +0 -0
  216. /package/dist/web/assets/{src-B_cC68fH.js → src-BqX54PbV.js} +0 -0
  217. /package/dist/web/assets/{table-COiJDPRA.js → table-C7X5UAEI.js} +0 -0
  218. /package/dist/web/assets/{tag-LMq02LfE.js → tag-CCtdV063.js} +0 -0
  219. /package/dist/web/assets/{utils-btZ8C8-R.js → utils-BNytJOb1.js} +0 -0
@@ -86,6 +86,31 @@ async *streamMessages(input: string) {
86
86
  provider.sendMessage(input).then(...)
87
87
  ```
88
88
 
89
+ ### Long-Lived Streaming Sessions
90
+
91
+ For chat-like features, maintain persistent streaming sessions instead of per-query execution:
92
+
93
+ ```typescript
94
+ // Good: Session-scoped streaming (v0.8.55+)
95
+ // Provider maintains AsyncGenerator per session
96
+ const streaming = provider.sendMessage(sessionId, content);
97
+ for await (const event of streaming) {
98
+ // Handle event (can receive multiple messages from same generator)
99
+ yield event;
100
+ }
101
+
102
+ // Avoid: Per-message query execution
103
+ for (const message of messages) {
104
+ const result = await query(message); // Restarts SDK each time
105
+ yield result;
106
+ }
107
+
108
+ // Key Pattern: Decoupled streaming loop
109
+ // Store streaming generator in session entry
110
+ // Follow-up messages push into existing generator
111
+ // FE disconnect ≠ abort (BE owns the connection)
112
+ ```
113
+
89
114
  ### Error Handling
90
115
  Use try-catch for async operations. Throw structured errors:
91
116
 
@@ -221,6 +246,63 @@ try {
221
246
  }
222
247
  ```
223
248
 
249
+ ### Provider Interface Pattern (Multi-Provider)
250
+
251
+ PPM supports multiple AI providers through an extensible interface pattern:
252
+
253
+ ```typescript
254
+ // Core interface defines required methods + optional capability methods
255
+ interface AIProvider {
256
+ id: string;
257
+ name: string;
258
+
259
+ // Required: All providers must implement these
260
+ createSession(config: SessionConfig): Promise<Session>;
261
+ resumeSession(sessionId: string): Promise<Session>;
262
+ listSessions(): Promise<SessionInfo[]>;
263
+ deleteSession(sessionId: string): Promise<void>;
264
+ sendMessage(sessionId: string, message: string, opts?: SendMessageOpts): AsyncIterable<ChatEvent>;
265
+
266
+ // Optional: Providers implement what they support
267
+ abortQuery?(sessionId: string): void;
268
+ getMessages?(sessionId: string): Promise<ChatMessage[]>;
269
+ listSessionsByDir?(dir: string): Promise<SessionInfo[]>;
270
+ ensureProjectPath?(sessionId: string, path: string): void;
271
+ isAvailable?(): Promise<boolean>;
272
+ }
273
+ ```
274
+
275
+ **SDK-based Providers (Claude):**
276
+ - Use Anthropic SDK for advanced features
277
+ - Implement all optional methods
278
+ - Streaming via SDK async generators
279
+
280
+ **CLI-based Providers (Cursor, Codex, Gemini):**
281
+ - Extend `CliProvider` abstract class
282
+ - Implement: `buildArgs()`, `mapEvent()`, `extractSessionId()`, `isAvailable()`
283
+ - Shared: spawn, parse NDJSON, abort, cleanup
284
+ - Override `spawnProcess()` for provider-specific quirks (e.g., workspace trust)
285
+ - Override `listSessions()` to read native provider history (e.g., SQLite)
286
+
287
+ **Usage Pattern (Type-Safe Optional Calls):**
288
+ ```typescript
289
+ // Good: Optional chaining for capabilities
290
+ await provider.abortQuery?.(sessionId); // Silently skips if not implemented
291
+ const messages = await provider.getMessages?.(sessionId) ?? [];
292
+
293
+ // Avoid: Duck-typing checks (old pattern)
294
+ if ("abortQuery" in provider) {
295
+ (provider as any).abortQuery(sessionId); // Not type-safe
296
+ }
297
+ ```
298
+
299
+ **Adding New Providers:**
300
+ 1. Create provider class implementing/extending AIProvider or CliProvider
301
+ 2. Implement `isAvailable()` for optional registration
302
+ 3. Register in `ProviderRegistry` via `bootstrapProviders()` if binary available
303
+ 4. Add event mapper if CLI-based (NDJSON → ChatEvent)
304
+ 5. ~100-150 lines per provider (extends CliProvider)
305
+
224
306
  ## API Conventions
225
307
 
226
308
  ### Response Envelope
@@ -252,18 +334,35 @@ GET /api/project/:name/files/... # Files (project-scoped)
252
334
  Structure WebSocket messages as typed JSON objects:
253
335
 
254
336
  ```typescript
255
- // Client -> Server (chat)
256
- { type: "message"; content: string }
337
+ // Client -> Server (chat, v0.8.55+)
338
+ { type: "message"; content: string; priority?: "now"|"next"|"later"; images?: {id: string; data: string}[] }
257
339
  { type: "cancel" }
258
- { type: "approval_response"; requestId: string; approved: boolean }
340
+ { type: "approval_response"; requestId: string; approved: boolean; reason?: string; data?: unknown }
341
+ { type: "ready" } // FE handshake after WS open
259
342
 
260
343
  // Server -> Client (chat)
261
- { type: "text"; content: string }
262
- { type: "tool_use"; tool: string; input: unknown }
344
+ { type: "text"; content: string; parentToolUseId?: string }
345
+ { type: "thinking"; content: string; parentToolUseId?: string }
346
+ { type: "tool_use"; tool: string; input: unknown; toolUseId?: string; parentToolUseId?: string }
347
+ { type: "tool_result"; output: string; isError?: boolean; toolUseId?: string; parentToolUseId?: string }
263
348
  { type: "approval_request"; requestId: string; tool: string; input: unknown }
264
- { type: "done"; sessionId: string }
349
+ { type: "done"; sessionId: string; contextWindowPct?: number; resultSubtype?: string }
265
350
  { type: "error"; message: string }
266
- ```
351
+ { type: "account_info"; accountId: string; accountLabel: string }
352
+ { type: "phase_changed"; phase: SessionPhase; elapsed?: number }
353
+ { type: "session_state"; sessionId: string; phase: SessionPhase; pendingApproval: {...}|null; sessionTitle: string|null }
354
+ { type: "turn_events"; events: unknown[] } // Buffered events on reconnect
355
+ { type: "title_updated"; title: string }
356
+ { type: "ping" } // Server keepalive
357
+ ```
358
+
359
+ **New Fields (v0.8.55+):**
360
+ - `priority` — Message priority for queue ordering (future: "now" interrupts, "next" queues first, "later" queues at end)
361
+ - `images` — Image attachments sent with message
362
+ - `phase_changed` — Phase transitions (initializing → connecting → thinking/streaming → idle)
363
+ - `session_state` — Session state snapshot on WS open/ready (includes current phase and pending approval)
364
+ - `turn_events` — Buffered events sent on reconnection for sync
365
+ - `parentToolUseId` — Hierarchical tool call support (nested tool results)
267
366
 
268
367
  ### Status Codes
269
368
  Use standard HTTP status codes:
@@ -646,3 +745,129 @@ if (options.share) {
646
745
 
647
746
  This keeps startup fast when features aren't used.
648
747
 
748
+ ---
749
+
750
+ ## Tab ID & URL Conventions (v0.8.77+)
751
+
752
+ ### Deterministic Tab IDs
753
+
754
+ Tab IDs are derived from type + metadata, not random:
755
+
756
+ ```typescript
757
+ // Format: "{type}:{identifier}" for most tabs, "{type}" for singletons
758
+ editor:src/index.ts // Editor tab with file path
759
+ chat:claude/abc123 // Chat with provider/session ID
760
+ terminal:1 // Terminal tab index
761
+ database:conn-1/users // Database: connection ID / table
762
+ git-graph // Singleton: no identifier
763
+ settings // Singleton: no identifier
764
+
765
+ // Derivation in panel-utils.ts
766
+ export function deriveTabId(type: TabType, metadata?: Record<string, unknown>): string {
767
+ switch (type) {
768
+ case "editor":
769
+ return `editor:${metadata?.filePath ?? "untitled"}`;
770
+ case "chat": {
771
+ const provider = metadata?.providerId ?? "default";
772
+ return `chat:${provider}/${metadata?.sessionId ?? randomId()}`;
773
+ }
774
+ case "terminal":
775
+ return `terminal:${metadata?.terminalIndex ?? 1}`;
776
+ case "git-graph":
777
+ return "git-graph"; // Singleton
778
+ // ... other cases
779
+ }
780
+ }
781
+ ```
782
+
783
+ ### URL Format
784
+
785
+ URLs are built from project name + deterministic tab ID:
786
+
787
+ ```typescript
788
+ // Format: /project/{projectName}/{type}/{identifier}
789
+ /project/ppm // Project root (no active tab)
790
+ /project/ppm/editor/src/index.ts // Open editor
791
+ /project/ppm/chat/claude/abc123 // Open chat
792
+ /project/ppm/terminal/1 // Open terminal
793
+ /project/ppm/database/conn-1/users // Open database
794
+ /project/ppm/git-graph // Git history (singleton)
795
+
796
+ // URL building in use-url-sync.ts
797
+ export function buildUrl(projectName: string, tabId: string | null): string {
798
+ const colonIdx = tabId?.indexOf(":");
799
+ if (colonIdx === -1) {
800
+ // Singleton
801
+ url += `/${tabId}`;
802
+ } else {
803
+ const [type, identifier] = tabId.split(":", 1);
804
+ url += `/${type}/${identifier}`;
805
+ }
806
+ return url;
807
+ }
808
+ ```
809
+
810
+ ### Deep Linking
811
+
812
+ When user navigates to a URL, parse it and auto-create tabs:
813
+
814
+ ```typescript
815
+ // In use-url-sync hook
816
+ export function parseUrlState(): UrlState {
817
+ const match = path.match(/^\/project\/([^/]+)(?:\/([^/]+)(\/.*)?)?/);
818
+ const [, projectName, tabType, tabIdentifierPath] = match;
819
+
820
+ // Build tab metadata from URL
821
+ const metadata = buildMetadataFromUrl(tabType, tabIdentifier, projectName);
822
+
823
+ // Auto-open tab if it doesn't exist
824
+ if (metadata) {
825
+ panelStore.openTab({
826
+ type: tabType,
827
+ title: getTabTitle(tabType, metadata),
828
+ metadata,
829
+ });
830
+ }
831
+ }
832
+ ```
833
+
834
+ ### Server-Side Workspace Persistence
835
+
836
+ Tab layouts are persisted in the `workspace_state` SQLite table:
837
+
838
+ ```typescript
839
+ // workspace.ts routes
840
+ GET /api/project/:name/workspace → { layout: PanelLayout, updatedAt: string }
841
+ PUT /api/project/:name/workspace → { updatedAt: string }
842
+
843
+ // PanelLayout structure
844
+ interface PanelLayout {
845
+ panels: Record<string, Panel>; // panelId → { tabs, activeTabId }
846
+ grid: string[][]; // Row/column grid of panel IDs
847
+ focusedPanelId: string;
848
+ }
849
+
850
+ // Sync is debounced (1.5s) client-side after layout changes
851
+ // Latest-wins: server timestamp compared with client localStorage
852
+ ```
853
+
854
+ ### Migration from Random IDs
855
+
856
+ Old tab IDs (tab-xxxx) are automatically migrated to deterministic format:
857
+
858
+ ```typescript
859
+ // In panel-utils.ts
860
+ export function migrateTabIdToDeterministic(tab: Tab): Tab {
861
+ if (tab.id.startsWith("tab-")) {
862
+ // Convert to deterministic ID
863
+ return {
864
+ ...tab,
865
+ id: deriveTabId(tab.type, tab.metadata),
866
+ };
867
+ }
868
+ return tab;
869
+ }
870
+ ```
871
+
872
+ This happens on first load per project; old URLs redirect to project root.
873
+
@@ -104,7 +104,7 @@ ppm/
104
104
  │ │ ├── use-health-check.ts # Detect server crashes/restarts via health endpoint
105
105
  │ │ ├── use-usage.ts # Fetch token usage from backend
106
106
  │ │ └── use-push-notification.ts # Web push notifications via Service Worker
107
- │ ├── lib/ # Utilities (11 files)
107
+ │ ├── lib/ # Utilities (12 files)
108
108
  │ │ ├── api-client.ts # Fetch wrapper with auth token, envelope unwrapping
109
109
  │ │ ├── api-settings.ts # AI settings API client (GET/PUT /api/settings/ai)
110
110
  │ │ ├── ws-client.ts # WebSocket with exponential backoff + Cloudflare handshake
@@ -113,6 +113,7 @@ ppm/
113
113
  │ │ ├── project-palette.ts # 12-color palette for project avatars
114
114
  │ │ ├── use-monaco-theme.ts # Sync Monaco Editor theme with app theme
115
115
  │ │ ├── color-utils.ts # WCAG color contrast helper
116
+ │ │ ├── csv-parser.ts # CSV state-machine parser/serializer
116
117
  │ │ └── utils.ts # Helpers (cn, randomId, basename, etc.)
117
118
  │ ├── styles/
118
119
  │ │ └── globals.css # Tailwind directives, custom CSS
@@ -131,9 +132,12 @@ ppm/
131
132
  │ │ ├── usage-badge.tsx # Token usage display
132
133
  │ │ ├── attachment-chips.tsx # Display attached files
133
134
  │ │ └── chat-placeholder.tsx # Empty state
134
- │ ├── editor/ # Code editor (650+ LOC, 3 files)
135
+ │ ├── editor/ # Code editor (800+ LOC, 6 files)
135
136
  │ │ ├── code-editor.tsx # Monaco Editor integration (@monaco-editor/react, v2.0+)
136
137
  │ │ ├── diff-viewer.tsx # Monaco diff viewer for git diffs (v2.0+)
138
+ │ │ ├── editor-breadcrumb.tsx # VSCode-style breadcrumb with nested dropdown
139
+ │ │ ├── editor-toolbar.tsx # File-type contextual toolbar
140
+ │ │ ├── csv-preview.tsx # CSV table viewer with @tanstack/react-table
137
141
  │ │ └── editor-placeholder.tsx
138
142
  │ ├── explorer/ # File tree (489 LOC, 2 files)
139
143
  │ │ ├── file-tree.tsx # Directory tree view
@@ -346,11 +350,13 @@ UI updates staged/unstaged lists
346
350
  | @monaco-editor/react | Code editor | 4.7.0 |
347
351
  | xterm | Terminal emulator | 6.0 |
348
352
  | zustand | State management | 5.0.11 |
349
- | @anthropic-ai/claude-agent-sdk | AI provider | 0.2.76 |
353
+ | @anthropic-ai/claude-agent-sdk | AI provider | 0.2.81 |
350
354
  | vite | Frontend bundler | 8.0 |
351
355
  | tailwindcss | Utility CSS | 4.2 |
352
356
  | radix-ui | Accessible components | 1.4.3 |
353
357
  | next-themes | Theme switcher | 0.4.6 |
358
+ | @tanstack/react-table | Table library | 8.21.3 |
359
+ | @tanstack/react-virtual | Virtual scrolling | 3.13.23 |
354
360
 
355
361
  ## Build Output
356
362
 
@@ -393,6 +393,27 @@ screens: {
393
393
  // - Word wrap toggle (Alt+Z)
394
394
  // - Monaco diff viewer for git diffs
395
395
  // - Theme sync with app dark/light mode
396
+ // - VSCode-style breadcrumb navigation (EditorBreadcrumb)
397
+ // - File-type contextual toolbar (EditorToolbar)
398
+ ```
399
+
400
+ ### CSV Preview
401
+
402
+ ```tsx
403
+ // src/web/components/editor/csv-preview.tsx
404
+ // Table viewer for CSV files with sorting and editing
405
+
406
+ <CsvPreview
407
+ content={csvString}
408
+ onContentChange={setCsvString}
409
+ />
410
+
411
+ // Features:
412
+ // - State-machine CSV parser (handles quoted fields, embedded commas/newlines)
413
+ // - Virtual scrolling (@tanstack/react-virtual) for large files
414
+ // - Column sorting via @tanstack/react-table
415
+ // - Inline cell editing with live serialization
416
+ // - Mobile-friendly table layout
396
417
  ```
397
418
 
398
419
  ### Terminal Component
@@ -2,7 +2,121 @@
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.8.53
5
+ **Current Version:** v0.8.76
6
+
7
+ ---
8
+
9
+ ## [0.8.77] — 2026-04-01 (Planned)
10
+
11
+ ### Added
12
+ - **Deterministic Tab URLs + Backend Workspace Sync**
13
+ - Tab IDs now deterministic ({type}:{identifier}) instead of random (tab-xxxx)
14
+ - URL format changed: `/project/{name}/{tabType}/{identifier}` (e.g., `/project/ppm/editor/src/index.ts`)
15
+ - New `workspace_state` database table (schema v10) persists tab layout per project
16
+ - GET/PUT `/api/project/:name/workspace` endpoints for server-side persistence
17
+ - Frontend syncs workspace layout to server with 1.5s debounce
18
+ - Deep linking from URL auto-creates tabs if missing
19
+ - Latest-wins conflict resolution: server timestamp > client localStorage
20
+ - Tab ID patterns standardized: `editor:path`, `chat:provider/sessionId`, `terminal:index`, etc.
21
+ - Migration from random IDs to deterministic IDs on first load
22
+
23
+ ### Technical Details
24
+ - **Files Created:**
25
+ - `src/server/routes/workspace.ts` — Workspace GET/PUT endpoints
26
+ - `src/web/hooks/use-workspace-sync.ts` — Server sync orchestration with debounce
27
+ - **Files Modified:**
28
+ - `src/services/db.service.ts` — Added workspace_state table (schema v10), helper functions
29
+ - `src/web/stores/panel-utils.ts` — Deterministic tab ID derivation logic
30
+ - `src/web/hooks/use-url-sync.ts` — URL parsing/building with new format
31
+ - `src/web/stores/panel-store.ts` — Load workspace from server on project switch
32
+ - `src/web/stores/tab-store.ts` — Tab interface updated with metadata
33
+ - **Breaking Changes:** URLs changed; old `/project/{name}/tab/{id}` URLs redirected or ignored (fallback to project root)
34
+ - **Migration:** Client-side: old random tab IDs migrated to deterministic format on first load
35
+
36
+ ### Benefits
37
+ - **Shareable deep links** — URLs point to specific files/chats (e.g., `/project/ppm/editor/src/index.ts`)
38
+ - **Cross-device persistence** — Workspace layout saved on server, restored on any device
39
+ - **URL-driven navigation** — Paste URL to recreate workspace state
40
+ - **Conflict-free sync** — Latest timestamp wins; no manual merge dialogs
41
+
42
+ ---
43
+
44
+ ## [0.8.61] — 2026-03-26 (Beta)
45
+
46
+ ### Added
47
+ - **Multi-Provider Architecture** — Generic AI provider system supporting Claude (SDK-based) and CLI-spawning providers
48
+ - `AIProvider` interface with optional capability methods (`abortQuery?`, `getMessages?`, `listSessionsByDir?`)
49
+ - `CliProvider` abstract base class for CLI-spawning providers (Cursor, Codex, Gemini)
50
+ - `CursorCliProvider` implementation — spawns `cursor-agent` with NDJSON streaming
51
+ - NDJSON line parser utility for TCP packet boundary handling
52
+ - Cursor event mapper — normalizes Cursor NDJSON → standard ChatEvent union
53
+ - Cursor history reader — loads sessions from `~/.cursor/chats/` SQLite DAG
54
+ - Provider selector UI component — users can choose provider when creating chat
55
+ - Async provider bootstrap — checks binary availability, registers only if available
56
+ - Workspace trust auto-retry — detects trust prompts, retries with `--trust` flag
57
+ - Process lifecycle management — SIGTERM → SIGKILL escalation, orphan cleanup
58
+
59
+ ### Technical Details
60
+ - **Files Created:**
61
+ - `src/utils/ndjson-line-parser.ts` — NDJSON streaming parser
62
+ - `src/providers/cli-provider-base.ts` — Abstract CliProvider base class
63
+ - `src/providers/cursor-cli/cursor-provider.ts` — CursorCliProvider
64
+ - `src/providers/cursor-cli/cursor-event-mapper.ts` — Event mapping
65
+ - `src/providers/cursor-cli/cursor-history.ts` — SQLite history reader
66
+ - `src/web/components/chat/provider-selector.tsx` — Provider selection UI
67
+ - `tests/unit/ndjson-line-parser.test.ts` — Parser tests
68
+ - `tests/unit/cursor-event-mapper.test.ts` — Mapper tests
69
+ - `tests/integration/cursor-provider.test.ts` — Integration tests
70
+ - `tests/integration/chat-service-multi-provider.test.ts` — Service tests
71
+ - **Files Modified:**
72
+ - `src/types/chat.ts` — Added optional capability methods to AIProvider, added `system` event type
73
+ - `src/types/config.ts` — Added `"cli"` type, `cli_command` field to AIProviderConfig
74
+ - `src/providers/registry.ts` — Added async `bootstrapProviders()` for conditional registration
75
+ - `src/server/ws/chat.ts` — Removed `as any` casts, use optional chaining for capabilities
76
+ - `src/services/chat.service.ts` — Use optional methods instead of duck-typing
77
+ - `src/web/components/chat/session-picker.tsx` — Integrated provider selector
78
+ - **Breaking Changes:** None (backward compatible, all tests passing)
79
+ - **Architecture:** All phases complete (6/6), 479 tests passing
80
+
81
+ ### Benefits
82
+ - Extensible foundation for Codex, Gemini, and future providers (~100-150 lines each)
83
+ - No more `as any` casts for provider methods — type-safe optional capability pattern
84
+ - CLI providers can override session history reading (e.g., Cursor SQLite DAG)
85
+ - Graceful degradation — missing CLI binary doesn't crash, logs info, skips provider
86
+
87
+ ---
88
+
89
+ ## [0.8.55] — 2026-03-26
90
+
91
+ ### Added
92
+ - **Streaming Input Migration** — Persistent AsyncGenerator session model for chat
93
+ - Provider maintains long-lived streaming input per session (not per message)
94
+ - Follow-up messages push into existing generator instead of abort-and-replace
95
+ - Single streaming loop decoupled from WebSocket message handler
96
+ - Message priority support (`now`/`next`/`later`) for intelligent message ordering
97
+ - Image attachment support in message sending
98
+ - Session state persistence across FE disconnections (5-minute cleanup timeout)
99
+ - Event buffering on reconnect: clients receive buffered turn events after reconnection
100
+ - Phase transitions: `idle` → `initializing` → `connecting` → `thinking`/`streaming` → `idle`
101
+
102
+ ### Technical Details
103
+ - **Provider Layer** (`src/providers/claude-agent-sdk.ts`): Maintains streaming session per sessionId
104
+ - **WebSocket Handler** (`src/server/ws/chat.ts`):
105
+ - `runStreamLoop()` executes independently (detached from WS scope)
106
+ - `activeSessions` map persists session state across FE disconnections
107
+ - `startSessionConsumer()` pattern replaces per-message `runStreamLoop()` calls
108
+ - Event buffering (turnEvents array, max 10k events) for reconnect sync
109
+ - Per-client ping intervals (15s keepalive)
110
+ - **Types** (`src/types/api.ts`, `src/types/chat.ts`):
111
+ - `ChatWsClientMessage` extended with `priority?: string` and optional `images`
112
+ - `SessionPhase` = "initializing" | "connecting" | "thinking" | "streaming" | "idle"
113
+ - `SessionEntry` tracks clients, abort handle, phase, pending approvals, buffered events
114
+ - **Frontend** (`src/web/components/chat/message-input.tsx`): PriorityToggle component (visible during streaming)
115
+ - **Benefits**:
116
+ - No SDK subprocess restarts between messages (faster context preservation)
117
+ - Tool approvals integrated into streaming (no query restart)
118
+ - Message buffering prevents loss on FE reconnection
119
+ - Cleaner architecture: BE owns Claude connection, FE disconnect ≠ abort
6
120
 
7
121
  ---
8
122
 
@@ -1,6 +1,6 @@
1
1
  # PPM Project Roadmap
2
2
 
3
- **Last Updated:** March 22, 2026
3
+ **Last Updated:** March 27, 2026
4
4
 
5
5
  ## Vision
6
6
 
@@ -56,19 +56,26 @@ PPM is the **lightest path from phone to code** — a self-hosted, BYOK, multi-d
56
56
 
57
57
  ### v0.9.0 — "Open Platform" (Q2–Q3 2026)
58
58
 
59
- **Theme:** Multi-provider AI + extension system. Expand user base beyond Claude-only developers.
59
+ **Theme:** Multi-provider AI (Claude + Cursor) + extension system. Ship a focused release, expand providers later.
60
60
 
61
- | Feature | Priority | Description |
62
- |---------|----------|-------------|
63
- | **Multi-provider AI** | Critical | Refactor `ProviderInterface` for clean provider abstraction. Tiered support: Tier 1 (full agentic) = Claude Agent SDK; Tier 2 (chat + tools) = Gemini CLI, OpenAI Codex; Tier 3 (chat-only) = any OpenAI-compatible API. Clean base code for future Chinese providers (DeepSeek, Qwen). |
64
- | **Extension architecture** | High | Dynamic extension loading system. Extensions = npm packages exporting skills + optional UI panels. First extension: extract DB viewer from core. Extension API: register routes, UI panels, sidebar tabs, skills. Config: `"extensions": ["@ppm/ext-database", "@ppm/ext-docker"]`. |
65
- | **MCP Management** | Medium | UI to add/remove/configure MCP servers. Test connection. Per-project MCP overrides. Store in SQLite. Pass to Agent SDK via `mcpServers`. |
61
+ **Overall progress: ~40%** (1/3 features complete, merge all at 100%)
66
62
 
67
- **Multi-provider tiered approach:**
63
+ | Feature | Priority | Status | Description |
64
+ |---------|----------|--------|-------------|
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
+ | **Extension architecture** | High | 🔴 0% | Dynamic extension loading. Extensions = npm packages. First extension: extract DB viewer from core. Extension API: register routes, UI panels, sidebar tabs, skills. Config: `"extensions": ["@ppm/ext-database", "@ppm/ext-docker"]`. |
67
+ | **MCP Management** | Medium | 🔴 0% | UI to add/remove/configure MCP servers. Test connection. Per-project MCP overrides. Store in SQLite. Pass to Agent SDK via `mcpServers`. |
68
+
69
+ **Multi-provider — v0.9 scope (reduced):**
68
70
  - Tier 1 (full agentic): Claude Agent SDK — file edit, terminal, git, full autonomy
69
- - Tier 2 (chat + tools): Provider-specific CLIs (Gemini CLI, Codex) — agentic via their own tool system
70
- - Tier 3 (chat-only): Any OpenAI-compatible API conversation only, no tools
71
- - Provider interface refactor is foundation work — do it clean now, avoid painful refactor later
71
+ - Tier 2 (agentic CLI): Cursor — agentic via its own tool system
72
+ - Provider interface is clean enough to add more providers later without refactor
73
+
74
+ **Deferred to v0.9.5+:**
75
+ - Gemini CLI (Tier 2)
76
+ - OpenAI Codex (Tier 2)
77
+ - Tier 3 (chat-only): Any OpenAI-compatible API
78
+ - Chinese providers (DeepSeek, Qwen) — v1.0+
72
79
 
73
80
  **Extension architecture — design principles:**
74
81
  - Extensions are npm packages: `ppm ext install @ppm/ext-database`
@@ -79,15 +86,27 @@ PPM is the **lightest path from phone to code** — a self-hosted, BYOK, multi-d
79
86
 
80
87
  ---
81
88
 
82
- ### v0.10.0 — "Intelligence" (Q3 2026)
89
+ ### v0.10.0 — "Enhanced Workflow" (Q3 2026)
83
90
 
84
- **Theme:** PPM's own AI layer. Built-in bot + programmable skills.
91
+ **Theme:** Chat UX upgrade + git workflow. High-impact, independent features that ship fast.
85
92
 
86
93
  | Feature | Priority | Description |
87
94
  |---------|----------|-------------|
95
+ | **Chat history graph** | High | Visual branching tree of chat sessions. Fork conversations, navigate history graph. Game-changer for AI chat UX. |
96
+ | **Worktree management** | Medium | UI to create/switch/delete git worktrees. Use different providers on different branches. Integrated with project switcher. |
97
+
98
+ ---
99
+
100
+ ### v0.11.0 — "Intelligence" (Q3–Q4 2026)
101
+
102
+ **Theme:** Event system + PPM's own AI layer. Hooks → Skills API → Clawbot dependency chain.
103
+
104
+ | Feature | Priority | Description |
105
+ |---------|----------|-------------|
106
+ | **Hooks system** | High | Event hooks for PPM lifecycle (file save, git commit, chat message, etc.). Foundation for Skills API and deeper extension integration. |
88
107
  | **PPM Skills API** | High | Stable internal API for AI to control PPM: file.read/write/search, terminal.run, git.status/commit/diff, db.query, editor.open/goto, project.switch. Skills are the bridge between AI and PPM features. |
89
108
  | **Built-in Clawbot** | High | Lightweight AI agent built into PPM using Anthropic Messages API (not Agent SDK). Uses Skills API + MCP tools. Instant response, no external CLI deps. For quick tasks: file search, code explanation, simple refactors. |
90
- | **Inline SQL** | Medium | Select text in Monaco run as SQL. Connection picker in editor context menu. Results panel below editor. Leverages existing DB service. |
109
+ | **More providers** | Medium | Gemini CLI (Tier 2), OpenAI Codex (Tier 2), Tier 3 chat-only (any OpenAI-compatible API). Provider interface already clean from v0.9. |
91
110
 
92
111
  **Built-in Clawbot — why it matters:**
93
112
  - Claude Agent SDK spawns subprocess — heavy, slow startup, requires CLI installed
@@ -106,6 +125,7 @@ PPM is the **lightest path from phone to code** — a self-hosted, BYOK, multi-d
106
125
  | **Self-hosted PPM Cloud** | High | Docker image of PPM Cloud for enterprise/team. Same codebase, self-hosted config flag. `docker-compose up` and it works. LDAP/SSO integration. |
107
126
  | **PPM Marketplace** | High | Publish/install/update extensions. Browse community extensions. Revenue sharing for paid extensions. Clawbot can create extension → test → publish in minutes. |
108
127
  | **Stability & hardening** | Critical | Security audit, performance optimization, comprehensive test coverage (>80%), documentation for contributors, CI/CD pipeline. |
128
+ | **Inline SQL** | Medium | Select text in Monaco → run as SQL. Connection picker in editor context menu. Results panel below editor. Leverages existing DB service. |
109
129
 
110
130
  ---
111
131
 
@@ -130,6 +150,7 @@ Features to pick from after v1.0. Will be reviewed and scheduled based on user f
130
150
  | **Cross-platform binaries** | Distribution | Compile macOS/Linux/Windows binaries via `bun build --compile`. `npx ppm` without Bun. |
131
151
  | **OLED dark mode** | UX | True black background for OLED screens. |
132
152
  | **Collaborative editing** | Social | Real-time multi-user file editing with CRDT (yjs/automerge). |
153
+ | **Custom domain** | Cloud | Map custom domain to PPM Cloud tunnel URL. DNS CNAME + SSL via Let's Encrypt or Cloudflare. Access PPM at `code.yourdomain.com`. |
133
154
 
134
155
  ---
135
156
 
@@ -139,9 +160,10 @@ Features to pick from after v1.0. Will be reviewed and scheduled based on user f
139
160
  |---------|-------|-------------|--------|
140
161
  | **v0.7** | Multi-Account & Mobile | Account management, usage tracking, mobile UX | ✅ Current |
141
162
  | **v0.8** | Always On | PPM Cloud, auto-start, AI chat enhancements | Q2 2026 |
142
- | **v0.9** | Open Platform | Multi-provider AI, extension architecture, MCP | Q2–Q3 2026 |
143
- | **v0.10** | Intelligence | Skills API, built-in Clawbot, inline SQL | Q3 2026 |
144
- | **v1.0** | Production Ready | Self-hosted Cloud, Marketplace, stability | Q4 2026 |
163
+ | **v0.9** | Open Platform | Multi-provider (Claude + Cursor), extension architecture, MCP | Q2–Q3 2026 |
164
+ | **v0.10** | Enhanced Workflow | Chat history graph, worktree management | Q3 2026 |
165
+ | **v0.11** | Intelligence | Hooks, Skills API, Clawbot, more providers (Gemini/Codex/Tier 3) | Q3–Q4 2026 |
166
+ | **v1.0** | Production Ready | Self-hosted Cloud, Marketplace, stability, inline SQL | Q4 2026 |
145
167
 
146
168
  ---
147
169
 
@@ -149,7 +171,7 @@ Features to pick from after v1.0. Will be reviewed and scheduled based on user f
149
171
 
150
172
  1. **Own "phone to code"** — PPM wins on multi-device access. Don't chase Cursor/Windsurf feature parity.
151
173
  2. **PPM Cloud stays razor-thin** — Device registry + tunnel URLs only. No code storage. No cloud execution.
152
- 3. **Multi-provider is tiered** — Full agentic = Claude SDK. Other providers get appropriate tier. Clean interface for future providers.
174
+ 3. **Multi-provider is tiered** — v0.9: Claude SDK (Tier 1) + Cursor (Tier 2). v0.11: Gemini, Codex, Tier 3. Clean interface for future providers.
153
175
  4. **Extensions keep core lightweight** — Features are opt-in. DB viewer, future tools = extensions. Core stays fast.
154
176
  5. **Clawbot enables the ecosystem** — Users create extensions with AI, publish to Marketplace. Zero-friction.
155
177
  6. **Self-hosted first, always** — Cloud is optional convenience. PPM works 100% offline/local.
@@ -160,7 +182,7 @@ Features to pick from after v1.0. Will be reviewed and scheduled based on user f
160
182
 
161
183
  | Item | Priority | Notes |
162
184
  |------|----------|-------|
163
- | Refactor ProviderInterface for multi-provider | High | Foundation for v0.9 |
185
+ | ~~Refactor ProviderInterface for multi-provider~~ | ~~High~~ | Done on beta branch (v0.9.0-beta.5) |
164
186
  | Simplify ChatService streaming | Medium | Reduce async generator complexity |
165
187
  | Extract WebSocket common logic | Low | DRY for chat/terminal WS |
166
188
  | Round-robin cursor bug in AccountSelector | Medium | Positional cursor not advancing correctly |