@hienlh/ppm 0.17.16 → 0.17.18

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 (464) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/cli-reference.md +4 -4
  4. package/assets/skills/ppm/references/http-api.md +5 -1
  5. package/dist/web/assets/{ai-resource-editor-CsossqDO.js → ai-resource-editor-BgWg-ty8.js} +1 -1
  6. package/dist/web/assets/{angular-html-CkxY2iBQ.js → angular-html-2g8YGiJY.js} +1 -1
  7. package/dist/web/assets/{angular-ts-BePeGkkC.js → angular-ts-BwSmoLfh.js} +1 -1
  8. package/dist/web/assets/{api-settings-DAk7D-NP.js → api-settings-BJTjIG4U.js} +1 -1
  9. package/dist/web/assets/{api-themes-84MT-XqR.js → api-themes-DuLpbQFV.js} +1 -1
  10. package/dist/web/assets/{apl-DBS7PWvB.js → apl-B44SMKHp.js} +1 -1
  11. package/dist/web/assets/architecture-PBZL5I3N-gLlhsjM9.js +1 -0
  12. package/dist/web/assets/{astro-Clbk1k4h.js → astro-E5620wek.js} +1 -1
  13. package/dist/web/assets/{audio-preview-DIkokvPM.js → audio-preview-CLn1LTxc.js} +1 -1
  14. package/dist/web/assets/{blade-BeBsBuGS.js → blade-D-VQWAHB.js} +1 -1
  15. package/dist/web/assets/{bsl-DYigT3JZ.js → bsl-Bq5qFzpU.js} +1 -1
  16. package/dist/web/assets/{cairo-BjpO78wI.js → cairo-Cn9-ZKRa.js} +1 -1
  17. package/dist/web/assets/chat-tab-tTUVo4G3.js +13 -0
  18. package/dist/web/assets/{cobol-CpuUx69s.js → cobol-IM3E3iCH.js} +1 -1
  19. package/dist/web/assets/code-editor-DMfUjdk5.js +10 -0
  20. package/dist/web/assets/{coffee-FoFd2Cxo.js → coffee-24wG7ekn.js} +1 -1
  21. package/dist/web/assets/{conflict-editor-Dqm1bkyL.js → conflict-editor-BosR4PW3.js} +3 -3
  22. package/dist/web/assets/{cpp-CO_nPfdr.js → cpp-DH9Cqt4g.js} +1 -1
  23. package/dist/web/assets/{crystal-D-UygAAF.js → crystal-4nZYHhdi.js} +1 -1
  24. package/dist/web/assets/{csv-preview-CvP4xzug.js → csv-preview-BqszurHp.js} +1 -1
  25. package/dist/web/assets/data-grid-overlay-editor-DxdOKRAY.js +1 -0
  26. package/dist/web/assets/data-grid-types-CzEQU9PJ.js +1 -0
  27. package/dist/web/assets/database-viewer-CiomoOU6.js +1 -0
  28. package/dist/web/assets/dialog-BpkAWOy8.js +1 -0
  29. package/dist/web/assets/{diff-viewer-BfWixe_z.js → diff-viewer-aOrOaGWx.js} +1 -1
  30. package/dist/web/assets/dist-Bn3E6I4R.js +11 -0
  31. package/dist/web/assets/{docx-preview-CguUoTKe.js → docx-preview-BAzpJdua.js} +1 -1
  32. package/dist/web/assets/{edge-BSzxzX11.js → edge-DTI9d2Qi.js} +1 -1
  33. package/dist/web/assets/{elixir-DH1-Mrep.js → elixir-Ys0wioho.js} +1 -1
  34. package/dist/web/assets/{elm-B0ln5J70.js → elm-Cihc9TAz.js} +1 -1
  35. package/dist/web/assets/{erb-GIz6NnlZ.js → erb-BwYBWK2-.js} +1 -1
  36. package/dist/web/assets/{erlang-B8MgYhib.js → erlang-CzlD_Joo.js} +1 -1
  37. package/dist/web/assets/esm-JLkfN0k-.js +2 -0
  38. package/dist/web/assets/{extension-webview-DTpFt1eL.js → extension-webview-CKUUvgOr.js} +1 -1
  39. package/dist/web/assets/{fortran-fixed-form-D_-TrK6U.js → fortran-fixed-form-CT90EtSp.js} +1 -1
  40. package/dist/web/assets/{fsharp-C9cLCk2b.js → fsharp-LasxezlU.js} +1 -1
  41. package/dist/web/assets/{gdresource-CsZ3lXwY.js → gdresource-XqgPdfiE.js} +1 -1
  42. package/dist/web/assets/{git-commit-xmq0KqrA.js → git-commit-CZBgPolw.js} +1 -1
  43. package/dist/web/assets/{git-log-panel-DUX7N5tN.js → git-log-panel-B6GFWhj4.js} +1 -1
  44. package/dist/web/assets/{git-rebase-D3O-jsSX.js → git-rebase-C2VzKDB1.js} +1 -1
  45. package/dist/web/assets/gitGraph-HDMCJU4V-BKGq--Jw.js +1 -0
  46. package/dist/web/assets/glide-data-grid-z9FPvRBG.js +138 -0
  47. package/dist/web/assets/{glimmer-js-g74gESrC.js → glimmer-js-DfjWcOLD.js} +1 -1
  48. package/dist/web/assets/{glimmer-ts-DF4aDWHT.js → glimmer-ts-DBldQATc.js} +1 -1
  49. package/dist/web/assets/{glsl-D5LmfW7l.js → glsl-CzNv1NPP.js} +1 -1
  50. package/dist/web/assets/{graphql-_KI4RuXL.js → graphql-DDyLtQb4.js} +1 -1
  51. package/dist/web/assets/group-chat-tab-Bu31mwmF.js +1 -0
  52. package/dist/web/assets/{hack-B1NaTC39.js → hack-CtoYNYkC.js} +1 -1
  53. package/dist/web/assets/{haml-DDFXadZ4.js → haml-CnBVap7G.js} +1 -1
  54. package/dist/web/assets/{handlebars-Bp-Jq3K9.js → handlebars-Dg51esUH.js} +1 -1
  55. package/dist/web/assets/{html-DyB9_5Tj.js → html-Ec2_evSy.js} +1 -1
  56. package/dist/web/assets/{html-derivative-CC60wi-3.js → html-derivative-Cy-F7ZqL.js} +1 -1
  57. package/dist/web/assets/{http-C-9qngH4.js → http-C236v8Tg.js} +1 -1
  58. package/dist/web/assets/{hurl-BtxgpNDs.js → hurl-0rMD2gPU.js} +1 -1
  59. package/dist/web/assets/{hxml-DOoOS9op.js → hxml-e5uS5jTD.js} +1 -1
  60. package/dist/web/assets/{image-preview-CZg6FQ3w.js → image-preview-CaX4gpN6.js} +1 -1
  61. package/dist/web/assets/index-4M24PSHW.js +50 -0
  62. package/dist/web/assets/index-BYFXiFsa.css +2 -0
  63. package/dist/web/assets/info-3K5VOQVL-BOhGriBF.js +1 -0
  64. package/dist/web/assets/{jinja-cN-150ao.js → jinja-DybFmhfq.js} +1 -1
  65. package/dist/web/assets/{jison-CqjsqPq5.js → jison-hkHbixdZ.js} +1 -1
  66. package/dist/web/assets/{julia-C-I5m8W4.js → julia-QnxY6sRE.js} +1 -1
  67. package/dist/web/assets/{just-CgsSW9zs.js → just-1Rs-nm8g.js} +1 -1
  68. package/dist/web/assets/keybindings-store-Cv-U85V6.js +1 -0
  69. package/dist/web/assets/{latex-BFzR9N2d.js → latex-jEBQqA7u.js} +1 -1
  70. package/dist/web/assets/{liquid-BWfr-_iC.js → liquid-DfJaZpxG.js} +1 -1
  71. package/dist/web/assets/{lua-DsXVun6g.js → lua-ti0Jr6-C.js} +1 -1
  72. package/dist/web/assets/{markdown-renderer-CiQkvWP_.js → markdown-renderer-BNXJygz9.js} +3 -3
  73. package/dist/web/assets/markdown-renderer-DCsEEdo7.js +1 -0
  74. package/dist/web/assets/{marko-DWBbo09n.js → marko-DNj4AhPg.js} +1 -1
  75. package/dist/web/assets/{mdc-CUMe9LOG.js → mdc-DPFwQFe4.js} +1 -1
  76. package/dist/web/assets/mobile-bottom-sheet--5_YEewV.js +1 -0
  77. package/dist/web/assets/{nextflow-CoYbADOm.js → nextflow-BUrFV5aB.js} +1 -1
  78. package/dist/web/assets/{nginx-z-JRZREw.js → nginx-BrW9NdOb.js} +1 -1
  79. package/dist/web/assets/{nim-Ci4LHyBm.js → nim-DS_6PF2p.js} +1 -1
  80. package/dist/web/assets/notification-store-DADt1gJH.js +1 -0
  81. package/dist/web/assets/{number-overlay-editor-lnnuUpPI.js → number-overlay-editor-CReXWiQ1.js} +1 -1
  82. package/dist/web/assets/packet-RMMSAZCW-CdLFnL8a.js +1 -0
  83. package/dist/web/assets/{panel-store-D6RV_ztx.js → panel-store-DnbT1c8L.js} +1 -1
  84. package/dist/web/assets/{pdf-preview-B6ZqANpw.js → pdf-preview-DZzbm9A4.js} +1 -1
  85. package/dist/web/assets/{perl-pbDimKOO.js → perl-ytm9vKO_.js} +1 -1
  86. package/dist/web/assets/{php-B003E5v7.js → php-DWTD1Usw.js} +1 -1
  87. package/dist/web/assets/pie-UPGHQEXC-COC-h0zz.js +1 -0
  88. package/dist/web/assets/{trash-2-CbbD5QF1.js → plus-DcuJuDC3.js} +1 -1
  89. package/dist/web/assets/postgres-viewer-BxpeiVkt.js +3 -0
  90. package/dist/web/assets/{project-store-Cekcjcom.js → project-store-Sbnqc41N.js} +1 -1
  91. package/dist/web/assets/{pug-AoueAWJo.js → pug-Bt7KL3_r.js} +1 -1
  92. package/dist/web/assets/{qml-CepxioQA.js → qml-Dexdon5J.js} +1 -1
  93. package/dist/web/assets/radar-KQ55EAFF-CiCfdJNL.js +1 -0
  94. package/dist/web/assets/{razor-lIvU-Gu-.js → razor-DnYCSl0G.js} +1 -1
  95. package/dist/web/assets/{rst-Dn-dwElE.js → rst-BEFYzwF8.js} +1 -1
  96. package/dist/web/assets/{ruby-DmsD1Iz5.js → ruby-BPaXFEjN.js} +1 -1
  97. package/dist/web/assets/{sas-6IM2P4YP.js → sas-CywiNBp8.js} +1 -1
  98. package/dist/web/assets/{scss-RgYEZQqh.js → scss-CGk0L6hN.js} +1 -1
  99. package/dist/web/assets/{settings-store-BkSf4SPQ.js → settings-store-DkRZhj-g.js} +2 -2
  100. package/dist/web/assets/settings-tab-DJOhpW09.js +1 -0
  101. package/dist/web/assets/{shaderlab-B0MNEwt4.js → shaderlab-Byjk0kH7.js} +1 -1
  102. package/dist/web/assets/{shellsession-F8S4LrQI.js → shellsession-DbA70QB6.js} +1 -1
  103. package/dist/web/assets/{soy-DDlMbX7u.js → soy-Bm97kWiA.js} +1 -1
  104. package/dist/web/assets/sparkles-B3DmG8PJ.js +1 -0
  105. package/dist/web/assets/{sparql-kthmGAAe.js → sparql-CawLnniT.js} +1 -1
  106. package/dist/web/assets/{sql-query-editor-BYNM0RRl.js → sql-query-editor-9TwIPvRj.js} +1 -1
  107. package/dist/web/assets/sqlite-viewer-D-3i_x28.js +1 -0
  108. package/dist/web/assets/{stata-Tfbew8La.js → stata-RifEDtNO.js} +1 -1
  109. package/dist/web/assets/{surrealql-C0LIGqRx.js → surrealql-DGRQa5Lm.js} +1 -1
  110. package/dist/web/assets/{svelte-CfTy8CGF.js → svelte-w9Hb12aM.js} +1 -1
  111. package/dist/web/assets/system-monitor-tab-CuVVp2wt.js +1 -0
  112. package/dist/web/assets/{tab-store-CcLQfu8H.js → tab-store-vn0viMOP.js} +1 -1
  113. package/dist/web/assets/{templ-V5H1Sbfy.js → templ-BpUwjJXV.js} +1 -1
  114. package/dist/web/assets/{terminal-tab-CK3lX-0Y.js → terminal-tab-DrSqSrdX.js} +2 -2
  115. package/dist/web/assets/{tex-C3H3uMZf.js → tex-DJFSsuTD.js} +1 -1
  116. package/dist/web/assets/tool-cards-CtE_JnF-.js +7 -0
  117. package/dist/web/assets/treemap-KZPCXAKY-CZwuSCtA.js +1 -0
  118. package/dist/web/assets/{ts-tags-7AXgG5YS.js → ts-tags-vp9Qvx2O.js} +1 -1
  119. package/dist/web/assets/{twig-BSyS38T3.js → twig-CoazxjRB.js} +1 -1
  120. package/dist/web/assets/{use-blob-url-CpyRr7Cx.js → use-blob-url-DdXgVqfz.js} +1 -1
  121. package/dist/web/assets/use-is-mobile-CE6AamG6.js +1 -0
  122. package/dist/web/assets/{use-monaco-theme-Jj2OxkAE.js → use-monaco-theme-BvzqffqR.js} +1 -1
  123. package/dist/web/assets/{vendor-mermaid-DGOXKrJC.js → vendor-mermaid-DPpYkrMf.js} +193 -193
  124. package/dist/web/assets/{video-preview-BOHxkwKG.js → video-preview-DxdyVATR.js} +1 -1
  125. package/dist/web/assets/{vue-C4d_br9u.js → vue-CnshsiXd.js} +1 -1
  126. package/dist/web/assets/{vue-html-CGdoz4vW.js → vue-html-CZshynFp.js} +1 -1
  127. package/dist/web/assets/{vue-vine-CqNolQlN.js → vue-vine-C5gtp_T1.js} +1 -1
  128. package/dist/web/assets/{xml-UmCmu-Od.js → xml-BEGJasgj.js} +1 -1
  129. package/dist/web/assets/{xsl-DJG6USGh.js → xsl-DXpRLjYk.js} +1 -1
  130. package/dist/web/index.html +22 -20
  131. package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
  132. package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
  133. package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
  134. package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
  135. package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
  136. package/dist/web/sw.js +1 -1
  137. package/docs/journal/binary-self-upgrade-260724.md +66 -0
  138. package/nul/docs/claude-agent-sdk-reference.md +780 -0
  139. package/nul/docs/code-standards.md +1080 -0
  140. package/nul/docs/codebase-summary.md +916 -0
  141. package/nul/docs/deployment-guide.md +702 -0
  142. package/nul/docs/design-guidelines.md +767 -0
  143. package/nul/docs/extension-development-guide.md +629 -0
  144. package/nul/docs/journals/260414-1400-ext-git-graph-port-complete.md +147 -0
  145. package/nul/docs/journals/260414-1452-git-graph-faithful-port.md +144 -0
  146. package/nul/docs/journals/260414-1810-git-graph-ui-improvements-complete.md +261 -0
  147. package/nul/docs/journals/260414-2001-bundled-extensions.md +219 -0
  148. package/nul/docs/journals/260415-frontend-memory-optimization.md +73 -0
  149. package/nul/docs/lessons-learned.md +70 -0
  150. package/nul/docs/project-changelog.md +574 -0
  151. package/nul/docs/project-overview-pdr.md +205 -0
  152. package/nul/docs/project-roadmap.md +222 -0
  153. package/nul/docs/system-architecture.md +1961 -0
  154. package/package.json +1 -1
  155. package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
  156. package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
  157. package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
  158. package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
  159. package/spike-group-channel.mjs +156 -0
  160. package/spike-group-chat.mjs +181 -0
  161. package/src/server/routes/group-chat.ts +118 -9
  162. package/src/services/group-chat/agent-runner.ts +15 -2
  163. package/src/services/group-chat/context-window.ts +15 -0
  164. package/src/services/group-chat/group-chat.service.ts +178 -79
  165. package/src/services/group-chat/group-chat.store.ts +30 -0
  166. package/src/services/group-chat/responder-router.ts +110 -0
  167. package/src/services/group-chat/transcript-archive.ts +18 -0
  168. package/src/services/group-chat/turn-engine.ts +183 -105
  169. package/src/services/jsonl-transcript-parser.ts +36 -0
  170. package/src/types/group-chat-ws.ts +2 -2
  171. package/src/types/group-chat.ts +33 -4
  172. package/src/web/components/group-chat/group-chat-tab.tsx +109 -14
  173. package/src/web/components/group-chat/group-create-dialog.tsx +10 -3
  174. package/src/web/components/group-chat/group-full-transcript-view.tsx +9 -8
  175. package/src/web/components/group-chat/group-member-edit-dialog.tsx +108 -0
  176. package/src/web/components/group-chat/group-member-roster.tsx +51 -23
  177. package/src/web/components/group-chat/group-message-item.tsx +45 -26
  178. package/src/web/components/group-chat/group-transcript-messages.tsx +126 -0
  179. package/src/web/components/layout/cloud-share-popover.tsx +10 -2
  180. package/src/web/components/layout/mobile-drawer-tab-bar.tsx +10 -0
  181. package/src/web/components/layout/mobile-drawer.tsx +26 -9
  182. package/src/web/components/layout/nav-section-rail.tsx +8 -2
  183. package/src/web/components/layout/upgrade-button.tsx +33 -11
  184. package/src/web/hooks/use-group-chat.ts +8 -1
  185. package/src/web/lib/api-group-chat.ts +47 -4
  186. package/src/web/lib/sidebar-tabs/tab-registry.ts +3 -1
  187. package/dist/web/assets/architecture-PBZL5I3N-NAS99xy0.js +0 -1
  188. package/dist/web/assets/chat-tab-Bh6l253v.js +0 -18
  189. package/dist/web/assets/click-outside-container-DXZVmhaX.js +0 -1
  190. package/dist/web/assets/code-editor-B7F-d4DK.js +0 -10
  191. package/dist/web/assets/data-grid-overlay-editor-CUlFsD-D.js +0 -1
  192. package/dist/web/assets/database-viewer-5oNTPgjF.js +0 -1
  193. package/dist/web/assets/dist-BsziT-tR.js +0 -11
  194. package/dist/web/assets/esm-CjVeOTNQ.js +0 -2
  195. package/dist/web/assets/gitGraph-HDMCJU4V-B4I3L4Sf.js +0 -1
  196. package/dist/web/assets/glide-data-grid-7vB1SNne.js +0 -138
  197. package/dist/web/assets/group-chat-tab-CNECY-zg.js +0 -1
  198. package/dist/web/assets/index-D2G6kmTs.js +0 -50
  199. package/dist/web/assets/index-DCmrZnq0.css +0 -2
  200. package/dist/web/assets/info-3K5VOQVL-CBbbOe3C.js +0 -1
  201. package/dist/web/assets/input-24Nf_edC.js +0 -1
  202. package/dist/web/assets/keybindings-store-D8Cppv4w.js +0 -1
  203. package/dist/web/assets/notification-store-DOGxkWih.js +0 -1
  204. package/dist/web/assets/packet-RMMSAZCW-fr8-QSTF.js +0 -1
  205. package/dist/web/assets/pie-UPGHQEXC-Biu7_Pbb.js +0 -1
  206. package/dist/web/assets/postgres-viewer-B23xTCSs.js +0 -3
  207. package/dist/web/assets/radar-KQ55EAFF-CMtNRpK6.js +0 -1
  208. package/dist/web/assets/settings-tab-D5hAA1bY.js +0 -1
  209. package/dist/web/assets/sparkles-2ii4lR7b.js +0 -1
  210. package/dist/web/assets/sqlite-viewer-DW9f2Hqj.js +0 -1
  211. package/dist/web/assets/system-monitor-tab-B5f4pgdX.js +0 -1
  212. package/dist/web/assets/treemap-KZPCXAKY-ZYVFjoaN.js +0 -1
  213. package/dist/web/assets/use-websocket-DrDBgIWU.js +0 -1
  214. /package/dist/web/assets/{abap-BZ3J5hF3.js → abap-wT6fgqwv.js} +0 -0
  215. /package/dist/web/assets/{actionscript-3-CkyGfT-8.js → actionscript-3-CqcHpPYj.js} +0 -0
  216. /package/dist/web/assets/{ada-CxNLBoe7.js → ada-DWE3Hn4E.js} +0 -0
  217. /package/dist/web/assets/{andromeeda-BPN5rsDz.js → andromeeda-DpQ-y-Jt.js} +0 -0
  218. /package/dist/web/assets/{apache-C1F5wHL6.js → apache-LRTUP4r3.js} +0 -0
  219. /package/dist/web/assets/{apex-BaNixdO-.js → apex-DwVeH5_h.js} +0 -0
  220. /package/dist/web/assets/{api-client-Dvzcc_EO.js → api-client-BK4NPNoY.js} +0 -0
  221. /package/dist/web/assets/{applescript-Cl_pdcch.js → applescript-DyQiO9JU.js} +0 -0
  222. /package/dist/web/assets/{apply-theme-ORrEdHKT.js → apply-theme-DxKsrDLA.js} +0 -0
  223. /package/dist/web/assets/{ara-B6UAE7M2.js → ara-Cf-OWd2S.js} +0 -0
  224. /package/dist/web/assets/{asciidoc-D9c9Twki.js → asciidoc-gocbKcVK.js} +0 -0
  225. /package/dist/web/assets/{asm-CNmtv7Y9.js → asm-C3meb5HY.js} +0 -0
  226. /package/dist/web/assets/{aurora-x-C9z-NyzR.js → aurora-x-BYcXaCIg.js} +0 -0
  227. /package/dist/web/assets/{awk-BfLHEnKa.js → awk-BcT6nIdN.js} +0 -0
  228. /package/dist/web/assets/{ayu-dark-Bn7trkt_.js → ayu-dark-CLZPoccg.js} +0 -0
  229. /package/dist/web/assets/{ayu-light-Dpya9hPf.js → ayu-light-CTyRQdS2.js} +0 -0
  230. /package/dist/web/assets/{ayu-mirage-Cc0jfABD.js → ayu-mirage-DjqfqZdo.js} +0 -0
  231. /package/dist/web/assets/{ballerina-BtiCmN7T.js → ballerina-BCARR-zK.js} +0 -0
  232. /package/dist/web/assets/{bat-tmFM76zR.js → bat-_yaRjWOb.js} +0 -0
  233. /package/dist/web/assets/{beancount-BjPqRrcH.js → beancount-BPESBMS9.js} +0 -0
  234. /package/dist/web/assets/{berry-D0SQBxN2.js → berry-BSLE-Sf8.js} +0 -0
  235. /package/dist/web/assets/{bibtex-CkjRWQKr.js → bibtex-CvUWWCVx.js} +0 -0
  236. /package/dist/web/assets/{bicep-Bhq28Cxc.js → bicep--vcqqALZ.js} +0 -0
  237. /package/dist/web/assets/{bird2-DFGInYb3.js → bird2-Clclqgh6.js} +0 -0
  238. /package/dist/web/assets/{c-CP8cB4k_.js → c-CSC2LwTp.js} +0 -0
  239. /package/dist/web/assets/{c3-oRytyeWo.js → c3-Cl1_jUnm.js} +0 -0
  240. /package/dist/web/assets/{cadence-Inf1rGb7.js → cadence-BwAD7vSC.js} +0 -0
  241. /package/dist/web/assets/{catppuccin-frappe-DAzribQo.js → catppuccin-frappe-hr0HKoFX.js} +0 -0
  242. /package/dist/web/assets/{catppuccin-latte-B6Y_mCCQ.js → catppuccin-latte-DXEIYlFO.js} +0 -0
  243. /package/dist/web/assets/{catppuccin-macchiato-COLbYqVL.js → catppuccin-macchiato-B2zST7sA.js} +0 -0
  244. /package/dist/web/assets/{catppuccin-mocha-DKWaML1B.js → catppuccin-mocha-CvrdnYv9.js} +0 -0
  245. /package/dist/web/assets/{chevron-right-BBc6ATtm.js → chevron-right-DeUue2aK.js} +0 -0
  246. /package/dist/web/assets/{circle-question-mark-CveWaCux.js → circle-question-mark-PaJgx-3d.js} +0 -0
  247. /package/dist/web/assets/{clarity-_ZLLrrsX.js → clarity-BeQd5TQD.js} +0 -0
  248. /package/dist/web/assets/{clojure-DWGVCg_P.js → clojure-Csdo0vb_.js} +0 -0
  249. /package/dist/web/assets/{cmake-Cwh3mug7.js → cmake-Cf0q7ddg.js} +0 -0
  250. /package/dist/web/assets/{code-7ZAlTvsm.js → code-TekEnDFM.js} +0 -0
  251. /package/dist/web/assets/{codeowners-BdV6CFYi.js → codeowners-CUEzlC7R.js} +0 -0
  252. /package/dist/web/assets/{codeql-NvupDIDf.js → codeql-DxHyogQt.js} +0 -0
  253. /package/dist/web/assets/{common-lisp-B-vtBh5k.js → common-lisp-WLb-tAda.js} +0 -0
  254. /package/dist/web/assets/{coq-M8-FTfiZ.js → coq-DFtMD6Wn.js} +0 -0
  255. /package/dist/web/assets/{csharp-B7Ugdtd-.js → csharp-DEnvXVjs.js} +0 -0
  256. /package/dist/web/assets/{css-86k3Z-YA.js → css-DMPpCfkZ.js} +0 -0
  257. /package/dist/web/assets/{csv-BX5XYhfH.js → csv-DTSgNSaP.js} +0 -0
  258. /package/dist/web/assets/{csv-parser-d56Fzpwc.js → csv-parser-B_xNn07y.js} +0 -0
  259. /package/dist/web/assets/{cue-B7099gQa.js → cue-BEmVIZmd.js} +0 -0
  260. /package/dist/web/assets/{cypher-D99mWVIu.js → cypher-CncQVhvU.js} +0 -0
  261. /package/dist/web/assets/{d-CFE8oQwh.js → d-BzUSBVex.js} +0 -0
  262. /package/dist/web/assets/{dark-plus-BEeeDwdg.js → dark-plus-mysFNRq2.js} +0 -0
  263. /package/dist/web/assets/{dart-HVSWGnI5.js → dart-BBu3nkcE.js} +0 -0
  264. /package/dist/web/assets/{database-BLht7BVM.js → database-COYAMZaw.js} +0 -0
  265. /package/dist/web/assets/{dax-DjDgSJkh.js → dax-DcLVz9_9.js} +0 -0
  266. /package/dist/web/assets/{desktop-B5A3i7J1.js → desktop-DS2PdvoU.js} +0 -0
  267. /package/dist/web/assets/{diff-CpTrlaE9.js → diff-aUYk5Qys.js} +0 -0
  268. /package/dist/web/assets/{dist-BSCgJs-V.js → dist-Dxl3dqhC.js} +0 -0
  269. /package/dist/web/assets/{docker-xBhmJeHj.js → docker-12Lj1QUD.js} +0 -0
  270. /package/dist/web/assets/{dotenv-DKKhLEv5.js → dotenv-D-P4bzvl.js} +0 -0
  271. /package/dist/web/assets/{dracula-CVwQuzra.js → dracula-C-eRNn4o.js} +0 -0
  272. /package/dist/web/assets/{dracula-soft-CG6mB-lf.js → dracula-soft-CBB1BI7O.js} +0 -0
  273. /package/dist/web/assets/{dream-maker-SC-IdYY3.js → dream-maker-C_j5A0r7.js} +0 -0
  274. /package/dist/web/assets/{edit-diff-preview-C50i1H63.js → edit-diff-preview-CRMu-6r1.js} +0 -0
  275. /package/dist/web/assets/{emacs-lisp-CIBFq6W8.js → emacs-lisp-W54LT7rh.js} +0 -0
  276. /package/dist/web/assets/{everforest-dark-odRT_lec.js → everforest-dark-B_ISUDeh.js} +0 -0
  277. /package/dist/web/assets/{everforest-light-or1Voqjl.js → everforest-light-CNUYa354.js} +0 -0
  278. /package/dist/web/assets/{fennel-tQd1z3iu.js → fennel-DK82-oso.js} +0 -0
  279. /package/dist/web/assets/{file-exclamation-point-C-lL0g_E.js → file-exclamation-point-DBEC-Kst.js} +0 -0
  280. /package/dist/web/assets/{fish-DSuZQG0k.js → fish-CbJVJzQj.js} +0 -0
  281. /package/dist/web/assets/{fluent-D7xI8a8e.js → fluent-Cug5cOL1.js} +0 -0
  282. /package/dist/web/assets/{fortran-free-form-BlAtcPd3.js → fortran-free-form-amHz32Zw.js} +0 -0
  283. /package/dist/web/assets/{gdscript-VsEQXBPm.js → gdscript-t_G6VYpv.js} +0 -0
  284. /package/dist/web/assets/{gdshader-iqHMOdwW.js → gdshader-DqZaHh4E.js} +0 -0
  285. /package/dist/web/assets/{genie-C4XU4g1a.js → genie-CIJtFlZK.js} +0 -0
  286. /package/dist/web/assets/{gherkin-C4nfQzAB.js → gherkin-CalidqLY.js} +0 -0
  287. /package/dist/web/assets/{github-dark-B_pbG3CC.js → github-dark-BG2xX0wa.js} +0 -0
  288. /package/dist/web/assets/{github-dark-default-kFbx7C95.js → github-dark-default-tDp_EOBs.js} +0 -0
  289. /package/dist/web/assets/{github-dark-dimmed-CpVfQEqs.js → github-dark-dimmed-DlSfPwW2.js} +0 -0
  290. /package/dist/web/assets/{github-dark-high-contrast-DYKqQwZh.js → github-dark-high-contrast-qZM7j5KB.js} +0 -0
  291. /package/dist/web/assets/{github-light-gEYBmKaj.js → github-light-CnvWTiyb.js} +0 -0
  292. /package/dist/web/assets/{github-light-default-DVHfMPQ2.js → github-light-default-Dy6OAt2Q.js} +0 -0
  293. /package/dist/web/assets/{github-light-high-contrast-ClVJDu-q.js → github-light-high-contrast-1BHQCOPl.js} +0 -0
  294. /package/dist/web/assets/{gleam-BLaPu7ft.js → gleam-D-2cpXVX.js} +0 -0
  295. /package/dist/web/assets/{gn-BKgerUyI.js → gn-C1cC4xLc.js} +0 -0
  296. /package/dist/web/assets/{gnuplot-cqJuEFb-.js → gnuplot-2Gp459mV.js} +0 -0
  297. /package/dist/web/assets/{go-B2EHdyD8.js → go-BA1q0q8h.js} +0 -0
  298. /package/dist/web/assets/{groovy-DdImoj8s.js → groovy-BlIa-VfI.js} +0 -0
  299. /package/dist/web/assets/{gruvbox-dark-hard-DlwstAds.js → gruvbox-dark-hard-BMIwVwaJ.js} +0 -0
  300. /package/dist/web/assets/{gruvbox-dark-medium-CiYEd1vd.js → gruvbox-dark-medium-DxfuBW3n.js} +0 -0
  301. /package/dist/web/assets/{gruvbox-dark-soft-C-wEa4h4.js → gruvbox-dark-soft-_NSslDDi.js} +0 -0
  302. /package/dist/web/assets/{gruvbox-light-hard-BqYmfna-.js → gruvbox-light-hard-CP5MIsL9.js} +0 -0
  303. /package/dist/web/assets/{gruvbox-light-medium-CxmfalT2.js → gruvbox-light-medium-BTMursH0.js} +0 -0
  304. /package/dist/web/assets/{gruvbox-light-soft-DGWXXMCB.js → gruvbox-light-soft-Bv6zFoVX.js} +0 -0
  305. /package/dist/web/assets/{haskell-H_C_IuVb.js → haskell-Da8UeSv-.js} +0 -0
  306. /package/dist/web/assets/{haxe-CGNSMpeZ.js → haxe-mcKlcxBa.js} +0 -0
  307. /package/dist/web/assets/{hcl-B7bSTSQf.js → hcl-BdZcx5JX.js} +0 -0
  308. /package/dist/web/assets/{hjson-DlJ-glQZ.js → hjson-pFXYbN4t.js} +0 -0
  309. /package/dist/web/assets/{hlsl-CehW5SJN.js → hlsl-Z0II0KR8.js} +0 -0
  310. /package/dist/web/assets/{horizon-Beabcz2d.js → horizon-CufN8DyP.js} +0 -0
  311. /package/dist/web/assets/{horizon-bright-BC87RXPa.js → horizon-bright-5qoQVyIY.js} +0 -0
  312. /package/dist/web/assets/{houston-Da7RFwUy.js → houston-DOFkQwep.js} +0 -0
  313. /package/dist/web/assets/{hy-m1H70w19.js → hy-BVbDMDcG.js} +0 -0
  314. /package/dist/web/assets/{imba-MExFsdAA.js → imba-DRM6LLS5.js} +0 -0
  315. /package/dist/web/assets/{ini-CJy_zmwy.js → ini-2KCnNvmz.js} +0 -0
  316. /package/dist/web/assets/{java-DqDFvJBH.js → java-BmvH-Ou1.js} +0 -0
  317. /package/dist/web/assets/{javascript-C3Wyv22X.js → javascript-B7VhU1Yc.js} +0 -0
  318. /package/dist/web/assets/{json-DnGBw_DZ.js → json-X1pNk0n7.js} +0 -0
  319. /package/dist/web/assets/{json5-CRN-zoN4.js → json5-CJeEJPTg.js} +0 -0
  320. /package/dist/web/assets/{jsonc-CKRUZNRo.js → jsonc-C1haTBsf.js} +0 -0
  321. /package/dist/web/assets/{jsonl-CGfD6FrH.js → jsonl-wQ8-AJgs.js} +0 -0
  322. /package/dist/web/assets/{jsonnet-nbFuDj0o.js → jsonnet-BrEjY3Xh.js} +0 -0
  323. /package/dist/web/assets/{jssm-D0ShzIqD.js → jssm-C6LgYbOs.js} +0 -0
  324. /package/dist/web/assets/{jsx-D27hbRbs.js → jsx-B2cfD_Fb.js} +0 -0
  325. /package/dist/web/assets/{kanagawa-dragon-Ov3f8K0T.js → kanagawa-dragon-6UCcJpLw.js} +0 -0
  326. /package/dist/web/assets/{kanagawa-lotus-CLyOqh-g.js → kanagawa-lotus-DOmfpsRg.js} +0 -0
  327. /package/dist/web/assets/{kanagawa-wave-Kj3dzabH.js → kanagawa-wave-CpxmPE7N.js} +0 -0
  328. /package/dist/web/assets/{kdl-DFF58Gu4.js → kdl-BrLEivmb.js} +0 -0
  329. /package/dist/web/assets/{kotlin-BM4PKaB4.js → kotlin-BO2FUnDB.js} +0 -0
  330. /package/dist/web/assets/{kusto-DrvHLCv5.js → kusto-Cev1yiq9.js} +0 -0
  331. /package/dist/web/assets/{laserwave-Db6i6ZkO.js → laserwave-BlEkf-eH.js} +0 -0
  332. /package/dist/web/assets/{lean-D0hEjzwk.js → lean-BLS3Xk2w.js} +0 -0
  333. /package/dist/web/assets/{less-BEj4npGG.js → less-BXmcsqcq.js} +0 -0
  334. /package/dist/web/assets/{lib-CBdlQ75W.js → lib-CxSBdrd7.js} +0 -0
  335. /package/dist/web/assets/{light-plus-DLIIbDea.js → light-plus-DS6zriYl.js} +0 -0
  336. /package/dist/web/assets/{llvm-DMt6umsO.js → llvm-C_wLnJgX.js} +0 -0
  337. /package/dist/web/assets/{log-BopvdtPW.js → log-Cg8pvmWi.js} +0 -0
  338. /package/dist/web/assets/{logo-CYSUFPFR.js → logo-C9-kEvPW.js} +0 -0
  339. /package/dist/web/assets/{luau-XC611HNO.js → luau-NwaGugfZ.js} +0 -0
  340. /package/dist/web/assets/{make-BMYZIfBj.js → make-DBpJE-Z8.js} +0 -0
  341. /package/dist/web/assets/{markdown-BLza5nvF.js → markdown-B7ZTAu7B.js} +0 -0
  342. /package/dist/web/assets/{material-theme-Uw27UfJW.js → material-theme-BsXH-Mss.js} +0 -0
  343. /package/dist/web/assets/{material-theme-darker-dfqt8XBY.js → material-theme-darker-CW9OlIeY.js} +0 -0
  344. /package/dist/web/assets/{material-theme-lighter-CPtBTblV.js → material-theme-lighter-D2PAh0Kw.js} +0 -0
  345. /package/dist/web/assets/{material-theme-ocean-BfZgVqC-.js → material-theme-ocean-Vz6DBVi3.js} +0 -0
  346. /package/dist/web/assets/{material-theme-palenight-DAZoVYg8.js → material-theme-palenight-D-GleP3G.js} +0 -0
  347. /package/dist/web/assets/{matlab-cSzmquiW.js → matlab-C7MYlMSn.js} +0 -0
  348. /package/dist/web/assets/{mdx-Dq0kW5VM.js → mdx-FkOOX3Dq.js} +0 -0
  349. /package/dist/web/assets/{mermaid-DoIKF0yS.js → mermaid-C_9qw5it.js} +0 -0
  350. /package/dist/web/assets/{min-dark-DyraimDU.js → min-dark-BowNvxk2.js} +0 -0
  351. /package/dist/web/assets/{min-light-Co9cbcUv.js → min-light-x8oWUmbL.js} +0 -0
  352. /package/dist/web/assets/{mipsasm-ph6cKgHn.js → mipsasm-Dmi1AZOT.js} +0 -0
  353. /package/dist/web/assets/{mojo-BBT0XwmY.js → mojo-C6z7mry2.js} +0 -0
  354. /package/dist/web/assets/{monokai-IP0T5Rtg.js → monokai-BQ4e_Hn0.js} +0 -0
  355. /package/dist/web/assets/{moonbit-B8cogNXl.js → moonbit-CEeNIoB4.js} +0 -0
  356. /package/dist/web/assets/{move-DkY26NtM.js → move-bALfKUa7.js} +0 -0
  357. /package/dist/web/assets/{narrat-rBs3byG0.js → narrat-DX4JlAWT.js} +0 -0
  358. /package/dist/web/assets/{nextflow-groovy-DkO9wMLP.js → nextflow-groovy-CKQWtRkO.js} +0 -0
  359. /package/dist/web/assets/{night-owl-jVR4lFji.js → night-owl-D4PQd726.js} +0 -0
  360. /package/dist/web/assets/{night-owl-light-fBYJ7s3U.js → night-owl-light-BQo_YHll.js} +0 -0
  361. /package/dist/web/assets/{nix-WX9oavu6.js → nix-B1jThkGj.js} +0 -0
  362. /package/dist/web/assets/{nord-Cf7G21Pu.js → nord-35quHUKo.js} +0 -0
  363. /package/dist/web/assets/{nushell-DQdzcJ1H.js → nushell-L6DbJTuL.js} +0 -0
  364. /package/dist/web/assets/{objective-c-JSO2uvZ-.js → objective-c-CTZ4bR2U.js} +0 -0
  365. /package/dist/web/assets/{objective-cpp-CZMGJ9Fb.js → objective-cpp-Dm1YDIdA.js} +0 -0
  366. /package/dist/web/assets/{ocaml-C4S4NSDo.js → ocaml-B3n__LGy.js} +0 -0
  367. /package/dist/web/assets/{odin-Y2jXCgVq.js → odin-DmqZ8n3i.js} +0 -0
  368. /package/dist/web/assets/{one-dark-pro-dVIxuoJY.js → one-dark-pro-16_UaUig.js} +0 -0
  369. /package/dist/web/assets/{one-light-BQ1IKfD1.js → one-light-B-GGjWn5.js} +0 -0
  370. /package/dist/web/assets/{openscad-Bddw4I3z.js → openscad-DTFB9Sj6.js} +0 -0
  371. /package/dist/web/assets/{pascal-BKVXrzC1.js → pascal-RcT1dQYN.js} +0 -0
  372. /package/dist/web/assets/{pkl-BTd-YRsO.js → pkl-Dr4yLQ_o.js} +0 -0
  373. /package/dist/web/assets/{plastic-eiGvBIxV.js → plastic-hp2Nry0R.js} +0 -0
  374. /package/dist/web/assets/{plsql-C290tieP.js → plsql-pFcO4WZ0.js} +0 -0
  375. /package/dist/web/assets/{po-DyDwh0tx.js → po-BkZ0_HvY.js} +0 -0
  376. /package/dist/web/assets/{poimandres-B87zc_g-.js → poimandres-BcTrxj7f.js} +0 -0
  377. /package/dist/web/assets/{polar-Cdub1YMT.js → polar-DHK3q8FF.js} +0 -0
  378. /package/dist/web/assets/{postcss-BcI_3K0v.js → postcss-Bv7WzijA.js} +0 -0
  379. /package/dist/web/assets/{powerquery-OdMrkJSe.js → powerquery-DpaU_hW9.js} +0 -0
  380. /package/dist/web/assets/{powershell-C8auT6xg.js → powershell-_Rqc0MGP.js} +0 -0
  381. /package/dist/web/assets/{prisma-DIbVv8PA.js → prisma-wyegE8us.js} +0 -0
  382. /package/dist/web/assets/{prolog-DbTOH3Ld.js → prolog-DspHCURv.js} +0 -0
  383. /package/dist/web/assets/{proto-C4Cq2dCD.js → proto-BOBiNGf5.js} +0 -0
  384. /package/dist/web/assets/{puppet-DJp3uqum.js → puppet-DfLvV8h2.js} +0 -0
  385. /package/dist/web/assets/{purescript-CLsV4rPU.js → purescript-BY5ZSOZy.js} +0 -0
  386. /package/dist/web/assets/{python-2gBtEza2.js → python-B758mMeg.js} +0 -0
  387. /package/dist/web/assets/{qmldir-DfhULcua.js → qmldir-C75bpHvy.js} +0 -0
  388. /package/dist/web/assets/{qss-DK--VTTV.js → qss-DcLTlbLd.js} +0 -0
  389. /package/dist/web/assets/{r-BArBO2Sa.js → r-D0G_Y1P5.js} +0 -0
  390. /package/dist/web/assets/{racket-5mijHsVy.js → racket-DTye_5qn.js} +0 -0
  391. /package/dist/web/assets/{raku-ir0suElk.js → raku-uJXBXkV0.js} +0 -0
  392. /package/dist/web/assets/{react-J9g6e3Rq.js → react-CorNPdie.js} +0 -0
  393. /package/dist/web/assets/{red-DSecpIOT.js → red-rs3HE3re.js} +0 -0
  394. /package/dist/web/assets/{reg-Dp30FusS.js → reg-DkTIaX7a.js} +0 -0
  395. /package/dist/web/assets/{regexp-DXJnEM_6.js → regexp-4TLSs4_m.js} +0 -0
  396. /package/dist/web/assets/{rel-Dr483ozj.js → rel-B0jumCjt.js} +0 -0
  397. /package/dist/web/assets/{resolve-theme-DJORYzKu.js → resolve-theme-CCoakF2E.js} +0 -0
  398. /package/dist/web/assets/{riscv-ZfBy5IA1.js → riscv-DtaWMlo0.js} +0 -0
  399. /package/dist/web/assets/{ron-ZJ_CljGP.js → ron-BCPxwJD6.js} +0 -0
  400. /package/dist/web/assets/{rose-pine-CcW_eJ6G.js → rose-pine-CNryoqGi.js} +0 -0
  401. /package/dist/web/assets/{rose-pine-dawn-v2l_UF-j.js → rose-pine-dawn-C-gJOQVP.js} +0 -0
  402. /package/dist/web/assets/{rose-pine-moon-CNXkwf3D.js → rose-pine-moon-BdXSXpZh.js} +0 -0
  403. /package/dist/web/assets/{rosmsg-BMYMVW84.js → rosmsg-CPG8X-D5.js} +0 -0
  404. /package/dist/web/assets/{rust-BR-zJh5q.js → rust-CxfW3ePx.js} +0 -0
  405. /package/dist/web/assets/{sass-BukPOBFz.js → sass-D-7bxo_L.js} +0 -0
  406. /package/dist/web/assets/{save-DO5xjW95.js → save-BM4p9_9n.js} +0 -0
  407. /package/dist/web/assets/{scala-DUFY7D6D.js → scala-DyUsfHNc.js} +0 -0
  408. /package/dist/web/assets/{scheme-Dl-dNNxQ.js → scheme-ROgrwiO-.js} +0 -0
  409. /package/dist/web/assets/{sdbl-DdmlQhia.js → sdbl-B1EAQWpp.js} +0 -0
  410. /package/dist/web/assets/{shellscript-UyhrvGcd.js → shellscript-HhmB_Ufy.js} +0 -0
  411. /package/dist/web/assets/{shield-check-m2QzZTuZ.js → shield-check-C3qYSU8a.js} +0 -0
  412. /package/dist/web/assets/{shield-off-DNn7VzJe.js → shield-off-D5tU7gQT.js} +0 -0
  413. /package/dist/web/assets/{slack-dark-Cv6Szr-q.js → slack-dark-DnmSY72G.js} +0 -0
  414. /package/dist/web/assets/{slack-ochin-V6Ii3lAm.js → slack-ochin-q97gHjcd.js} +0 -0
  415. /package/dist/web/assets/{smalltalk-BUCv5G91.js → smalltalk-B-1waXvA.js} +0 -0
  416. /package/dist/web/assets/{snazzy-light-B67a78qi.js → snazzy-light-C8yWDN2g.js} +0 -0
  417. /package/dist/web/assets/{solarized-dark-ccWnfYyA.js → solarized-dark-ClVDEMJZ.js} +0 -0
  418. /package/dist/web/assets/{solarized-light-C3WyRO5I.js → solarized-light-Bts6CUIP.js} +0 -0
  419. /package/dist/web/assets/{solidity-BFbRPNi_.js → solidity-DYxwp0om.js} +0 -0
  420. /package/dist/web/assets/{splunk-zdpwCJLE.js → splunk-jK6-Sr06.js} +0 -0
  421. /package/dist/web/assets/{sql-B2IPUsfz.js → sql-DPpd8aUS.js} +0 -0
  422. /package/dist/web/assets/{ssh-config-Co4cXifq.js → ssh-config-Bimb9LVr.js} +0 -0
  423. /package/dist/web/assets/{stylus-CmI75XY8.js → stylus-B2DgJRJw.js} +0 -0
  424. /package/dist/web/assets/{swift-Dfks9YQI.js → swift-ChEH2nFL.js} +0 -0
  425. /package/dist/web/assets/{synthwave-84-B3LERzNS.js → synthwave-84-DwSLbgAm.js} +0 -0
  426. /package/dist/web/assets/{system-verilog-BFvldiwl.js → system-verilog-DoVBMhJ8.js} +0 -0
  427. /package/dist/web/assets/{systemd-Cu4Vvgbu.js → systemd--Ux4VAYD.js} +0 -0
  428. /package/dist/web/assets/{table-CAVmgQMT.js → table-Di8pv2Y_.js} +0 -0
  429. /package/dist/web/assets/{talonscript-Bnq32pUB.js → talonscript-DQNx9nak.js} +0 -0
  430. /package/dist/web/assets/{tasl-Nwe2K3J1.js → tasl-Bwy_Y9uD.js} +0 -0
  431. /package/dist/web/assets/{tcl-zOnpywrl.js → tcl-Drea-2jA.js} +0 -0
  432. /package/dist/web/assets/{terraform-Bc9Vj-n3.js → terraform-4rS5HoFk.js} +0 -0
  433. /package/dist/web/assets/{tokyo-night-9_KyM8M2.js → tokyo-night-C9MqaxIn.js} +0 -0
  434. /package/dist/web/assets/{toml-ByH-fxOY.js → toml-tSgOSv42.js} +0 -0
  435. /package/dist/web/assets/{tsv-DrCnDClP.js → tsv-BmL4O1Zx.js} +0 -0
  436. /package/dist/web/assets/{tsx-B8LafSYr.js → tsx-BwzeA880.js} +0 -0
  437. /package/dist/web/assets/{turtle-DuyP_QCJ.js → turtle-2DYvssFL.js} +0 -0
  438. /package/dist/web/assets/{typescript-BTBJb5RD.js → typescript-ZC-vonSh.js} +0 -0
  439. /package/dist/web/assets/{typespec-DdOFo7M5.js → typespec-DkCEO45F.js} +0 -0
  440. /package/dist/web/assets/{typst-LYvnBWQu.js → typst-DAZdb2n6.js} +0 -0
  441. /package/dist/web/assets/{utils-CTg5uAYR.js → utils-CSCvNZxE.js} +0 -0
  442. /package/dist/web/assets/{v-BuBYA1_x.js → v-B_wsb4C1.js} +0 -0
  443. /package/dist/web/assets/{vala-vRIBYYe3.js → vala-BDBNS784.js} +0 -0
  444. /package/dist/web/assets/{vb-DwGi-IYM.js → vb-CuOoJSSa.js} +0 -0
  445. /package/dist/web/assets/{vendor-xterm-i6SHFTfU.js → vendor-xterm-CImj6i6Z.js} +0 -0
  446. /package/dist/web/assets/{verilog-6B1X4G2-.js → verilog-BCcOvOcc.js} +0 -0
  447. /package/dist/web/assets/{vesper-Dw1y_Ae7.js → vesper-vN8CREK5.js} +0 -0
  448. /package/dist/web/assets/{vhdl-DK8JwBzJ.js → vhdl-CI8PoqeM.js} +0 -0
  449. /package/dist/web/assets/{viml-BTKIEIqm.js → viml-B66dNP1B.js} +0 -0
  450. /package/dist/web/assets/{vitesse-black-BqqolhEg.js → vitesse-black-0bGIaCfD.js} +0 -0
  451. /package/dist/web/assets/{vitesse-dark-B9JL5WQA.js → vitesse-dark-Dk-RSXq-.js} +0 -0
  452. /package/dist/web/assets/{vitesse-light-DYJKj2se.js → vitesse-light-BUsA5RhG.js} +0 -0
  453. /package/dist/web/assets/{vyper-D-jDI3WH.js → vyper-dNodzEa8.js} +0 -0
  454. /package/dist/web/assets/{wasm-1-GlNdpk.js → wasm-BDxaNPCj.js} +0 -0
  455. /package/dist/web/assets/{wasm-BBW4WmT2.js → wasm-d6SE2_qm.js} +0 -0
  456. /package/dist/web/assets/{wenyan-B6cM7sDO.js → wenyan-D0h8Uz55.js} +0 -0
  457. /package/dist/web/assets/{wgsl-FaHGQgko.js → wgsl-vKs_vsvQ.js} +0 -0
  458. /package/dist/web/assets/{wikitext-DtIyPUrk.js → wikitext-BIWsPJit.js} +0 -0
  459. /package/dist/web/assets/{wit-BMVRif5Y.js → wit-6vmKi2H3.js} +0 -0
  460. /package/dist/web/assets/{wolfram-DQsCGohn.js → wolfram-zoNg6F1a.js} +0 -0
  461. /package/dist/web/assets/{x-C2VSAp7O.js → x-BsFLg-T8.js} +0 -0
  462. /package/dist/web/assets/{yaml-C-Va56Gt.js → yaml-W95qP6-B.js} +0 -0
  463. /package/dist/web/assets/{zenscript-DdobKGI-.js → zenscript-BxircxDx.js} +0 -0
  464. /package/dist/web/assets/{zig-vqDFwj6q.js → zig-DD79WQtz.js} +0 -0
