@hienlh/ppm 0.8.87 → 0.8.88

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 (181) hide show
  1. package/CHANGELOG.md +64 -40
  2. package/dist/web/assets/{_basePickBy-5PGDJbfF.js → _basePickBy-3Xe18azI.js} +1 -1
  3. package/dist/web/assets/{_baseUniq-BT4Ow4Kk.js → _baseUniq-Yy35llnn.js} +1 -1
  4. package/dist/web/assets/{api-settings-Bx1GaNmQ.js → api-settings-Dh4oFOpX.js} +1 -1
  5. package/dist/web/assets/{arc-BAOivWpI.js → arc-B9n1Gvb5.js} +1 -1
  6. package/dist/web/assets/architecture-PBZL5I3N-CFzkFKEL.js +1 -0
  7. package/dist/web/assets/{architectureDiagram-2XIMDMQ5-DWBCPMLF.js → architectureDiagram-2XIMDMQ5-DqAZP_F6.js} +1 -1
  8. package/dist/web/assets/{blockDiagram-WCTKOSBZ-TEF8Ally.js → blockDiagram-WCTKOSBZ-h3cDF2vI.js} +1 -1
  9. package/dist/web/assets/{browser-tab-DaHGm_0i.js → browser-tab-DJLH0eDY.js} +1 -1
  10. package/dist/web/assets/{c4Diagram-IC4MRINW-dV22iAsY.js → c4Diagram-IC4MRINW--pF1r5lr.js} +1 -1
  11. package/dist/web/assets/channel-C2fMafck.js +1 -0
  12. package/dist/web/assets/chat-tab-C8HFXqGS.js +8 -0
  13. package/dist/web/assets/{chunk-4BX2VUAB-D4tOov49.js → chunk-4BX2VUAB-C3aZvW7B.js} +1 -1
  14. package/dist/web/assets/{chunk-55IACEB6-DJ6BynZ4.js → chunk-55IACEB6-D5cABeB9.js} +1 -1
  15. package/dist/web/assets/{chunk-7E7YKBS2-CiyUJxNI.js → chunk-7E7YKBS2-CkFGv6Zs.js} +1 -1
  16. package/dist/web/assets/{chunk-7R4GIKGN-BbIFzsIv.js → chunk-7R4GIKGN-Dvbyu4Zw.js} +2 -2
  17. package/dist/web/assets/{chunk-C72U2L5F-D21mS_6G.js → chunk-C72U2L5F-CtqKiH4q.js} +1 -1
  18. package/dist/web/assets/{chunk-EGIJ26TM-DzqmU2Z7.js → chunk-EGIJ26TM-Cpr87sBR.js} +1 -1
  19. package/dist/web/assets/{chunk-FMBD7UC4-DXncblvW.js → chunk-FMBD7UC4-D23YVTOU.js} +1 -1
  20. package/dist/web/assets/{chunk-GEFDOKGD-BbQkJu8C.js → chunk-GEFDOKGD-tDjHsAUs.js} +1 -1
  21. package/dist/web/assets/chunk-GLR3WWYH-DBdWQ3zy.js +2 -0
  22. package/dist/web/assets/chunk-HHEYEP7N-BBw_z0fW.js +1 -0
  23. package/dist/web/assets/{chunk-JSJVCQXG-23tyvw8k.js → chunk-JSJVCQXG-BBmymCjA.js} +1 -1
  24. package/dist/web/assets/{chunk-KX2RTZJC-sQ0o-39C.js → chunk-KX2RTZJC-DP36BDiU.js} +1 -1
  25. package/dist/web/assets/{chunk-KYZI473N-BcUZNnwd.js → chunk-KYZI473N-Djw13C-3.js} +1 -1
  26. package/dist/web/assets/{chunk-L3YUKLVL-C7qGJrfV.js → chunk-L3YUKLVL-HG_eMj_C.js} +1 -1
  27. package/dist/web/assets/{chunk-MX3YWQON-BpS_PtKp.js → chunk-MX3YWQON-C2UEioMs.js} +1 -1
  28. package/dist/web/assets/{chunk-NQ4KR5QH-wMgTlP7f.js → chunk-NQ4KR5QH-DXUTQ-BL.js} +1 -1
  29. package/dist/web/assets/{chunk-O4XLMI2P-JC6EGoUz.js → chunk-O4XLMI2P-BsUWb9d0.js} +1 -1
  30. package/dist/web/assets/{chunk-OZEHJAEY-BXhYx3nO.js → chunk-OZEHJAEY-rG0P22U9.js} +1 -1
  31. package/dist/web/assets/{chunk-PQ6SQG4A-D6BTbCQw.js → chunk-PQ6SQG4A-DX0xW7kO.js} +1 -1
  32. package/dist/web/assets/{chunk-PU5JKC2W-Dw8ClWch.js → chunk-PU5JKC2W-C7Gry6md.js} +1 -1
  33. package/dist/web/assets/chunk-QZHKN3VN-DFKFM_C1.js +1 -0
  34. package/dist/web/assets/{chunk-R5LLSJPH-CFwSJijQ.js → chunk-R5LLSJPH-CMY0PkRK.js} +1 -1
  35. package/dist/web/assets/{chunk-WL4C6EOR-DfofndiH.js → chunk-WL4C6EOR-CXuQvlyu.js} +1 -1
  36. package/dist/web/assets/{chunk-XIRO2GV7-Djlmrely.js → chunk-XIRO2GV7-DRJEb7Zb.js} +1 -1
  37. package/dist/web/assets/{chunk-XPW4576I-BPQQBakK.js → chunk-XPW4576I-BPEX8KhL.js} +1 -1
  38. package/dist/web/assets/{chunk-XZSTWKYB-DxAOx4hG.js → chunk-XZSTWKYB-Cb0iqycX.js} +1 -1
  39. package/dist/web/assets/{chunk-YBOYWFTD-CeU4Q-xC.js → chunk-YBOYWFTD-av5aeHLq.js} +1 -1
  40. package/dist/web/assets/classDiagram-VBA2DB6C-Dp4Kk3Yb.js +1 -0
  41. package/dist/web/assets/classDiagram-v2-RAHNMMFH-D8IvcV_B.js +1 -0
  42. package/dist/web/assets/clone-B2hUek6n.js +1 -0
  43. package/dist/web/assets/code-editor-CaGdx-lS.js +2 -0
  44. package/dist/web/assets/{cose-bilkent-S5V4N54A-B_AWZsOP.js → cose-bilkent-S5V4N54A-qudEiMCT.js} +1 -1
  45. package/dist/web/assets/{csv-preview-DLqYtXxt.js → csv-preview-DUbHtTAS.js} +1 -1
  46. package/dist/web/assets/{dagre-Dbb5k38K.js → dagre-BFcnKyBF.js} +1 -1
  47. package/dist/web/assets/{dagre-KLK3FWXG-BH7aWGRP.js → dagre-KLK3FWXG-C3O-MTLf.js} +1 -1
  48. package/dist/web/assets/{database-viewer-DXk79Nel.js → database-viewer-i4Ddk6mO.js} +1 -1
  49. package/dist/web/assets/{diagram-E7M64L7V-B1Qz70Do.js → diagram-E7M64L7V-DxPjK7_c.js} +1 -1
  50. package/dist/web/assets/{diagram-IFDJBPK2-k55eVqVU.js → diagram-IFDJBPK2-sqTog_XV.js} +1 -1
  51. package/dist/web/assets/{diagram-P4PSJMXO-BkfNRc9U.js → diagram-P4PSJMXO-hzmp0GHK.js} +1 -1
  52. package/dist/web/assets/diff-viewer-DQDS7yjv.js +4 -0
  53. package/dist/web/assets/{erDiagram-INFDFZHY-CKzVujYI.js → erDiagram-INFDFZHY-DLeYhAAT.js} +1 -1
  54. package/dist/web/assets/{flowDiagram-PKNHOUZH-DIqcTrDV.js → flowDiagram-PKNHOUZH-CRxlE9Sr.js} +1 -1
  55. package/dist/web/assets/{ganttDiagram-A5KZAMGK-D4v7ZbVE.js → ganttDiagram-A5KZAMGK-BdjmoMLS.js} +1 -1
  56. package/dist/web/assets/git-graph-DUs-TN1u.js +1 -0
  57. package/dist/web/assets/gitGraph-HDMCJU4V-CNlas3Rz.js +1 -0
  58. package/dist/web/assets/{gitGraphDiagram-K3NZZRJ6-BTXo57mF.js → gitGraphDiagram-K3NZZRJ6-BeHSX7kk.js} +1 -1
  59. package/dist/web/assets/{graphlib-BcsNnGcW.js → graphlib-Duh_bWLa.js} +1 -1
  60. package/dist/web/assets/index-DhtLEnPD.css +2 -0
  61. package/dist/web/assets/index-Dm6RN1A1.js +37 -0
  62. package/dist/web/assets/info-3K5VOQVL-BDzTLc11.js +1 -0
  63. package/dist/web/assets/infoDiagram-LFFYTUFH-ZZmpgc6t.js +2 -0
  64. package/dist/web/assets/{isEmpty-bnrF3Qbc.js → isEmpty-B9L-Ge-H.js} +1 -1
  65. package/dist/web/assets/{ishikawaDiagram-PHBUUO56-BOyvKMmB.js → ishikawaDiagram-PHBUUO56-Cu0Rt1Ok.js} +1 -1
  66. package/dist/web/assets/{journeyDiagram-4ABVD52K-ufoasAy6.js → journeyDiagram-4ABVD52K-CgDI-UG4.js} +1 -1
  67. package/dist/web/assets/{kanban-definition-K7BYSVSG-Bi0UTUeN.js → kanban-definition-K7BYSVSG-h4g10UHL.js} +1 -1
  68. package/dist/web/assets/keybindings-store-qVLDZz97.js +1 -0
  69. package/dist/web/assets/{line-B78g-52T.js → line-B75-Rx70.js} +1 -1
  70. package/dist/web/assets/{linear-DP4mkX3m.js → linear-Bcjv9FQt.js} +1 -1
  71. package/dist/web/assets/{markdown-renderer-Brj8_LQM.js → markdown-renderer-L1NgC2Rw.js} +5 -5
  72. package/dist/web/assets/{mermaid-parser.core-DMIWdgEW.js → mermaid-parser.core-8u2leTXI.js} +2 -2
  73. package/dist/web/assets/{mindmap-definition-YRQLILUH-BsfWvIoO.js → mindmap-definition-YRQLILUH-BaOBwb-W.js} +1 -1
  74. package/dist/web/assets/{ordinal-_K3x1fkz.js → ordinal-LFEjVtwQ.js} +1 -1
  75. package/dist/web/assets/packet-RMMSAZCW-IVa5F-go.js +1 -0
  76. package/dist/web/assets/pie-UPGHQEXC-CvXHKAzp.js +1 -0
  77. package/dist/web/assets/{pieDiagram-SKSYHLDU-WP0XXw51.js → pieDiagram-SKSYHLDU-At5Kz0KK.js} +1 -1
  78. package/dist/web/assets/{postgres-viewer-CwkTGmqy.js → postgres-viewer-_uDispGW.js} +1 -1
  79. package/dist/web/assets/{quadrantDiagram-337W2JSQ-FHMogtsh.js → quadrantDiagram-337W2JSQ-CdjGIDfw.js} +1 -1
  80. package/dist/web/assets/radar-KQ55EAFF-Z-Tr5wtS.js +1 -0
  81. package/dist/web/assets/{requirementDiagram-Z7DCOOCP-BatTxyWb.js → requirementDiagram-Z7DCOOCP-B9F_Cx_p.js} +1 -1
  82. package/dist/web/assets/{sankeyDiagram-WA2Y5GQK-ClJuW3Hv.js → sankeyDiagram-WA2Y5GQK-RolPi8bU.js} +1 -1
  83. package/dist/web/assets/{sequenceDiagram-2WXFIKYE-ByxQqGgs.js → sequenceDiagram-2WXFIKYE-DM-tMAhx.js} +1 -1
  84. package/dist/web/assets/settings-tab-Bp4041i6.js +1 -0
  85. package/dist/web/assets/{sqlite-viewer-CFYTwgA8.js → sqlite-viewer-GW-QCjHn.js} +1 -1
  86. package/dist/web/assets/{stateDiagram-RAJIS63D-f8opcZNY.js → stateDiagram-RAJIS63D-C4EMl6jf.js} +1 -1
  87. package/dist/web/assets/stateDiagram-v2-FVOUBMTO-B-UjZch3.js +1 -0
  88. package/dist/web/assets/{tab-store-BJw7OCmy.js → tab-store--SlERlDs.js} +1 -1
  89. package/dist/web/assets/{terminal-tab-CCDLZA5Y.js → terminal-tab-E4cWujj4.js} +2 -2
  90. package/dist/web/assets/{timeline-definition-YZTLITO2-58BlOSf9.js → timeline-definition-YZTLITO2-A4PN_Efm.js} +1 -1
  91. package/dist/web/assets/treemap-KZPCXAKY-C9TYRE0k.js +1 -0
  92. package/dist/web/assets/{use-monaco-theme-CNzekTN3.js → use-monaco-theme-zABXAAla.js} +1 -1
  93. package/dist/web/assets/{vennDiagram-LZ73GAT5-BOSy9ma9.js → vennDiagram-LZ73GAT5-ywK7LMaH.js} +1 -1
  94. package/dist/web/assets/{xychartDiagram-JWTSCODW-z5MVJauZ.js → xychartDiagram-JWTSCODW-DylHYNtJ.js} +1 -1
  95. package/dist/web/index.html +10 -11
  96. package/dist/web/sw.js +1 -1
  97. package/docs/code-standards.md +155 -0
  98. package/docs/codebase-summary.md +261 -95
  99. package/docs/project-changelog.md +38 -3
  100. package/docs/project-roadmap.md +2 -2
  101. package/docs/system-architecture.md +151 -0
  102. package/package.json +1 -1
  103. package/src/providers/claude-agent-sdk.ts +244 -102
  104. package/src/providers/cli-provider-base.ts +238 -0
  105. package/src/providers/cursor-cli/cursor-event-mapper.ts +85 -0
  106. package/src/providers/cursor-cli/cursor-history.ts +207 -0
  107. package/src/providers/cursor-cli/cursor-provider.ts +146 -0
  108. package/src/providers/mock-provider.ts +1 -1
  109. package/src/providers/provider.interface.ts +1 -0
  110. package/src/providers/registry.ts +43 -4
  111. package/src/server/index.ts +6 -0
  112. package/src/server/routes/chat.ts +14 -3
  113. package/src/server/routes/mcp.ts +84 -0
  114. package/src/server/routes/settings.ts +14 -0
  115. package/src/server/ws/chat.ts +127 -81
  116. package/src/services/chat.service.ts +10 -15
  117. package/src/services/db.service.ts +8 -0
  118. package/src/services/mcp-config.service.ts +102 -0
  119. package/src/types/api.ts +1 -1
  120. package/src/types/chat.ts +23 -2
  121. package/src/types/config.ts +33 -11
  122. package/src/types/mcp.ts +47 -0
  123. package/src/utils/ndjson-line-parser.ts +36 -0
  124. package/src/web/components/chat/chat-history-bar.tsx +48 -29
  125. package/src/web/components/chat/chat-tab.tsx +29 -24
  126. package/src/web/components/chat/message-input.tsx +64 -5
  127. package/src/web/components/chat/provider-selector.tsx +150 -0
  128. package/src/web/components/chat/session-picker.tsx +3 -1
  129. package/src/web/components/settings/ai-settings-section.tsx +196 -137
  130. package/src/web/components/settings/mcp-server-dialog.tsx +208 -0
  131. package/src/web/components/settings/mcp-settings-section.tsx +143 -0
  132. package/src/web/components/settings/settings-tab.tsx +5 -2
  133. package/src/web/hooks/use-chat.ts +32 -15
  134. package/src/web/lib/api-mcp.ts +38 -0
  135. package/dist/web/assets/architecture-PBZL5I3N-DEO2f3VD.js +0 -1
  136. package/dist/web/assets/channel-wrd-NHWf.js +0 -1
  137. package/dist/web/assets/chat-tab-BDYE0KHF.js +0 -8
  138. package/dist/web/assets/chunk-GLR3WWYH-CzYx4w-r.js +0 -2
  139. package/dist/web/assets/chunk-HHEYEP7N-HRhYy3kG.js +0 -1
  140. package/dist/web/assets/chunk-QZHKN3VN-CYaTbeZf.js +0 -1
  141. package/dist/web/assets/classDiagram-VBA2DB6C-lse8oZoJ.js +0 -1
  142. package/dist/web/assets/classDiagram-v2-RAHNMMFH-CxkwuInd.js +0 -1
  143. package/dist/web/assets/clone-LRxlvnMj.js +0 -1
  144. package/dist/web/assets/code-editor-DTA3c9Y8.js +0 -2
  145. package/dist/web/assets/diff-viewer-HhIcsOQE.js +0 -4
  146. package/dist/web/assets/git-graph-CQtWu8yE.js +0 -1
  147. package/dist/web/assets/gitGraph-HDMCJU4V-Bwna3and.js +0 -1
  148. package/dist/web/assets/index-CgQXpBb_.css +0 -2
  149. package/dist/web/assets/index-DEeeRoka.js +0 -37
  150. package/dist/web/assets/info-3K5VOQVL-_vRxVNUm.js +0 -1
  151. package/dist/web/assets/infoDiagram-LFFYTUFH-B1CX0pbC.js +0 -2
  152. package/dist/web/assets/input-BglMT33g.js +0 -1
  153. package/dist/web/assets/keybindings-store-1CJ7VX57.js +0 -1
  154. package/dist/web/assets/packet-RMMSAZCW-DY5PNnZU.js +0 -1
  155. package/dist/web/assets/pie-UPGHQEXC-BHncZutv.js +0 -1
  156. package/dist/web/assets/radar-KQ55EAFF-DH0AOkUy.js +0 -1
  157. package/dist/web/assets/settings-tab-BDE1MsIh.js +0 -1
  158. package/dist/web/assets/stateDiagram-v2-FVOUBMTO-DrxVDY9q.js +0 -1
  159. package/dist/web/assets/treemap-KZPCXAKY-B2Xkyv-K.js +0 -1
  160. /package/dist/web/assets/{api-client-BfBM3I7n.js → api-client-BKIT_Qeg.js} +0 -0
  161. /package/dist/web/assets/{array-B9UHiPd-.js → array-DqLCdDFv.js} +0 -0
  162. /package/dist/web/assets/{chevron-right-DeV0ehiG.js → chevron-right-CHnjJt4E.js} +0 -0
  163. /package/dist/web/assets/{columns-2-DpsNbZOc.js → columns-2-DbesTfa7.js} +0 -0
  164. /package/dist/web/assets/{cytoscape.esm-BW-DbntU.js → cytoscape.esm-CWPXKqbJ.js} +0 -0
  165. /package/dist/web/assets/{defaultLocale-5eAKkKJC.js → defaultLocale-CrJzLgRD.js} +0 -0
  166. /package/dist/web/assets/{dist-lF8CoYII.js → dist-CALwEtco.js} +0 -0
  167. /package/dist/web/assets/{dist-CSJdAyA9.js → dist-Cep75xXf.js} +0 -0
  168. /package/dist/web/assets/{dist-DylI9XxN.js → dist-DGDPTxs1.js} +0 -0
  169. /package/dist/web/assets/{init-DlZdxViB.js → init-C0r9Gk5G.js} +0 -0
  170. /package/dist/web/assets/{isArrayLikeObject-B_v2FtYn.js → isArrayLikeObject-CGBoxvCD.js} +0 -0
  171. /package/dist/web/assets/{katex-Bqvo_ZG0.js → katex-DzXRfQ_m.js} +0 -0
  172. /package/dist/web/assets/{lib-BQ34Db2e.js → lib-BeaDXEkP.js} +0 -0
  173. /package/dist/web/assets/{math-069Z4SuC.js → math-y9zN1W-N.js} +0 -0
  174. /package/dist/web/assets/{path-6uRLdFF7.js → path-DIKpVbHL.js} +0 -0
  175. /package/dist/web/assets/{preload-helper-uTix4PVD.js → preload-helper-Bf_JiD2A.js} +0 -0
  176. /package/dist/web/assets/{react-ER-4DN55.js → react-SKk5z-bm.js} +0 -0
  177. /package/dist/web/assets/{rough.esm-JX0wREDd.js → rough.esm-nHaDi0Kw.js} +0 -0
  178. /package/dist/web/assets/{src-BqX54PbV.js → src-Dw4QhedI.js} +0 -0
  179. /package/dist/web/assets/{table-C7X5UAEI.js → table-CQVQM2SB.js} +0 -0
  180. /package/dist/web/assets/{tag-CCtdV063.js → tag-Q2dZiSPX.js} +0 -0
  181. /package/dist/web/assets/{utils-BNytJOb1.js → utils-DMiycH3O.js} +0 -0
@@ -1,103 +1,105 @@
1
1
  # PPM Codebase Summary
2
2
 
3
- Generated from codebase analysis of 135+ TypeScript files, ~22.5K LOC (including multi-account feature).
3
+ **Last Updated:** 2026-03-26
4
+ **Version:** 0.8.60
5
+ **Repository:** PPM (Project & Process Manager) — Multi-provider web IDE/project manager with Claude Agent SDK
6
+
7
+ **Core Statistics:**
8
+ - **303 files** across CLI, server, web, and test layers
9
+ - **490,667 tokens** total codebase size
10
+ - **492 passing tests** (13 new tests for provider models API)
11
+ - **Tech Stack:** Bun (runtime), Hono (HTTP), React (UI), Claude Agent SDK (AI)
12
+
13
+ ---
4
14
 
5
15
  ## Directory Structure
6
16
 
7
17
  ```
8
- ppm/
9
- ├── src/
10
- │ ├── index.ts # CLI entry point (Commander.js program)
11
- ├── cli/
12
- │ ├── commands/ # CLI command implementations (14 files, 1700 LOC)
13
- │ │ │ ├── start.ts # Start server (background by default, --foreground/-f, --share/-s for tunnel)
14
- │ │ ├── stop.ts # Stop daemon (reads status.json or ppm.pid, graceful shutdown)
15
- │ │ ├── restart.ts # Restart daemon (keeps tunnel alive)
16
- │ │ │ ├── status.ts # Show daemon status
17
- │ │ ├── open.ts # Open browser to http://localhost:PORT
18
- │ │ ├── logs.ts # Tail daemon logs
19
- │ │ ├── report.ts # File bug report on GitHub
20
- │ │ ├── init.ts # Initialize config (scan git repos, DB profile support)
21
- │ │ ├── projects.ts # Add/remove/list/scan projects
22
- │ │ ├── config-cmd.ts # View/set config values
23
- │ │ ├── git-cmd.ts # Git operations (status, diff, log, commit)
24
- │ │ ├── chat-cmd.ts # Chat CLI (send messages, manage sessions)
25
- │ │ │ └── db-cmd.ts # Database CLI (list, query, manage connections)
26
- │ │ └── utils/
27
- │ │ └── project-resolver.ts # Resolve project name -> path
28
- │ ├── server/
29
- │ │ ├── index.ts # Hono server setup, Bun.serve, WebSocket upgrade
30
- │ ├── middleware/
31
- │ │ └── auth.ts # Token validation middleware
32
- │ ├── routes/
33
- │ │ │ ├── settings.ts # GET/PUT /api/settings/ai (AI provider config)
34
- │ │ ├── projects.ts # GET/POST /api/projects, DELETE /:name
35
- │ │ ├── accounts.ts # GET/POST/PUT/DELETE /api/accounts, POST activate
36
- │ │ ├── project-scoped.ts # Mount chat, git, files under /api/project/:name/*
37
- │ │ │ ├── chat.ts # GET/POST/DELETE sessions, GET messages, usage, slash-items
38
- │ │ ├── git.ts # GET status, diff, log, graph; POST commit, stage, discard
39
- │ │ ├── files.ts # GET tree, read, diff; PUT write; POST mkdir, delete
40
- │ │ ├── database.ts # GET/POST/PUT/DELETE /api/db/connections (CRUD), query execution
41
- │ │ │ └── static.ts # Serve dist/web/index.html (frontend)
42
- ├── helpers/
43
- │ │ └── resolve-project.ts # Helper to resolve project from request params
44
- │ └── ws/
45
- ├── chat.ts # WebSocket chat streaming (220 LOC)
46
- │ └── terminal.ts # WebSocket terminal I/O (terminal.service.ts integration)
47
- │ ├── providers/ # AI Provider adapters (4 files, 1190 LOC)
48
- ├── provider.interface.ts # AIProvider interface (createSession, sendMessage, onToolApproval)
49
- ├── claude-agent-sdk.ts # Primary: SDK integration, tool approval, Windows CLI fallback, .env poisoning mitigation
50
- ├── mock-provider.ts # Test provider (ignores config)
51
- │ └── registry.ts # ProviderRegistry (singleton, router to active provider)
52
- │ ├── services/ # Business logic (20 files, 3300+ LOC)
53
- │ │ ├── chat.service.ts # Session lifecycle, message streaming
54
- │ │ ├── config.service.ts # Config loading (YAML→SQLite migration)
55
- │ │ ├── db.service.ts # SQLite persistence (schema v5, WAL mode, 9 tables, connection/account CRUD)
56
- │ ├── account.service.ts # Account CRUD, token encryption/decryption, active selection
57
- │ │ ├── account-selector.service.ts # Select active account based on config
58
- ├── project.service.ts # Project CRUD, scanning, resolution
59
- │ ├── file.service.ts # File ops with path validation
60
- │ │ ├── git.service.ts # Git operations (status, diff, log, graph)
61
- ├── terminal.service.ts # PTY management, Bun.spawn native shell
62
- ├── claude-usage.service.ts # Token tracking, cost calculation
63
- ├── push-notification.service.ts # Web push subscriptions
64
- ├── session-log.service.ts # Session audit logs with redaction
65
- ├── slash-items.service.ts # /slash command detection & completion
66
- ├── git-dirs.service.ts # Cached git directory discovery
67
- ├── cloudflared.service.ts # Download cloudflared binary (platform-specific)
68
- │ ├── tunnel.service.ts # Cloudflare Quick Tunnel lifecycle
69
- │ │ ├── table-cache.service.ts # Table metadata cache & search for DB connections
70
- │ │ └── database/ # Database adapters & registry
71
- │ │ ├── adapter-registry.ts # DatabaseAdapter registry (extensible)
72
- │ │ ├── sqlite-adapter.ts # SQLite connection, query execution
73
- │ │ ├── postgres-adapter.ts # PostgreSQL connection, query execution
74
- ├── init-adapters.ts # Initialize adapters at server start
75
- │ │ └── readonly-check.ts # isReadOnlyQuery() safety regex (CTE-safe)
76
- │ ├── lib/ # Shared utilities (2 files)
77
- │ │ ├── account-crypto.ts # AES-256 encryption/decryption for API keys
78
- │ │ └── network-utils.ts # Network utility helpers
79
- │ ├── types/ # TypeScript interfaces (7 files, 450 LOC)
80
- │ │ ├── api.ts # ApiResponse envelope, WebSocket message types
81
- ├── chat.ts # Session, Message, ChatEvent types
82
- │ │ ├── config.ts # Config schema
83
- │ │ ├── database.ts # DatabaseAdapter, DbConnectionConfig, DbTableInfo, etc.
84
- │ │ ├── git.ts # GitStatus, GitDiff, GitCommit types
85
- ├── project.ts # Project interface
86
- │ └── terminal.ts # Terminal types
87
- └── web/ # React frontend (Vite)
88
- ├── main.tsx # React mount (<App> into #root)
89
- ├── app.tsx # Root component (auth check, project load, theme)
90
- ├── stores/ # Zustand state stores (6 files)
91
- ├── project-store.ts # Active project, projects list, localStorage persistence
92
- │ │ ├── tab-store.ts # Tab facade, delegates to panel-store
93
- │ │ ├── panel-store.ts # Grid layout, panel creation/movement, keep-alive snapshots
94
- │ │ ├── panel-utils.ts # Layout algorithm helpers, grid manipulation
95
- │ │ ├── file-store.ts # File cache
96
- │ │ └── settings-store.ts # Theme, sidebar state, git view mode, device name
97
- │ ├── hooks/ # Custom React hooks (9 files)
98
- │ │ ├── use-chat.ts # Chat streaming, messages, approvals, context window tracking
99
- │ │ ├── use-websocket.ts # WebSocket connection with auto-reconnect
100
- │ │ ├── use-terminal.ts # Terminal connection and streaming
18
+ src/
19
+ ├── cli/
20
+ │ ├── commands/ # 13 CLI commands (start, stop, init, config, chat, db, git, etc.)
21
+ └── utils/
22
+ └── project-resolver.ts # Resolve project name -> path
23
+ ├── server/
24
+ │ ├── index.ts # Hono server setup, Bun.serve, WebSocket upgrade
25
+ │ ├── middleware/
26
+ │ │ └── auth.ts # Token validation middleware
27
+ │ ├── routes/
28
+ │ │ ├── settings.ts # GET/PUT /api/settings/ai, GET /api/settings/ai/providers/:id/models
29
+ │ │ ├── chat.ts # Sessions, messages, GET /chat/providers/:providerId/models
30
+ │ │ ├── projects.ts # Project CRUD, reorder, color
31
+ │ │ ├── accounts.ts # Account management (multi-account support)
32
+ │ │ ├── database.ts # DB connection CRUD, schema management
33
+ │ │ ├── git.ts # Git operations (status, commit, log, graph)
34
+ │ │ ├── files.ts # File operations (read, write, tree)
35
+ │ │ ├── mcp.ts # MCP server CRUD + import (GET, POST, PUT, DELETE)
36
+ │ │ ├── upgrade.ts # Version checking, upgrade
37
+ │ │ └── static.ts # Serve frontend (dist/web)
38
+ │ ├── helpers/
39
+ │ │ └── resolve-project.ts # Resolve project from request params
40
+ └── ws/
41
+ ├── chat.ts # WebSocket chat streaming
42
+ └── terminal.ts # WebSocket terminal I/O
43
+ ├── providers/ # AI Provider adapters
44
+ │ ├── provider.interface.ts # AIProvider interface (ADDED: listModels?())
45
+ │ ├── claude-agent-sdk.ts # Primary provider (listModels: hardcoded 2 models)
46
+ │ ├── cursor-cli/
47
+ │ │ └── cursor-provider.ts # CLI-based provider (listModels: subprocess with TTL cache)
48
+ │ ├── cli-provider-base.ts # Abstract base for CLI providers
49
+ │ ├── mock-provider.ts # Test provider
50
+ └── registry.ts # Provider routing (list() vs listAll())
51
+ ├── services/ # Business logic (25+ files)
52
+ │ ├── chat.service.ts # Session/message streaming
53
+ ├── config.service.ts # Config loading/persistence
54
+ ├── db.service.ts # SQLite CRUD (schema migrations)
55
+ │ ├── file.service.ts # File operations
56
+ ├── git.service.ts # Git commands
57
+ │ ├── terminal.service.ts # PTY management
58
+ │ ├── account.service.ts # Account CRUD & encryption
59
+ │ ├── upgrade.service.ts # Version checking, installation
60
+ │ ├── mcp-config.service.ts # MCP server CRUD (list, get, set, remove, import)
61
+ ├── database/
62
+ ├── adapter-registry.ts # SQLite/Postgres adapter registry
63
+ │ │ ├── sqlite-adapter.ts
64
+ │ │ ├── postgres-adapter.ts
65
+ │ │ └── readonly-check.ts # CTE-safe readonly validation
66
+ └── ... (20+ other services)
67
+ ├── lib/
68
+ │ ├── account-crypto.ts # AES-256 encryption
69
+ └── network-utils.ts
70
+ ├── types/
71
+ │ ├── chat.ts # Session, Message, ChatEvent, ModelOption, AIProvider
72
+ │ ├── api.ts # ApiResponse envelope
73
+ │ ├── config.ts
74
+ │ ├── database.ts
75
+ │ ├── git.ts
76
+ │ ├── mcp.ts # McpServerConfig, McpTransportType, validation
77
+ │ ├── project.ts
78
+ └── terminal.ts
79
+ └── web/ # React frontend (Vite + React 18)
80
+ ├── app.tsx # Root component
81
+ ├── stores/ # Zustand state (6 stores)
82
+ ├── hooks/ # Custom hooks (9 hooks)
83
+ ├── components/
84
+ │ ├── chat/
85
+ │ │ ├── chat-tab.tsx
86
+ ├── message-list.tsx
87
+ │ │ ├── message-input.tsx
88
+ │ │ ├── provider-selector.tsx
89
+ ├── chat-history-bar.tsx # ADDED: Provider badges, provider-aware usage
90
+ │ │ └── ... (6 other chat components)
91
+ │ ├── settings/
92
+ │ │ ├── ai-settings-section.tsx # UPDATED: Per-provider tabs, dynamic model dropdowns
93
+ │ │ ├── mcp-settings-section.tsx # ADDED: MCP servers tab (list, add, edit, delete)
94
+ │ │ └── mcp-server-dialog.tsx # ADDED: Add/Edit MCP server dialog
95
+ │ ├── database/
96
+ ├── editor/
97
+ ├── explorer/
98
+ ├── git/
99
+ ├── layout/
100
+ ├── terminal/
101
+ └── ui/
102
+ └── lib/
101
103
  │ │ ├── use-url-sync.ts # Sync browser URL with active project/tab state
102
104
  │ │ ├── use-tab-drag.ts # Tab drag-and-drop logic
103
105
  │ │ ├── use-global-keybindings.ts # Global shortcuts (Shift+Shift palette, Alt+[/] tab cycling)
@@ -107,6 +109,7 @@ ppm/
107
109
  │ ├── lib/ # Utilities (12 files)
108
110
  │ │ ├── api-client.ts # Fetch wrapper with auth token, envelope unwrapping
109
111
  │ │ ├── api-settings.ts # AI settings API client (GET/PUT /api/settings/ai)
112
+ │ │ ├── api-mcp.ts # ADDED: MCP settings API client (CRUD + import)
110
113
  │ │ ├── ws-client.ts # WebSocket with exponential backoff + Cloudflare handshake
111
114
  │ │ ├── file-support.ts # File type detection (language, icons, preview)
112
115
  │ │ ├── project-avatar.ts # Smart project initials (collision resolution)
@@ -370,11 +373,174 @@ UI updates staged/unstaged lists
370
373
  - PWA manifest, service worker
371
374
  - Assets (~500KB gzipped)
372
375
 
376
+ ## Multi-Provider Architecture (v0.8.60)
377
+
378
+ ### Dynamic Model Listing Feature
379
+
380
+ **Problem:** Different AI providers expose different models. Claude has hardcoded models, but CLI-based providers (e.g., Cursor) discover models at runtime.
381
+
382
+ **Solution:** Optional `listModels()` method on `AIProvider` interface
383
+
384
+ ### Provider Interface
385
+
386
+ ```typescript
387
+ // src/types/chat.ts
388
+ export interface ModelOption {
389
+ value: string; // Model ID (e.g., "claude-sonnet-4-6")
390
+ label: string; // Display name (e.g., "Claude Sonnet 4.6")
391
+ }
392
+
393
+ interface AIProvider {
394
+ // Required methods
395
+ createSession(): Promise<Session>;
396
+ sendMessage(sessionId, message, context?): AsyncIterable<ChatEvent>;
397
+
398
+ // Optional methods
399
+ listModels?(): Promise<ModelOption[]>;
400
+ isAvailable?(): Promise<boolean>;
401
+ // ... (5 other optional methods)
402
+ }
403
+ ```
404
+
405
+ ### Provider Implementations
406
+
407
+ #### Claude (agent-sdk.ts)
408
+ - `listModels()` returns hardcoded 2 models: Sonnet 4.6, Opus 4.6
409
+ - Direct implementation (no subprocess)
410
+
411
+ #### Cursor (cursor-provider.ts)
412
+ - `listModels()` runs `cursor-agent --list-models` subprocess
413
+ - 5-minute TTL cache (prevents repeated subprocess calls)
414
+ - 10-second timeout (graceful fallback to empty list)
415
+ - Extends `CliProvider` abstract base
416
+
417
+ #### Mock (mock-provider.ts)
418
+ - For testing; returns canned models
419
+
420
+ ### API Endpoints
421
+
422
+ **Global Models Endpoint** (`GET /api/settings/ai/providers/:id/models`)
423
+ ```typescript
424
+ // Used in Settings UI (no project context needed)
425
+ settingsRoutes.get("/ai/providers/:id/models", async (c) => {
426
+ const provider = providerRegistry.get(c.req.param("id"));
427
+ const models = await provider.listModels?.() ?? [];
428
+ return c.json(ok(models));
429
+ });
430
+ ```
431
+
432
+ **Project-Scoped Models Endpoint** (`GET /api/project/:name/chat/providers/:providerId/models`)
433
+ ```typescript
434
+ // Used in Chat tab (scoped to project for consistency)
435
+ chatRoutes.get("/providers/:providerId/models", async (c) => {
436
+ const provider = providerRegistry.get(c.req.param("providerId"));
437
+ const models = await provider.listModels?.() ?? [];
438
+ return c.json(ok(models));
439
+ });
440
+ ```
441
+
442
+ ### Provider Registry Pattern
443
+
444
+ **list() — User-facing providers:**
445
+ ```typescript
446
+ list(): ProviderInfo[] {
447
+ return [
448
+ { id: "claude", name: "Claude" },
449
+ { id: "cursor", name: "Cursor" }
450
+ // mock excluded
451
+ ];
452
+ }
453
+ ```
454
+
455
+ **listAll() — All providers (internal):**
456
+ ```typescript
457
+ listAll(): ProviderInfo[] {
458
+ return [..., { id: "mock", name: "Mock" }];
459
+ }
460
+ ```
461
+
462
+ **Auto-Bootstrap:**
463
+ ```typescript
464
+ // On startup, detect CLI providers
465
+ async bootstrapProviders() {
466
+ const cursor = this.providers.get("cursor");
467
+ if (cursor && await cursor.isAvailable?.()) {
468
+ // Auto-create config entry if detected
469
+ // Save config (only if new)
470
+ }
471
+ }
472
+ ```
473
+
474
+ ### UI Components
475
+
476
+ #### AI Settings Section (ai-settings-section.tsx) — UPDATED
477
+ - Per-provider tabs (Claude, Cursor, etc.)
478
+ - Dynamic model dropdowns fetched from `/api/settings/ai/providers/:id/models`
479
+ - Fallback to hardcoded models if API call fails
480
+ - Provider-aware settings (SDK vs CLI options)
481
+
482
+ #### Chat History Bar (chat-history-bar.tsx) — ADDED
483
+ - Provider badges showing active provider for each session
484
+ - Provider-aware usage display:
485
+ - **Claude:** Full stats `(tokens_in:X, tokens_out:Y, cost: $Z)`
486
+ - **Other:** Context-only `(tokens: X)`
487
+
488
+ ### Configuration
489
+
490
+ ```yaml
491
+ ai:
492
+ default_provider: claude
493
+ providers:
494
+ claude:
495
+ type: agent-sdk
496
+ model: claude-sonnet-4-6 # from listModels()
497
+ effort: high
498
+ max_turns: 100
499
+ cursor:
500
+ type: cli
501
+ model: cursor-fast # from listModels()
502
+ ```
503
+
504
+ ### Testing
505
+
506
+ **New Integration Tests (13 tests):**
507
+ - `provider-models-api.test.ts` — Model API endpoints
508
+ - `chat-service-multi-provider.test.ts` — Multi-provider flows
509
+ - `cursor-provider.test.ts` — Subprocess TTL cache, timeout handling
510
+
511
+ ---
512
+
373
513
  ## Testing Strategy
374
514
 
375
515
  | Test Type | Location | Coverage |
376
516
  |-----------|----------|----------|
377
517
  | Unit | tests/unit/ | Services, utilities |
378
- | Integration | tests/integration/ | API routes, WebSocket |
518
+ | Integration | tests/integration/ | API routes, WebSocket, provider models |
379
519
  | E2E | None yet | Planned for v3 |
380
520
 
521
+ **Key Gotchas:**
522
+ - Test DB isolated per test (never writes to ~/.ppm/ppm.db)
523
+ - Auth disabled in test mode (test-setup.ts)
524
+ - Mock provider used for deterministic responses
525
+ - 492 passing tests (0 failures, v0.8.60)
526
+
527
+ ---
528
+
529
+ ## Recent Changes (v0.8.60)
530
+
531
+ ### Added
532
+ - **Dynamic Model Listing** — `listModels?()` on AIProvider interface
533
+ - **Provider Models APIs** — Global and project-scoped endpoints
534
+ - **AI Settings UI** — Per-provider tabs with dynamic model dropdowns
535
+ - **Chat History Badges** — Provider-aware usage display
536
+ - **13 new integration tests** for provider models API
537
+
538
+ ### Technical Details
539
+ - `ModelOption` type: `{ value: string; label: string }`
540
+ - Claude: Hardcoded 2 models
541
+ - Cursor: Subprocess with 5-min TTL cache, 10s timeout
542
+ - Registry: `list()` vs `listAll()` distinction
543
+ - Bootstrap: Auto-detect CLI providers on startup
544
+
545
+ ---
546
+
@@ -2,11 +2,11 @@
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.76
5
+ **Current Version:** v0.8.87
6
6
 
7
7
  ---
8
8
 
9
- ## [0.8.77] — 2026-04-01 (Planned)
9
+ ## [0.8.77] — 2026-04-01
10
10
 
11
11
  ### Added
12
12
  - **Deterministic Tab URLs + Backend Workspace Sync**
@@ -41,6 +41,41 @@ All notable changes to PPM are documented here. Format follows [Keep a Changelog
41
41
 
42
42
  ---
43
43
 
44
+ ## [0.8.63] — 2026-03-28
45
+
46
+ ### Added
47
+ - **MCP Server Management** — Configure Model Context Protocol servers via Settings UI
48
+ - REST API: GET/POST/PUT/DELETE `/api/settings/mcp`, plus import endpoints
49
+ - Storage: SQLite `mcp_servers` table (name, transport, config JSON)
50
+ - UI: Settings tab with server list, add/edit dialog, delete action
51
+ - Auto-import: Reads `~/.claude.json` on first access (skips existing/invalid)
52
+ - Validation: Name (alphanumeric, max 50 chars) + transport-specific config checks
53
+ - SDK integration: Servers passed to `query()` as `mcpServers`, tools auto-allowed via `mcp__*` wildcard
54
+
55
+ ### Technical Details
56
+ - **Files Created:**
57
+ - `src/types/mcp.ts` — McpServerConfig types, validation functions
58
+ - `src/services/mcp-config.service.ts` — CRUD service + bulk import
59
+ - `src/server/routes/mcp.ts` — REST API endpoints
60
+ - `src/web/lib/api-mcp.ts` — Frontend API client
61
+ - `src/web/components/settings/mcp-settings-section.tsx` — Settings UI
62
+ - `src/web/components/settings/mcp-server-dialog.tsx` — Add/Edit dialog
63
+ - **Files Modified:**
64
+ - `src/services/db.service.ts` — Schema v8 migration (mcp_servers table)
65
+ - `src/server/index.ts` — Route registration
66
+ - `src/providers/claude-agent-sdk.ts` — mcpServers + mcp__* allowedTools
67
+ - `src/web/components/settings/settings-tab.tsx` — MCP category added
68
+
69
+ ---
70
+
71
+ ## [0.8.62] — 2026-03-26
72
+
73
+ ### Added
74
+ - **Cmd+Shift+V shortcut** — Command palette entry for voice input
75
+ - **Voice input** — Web Speech API integration for chat
76
+
77
+ ---
78
+
44
79
  ## [0.8.61] — 2026-03-26 (Beta)
45
80
 
46
81
  ### Added
@@ -76,7 +111,7 @@ All notable changes to PPM are documented here. Format follows [Keep a Changelog
76
111
  - `src/services/chat.service.ts` — Use optional methods instead of duck-typing
77
112
  - `src/web/components/chat/session-picker.tsx` — Integrated provider selector
78
113
  - **Breaking Changes:** None (backward compatible, all tests passing)
79
- - **Architecture:** All phases complete (6/6), 479 tests passing
114
+ - **Architecture:** All phases complete (6/6), 555 tests passing
80
115
 
81
116
  ### Benefits
82
117
  - Extensible foundation for Codex, Gemini, and future providers (~100-150 lines each)
@@ -58,13 +58,13 @@ 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: ~40%** (1/3 features complete, merge all at 100%)
61
+ **Overall progress: ~53%** (2/3 features complete, merge all at 100%)
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
+ | **MCP Management** | Medium | ✅ Done | REST API (CRUD + import), SQLite storage, Settings UI, auto-import from `~/.claude.json`, validation, SDK integration. |
66
67
  | **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
68
 
69
69
  **Multi-provider — v0.9 scope (reduced):**
70
70
  - Tier 1 (full agentic): Claude Agent SDK — file edit, terminal, git, full autonomy
@@ -977,6 +977,157 @@ ppm db data <name> <table> # Show table data (paginated)
977
977
 
978
978
  ---
979
979
 
980
+ ## MCP Server Management
981
+
982
+ ### Overview
983
+ MCP (Model Context Protocol) servers extend Claude with custom tools and resources. PPM manages MCP server configurations via Settings UI, storing them in SQLite and passing them to the Claude Agent SDK.
984
+
985
+ **Features:**
986
+ - **Add/Edit/Delete** MCP servers via Settings UI
987
+ - **Auto-import** from `~/.claude.json` on first access (convenience, no forced import)
988
+ - **Three transport types:** stdio, HTTP, SSE
989
+ - **Validation** on name and config before storage
990
+ - **SDK integration:** Servers passed to `query()` as `mcpServers` object, tools auto-allowed via `mcp__*` wildcard
991
+
992
+ ### Storage Schema
993
+
994
+ ```sql
995
+ CREATE TABLE mcp_servers (
996
+ name TEXT PRIMARY KEY,
997
+ transport TEXT NOT NULL DEFAULT 'stdio', -- 'stdio' | 'http' | 'sse'
998
+ config TEXT NOT NULL, -- JSON: McpServerConfig
999
+ created_at TEXT DEFAULT (datetime('now')),
1000
+ updated_at TEXT DEFAULT (datetime('now'))
1001
+ );
1002
+ ```
1003
+
1004
+ **Config Format (JSON):**
1005
+ ```json
1006
+ {
1007
+ "type": "stdio",
1008
+ "command": "path/to/server",
1009
+ "args": ["--flag"],
1010
+ "env": { "VAR": "value" }
1011
+ }
1012
+ ```
1013
+
1014
+ Or HTTP/SSE:
1015
+ ```json
1016
+ {
1017
+ "type": "http",
1018
+ "url": "http://localhost:3000",
1019
+ "headers": { "Authorization": "Bearer token" }
1020
+ }
1021
+ ```
1022
+
1023
+ ### REST API
1024
+
1025
+ **Endpoints** (`src/server/routes/mcp.ts`):
1026
+
1027
+ | Method | Endpoint | Description |
1028
+ |--------|----------|-------------|
1029
+ | **GET** | `/api/settings/mcp` | List all servers; auto-import on first access |
1030
+ | **GET** | `/api/settings/mcp/:name` | Get single server config |
1031
+ | **POST** | `/api/settings/mcp` | Add new server (validates name + config) |
1032
+ | **PUT** | `/api/settings/mcp/:name` | Update existing server |
1033
+ | **DELETE** | `/api/settings/mcp/:name` | Remove server |
1034
+ | **GET** | `/api/settings/mcp/import/preview` | Preview servers in `~/.claude.json` |
1035
+ | **POST** | `/api/settings/mcp/import` | Bulk import from `~/.claude.json` |
1036
+
1037
+ **Add Server Example:**
1038
+ ```bash
1039
+ POST /api/settings/mcp
1040
+ Content-Type: application/json
1041
+
1042
+ {
1043
+ "name": "file-server",
1044
+ "config": {
1045
+ "type": "stdio",
1046
+ "command": "/usr/local/bin/file-server",
1047
+ "args": ["--port", "8000"]
1048
+ }
1049
+ }
1050
+ ```
1051
+
1052
+ ### Service Layer
1053
+
1054
+ **McpConfigService** (`src/services/mcp-config.service.ts`):
1055
+ - `list()` — Record<name, McpServerConfig> (SDK-compatible format)
1056
+ - `listWithMeta()` — Array with metadata (for UI)
1057
+ - `get(name)` — Single server config
1058
+ - `set(name, config)` — Add or update (upsert)
1059
+ - `remove(name)` — Delete server
1060
+ - `exists(name)` — Check if name exists
1061
+ - `bulkImport(servers)` — Transactional import from `~/.claude.json`, skips existing/invalid
1062
+
1063
+ **Validation:**
1064
+ - `validateMcpName(name)` — alphanumeric + hyphens/underscores, max 50 chars
1065
+ - `validateMcpConfig(config)` — type-specific checks (command for stdio, url for http/sse)
1066
+
1067
+ ### Frontend Integration
1068
+
1069
+ **UI Components:**
1070
+ - `MCP Settings Section` (`src/web/components/settings/mcp-settings-section.tsx`) — Tab in Settings UI
1071
+ - `MCP Server Dialog` (`src/web/components/settings/mcp-server-dialog.tsx`) — Add/Edit modal
1072
+ - `API client` (`src/web/lib/api-mcp.ts`) — Fetch/mutate operations
1073
+
1074
+ **Workflow:**
1075
+ 1. User opens Settings → MCP tab
1076
+ 2. **GET** `/api/settings/mcp` (auto-imports on first access)
1077
+ 3. Display list with transport badge + actions (edit, delete)
1078
+ 4. Click "Add" → Dialog with name + transport selector + config fields
1079
+ 5. **POST** to `/api/settings/mcp` or **PUT** to update
1080
+ 6. On success, list refreshes
1081
+
1082
+ ### SDK Integration
1083
+
1084
+ **Claude Agent SDK Provider** (`src/providers/claude-agent-sdk.ts`):
1085
+ ```typescript
1086
+ // Line ~574
1087
+ const mcpServers = mcpConfigService.list();
1088
+ const hasMcp = Object.keys(mcpServers).length > 0;
1089
+
1090
+ // Line ~589: Pass to query() if servers exist
1091
+ const mcpTools = ["mcp__*"];
1092
+ const queryConfig = {
1093
+ // ... other options
1094
+ ...(hasMcp && { mcpServers }),
1095
+ allowedTools: [...otherTools, ...mcpTools],
1096
+ };
1097
+
1098
+ const query = new Query(messages, queryConfig);
1099
+ ```
1100
+
1101
+ **Tool Allow List:**
1102
+ - All MCP tools automatically allowed via wildcard `mcp__*`
1103
+ - MCP server connection failures don't block chat (logged as warning)
1104
+
1105
+ ### Import Flow
1106
+
1107
+ **Auto-import on first access:**
1108
+ 1. GET `/api/settings/mcp` called
1109
+ 2. If table is empty, read `~/.claude.json`
1110
+ 3. If `mcpServers` key exists, bulk import (validate + skip duplicates)
1111
+ 4. Return populated list
1112
+
1113
+ **Manual import:**
1114
+ 1. GET `/api/settings/mcp/import/preview` — show what's available
1115
+ 2. POST `/api/settings/mcp/import` — import validated servers
1116
+ 3. Returns `{ imported: N, skipped: M }`
1117
+
1118
+ ### Error Handling
1119
+
1120
+ | Scenario | Response |
1121
+ |----------|----------|
1122
+ | Invalid name (non-alphanumeric) | 400 Bad Request |
1123
+ | Invalid config (missing required fields) | 400 Bad Request |
1124
+ | Duplicate name | 409 Conflict |
1125
+ | Server not found (GET/:name, PUT/:name, DELETE/:name) | 404 Not Found |
1126
+ | `~/.claude.json` not found (import) | 404 Not Found |
1127
+ | Corrupt config JSON (recovery) | Log warning, skip entry, continue |
1128
+
1129
+ ---
1130
+
980
1131
  ## Deployment Architecture
981
1132
 
982
1133
  ### Single-Machine Deployment (Current)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.8.87",
3
+ "version": "0.8.88",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",