@@ -0,0 +1,1080 @@
1
+ # PPM Code Standards & Conventions
2
+
3
+ ## File Naming
4
+
5
+ | File Type | Convention | Example | Purpose |
6
+ |-----------|-----------|---------|---------|
7
+ | CLI commands | kebab-case | `start-cmd.ts`, `init.ts` | Descriptive command names |
8
+ | Services | kebab-case | `chat.service.ts`, `file.service.ts` | `{feature}.service.ts` pattern |
9
+ | Providers | kebab-case | `claude-agent-sdk.ts`, `mock-provider.ts` | `{name}-provider.ts` or `{name}.ts` |
10
+ | Routes | kebab-case | `chat.ts`, `project-scoped.ts` | Describe HTTP route group |
11
+ | WebSocket | kebab-case | `chat.ts`, `terminal.ts` | Match feature area |
12
+ | Components | PascalCase | `ChatTab.tsx`, `FileTree.tsx` | React convention |
13
+ | Hooks | camelCase with `use` prefix | `useChat.ts`, `useTerminal.ts` | React hook convention |
14
+ | Stores | kebab-case | `chat-store.ts`, `project-store.ts` | Zustand store files |
15
+ | Utilities | kebab-case | `utils.ts`, `file-support.ts` | Grouped by function |
16
+ | Types | kebab-case | `api.ts`, `chat.ts` | Group related types together |
17
+ | Tests | kebab-case with `.test.ts` | `chat.service.test.ts` | Match source file name |
18
+
19
+ ## TypeScript Conventions
20
+
21
+ ### Strict Mode
22
+ All files use TypeScript strict mode (`tsconfig.json` `"strict": true`).
23
+
24
+ ```typescript
25
+ // Required:
26
+ - Explicit return types on functions
27
+ - No `any` types (use `unknown` if necessary)
28
+ - No implicit `any` parameters
29
+ - Exhaustive type checking (switch, conditionals)
30
+ ```
31
+
32
+ ### Path Aliases
33
+ Use `@/*` alias for web layer imports (configured in `tsconfig.json`):
34
+
35
+ ```typescript
36
+ // Good
37
+ import { useChat } from "@/hooks/use-chat";
38
+ import { chatStore } from "@/stores/chat-store";
39
+
40
+ // Avoid
41
+ import { useChat } from "../../hooks/use-chat";
42
+ ```
43
+
44
+ ### Type Definitions
45
+ Place types near usage. Group related types in single files:
46
+
47
+ ```typescript
48
+ // Good: src/types/chat.ts
49
+ export interface Session { id: string; title: string; }
50
+ export interface Message { id: string; content: string; role: "user" | "assistant"; }
51
+ export type ChatEvent = { type: "text"; content: string } | { type: "done" };
52
+
53
+ // Avoid: spread across separate files
54
+ ```
55
+
56
+ ### Enums & Unions
57
+ Prefer discriminated unions over enums for better tree-shaking:
58
+
59
+ ```typescript
60
+ // Good: Discriminated union
61
+ type ChatEvent =
62
+ | { type: "text"; content: string }
63
+ | { type: "tool_use"; tool: string; input: unknown }
64
+ | { type: "done" };
65
+
66
+ // Avoid: Enum
67
+ enum MessageType {
68
+ TEXT,
69
+ TOOL_USE,
70
+ DONE,
71
+ }
72
+ ```
73
+
74
+ ### Async/Await
75
+ Always use `async`/`await` over Promise chains. Use async generators for streaming:
76
+
77
+ ```typescript
78
+ // Good: Async generator for streaming
79
+ async *streamMessages(input: string) {
80
+ for await (const event of provider.sendMessage(input)) {
81
+ yield event;
82
+ }
83
+ }
84
+
85
+ // Avoid: Promise chains
86
+ provider.sendMessage(input).then(...)
87
+ ```
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
+
114
+ ### Error Handling
115
+ Use try-catch for async operations. Throw structured errors:
116
+
117
+ ```typescript
118
+ // Good
119
+ try {
120
+ const file = await FileService.read(path);
121
+ } catch (error) {
122
+ const message = error instanceof Error ? error.message : "Unknown error";
123
+ console.error(`Failed to read ${path}: ${message}`);
124
+ throw new Error(`FileService.read failed: ${message}`);
125
+ }
126
+
127
+ // Avoid: Silent failures
128
+ const file = await FileService.read(path).catch(() => null);
129
+ ```
130
+
131
+ ## Component Patterns
132
+
133
+ ### React Components
134
+ Use functional components with hooks. Keep components focused:
135
+
136
+ ```typescript
137
+ // Good: Single responsibility
138
+ export function ChatTab() {
139
+ const { messages, sendMessage } = useChat();
140
+ return <div>/* Chat UI */</div>;
141
+ }
142
+
143
+ // Avoid: God component
144
+ export function ChatTab() {
145
+ // File management, git status, terminal, chat
146
+ }
147
+ ```
148
+
149
+ ### Zustand Stores (useShallow)
150
+ Define stores as singleton exports. Use `useShallow` when destructuring state to prevent unnecessary re-renders:
151
+
152
+ ```typescript
153
+ // Good: src/web/stores/chat-store.ts
154
+ export const chatStore = create<ChatState>((set) => ({
155
+ messages: [],
156
+ addMessage: (msg) => set((state) => ({ messages: [...state.messages, msg] })),
157
+ }));
158
+
159
+ // Usage with useShallow (avoids full re-render on object mutations)
160
+ const { messages, addMessage } = chatStore(useShallow((state) => ({
161
+ messages: state.messages,
162
+ addMessage: state.addMessage,
163
+ })));
164
+
165
+ // Single selector still works without useShallow
166
+ const messages = chatStore((state) => state.messages);
167
+ ```
168
+
169
+ ### Component Memoization (React.memo)
170
+ Memoize expensive components to prevent re-renders from parent updates:
171
+
172
+ ```typescript
173
+ // Good: Memoized heavy component
174
+ export const CodeEditor = memo(function CodeEditor({ filePath }: Props) {
175
+ return <MonacoEditor path={filePath} />;
176
+ });
177
+
178
+ // Memoized components: CodeEditor, MessageBubble, ProjectBar, ProjectAvatar, TerminalTab, PanelLayout, Sidebar, StatusBar, StatusBarEntry, TabBar, TreeNode
179
+ ```
180
+
181
+ ### Custom Hooks
182
+ Extract logic into hooks for reusability. Return stable references:
183
+
184
+ ```typescript
185
+ // Good: useChat hook
186
+ export function useChat() {
187
+ const [messages, setMessages] = useState<Message[]>([]);
188
+
189
+ const sendMessage = useCallback(async (text: string) => {
190
+ // Send logic
191
+ }, []);
192
+
193
+ return { messages, sendMessage };
194
+ }
195
+ ```
196
+
197
+ ### Lazy-Loaded Tab Content
198
+ Use React.lazy() for code splitting:
199
+
200
+ ```typescript
201
+ // Good
202
+ const ChatTab = lazy(() => import("./chat-tab").then(m => ({ default: m.ChatTab })));
203
+
204
+ // In component
205
+ <Suspense fallback={<Spinner />}>
206
+ <ChatTab />
207
+ </Suspense>
208
+ ```
209
+
210
+ ## Service Patterns
211
+
212
+ ### Singleton Services
213
+ Services are singletons exported as functions or instances:
214
+
215
+ ```typescript
216
+ // Good: services/chat.service.ts
217
+ export async function createSession(projectPath: string): Promise<Session> {
218
+ // Shared logic across all callers
219
+ }
220
+
221
+ // Good: services/config.service.ts
222
+ export const ConfigService = {
223
+ load: () => YAML.parse(configFile),
224
+ save: (config) => YAML.stringify(config),
225
+ };
226
+ ```
227
+
228
+ ### Dependency Injection
229
+ Services should receive dependencies as parameters or imports:
230
+
231
+ ```typescript
232
+ // Good: Pass dependencies explicitly
233
+ export async function streamChat(
234
+ session: Session,
235
+ message: string,
236
+ provider: AIProvider, // Dependency
237
+ ) {
238
+ // Use provider
239
+ }
240
+
241
+ // Avoid: Implicit globals
242
+ import { globalProvider } from "./global"; // Hidden dependency
243
+ ```
244
+
245
+ ### Error Propagation
246
+ Services throw descriptive errors; routes catch and format:
247
+
248
+ ```typescript
249
+ // Good: Service throws
250
+ export function validatePath(path: string) {
251
+ if (path.includes("..")) {
252
+ throw new Error(`Path traversal detected: ${path}`);
253
+ }
254
+ }
255
+
256
+ // Good: Route catches and formats
257
+ try {
258
+ const content = await FileService.read(path);
259
+ res.json(ok(content));
260
+ } catch (error) {
261
+ const msg = error instanceof Error ? error.message : "Unknown";
262
+ res.json(err(msg));
263
+ res.status(400);
264
+ }
265
+ ```
266
+
267
+ ### Provider Interface Pattern (Multi-Provider)
268
+
269
+ PPM supports multiple AI providers through an extensible interface pattern:
270
+
271
+ ```typescript
272
+ // Core interface defines required methods + optional capability methods
273
+ interface AIProvider {
274
+ id: string;
275
+ name: string;
276
+
277
+ // Required: All providers must implement these
278
+ createSession(config: SessionConfig): Promise<Session>;
279
+ resumeSession(sessionId: string): Promise<Session>;
280
+ listSessions(): Promise<SessionInfo[]>;
281
+ deleteSession(sessionId: string): Promise<void>;
282
+ sendMessage(sessionId: string, message: string, opts?: SendMessageOpts): AsyncIterable<ChatEvent>;
283
+
284
+ // Optional: Providers implement what they support
285
+ abortQuery?(sessionId: string): void;
286
+ getMessages?(sessionId: string): Promise<ChatMessage[]>;
287
+ listSessionsByDir?(dir: string): Promise<SessionInfo[]>;
288
+ ensureProjectPath?(sessionId: string, path: string): void;
289
+ isAvailable?(): Promise<boolean>;
290
+ }
291
+ ```
292
+
293
+ **SDK-based Providers (Claude):**
294
+ - Use Anthropic SDK for advanced features
295
+ - Implement all optional methods
296
+ - Streaming via SDK async generators
297
+
298
+ **CLI-based Providers (Cursor, Codex, Gemini):**
299
+ - Extend `CliProvider` abstract class
300
+ - Implement: `buildArgs()`, `mapEvent()`, `extractSessionId()`, `isAvailable()`
301
+ - Shared: spawn, parse NDJSON, abort, cleanup
302
+ - Override `spawnProcess()` for provider-specific quirks (e.g., workspace trust)
303
+ - Override `listSessions()` to read native provider history (e.g., SQLite)
304
+
305
+ **Usage Pattern (Type-Safe Optional Calls):**
306
+ ```typescript
307
+ // Good: Optional chaining for capabilities
308
+ await provider.abortQuery?.(sessionId); // Silently skips if not implemented
309
+ const messages = await provider.getMessages?.(sessionId) ?? [];
310
+
311
+ // Avoid: Duck-typing checks (old pattern)
312
+ if ("abortQuery" in provider) {
313
+ (provider as any).abortQuery(sessionId); // Not type-safe
314
+ }
315
+ ```
316
+
317
+ **Adding New Providers:**
318
+ 1. Create provider class implementing/extending AIProvider or CliProvider
319
+ 2. Implement `isAvailable()` for optional registration
320
+ 3. Register in `ProviderRegistry` via `bootstrapProviders()` if binary available
321
+ 4. Add event mapper if CLI-based (NDJSON → ChatEvent)
322
+ 5. ~100-150 lines per provider (extends CliProvider)
323
+
324
+ ## API Conventions
325
+
326
+ ### Response Envelope
327
+ All REST responses use the `ApiResponse<T>` envelope:
328
+
329
+ ```typescript
330
+ // Good
331
+ { ok: true, data: { /* payload */ } }
332
+ { ok: false, error: "descriptive error message" }
333
+
334
+ // Avoid: Inconsistent shapes
335
+ { success: true, result: { } }
336
+ { error: "error message" } // No shape contract
337
+ ```
338
+
339
+ ### Project-Scoped Routes
340
+ All project-specific endpoints use the pattern `/api/project/:name/*`:
341
+
342
+ ```
343
+ GET /api/projects # List all projects
344
+ POST /api/projects # Create project
345
+ DELETE /api/projects/:name # Delete project
346
+ GET /api/project/:name/chat/... # Chat (project-scoped)
347
+ GET /api/project/:name/git/... # Git (project-scoped)
348
+ GET /api/project/:name/files/... # Files (project-scoped)
349
+ ```
350
+
351
+ ### WebSocket Message Formats
352
+ Structure WebSocket messages as typed JSON objects:
353
+
354
+ ```typescript
355
+ // Client -> Server (chat, v0.8.55+)
356
+ { type: "message"; content: string; priority?: "now"|"next"|"later"; images?: {id: string; data: string}[] }
357
+ { type: "cancel" }
358
+ { type: "approval_response"; requestId: string; approved: boolean; reason?: string; data?: unknown }
359
+ { type: "ready" } // FE handshake after WS open
360
+
361
+ // Server -> Client (chat)
362
+ { type: "text"; content: string; parentToolUseId?: string }
363
+ { type: "thinking"; content: string; parentToolUseId?: string }
364
+ { type: "tool_use"; tool: string; input: unknown; toolUseId?: string; parentToolUseId?: string }
365
+ { type: "tool_result"; output: string; isError?: boolean; toolUseId?: string; parentToolUseId?: string }
366
+ { type: "approval_request"; requestId: string; tool: string; input: unknown }
367
+ { type: "done"; sessionId: string; contextWindowPct?: number; resultSubtype?: string }
368
+ { type: "error"; message: string }
369
+ { type: "account_info"; accountId: string; accountLabel: string }
370
+ { type: "phase_changed"; phase: SessionPhase; elapsed?: number }
371
+ { type: "session_state"; sessionId: string; phase: SessionPhase; pendingApproval: {...}|null; sessionTitle: string|null }
372
+ { type: "turn_events"; events: unknown[] } // Buffered events on reconnect
373
+ { type: "title_updated"; title: string }
374
+ { type: "ping" } // Server keepalive
375
+ ```
376
+
377
+ **New Fields (v0.8.55+):**
378
+ - `priority` — Message priority for queue ordering (future: "now" interrupts, "next" queues first, "later" queues at end)
379
+ - `images` — Image attachments sent with message
380
+ - `phase_changed` — Phase transitions (initializing → connecting → thinking/streaming → idle)
381
+ - `session_state` — Session state snapshot on WS open/ready (includes current phase and pending approval)
382
+ - `turn_events` — Buffered events sent on reconnection for sync
383
+ - `parentToolUseId` — Hierarchical tool call support (nested tool results)
384
+
385
+ ### Status Codes
386
+ Use standard HTTP status codes:
387
+
388
+ ```typescript
389
+ // Success
390
+ 200 OK - GET successful, POST/PUT/DELETE with response body
391
+ 201 Created - POST created resource
392
+ 204 No Content - DELETE successful
393
+
394
+ // Client error
395
+ 400 Bad Request - Invalid input, validation failure
396
+ 401 Unauthorized - Missing/invalid auth token
397
+ 403 Forbidden - Authenticated but not authorized (rare in PPM)
398
+ 404 Not Found - Project/file/session not found
399
+
400
+ // Server error
401
+ 500 Internal Error - Unexpected exception
402
+ ```
403
+
404
+ ## Import/Export Conventions
405
+
406
+ ### Named Exports (Preferred)
407
+ Use named exports for better tree-shaking and clarity:
408
+
409
+ ```typescript
410
+ // Good: services/file.service.ts
411
+ export async function read(path: string): Promise<string> { }
412
+ export async function write(path: string, content: string): Promise<void> { }
413
+
414
+ // Usage
415
+ import { read, write } from "./services/file.service";
416
+ ```
417
+
418
+ ### Default Exports (React Components Only)
419
+ Use default exports for React components (enables lazy loading):
420
+
421
+ ```typescript
422
+ // Good: components/chat/chat-tab.tsx
423
+ export default function ChatTab() { }
424
+
425
+ // Usage
426
+ const ChatTab = lazy(() => import("./components/chat/chat-tab"));
427
+ ```
428
+
429
+ ### Wildcard Imports (Avoid)
430
+ Avoid wildcard imports except for types:
431
+
432
+ ```typescript
433
+ // Good: Explicit imports
434
+ import { send, receive } from "./ws-client";
435
+
436
+ // Good: Type wildcard (rare)
437
+ import type * as Types from "./types";
438
+
439
+ // Avoid: Implicit exports
440
+ import * as WsClient from "./ws-client";
441
+ WsClient.send(); // Unclear what's exported
442
+ ```
443
+
444
+ ## Error Handling Patterns
445
+
446
+ ### Service Layer
447
+ Throw descriptive errors with context:
448
+
449
+ ```typescript
450
+ // Good
451
+ throw new Error(`GitService.commit failed: ${error.message}`);
452
+ throw new Error(`FileService: path traversal detected: ${path}`);
453
+
454
+ // Avoid: Generic errors
455
+ throw new Error("Failed");
456
+ throw error; // Re-throw loses context
457
+ ```
458
+
459
+ ### Route Layer
460
+ Catch, format, and return `ApiResponse` with error:
461
+
462
+ ```typescript
463
+ // Good
464
+ try {
465
+ const result = await service.doSomething();
466
+ return res.json(ok(result));
467
+ } catch (error) {
468
+ const message = error instanceof Error ? error.message : "Unknown error";
469
+ res.status(400);
470
+ return res.json(err(message));
471
+ }
472
+ ```
473
+
474
+ ### Component Layer
475
+ Handle errors from API calls with user-friendly messages:
476
+
477
+ ```typescript
478
+ // Good
479
+ try {
480
+ const response = await api.post("/...");
481
+ if (!response.ok) {
482
+ setError(response.error);
483
+ return;
484
+ }
485
+ setState(response.data);
486
+ } catch (error) {
487
+ setError("Network error. Please try again.");
488
+ }
489
+ ```
490
+
491
+ ## Testing Conventions
492
+
493
+ ### Test File Location
494
+ Tests colocate with source files or in `tests/` directory:
495
+
496
+ ```
497
+ src/services/chat.service.ts
498
+ tests/unit/services/chat.service.test.ts ← Match path, add .test suffix
499
+
500
+ src/web/hooks/use-chat.ts
501
+ tests/unit/hooks/use-chat.test.ts
502
+ ```
503
+
504
+ ### Test Structure
505
+ Use AAA pattern (Arrange, Act, Assert):
506
+
507
+ ```typescript
508
+ describe("ChatService", () => {
509
+ it("should create session with unique ID", () => {
510
+ // Arrange
511
+ const projectPath = "/tmp/project";
512
+
513
+ // Act
514
+ const session = ChatService.createSession(projectPath);
515
+ const session2 = ChatService.createSession(projectPath);
516
+
517
+ // Assert
518
+ expect(session.id).not.toBe(session2.id);
519
+ });
520
+ });
521
+ ```
522
+
523
+ ### Mocking
524
+ Mock external dependencies (providers, file system):
525
+
526
+ ```typescript
527
+ // Good: Mock provider
528
+ const mockProvider = {
529
+ createSession: () => ({ id: "test-id" }),
530
+ sendMessage: async function*() { yield { type: "text", content: "response" }; },
531
+ };
532
+
533
+ // Avoid: Mock implementation details
534
+ jest.spyOn(fs, "readFile").mockResolvedValue("content");
535
+ ```
536
+
537
+ ## Security Conventions
538
+
539
+ ### Path Traversal Protection
540
+ Always validate file paths before operations:
541
+
542
+ ```typescript
543
+ // Good: Validate before file access
544
+ export function validatePath(path: string) {
545
+ const normalized = Path.normalize(path);
546
+ if (normalized.startsWith("..")) {
547
+ throw new Error("Path traversal detected");
548
+ }
549
+ if (!normalized.startsWith(projectPath)) {
550
+ throw new Error("Access denied: outside project directory");
551
+ }
552
+ }
553
+ ```
554
+
555
+ ### Token-Based Auth
556
+ Every API route requires token validation via middleware:
557
+
558
+ ```typescript
559
+ // Good: Middleware validates token
560
+ app.use("/api", authMiddleware);
561
+
562
+ // In middleware
563
+ const token = req.header("Authorization");
564
+ if (!token || token !== config.auth.token) {
565
+ res.status(401);
566
+ res.json(err("Unauthorized"));
567
+ }
568
+ ```
569
+
570
+ ### Input Validation
571
+ Validate all user input (file paths, command arguments, message content):
572
+
573
+ ```typescript
574
+ // Good: Validate before processing
575
+ if (!path || typeof path !== "string") {
576
+ throw new Error("Invalid path");
577
+ }
578
+
579
+ if (message.length > 10000) {
580
+ throw new Error("Message too long");
581
+ }
582
+ ```
583
+
584
+ ## Documentation Conventions
585
+
586
+ ### Inline Comments
587
+ Use comments for **why**, not **what**. Let code speak for itself:
588
+
589
+ ```typescript
590
+ // Good: Explain intent
591
+ // Expand node lazily to avoid blocking on large directories
592
+ async function loadChildren(node: TreeNode) {
593
+ // ...
594
+ }
595
+
596
+ // Avoid: Obvious comments
597
+ // Set messages to empty array
598
+ const [messages, setMessages] = useState([]);
599
+ ```
600
+
601
+ ### JSDoc for Public APIs
602
+ Document exported functions with JSDoc:
603
+
604
+ ```typescript
605
+ /**
606
+ * Stream chat messages from AI provider.
607
+ *
608
+ * @param sessionId - Chat session ID
609
+ * @param message - User message text
610
+ * @param provider - AI provider (defaults to registry.default)
611
+ * @yields ChatEvent objects (text, tool_use, approval_request, done)
612
+ * @throws Error if session not found or provider fails
613
+ */
614
+ export async *streamMessages(
615
+ sessionId: string,
616
+ message: string,
617
+ provider?: AIProvider,
618
+ ) {
619
+ // ...
620
+ }
621
+ ```
622
+
623
+ ### Type Comments
624
+ Use type comments for complex types:
625
+
626
+ ```typescript
627
+ // File status with git tracking info
628
+ type FileStatus =
629
+ | { status: "modified" }
630
+ | { status: "untracked" }
631
+ | { status: "staged"; originalPath?: string };
632
+ ```
633
+
634
+ ## Performance Conventions
635
+
636
+ ### Code Splitting
637
+ Use `React.lazy()` + `Suspense` for routes, tab components, and heavy renderers:
638
+
639
+ ```typescript
640
+ // Good: Lazy-load terminal component
641
+ const TerminalTab = lazy(() => import("./terminal-tab"));
642
+
643
+ // Good: Lazy-load markdown renderer on 3+ sites
644
+ const MarkdownRenderer = lazy(() => import("./markdown-renderer"));
645
+ ```
646
+
647
+ ### Vendor Chunk Splitting (vite.config.ts)
648
+ Manually split large vendor libraries into separate chunks for better caching:
649
+
650
+ ```typescript
651
+ // Chunks: vendor-monaco, vendor-mermaid, vendor-xterm, vendor-markdown, vendor-ui
652
+ manualChunks(id: string) {
653
+ if (id.includes("node_modules/monaco-editor")) return "vendor-monaco";
654
+ if (id.includes("node_modules/mermaid")) return "vendor-mermaid";
655
+ if (id.includes("node_modules/@xterm")) return "vendor-xterm";
656
+ // ... etc
657
+ }
658
+ ```
659
+
660
+ ### Dynamic Imports (On-Demand Loading)
661
+ Load heavy libraries only when needed:
662
+
663
+ ```typescript
664
+ // Good: Mermaid diagram support on-demand in markdown
665
+ let mermaidPromise: Promise<typeof import("mermaid")> | null = null;
666
+ async function loadMermaid() {
667
+ if (!mermaidPromise) {
668
+ mermaidPromise = import("mermaid").then((mod) => mod.default);
669
+ }
670
+ return mermaidPromise;
671
+ }
672
+ ```
673
+
674
+ ### Chat Pagination & Message Caps
675
+ - **Chat history:** Load 50 messages per page with load-more button
676
+ - **Team activity:** Cap `teamActivityRef` at 500 messages to prevent unbounded growth
677
+
678
+ ### Memoization
679
+ Memoize expensive computations and callbacks:
680
+
681
+ ```typescript
682
+ // Good: Memoize filter result
683
+ const filteredFiles = useMemo(
684
+ () => files.filter(f => f.name.includes(query)),
685
+ [files, query]
686
+ );
687
+
688
+ // Good: Stable callback reference
689
+ const handleClick = useCallback(() => {
690
+ // ...
691
+ }, [dependencies]);
692
+ ```
693
+
694
+ ### Bundle Analysis
695
+ Monitor bundle size growth:
696
+
697
+ ```bash
698
+ # Check bundle stats
699
+ bun run build && ls -lh dist/web/assets/
700
+ ```
701
+
702
+ ## Git Conventions
703
+
704
+ ### Commit Messages
705
+ Use conventional commit format:
706
+
707
+ ```
708
+ feat: add file attachment support to chat
709
+ fix: resolve WebSocket reconnection issue
710
+ refactor: simplify GitService.status method
711
+ docs: update deployment guide
712
+ test: add chat-service unit tests
713
+ chore: upgrade TypeScript to 5.9.3
714
+ ```
715
+
716
+ ### Branch Names
717
+ Use descriptive kebab-case names:
718
+
719
+ ```
720
+ feature/chat-file-attachments
721
+ fix/websocket-reconnect
722
+ refactor/service-layer-cleanup
723
+ docs/deployment-guide
724
+ ```
725
+
726
+ ## CLI Design Patterns
727
+
728
+ ### Command Option Handling
729
+ When adding new options to CLI commands (e.g., `ppm start`):
730
+
731
+ **Option Naming:**
732
+ - Use long form: `--foreground`, `--share` (not short-only)
733
+ - Add short form if common: `-f`, `-s` (optional)
734
+ - Keep defaults sensible (e.g., daemon mode is default)
735
+
736
+ **Implementation Pattern:**
737
+ ```typescript
738
+ program
739
+ .command("start")
740
+ .option("-p, --port <port>", "Port to listen on")
741
+ .option("-f, --foreground", "Run in foreground")
742
+ .option("-s, --share", "Enable public URL via tunnel")
743
+ .action(async (options) => {
744
+ // options.port, options.foreground, options.share as booleans
745
+ });
746
+ ```
747
+
748
+ **Server Handling:**
749
+ ```typescript
750
+ export async function startServer(options: {
751
+ port?: string;
752
+ foreground?: boolean;
753
+ share?: boolean;
754
+ config?: string;
755
+ }) {
756
+ const isDaemon = !options.foreground; // Explicit: daemon is default
757
+
758
+ if (isDaemon) {
759
+ // Spawn child process
760
+ const child = Bun.spawn(/* ... */);
761
+ // Poll for status.json, show URLs
762
+ } else {
763
+ // Foreground: serve with logs
764
+ const server = Bun.serve(/* ... */);
765
+ }
766
+
767
+ if (options.share) {
768
+ // Start tunnel (works in both daemon + foreground)
769
+ }
770
+ }
771
+ ```
772
+
773
+ ### Status File Format
774
+ Daemon process communicates back via JSON file at `~/.ppm/status.json`:
775
+
776
+ ```json
777
+ {
778
+ "pid": 12345,
779
+ "port": 8080,
780
+ "host": "0.0.0.0",
781
+ "shareUrl": "https://abc-123.trycloudflare.com"
782
+ }
783
+ ```
784
+
785
+ **Backward Compatibility:** Fallback to `~/.ppm/ppm.pid` for legacy support.
786
+
787
+ ### Feature Service Loading (Lazy)
788
+ Services that require external dependencies (e.g., cloudflared) should be lazy-imported:
789
+
790
+ ```typescript
791
+ if (options.share) {
792
+ // Only download cloudflared if --share was used
793
+ const { ensureCloudflared } = await import("../services/cloudflared.service.ts");
794
+ await ensureCloudflared();
795
+ }
796
+ ```
797
+
798
+ This keeps startup fast when features aren't used.
799
+
800
+ ---
801
+
802
+ ## Tab ID & URL Conventions (v0.8.77+)
803
+
804
+ ### Deterministic Tab IDs
805
+
806
+ Tab IDs are derived from type + metadata, not random:
807
+
808
+ ```typescript
809
+ // Format: "{type}:{identifier}" for most tabs, "{type}" for singletons
810
+ editor:src/index.ts // Editor tab with file path
811
+ chat:claude/abc123 // Chat with provider/session ID
812
+ terminal:1 // Terminal tab index
813
+ database:conn-1/users // Database: connection ID / table
814
+ git-graph // Singleton: no identifier
815
+ settings // Singleton: no identifier
816
+
817
+ // Derivation in panel-utils.ts
818
+ export function deriveTabId(type: TabType, metadata?: Record<string, unknown>): string {
819
+ switch (type) {
820
+ case "editor":
821
+ return `editor:${metadata?.filePath ?? "untitled"}`;
822
+ case "chat": {
823
+ const provider = metadata?.providerId ?? "default";
824
+ return `chat:${provider}/${metadata?.sessionId ?? randomId()}`;
825
+ }
826
+ case "terminal":
827
+ return `terminal:${metadata?.terminalIndex ?? 1}`;
828
+ case "git-graph":
829
+ return "git-graph"; // Singleton
830
+ // ... other cases
831
+ }
832
+ }
833
+ ```
834
+
835
+ ### URL Format
836
+
837
+ URLs are built from project name + deterministic tab ID:
838
+
839
+ ```typescript
840
+ // Format: /project/{projectName}/{type}/{identifier}
841
+ /project/ppm // Project root (no active tab)
842
+ /project/ppm/editor/src/index.ts // Open editor
843
+ /project/ppm/chat/claude/abc123 // Open chat
844
+ /project/ppm/terminal/1 // Open terminal
845
+ /project/ppm/database/conn-1/users // Open database
846
+ /project/ppm/git-graph // Git history (singleton)
847
+
848
+ // URL building in use-url-sync.ts
849
+ export function buildUrl(projectName: string, tabId: string | null): string {
850
+ const colonIdx = tabId?.indexOf(":");
851
+ if (colonIdx === -1) {
852
+ // Singleton
853
+ url += `/${tabId}`;
854
+ } else {
855
+ const [type, identifier] = tabId.split(":", 1);
856
+ url += `/${type}/${identifier}`;
857
+ }
858
+ return url;
859
+ }
860
+ ```
861
+
862
+ ### Deep Linking
863
+
864
+ When user navigates to a URL, parse it and auto-create tabs:
865
+
866
+ ```typescript
867
+ // In use-url-sync hook
868
+ export function parseUrlState(): UrlState {
869
+ const match = path.match(/^\/project\/([^/]+)(?:\/([^/]+)(\/.*)?)?/);
870
+ const [, projectName, tabType, tabIdentifierPath] = match;
871
+
872
+ // Build tab metadata from URL
873
+ const metadata = buildMetadataFromUrl(tabType, tabIdentifier, projectName);
874
+
875
+ // Auto-open tab if it doesn't exist
876
+ if (metadata) {
877
+ panelStore.openTab({
878
+ type: tabType,
879
+ title: getTabTitle(tabType, metadata),
880
+ metadata,
881
+ });
882
+ }
883
+ }
884
+ ```
885
+
886
+ ### Server-Side Workspace Persistence
887
+
888
+ Tab layouts are persisted in the `workspace_state` SQLite table:
889
+
890
+ ```typescript
891
+ // workspace.ts routes
892
+ GET /api/project/:name/workspace → { layout: PanelLayout, updatedAt: string }
893
+ PUT /api/project/:name/workspace → { updatedAt: string }
894
+
895
+ // PanelLayout structure
896
+ interface PanelLayout {
897
+ panels: Record<string, Panel>; // panelId → { tabs, activeTabId }
898
+ grid: string[][]; // Row/column grid of panel IDs
899
+ focusedPanelId: string;
900
+ }
901
+
902
+ // Sync is debounced (1.5s) client-side after layout changes
903
+ // Latest-wins: server timestamp compared with client localStorage
904
+ ```
905
+
906
+ ### Migration from Random IDs
907
+
908
+ Old tab IDs (tab-xxxx) are automatically migrated to deterministic format:
909
+
910
+ ```typescript
911
+ // In panel-utils.ts
912
+ export function migrateTabIdToDeterministic(tab: Tab): Tab {
913
+ if (tab.id.startsWith("tab-")) {
914
+ // Convert to deterministic ID
915
+ return {
916
+ ...tab,
917
+ id: deriveTabId(tab.type, tab.metadata),
918
+ };
919
+ }
920
+ return tab;
921
+ }
922
+ ```
923
+
924
+ This happens on first load per project; old URLs redirect to project root.
925
+
926
+ ---
927
+
928
+ ## Provider Pattern (Multi-Provider Architecture)
929
+
930
+ ### AIProvider Interface
931
+ All AI providers implement the `AIProvider` interface from `src/types/chat.ts`:
932
+
933
+ ```typescript
934
+ interface AIProvider {
935
+ // Required methods
936
+ createSession(): Promise<Session>;
937
+ sendMessage(sessionId: string, message: string, context?: FileContext[]): AsyncIterable<ChatEvent>;
938
+
939
+ // Optional methods (v0.8.60+)
940
+ listModels?(): Promise<ModelOption[]>;
941
+ isAvailable?(): Promise<boolean>;
942
+ }
943
+
944
+ interface ModelOption {
945
+ value: string; // Model ID (e.g., "claude-sonnet-4-6")
946
+ label: string; // Display name (e.g., "Claude Sonnet 4.6")
947
+ }
948
+ ```
949
+
950
+ ### Adding a New Provider
951
+
952
+ **Step 1: Create provider file** (`src/providers/{name}-provider.ts`)
953
+ ```typescript
954
+ export class MyProvider implements AIProvider {
955
+ async createSession(): Promise<Session> {
956
+ // Return { id: string; ... }
957
+ }
958
+
959
+ async *sendMessage(sessionId, message, context?) {
960
+ // Stream ChatEvent objects
961
+ yield { type: "text", content: "response" };
962
+ }
963
+
964
+ // Optional: Discover available models
965
+ async listModels(): Promise<ModelOption[]> {
966
+ return [
967
+ { value: "model-1", label: "Model 1" },
968
+ { value: "model-2", label: "Model 2" }
969
+ ];
970
+ }
971
+
972
+ // Optional: Check provider availability
973
+ async isAvailable(): Promise<boolean> {
974
+ // Check binary exists, API key set, etc.
975
+ return true;
976
+ }
977
+ }
978
+ ```
979
+
980
+ **Step 2: Register in ProviderRegistry** (`src/providers/registry.ts`)
981
+ ```typescript
982
+ constructor() {
983
+ this.providers.set("my-provider", {
984
+ id: "my-provider",
985
+ name: "My Provider",
986
+ instance: new MyProvider()
987
+ });
988
+ }
989
+ ```
990
+
991
+ **Step 3: Add to config schema** (`schemas/ppm-config.schema.json`)
992
+ ```json
993
+ {
994
+ "ai.providers.my_provider": {
995
+ "type": "object",
996
+ "properties": {
997
+ "type": { "const": "my-type" },
998
+ "model": { "type": "string" }
999
+ }
1000
+ }
1001
+ }
1002
+ ```
1003
+
1004
+ ### CLI Provider Pattern
1005
+
1006
+ For subprocess-based providers (e.g., Cursor), extend `CliProvider`:
1007
+
1008
+ ```typescript
1009
+ import { CliProvider } from "./cli-provider-base";
1010
+
1011
+ export class CursorProvider extends CliProvider {
1012
+ async listModels(): Promise<ModelOption[]> {
1013
+ // Cache result with TTL
1014
+ if (this.modelsCache && Date.now() < this.modelsCache.expiry) {
1015
+ return this.modelsCache.models;
1016
+ }
1017
+
1018
+ try {
1019
+ // Execute subprocess with timeout
1020
+ const result = await this.executeWithTimeout(
1021
+ "cursor-agent --list-models",
1022
+ 10000 // 10 second timeout
1023
+ );
1024
+
1025
+ const models = JSON.parse(result);
1026
+
1027
+ // Cache for 5 minutes
1028
+ this.modelsCache = {
1029
+ models,
1030
+ expiry: Date.now() + 5 * 60 * 1000
1031
+ };
1032
+
1033
+ return models;
1034
+ } catch (error) {
1035
+ // Graceful fallback on timeout/error
1036
+ return [];
1037
+ }
1038
+ }
1039
+ }
1040
+ ```
1041
+
1042
+ ### Testing Providers
1043
+
1044
+ Mock for deterministic testing:
1045
+
1046
+ ```typescript
1047
+ // tests/unit/providers/my-provider.test.ts
1048
+ describe("MyProvider", () => {
1049
+ it("should stream messages", async () => {
1050
+ const provider = new MyProvider();
1051
+ const session = await provider.createSession();
1052
+
1053
+ const events: ChatEvent[] = [];
1054
+ for await (const event of provider.sendMessage(session.id, "test")) {
1055
+ events.push(event);
1056
+ }
1057
+
1058
+ expect(events.length).toBeGreaterThan(0);
1059
+ });
1060
+ });
1061
+ ```
1062
+
1063
+ ### Provider Registry Usage
1064
+
1065
+ ```typescript
1066
+ // Get specific provider
1067
+ const provider = providerRegistry.get("claude");
1068
+
1069
+ // Get user-facing providers (for dropdowns)
1070
+ const providers = providerRegistry.list();
1071
+
1072
+ // Get all providers including mock (internal)
1073
+ const allProviders = providerRegistry.listAll();
1074
+
1075
+ // Check if provider is available
1076
+ const available = await provider.isAvailable?.();
1077
+
1078
+ // List models
1079
+ const models = await provider.listModels?.() ?? [];
1080
+ ```