diffing 0.6.0 → 0.7.0

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 (196) hide show
  1. package/README.md +138 -100
  2. package/dist/apply-suggestion-BAuyCYk6.mjs +54 -0
  3. package/dist/cli-agent-BkkRbKqq.mjs +513 -0
  4. package/dist/cli-agent-BlMtx1Tx.mjs +736 -0
  5. package/dist/cli-agent-C4Fb90GL.mjs +728 -0
  6. package/dist/cli-agent-ChALuNbw.mjs +841 -0
  7. package/dist/cli-agent-D71pq2ep.mjs +841 -0
  8. package/dist/cli-agent-qBT8clNM.mjs +841 -0
  9. package/dist/cli-gh-6qiblgj7.mjs +150 -0
  10. package/dist/cli-gh-D5OCnjNe.mjs +150 -0
  11. package/dist/cli-gh-DgqscIkg.mjs +147 -0
  12. package/dist/cli-gh-KjPnyST6.mjs +150 -0
  13. package/dist/cli.mjs +38 -66
  14. package/dist/client/assets/{angular-html-BotC7B4-.js → angular-html-Dh86jUvr.js} +1 -1
  15. package/dist/client/assets/{angular-ts-DQTHeTJm.js → angular-ts-Bh4wRJtT.js} +1 -1
  16. package/dist/client/assets/{apl-Bwz5Dpd_.js → apl-BSXBHduT.js} +1 -1
  17. package/dist/client/assets/{arc-D-TSgv-7.js → arc-CRQlhBrV.js} +1 -1
  18. package/dist/client/assets/{architectureDiagram-3BPJPVTR-DLe25t8t.js → architectureDiagram-3BPJPVTR-BXq3UvEv.js} +1 -1
  19. package/dist/client/assets/{astro-DjkoD2fR.js → astro-Bh00klUw.js} +1 -1
  20. package/dist/client/assets/{blade-Cap2uJIA.js → blade-CisA3zhm.js} +1 -1
  21. package/dist/client/assets/{blockDiagram-GPEHLZMM-B-bihLYO.js → blockDiagram-GPEHLZMM-Bw-PTAJr.js} +1 -1
  22. package/dist/client/assets/{c-C7JoAATL.js → c-D-pHVWrf.js} +1 -1
  23. package/dist/client/assets/{c4Diagram-AAUBKEIU-CoCcjXEC.js → c4Diagram-AAUBKEIU-C0XyiqC_.js} +1 -1
  24. package/dist/client/assets/channel-CDxgOu-3.js +1 -0
  25. package/dist/client/assets/{chunk-2J33WTMH-Sm_uQrGq.js → chunk-2J33WTMH-DekjZXNj.js} +1 -1
  26. package/dist/client/assets/{chunk-3OPIFGDE-Ct83Sp4P.js → chunk-3OPIFGDE-BtROpcL8.js} +1 -1
  27. package/dist/client/assets/{chunk-4BX2VUAB-DrqChk1a.js → chunk-4BX2VUAB-C92XLTlx.js} +1 -1
  28. package/dist/client/assets/{chunk-55IACEB6-CodeFb9E.js → chunk-55IACEB6-CceUgh8N.js} +1 -1
  29. package/dist/client/assets/{chunk-5ZQYHXKU-CpEx4dmS.js → chunk-5ZQYHXKU-DSbYPAe7.js} +1 -1
  30. package/dist/client/assets/{chunk-727SXJPM-D3U6TUmk.js → chunk-727SXJPM-DVzUJa-9.js} +1 -1
  31. package/dist/client/assets/{chunk-AQP2D5EJ-Dr-_DyBy.js → chunk-AQP2D5EJ-6n3Z_IFa.js} +1 -1
  32. package/dist/client/assets/{chunk-BSJP7CBP-Bnyml8mU.js → chunk-BSJP7CBP-BgVLnkxr.js} +1 -1
  33. package/dist/client/assets/{chunk-CSCIHK7Q-BJSFGTlk.js → chunk-CSCIHK7Q-CkGMXntp.js} +1 -1
  34. package/dist/client/assets/{chunk-FMBD7UC4-BqiwDIwx.js → chunk-FMBD7UC4-BM8lrLBo.js} +1 -1
  35. package/dist/client/assets/{chunk-KSCS5N6A-D-H-HBRn.js → chunk-KSCS5N6A-Bm2pjMQg.js} +1 -1
  36. package/dist/client/assets/{chunk-L5ZTLDWV-CVMSAF7h.js → chunk-L5ZTLDWV-VL17ikXM.js} +1 -1
  37. package/dist/client/assets/{chunk-LZXEDZCA-BQsF6HF3.js → chunk-LZXEDZCA-B629g67M.js} +2 -2
  38. package/dist/client/assets/{chunk-ND2GUHAM-eoM20ySO.js → chunk-ND2GUHAM-CjZGwhKA.js} +1 -1
  39. package/dist/client/assets/{chunk-NZK2D7GU-B1iU0i4a.js → chunk-NZK2D7GU-DGsLjESY.js} +1 -1
  40. package/dist/client/assets/{chunk-O5CBEL6O-CcIPuHUC.js → chunk-O5CBEL6O-EyUrGY3l.js} +1 -1
  41. package/dist/client/assets/chunk-QZHKN3VN-DJCeMpP8.js +1 -0
  42. package/dist/client/assets/chunk-WU5MYG2G-BbuOkMaH.js +1 -0
  43. package/dist/client/assets/{chunk-XPW4576I-jSj3E63-.js → chunk-XPW4576I-C2TeAQmQ.js} +1 -1
  44. package/dist/client/assets/classDiagram-4FO5ZUOK-D5oJbiED.js +1 -0
  45. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-5MJt5ZxQ.js +1 -0
  46. package/dist/client/assets/{cobol-DkiK_msx.js → cobol-DsRZMP16.js} +1 -1
  47. package/dist/client/assets/{coffee-f7z4wIqP.js → coffee-DP0-MMsE.js} +1 -1
  48. package/dist/client/assets/{cose-bilkent-S5V4N54A-Dikk-RXu.js → cose-bilkent-S5V4N54A-DMmK0b0W.js} +1 -1
  49. package/dist/client/assets/{cpp-DB_FV8ps.js → cpp-B6XYbbze.js} +1 -1
  50. package/dist/client/assets/{crystal-BRBnyZFz.js → crystal-dBLPFLMp.js} +1 -1
  51. package/dist/client/assets/{css-GpNBLqRF.js → css-tgwuSZL0.js} +1 -1
  52. package/dist/client/assets/{dagre-BM42HDAG-tErJNBcm.js → dagre-BM42HDAG-CgNkf0TR.js} +1 -1
  53. package/dist/client/assets/{diagram-2AECGRRQ-D0-DgitC.js → diagram-2AECGRRQ-ocAqn0LV.js} +1 -1
  54. package/dist/client/assets/{diagram-5GNKFQAL-DqMjQTtW.js → diagram-5GNKFQAL-CB1Yo4Gc.js} +1 -1
  55. package/dist/client/assets/{diagram-KO2AKTUF-CP7V9tqS.js → diagram-KO2AKTUF-B2vpMBRT.js} +1 -1
  56. package/dist/client/assets/{diagram-LMA3HP47-tCGdsz4p.js → diagram-LMA3HP47-DuxZOaQS.js} +1 -1
  57. package/dist/client/assets/{diagram-OG6HWLK6-DtDnaqso.js → diagram-OG6HWLK6-CkShDmcv.js} +1 -1
  58. package/dist/client/assets/{dist-B5Eb-OKZ.js → dist-BgZIvk5w.js} +1 -1
  59. package/dist/client/assets/{edge-DqnWvViE.js → edge-Bokubn8E.js} +1 -1
  60. package/dist/client/assets/{elixir-BSuxOIIa.js → elixir-BEzcf3Xg.js} +1 -1
  61. package/dist/client/assets/{elm-B6U1Is0L.js → elm-vJyLTAER.js} +1 -1
  62. package/dist/client/assets/{erDiagram-TEJ5UH35-C5S0U2x3.js → erDiagram-TEJ5UH35-BFV1NQAH.js} +1 -1
  63. package/dist/client/assets/{erb-kK3QM18H.js → erb-DDkAmjBS.js} +1 -1
  64. package/dist/client/assets/{flowDiagram-I6XJVG4X-NNqmBYk2.js → flowDiagram-I6XJVG4X-DU8xbFdO.js} +1 -1
  65. package/dist/client/assets/{ganttDiagram-6RSMTGT7-8ng09X8S.js → ganttDiagram-6RSMTGT7-DCb7Qh0c.js} +1 -1
  66. package/dist/client/assets/{git-rebase-BDKTWtxr.js → git-rebase-DWcifAdK.js} +1 -1
  67. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-D_HBvwVl.js → gitGraphDiagram-PVQCEYII-C_Tw-piE.js} +1 -1
  68. package/dist/client/assets/{glimmer-js-BthGlRra.js → glimmer-js--HDru5ys.js} +1 -1
  69. package/dist/client/assets/{glimmer-ts-CS8Z9z2h.js → glimmer-ts-DAdXisHh.js} +1 -1
  70. package/dist/client/assets/{glsl-C5ERhU2v.js → glsl-CmIRlJht.js} +1 -1
  71. package/dist/client/assets/{graphql-sfgTqbIp.js → graphql-tx4-7I3U.js} +1 -1
  72. package/dist/client/assets/{hack-EeOmOf4N.js → hack-yA0XjhF7.js} +1 -1
  73. package/dist/client/assets/{haml-DVf5-Ndq.js → haml-BR5MUE1b.js} +1 -1
  74. package/dist/client/assets/{handlebars-BSLgW49o.js → handlebars-BPOF7iOf.js} +1 -1
  75. package/dist/client/assets/{html-DoOSf-hz.js → html-BbteRk96.js} +1 -1
  76. package/dist/client/assets/{html-derivative-BM0DLd1Z.js → html-derivative-59NNrcMm.js} +1 -1
  77. package/dist/client/assets/{http-d2p18QI6.js → http-D3odGQI4.js} +1 -1
  78. package/dist/client/assets/{hurl-CH2I2f66.js → hurl-BLTj8fPj.js} +1 -1
  79. package/dist/client/assets/index-BZcKS8Sb.css +1 -0
  80. package/dist/client/assets/{index-CJI1E0bV.js → index-DHXpO7eS.js} +137 -104
  81. package/dist/client/assets/{infoDiagram-5YYISTIA-Cgg2FsCN.js → infoDiagram-5YYISTIA-CI6hJyRl.js} +1 -1
  82. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-pYRQFecr.js → ishikawaDiagram-YF4QCWOH-gMS1P1LQ.js} +1 -1
  83. package/dist/client/assets/{java-BFiqhlWm.js → java-CsViuzs_.js} +1 -1
  84. package/dist/client/assets/{javascript-YWa7zRPZ.js → javascript-DQgsl2Dh.js} +1 -1
  85. package/dist/client/assets/{jinja-h_UdmDAn.js → jinja-DQNp2NUA.js} +1 -1
  86. package/dist/client/assets/{jison-cOvcjPYz.js → jison-CDDOZQdc.js} +1 -1
  87. package/dist/client/assets/{journeyDiagram-JHISSGLW-D3EBlW7m.js → journeyDiagram-JHISSGLW-BTQ_Ws_h.js} +1 -1
  88. package/dist/client/assets/{json-DT0GjoRi.js → json-DP4TqCuE.js} +1 -1
  89. package/dist/client/assets/{jsx-BaYjgZVH.js → jsx-Df5hn0Tf.js} +1 -1
  90. package/dist/client/assets/{julia-Cg36ySxC.js → julia--u33yGLi.js} +1 -1
  91. package/dist/client/assets/{just-KYC4U1y4.js → just-BCpMmSOD.js} +1 -1
  92. package/dist/client/assets/{kanban-definition-UN3LZRKU-DA42mt4M.js → kanban-definition-UN3LZRKU-CvqGbLR_.js} +1 -1
  93. package/dist/client/assets/{latex-DpuXxlsH.js → latex-D8D4jZGw.js} +1 -1
  94. package/dist/client/assets/{line-D91vf_Yl.js → line-DBgaS_RW.js} +1 -1
  95. package/dist/client/assets/{linear-CL7WzrL5.js → linear-RQVzKNVU.js} +1 -1
  96. package/dist/client/assets/{liquid-Dua3sjBU.js → liquid-CkH85f12.js} +1 -1
  97. package/dist/client/assets/{lua-CpDMEbOh.js → lua-iKoV7WE3.js} +1 -1
  98. package/dist/client/assets/{marko-rM0rxrJ5.js → marko-N8ScQSqT.js} +1 -1
  99. package/dist/client/assets/{mdc-D-E4W6ne.js → mdc-BvHTOxsp.js} +1 -1
  100. package/dist/client/assets/{mermaid-parser.core-BLQDdrsf.js → mermaid-parser.core-CwDRwMR7.js} +1 -1
  101. package/dist/client/assets/{mermaid.core-BBR2Otc_.js → mermaid.core-D2LDJgXO.js} +3 -3
  102. package/dist/client/assets/{mindmap-definition-RKZ34NQL-DMrApv-_.js → mindmap-definition-RKZ34NQL-FRnPh642.js} +1 -1
  103. package/dist/client/assets/{nginx-wZWgcQLM.js → nginx-DGgvASuI.js} +1 -1
  104. package/dist/client/assets/{nim-Bh7OZsZh.js → nim-D-v9hzvR.js} +1 -1
  105. package/dist/client/assets/{perl-DtEqNC_V.js → perl-CG8leJ8K.js} +1 -1
  106. package/dist/client/assets/{php-DX7bHce8.js → php-DvkeSP10.js} +1 -1
  107. package/dist/client/assets/{pieDiagram-4H26LBE5-B2f9_PO6.js → pieDiagram-4H26LBE5-CepOg38Y.js} +1 -1
  108. package/dist/client/assets/{pug-r67inScB.js → pug-B4T7-xaL.js} +1 -1
  109. package/dist/client/assets/{qml-f1bOqUwX.js → qml-BE6D5Bxn.js} +1 -1
  110. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DgdKno5G.js → quadrantDiagram-W4KKPZXB-DOFAsP60.js} +1 -1
  111. package/dist/client/assets/{r-Cpm-_vkr.js → r-BKZg9Mcq.js} +1 -1
  112. package/dist/client/assets/{razor-CEymfLfo.js → razor-D-02oaxL.js} +1 -1
  113. package/dist/client/assets/{regexp-DCnAJwuE.js → regexp-BDFvAR1k.js} +1 -1
  114. package/dist/client/assets/{requirementDiagram-4Y6WPE33-BIRl56-J.js → requirementDiagram-4Y6WPE33-BBVNH-2W.js} +1 -1
  115. package/dist/client/assets/review-export-D5iIl46_.js +7 -0
  116. package/dist/client/assets/{rst-Cm1M2uc8.js → rst-BcT26OSj.js} +1 -1
  117. package/dist/client/assets/{ruby-B49kkGT0.js → ruby-Cq_YQvFT.js} +1 -1
  118. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-7XZZmV9N.js → sankeyDiagram-5OEKKPKP-BQfCTbBX.js} +1 -1
  119. package/dist/client/assets/{sas-BOXC9Iq4.js → sas-DN9oTxKY.js} +1 -1
  120. package/dist/client/assets/{scss-DU2HGWZP.js → scss-BK_sp3z2.js} +1 -1
  121. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-CtIjo3i9.js → sequenceDiagram-3UESZ5HK-CKAW2eyx.js} +1 -1
  122. package/dist/client/assets/{shellscript-YFjx1NBH.js → shellscript-CvNpOu0J.js} +1 -1
  123. package/dist/client/assets/{shellsession-4zfyiKm-.js → shellsession-8-yj2cJj.js} +1 -1
  124. package/dist/client/assets/{soy-CRExh-ql.js → soy-C6Q9atPF.js} +1 -1
  125. package/dist/client/assets/{sql-2Iw6Ta1F.js → sql-CBL3BuHb.js} +1 -1
  126. package/dist/client/assets/{src-Dn3_YOHM.js → src-D-VUWIK5.js} +1 -1
  127. package/dist/client/assets/{stata-DflAcpzB.js → stata-B2Q7Vqzu.js} +1 -1
  128. package/dist/client/assets/{stateDiagram-AJRCARHV-D_w9F0BN.js → stateDiagram-AJRCARHV-BGJiv0qO.js} +1 -1
  129. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CdUgMghp.js +1 -0
  130. package/dist/client/assets/{surrealql-CMprrxj1.js → surrealql-C-A2N8vk.js} +1 -1
  131. package/dist/client/assets/{svelte-Bb_Bxt4a.js → svelte-VAut_-uy.js} +1 -1
  132. package/dist/client/assets/{templ-MJ_5gYAf.js → templ-mUetM--K.js} +1 -1
  133. package/dist/client/assets/{tex-BQ33E56K.js → tex-BT53mWGp.js} +1 -1
  134. package/dist/client/assets/{timeline-definition-PNZ67QCA-BzgsTjf8.js → timeline-definition-PNZ67QCA-lVwMt1rk.js} +1 -1
  135. package/dist/client/assets/{ts-tags-Cddttr6f.js → ts-tags-VkIOkgLN.js} +1 -1
  136. package/dist/client/assets/{tsx-BFchi_eS.js → tsx-D_aaGHTf.js} +1 -1
  137. package/dist/client/assets/{twig-CA8ssIYI.js → twig-CufwqKkk.js} +1 -1
  138. package/dist/client/assets/{typescript-tdiTR9sY.js → typescript-osFD2CEV.js} +1 -1
  139. package/dist/client/assets/{vennDiagram-CIIHVFJN-CMjPo4Qf.js → vennDiagram-CIIHVFJN-_K9NtpA5.js} +1 -1
  140. package/dist/client/assets/{vue-BbZNP0Rd.js → vue-Bub1rXmL.js} +1 -1
  141. package/dist/client/assets/{vue-html-02g5ipYx.js → vue-html-CN3x2pYg.js} +1 -1
  142. package/dist/client/assets/{vue-vine-9lCbaeLR.js → vue-vine-C0csYkGm.js} +1 -1
  143. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Cy6qwCgU.js → wardleyDiagram-YWT4CUSO-B2jTMfra.js} +1 -1
  144. package/dist/client/assets/{xml-BPPCB2P8.js → xml-my6OjeGb.js} +1 -1
  145. package/dist/client/assets/{xsl-1-u1Beet.js → xsl-Bx0YVn26.js} +1 -1
  146. package/dist/client/assets/{xychartDiagram-2RQKCTM6-q9ysdRiS.js → xychartDiagram-2RQKCTM6-DfZ4YYXU.js} +1 -1
  147. package/dist/client/assets/{yaml-79W-bav7.js → yaml-DanwOypk.js} +1 -1
  148. package/dist/client/favicon.svg +40 -0
  149. package/dist/client/index.html +7 -3
  150. package/dist/comment-format-B5AcGcR-.mjs +118 -0
  151. package/dist/comment-format-CKTt0fzi.mjs +113 -0
  152. package/dist/comment-format-DG_PAehC.mjs +117 -0
  153. package/dist/completions-ZSKYvW06.mjs +126 -0
  154. package/dist/diff-fingerprint-CWa-soYD.mjs +78 -0
  155. package/dist/doctor-q6oA64hm.mjs +195 -0
  156. package/dist/find-tui-binary-CRpbYXLC.mjs +52 -0
  157. package/dist/git-Czo96ymM.mjs +789 -0
  158. package/dist/git-DaP_1rVy.mjs +2 -0
  159. package/dist/github-C9GLNak7.mjs +2 -0
  160. package/dist/github-HfIlIS-J.mjs +525 -0
  161. package/dist/mcp-7eXATtez.mjs +1334 -0
  162. package/dist/mcp-BBk8AIO4.mjs +1334 -0
  163. package/dist/mcp-BJxrW9dS.mjs +1334 -0
  164. package/dist/mcp-BgZO-iXI.mjs +1047 -0
  165. package/dist/mcp-BhT5GgYF.mjs +1334 -0
  166. package/dist/mcp-BsGdNA88.mjs +1334 -0
  167. package/dist/mcp-BxBm3d5l.mjs +1047 -0
  168. package/dist/mcp-Ca9QAlJ0.mjs +1334 -0
  169. package/dist/mcp-CphIUJUg.mjs +1047 -0
  170. package/dist/mcp-eor39qmD.mjs +1352 -0
  171. package/dist/plan-format-BQyx--mp.mjs +144 -0
  172. package/dist/plan-format-Bi46QS0m.mjs +132 -0
  173. package/dist/review-export-BoV0884M.mjs +66 -0
  174. package/dist/review-export-CKTI5Od_.mjs +66 -0
  175. package/dist/review-export-DjxKBeIZ.mjs +66 -0
  176. package/dist/server-AisDKkpW.mjs +3227 -0
  177. package/dist/server-BUsRhL1B.mjs +3283 -0
  178. package/dist/server-BZBFquG8.mjs +3875 -0
  179. package/dist/server-Bc2m9R5-.mjs +3263 -0
  180. package/dist/server-Bmt57wV5.mjs +3875 -0
  181. package/dist/server-C105rwQw.mjs +3283 -0
  182. package/dist/server-D0Ni8PKP.mjs +3252 -0
  183. package/dist/server-D2xEbakj.mjs +3874 -0
  184. package/dist/server-TOElOuNP.mjs +3258 -0
  185. package/dist/server-lock-DaSx7DjW.mjs +1049 -0
  186. package/dist/server-lock-uqSdDYeR.mjs +153 -0
  187. package/dist/settings-BVnzGFL7.mjs +478 -0
  188. package/dist/update-check-fe0tR8tx.mjs +129 -0
  189. package/package.json +1 -1
  190. package/dist/client/assets/channel-h7e2aSOW.js +0 -1
  191. package/dist/client/assets/chunk-QZHKN3VN-HzjMGBiA.js +0 -1
  192. package/dist/client/assets/chunk-WU5MYG2G-D_yZ0nqc.js +0 -1
  193. package/dist/client/assets/classDiagram-4FO5ZUOK-eToeXGJV.js +0 -1
  194. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-D_EfzX89.js +0 -1
  195. package/dist/client/assets/index-Ct9zZxJ5.css +0 -1
  196. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Brvg0TWH.js +0 -1
@@ -0,0 +1,153 @@
1
+ import { d as getProjectStorageDir, p as getRepoRoot } from "./git-Czo96ymM.mjs";
2
+ import { join } from "node:path";
3
+ import { mkdirSync, readFileSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
4
+ //#region src/lib/server-lock.ts
5
+ const STARTUP_LEASE_STALE_MS = 3e4;
6
+ /**
7
+ * Produce a stable comparison key for the diff a server displays. Runtime-only
8
+ * web options are excluded so `--port`/`--no-open` do not create false scope
9
+ * mismatches between a user-started session and an MCP request.
10
+ */
11
+ function diffScopeKey(options) {
12
+ const { port: _port, host: _host, noOpen: _noOpen, help: _help, version: _version, outputMode: _outputMode, tui: _tui, gpu: _gpu, noExtDiff: _noExtDiff, ...scope } = options;
13
+ return JSON.stringify(scope);
14
+ }
15
+ function lockPath(repoRoot) {
16
+ return join(getProjectStorageDir(repoRoot), "server.json");
17
+ }
18
+ function writeServerLock(lock) {
19
+ const path = lockPath(lock.repoRoot);
20
+ mkdirSync(join(path, ".."), { recursive: true });
21
+ writeFileSync(path, JSON.stringify(lock, null, 2), "utf-8");
22
+ }
23
+ function readServerLock(repoRoot) {
24
+ try {
25
+ const raw = readFileSync(lockPath(repoRoot), "utf-8");
26
+ const lock = JSON.parse(raw);
27
+ if (typeof lock.port !== "number" || typeof lock.pid !== "number") return null;
28
+ return lock;
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+ /**
34
+ * True if the process named by the lock is still alive and the lock belongs to
35
+ * this repo. `process.kill(pid, 0)` sends no signal — it just probes existence.
36
+ */
37
+ function isLockAlive(lock, expectedRepoRoot) {
38
+ try {
39
+ process.kill(lock.pid, 0);
40
+ } catch {
41
+ return false;
42
+ }
43
+ try {
44
+ return lock.repoRoot === (expectedRepoRoot ?? getRepoRoot());
45
+ } catch {
46
+ return true;
47
+ }
48
+ }
49
+ function removeServerLock(repoRoot) {
50
+ try {
51
+ rmSync(lockPath(repoRoot), { force: true });
52
+ } catch {}
53
+ }
54
+ /** Remove server.json only while holding the startup lease and only if exact ownership still matches. */
55
+ function removeServerLockIfOwned(repoRoot, pid, ownerId) {
56
+ const cleanupLease = acquireServerStartupLease(repoRoot, `cleanup-${ownerId}`);
57
+ if (!cleanupLease) return false;
58
+ try {
59
+ const lock = readServerLock(repoRoot);
60
+ if (!lock || lock.pid !== pid || lock.ownerId !== ownerId) return false;
61
+ removeServerLock(repoRoot);
62
+ return true;
63
+ } finally {
64
+ cleanupLease.release();
65
+ }
66
+ }
67
+ /**
68
+ * Atomically reserve server startup for one repository. The exclusive-create
69
+ * directory is the cross-process serialization point; callers must re-read
70
+ * server.json after acquiring it because another process may have completed
71
+ * startup between their first lock check and this lease acquisition.
72
+ */
73
+ function acquireServerStartupLease(repoRoot, ownerId, now = Date.now()) {
74
+ const path = join(getProjectStorageDir(repoRoot), "server-startup.lock");
75
+ const recordPath = join(path, "lease.json");
76
+ const ownerMarkerPath = join(path, `owner-${encodeURIComponent(ownerId)}`);
77
+ mkdirSync(join(path, ".."), { recursive: true });
78
+ const tryCreate = () => {
79
+ try {
80
+ mkdirSync(path, {
81
+ recursive: false,
82
+ mode: 448
83
+ });
84
+ } catch (error) {
85
+ if (error?.code === "EEXIST") return false;
86
+ throw error;
87
+ }
88
+ try {
89
+ writeFileSync(recordPath, JSON.stringify({
90
+ ownerId,
91
+ createdAt: now,
92
+ pid: process.pid
93
+ }), "utf-8");
94
+ writeFileSync(ownerMarkerPath, "", "utf-8");
95
+ } catch (error) {
96
+ rmSync(path, {
97
+ recursive: true,
98
+ force: true
99
+ });
100
+ throw error;
101
+ }
102
+ return true;
103
+ };
104
+ if (!tryCreate()) {
105
+ let reclaim = false;
106
+ let acquiredAfterRace = false;
107
+ try {
108
+ const record = JSON.parse(readFileSync(recordPath, "utf-8"));
109
+ if (typeof record.createdAt !== "number" || typeof record.pid !== "number") throw new Error("Malformed startup lease");
110
+ let ownerIsAlive = true;
111
+ try {
112
+ process.kill(record.pid, 0);
113
+ } catch (error) {
114
+ ownerIsAlive = error?.code !== "ESRCH";
115
+ }
116
+ reclaim = now - record.createdAt > STARTUP_LEASE_STALE_MS && !ownerIsAlive;
117
+ if (!reclaim) return null;
118
+ } catch {
119
+ try {
120
+ reclaim = now - statSync(path).mtimeMs > STARTUP_LEASE_STALE_MS;
121
+ } catch (statError) {
122
+ if (statError?.code !== "ENOENT") return null;
123
+ if (!tryCreate()) return null;
124
+ acquiredAfterRace = true;
125
+ }
126
+ if (!reclaim && !acquiredAfterRace) return null;
127
+ }
128
+ if (reclaim && !acquiredAfterRace) {
129
+ const stalePath = `${path}.stale-${process.pid}-${now}`;
130
+ renameSync(path, stalePath);
131
+ rmSync(stalePath, {
132
+ recursive: true,
133
+ force: true
134
+ });
135
+ }
136
+ if (!acquiredAfterRace && !tryCreate()) return null;
137
+ }
138
+ let released = false;
139
+ return {
140
+ ownerId,
141
+ release() {
142
+ if (released) return;
143
+ released = true;
144
+ try {
145
+ unlinkSync(ownerMarkerPath);
146
+ unlinkSync(recordPath);
147
+ rmdirSync(path);
148
+ } catch {}
149
+ }
150
+ };
151
+ }
152
+ //#endregion
153
+ export { removeServerLock as a, readServerLock as i, diffScopeKey as n, removeServerLockIfOwned as o, isLockAlive as r, writeServerLock as s, acquireServerStartupLease as t };
@@ -0,0 +1,478 @@
1
+ import { d as getProjectStorageDir, p as getRepoRoot } from "./git-Czo96ymM.mjs";
2
+ import { join } from "node:path";
3
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ //#region src/lib/symbols.ts
6
+ const SYMBOL_PATTERNS = [
7
+ {
8
+ pattern: /^\s*(?:export\s+)?(?:async\s+)?function\s+(\w[\w$]*)\s*[<(]/i,
9
+ kind: "function",
10
+ nameGroup: 1
11
+ },
12
+ {
13
+ pattern: /^\s*(?:export\s+)?(?:const|let|var)\s+(\w[\w$]*)\s*=\s*(?:async\s*)?\(/i,
14
+ kind: "function",
15
+ nameGroup: 1
16
+ },
17
+ {
18
+ pattern: /^\s*(?:export\s+)?(?:const|let|var)\s+(\w[\w$]*)\s*=\s*(?:async\s+)?function/i,
19
+ kind: "function",
20
+ nameGroup: 1
21
+ },
22
+ {
23
+ pattern: /^\s*(?:export\s+)?class\s+(\w[\w$]*)/i,
24
+ kind: "class",
25
+ nameGroup: 1
26
+ },
27
+ {
28
+ pattern: /^\s*(?:export\s+)?interface\s+(\w[\w$]*)/i,
29
+ kind: "interface",
30
+ nameGroup: 1
31
+ },
32
+ {
33
+ pattern: /^\s*(?:export\s+)?type\s+(\w[\w$]*)\s*=/i,
34
+ kind: "type",
35
+ nameGroup: 1
36
+ },
37
+ {
38
+ pattern: /^\s*(?:export\s+)?enum\s+(\w[\w$]*)/i,
39
+ kind: "enum",
40
+ nameGroup: 1
41
+ },
42
+ {
43
+ pattern: /^\s*(?:export\s+)?(?:const|let|var)\s+(\w[\w$]*)\s*=/i,
44
+ kind: "variable",
45
+ nameGroup: 1
46
+ },
47
+ {
48
+ pattern: /^\s*def\s+(\w[\w$]*)\s*\(/i,
49
+ kind: "function",
50
+ nameGroup: 1
51
+ },
52
+ {
53
+ pattern: /^\s*(?:pub(?:\s*\(\w+\))?\s+)?fn\s+(\w[\w$]*)\s*[<(]/i,
54
+ kind: "function",
55
+ nameGroup: 1
56
+ },
57
+ {
58
+ pattern: /^\s*(?:pub(?:\s*\(\w+\))?\s+)?struct\s+(\w[\w$]*)/i,
59
+ kind: "struct",
60
+ nameGroup: 1
61
+ },
62
+ {
63
+ pattern: /^\s*(?:pub(?:\s*\(\w+\))?\s+)?enum\s+(\w[\w$]*)/i,
64
+ kind: "enum",
65
+ nameGroup: 1
66
+ },
67
+ {
68
+ pattern: /^\s*(?:pub(?:\s*\(\w+\))?\s+)?impl\s+(\w[\w$]*)/i,
69
+ kind: "impl",
70
+ nameGroup: 1
71
+ },
72
+ {
73
+ pattern: /^\s*(?:pub(?:\s*\(\w+\))?\s+)?trait\s+(\w[\w$]*)/i,
74
+ kind: "trait",
75
+ nameGroup: 1
76
+ },
77
+ {
78
+ pattern: /^\s*func\s+(\w[\w$]*)\s*\(/i,
79
+ kind: "function",
80
+ nameGroup: 1
81
+ },
82
+ {
83
+ pattern: /^\s*(?:func\s+)?\((\w[\w$]*)\s+\*?\w+\)\s+(\w[\w$]*)\s*\(/i,
84
+ kind: "method",
85
+ nameGroup: 2
86
+ }
87
+ ];
88
+ /**
89
+ * Returns the symbol defined on this line (name + kind), or null if the line
90
+ * is not a recognizable definition. The first matching pattern wins.
91
+ */
92
+ function classifySymbolLine(line) {
93
+ for (const { pattern, kind, nameGroup } of SYMBOL_PATTERNS) {
94
+ const match = pattern.exec(line);
95
+ if (match) {
96
+ const name = match[nameGroup];
97
+ if (name) return {
98
+ name,
99
+ kind
100
+ };
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+ //#endregion
106
+ //#region src/lib/search.ts
107
+ /**
108
+ * Server-side code search powered by `@ff-labs/fff-node` — a native (Rust)
109
+ * fuzzy file finder + live grep. Because it is a native Node addon it cannot
110
+ * run in the browser, so the React client calls the `/api/search` endpoint and
111
+ * this module owns the single long-lived `FileFinder` instance for the repo.
112
+ *
113
+ * Design notes:
114
+ * - The native binary is loaded lazily via dynamic import inside `init()` so a
115
+ * missing/incompatible platform binary degrades gracefully (search returns
116
+ * an error) instead of crashing the whole server at module load.
117
+ * - One finder per process, keyed implicitly to the repo root. Its frecency /
118
+ * history databases live under the project's `~/.diffing/<repo>` storage dir
119
+ * so we never write stray files into the user's repository.
120
+ * - fff runs its own file-system watcher, so the index stays fresh as the
121
+ * working tree changes during a review.
122
+ * - "Symbols" search is plain grep for the identifier, then each hit line is
123
+ * classified with the shared {@link classifySymbolLine} patterns — fff 0.8.x
124
+ * does not populate `isDefinition`, so we do the classification ourselves.
125
+ */
126
+ const DEFAULT_LIMIT = 60;
127
+ const MAX_LIMIT = 200;
128
+ /** Internal page size when no path filter — generous so diff-first client
129
+ * ranking has enough to work with. */
130
+ const SCAN_PAGE = 200;
131
+ /** Larger page size when filtering to a path set, so the post-filter is
132
+ * effectively complete for normal-sized diffs/repos. */
133
+ const SCAN_PAGE_FILTERED = 1e3;
134
+ let finder = null;
135
+ let initPromise = null;
136
+ let initError = null;
137
+ let shutdownHooked = false;
138
+ function clampLimit(limit) {
139
+ if (!limit || limit < 1) return DEFAULT_LIMIT;
140
+ return Math.min(limit, MAX_LIMIT);
141
+ }
142
+ async function init() {
143
+ try {
144
+ const { FileFinder } = await import("@ff-labs/fff-node");
145
+ const root = getRepoRoot();
146
+ const dbDir = join(getProjectStorageDir(root), "fff");
147
+ mkdirSync(dbDir, { recursive: true });
148
+ const created = FileFinder.create({
149
+ basePath: root,
150
+ frecencyDbPath: join(dbDir, "frecency.db"),
151
+ historyDbPath: join(dbDir, "history.db"),
152
+ logLevel: "error"
153
+ });
154
+ if (!created.ok) {
155
+ initError = created.error;
156
+ return null;
157
+ }
158
+ const f = created.value;
159
+ if (!shutdownHooked) {
160
+ shutdownHooked = true;
161
+ process.once("exit", () => {
162
+ try {
163
+ f.destroy();
164
+ } catch {}
165
+ });
166
+ }
167
+ await f.waitForScan(8e3);
168
+ finder = f;
169
+ return f;
170
+ } catch (err) {
171
+ initError = err?.message ?? String(err);
172
+ return null;
173
+ }
174
+ }
175
+ async function getFinder() {
176
+ if (finder) return finder;
177
+ if (initError) return null;
178
+ if (!initPromise) initPromise = init();
179
+ return initPromise;
180
+ }
181
+ function isIndexing(f) {
182
+ try {
183
+ return f.isScanning();
184
+ } catch {
185
+ return false;
186
+ }
187
+ }
188
+ async function searchFiles(query, opts = {}) {
189
+ const f = await getFinder();
190
+ if (!f) return {
191
+ scope: "files",
192
+ items: [],
193
+ total: 0,
194
+ indexing: false,
195
+ error: initError ?? "Search unavailable"
196
+ };
197
+ const limit = clampLimit(opts.limit);
198
+ const pathSet = opts.paths ? new Set(opts.paths) : null;
199
+ const pageSize = pathSet ? SCAN_PAGE_FILTERED : Math.max(SCAN_PAGE, limit);
200
+ const res = f.fileSearch(query, { pageSize });
201
+ if (!res.ok) return {
202
+ scope: "files",
203
+ items: [],
204
+ total: 0,
205
+ indexing: isIndexing(f),
206
+ error: res.error
207
+ };
208
+ const items = [];
209
+ const { items: raw, scores } = res.value;
210
+ for (let i = 0; i < raw.length && items.length < limit; i++) {
211
+ const it = raw[i];
212
+ if (pathSet && !pathSet.has(it.relativePath)) continue;
213
+ const score = scores[i];
214
+ items.push({
215
+ path: it.relativePath,
216
+ fileName: it.fileName,
217
+ gitStatus: it.gitStatus,
218
+ matchType: score?.matchType ?? "",
219
+ exact: !!score?.exactMatch
220
+ });
221
+ }
222
+ return {
223
+ scope: "files",
224
+ items,
225
+ total: res.value.totalMatched,
226
+ indexing: isIndexing(f)
227
+ };
228
+ }
229
+ async function searchContent(query, opts = {}) {
230
+ const f = await getFinder();
231
+ if (!f) return {
232
+ scope: "text",
233
+ items: [],
234
+ total: 0,
235
+ indexing: false,
236
+ error: initError ?? "Search unavailable"
237
+ };
238
+ if (!query) return {
239
+ scope: "text",
240
+ items: [],
241
+ total: 0,
242
+ indexing: isIndexing(f)
243
+ };
244
+ const limit = clampLimit(opts.limit);
245
+ const pathSet = opts.paths ? new Set(opts.paths) : null;
246
+ const pageSize = pathSet ? SCAN_PAGE_FILTERED : Math.max(SCAN_PAGE, limit);
247
+ const res = f.grep(query, {
248
+ mode: opts.regex ? "regex" : "plain",
249
+ smartCase: true,
250
+ beforeContext: 0,
251
+ afterContext: 0,
252
+ pageSize
253
+ });
254
+ if (!res.ok) return {
255
+ scope: "text",
256
+ items: [],
257
+ total: 0,
258
+ indexing: isIndexing(f),
259
+ error: res.error
260
+ };
261
+ const items = [];
262
+ for (const m of res.value.items) {
263
+ if (items.length >= limit) break;
264
+ if (pathSet && !pathSet.has(m.relativePath)) continue;
265
+ items.push(toContentHit(m));
266
+ }
267
+ return {
268
+ scope: "text",
269
+ items,
270
+ total: res.value.totalMatched,
271
+ indexing: isIndexing(f),
272
+ regexError: res.value.regexFallbackError || void 0
273
+ };
274
+ }
275
+ function toContentHit(m) {
276
+ return {
277
+ path: m.relativePath,
278
+ fileName: m.fileName,
279
+ line: m.lineNumber,
280
+ col: m.col,
281
+ content: m.lineContent,
282
+ matchRanges: m.matchRanges ?? [],
283
+ gitStatus: m.gitStatus
284
+ };
285
+ }
286
+ async function searchSymbols(query, opts = {}) {
287
+ const f = await getFinder();
288
+ if (!f) return {
289
+ scope: "symbols",
290
+ items: [],
291
+ total: 0,
292
+ indexing: false,
293
+ error: initError ?? "Search unavailable"
294
+ };
295
+ if (!query) return {
296
+ scope: "symbols",
297
+ items: [],
298
+ total: 0,
299
+ indexing: isIndexing(f)
300
+ };
301
+ const limit = clampLimit(opts.limit);
302
+ const pathSet = opts.paths ? new Set(opts.paths) : null;
303
+ const pageSize = pathSet ? SCAN_PAGE_FILTERED : Math.max(SCAN_PAGE_FILTERED, limit * 8);
304
+ const res = f.grep(query, {
305
+ mode: "plain",
306
+ smartCase: true,
307
+ beforeContext: 0,
308
+ afterContext: 0,
309
+ pageSize
310
+ });
311
+ if (!res.ok) return {
312
+ scope: "symbols",
313
+ items: [],
314
+ total: 0,
315
+ indexing: isIndexing(f),
316
+ error: res.error
317
+ };
318
+ const items = [];
319
+ const ql = query.toLowerCase();
320
+ const seen = /* @__PURE__ */ new Set();
321
+ for (const m of res.value.items) {
322
+ if (items.length >= limit) break;
323
+ if (pathSet && !pathSet.has(m.relativePath)) continue;
324
+ const sym = classifySymbolLine(m.lineContent);
325
+ if (!sym) continue;
326
+ if (!sym.name.toLowerCase().includes(ql)) continue;
327
+ const key = `${m.relativePath}:${m.lineNumber}:${sym.name}`;
328
+ if (seen.has(key)) continue;
329
+ seen.add(key);
330
+ const nameIdx = m.lineContent.indexOf(sym.name);
331
+ const matchRanges = nameIdx >= 0 ? [[nameIdx, nameIdx + sym.name.length]] : m.matchRanges ?? [];
332
+ items.push({
333
+ name: sym.name,
334
+ kind: sym.kind,
335
+ path: m.relativePath,
336
+ fileName: m.fileName,
337
+ line: m.lineNumber,
338
+ content: m.lineContent,
339
+ matchRanges,
340
+ gitStatus: m.gitStatus
341
+ });
342
+ }
343
+ return {
344
+ scope: "symbols",
345
+ items,
346
+ total: items.length,
347
+ indexing: isIndexing(f)
348
+ };
349
+ }
350
+ async function searchAll(query, opts = {}) {
351
+ const limit = clampLimit(opts.limit);
352
+ const [filesRes, contentRes, symbolsRes] = await Promise.all([
353
+ searchFiles(query, {
354
+ ...opts,
355
+ limit
356
+ }),
357
+ searchContent(query, {
358
+ ...opts,
359
+ limit
360
+ }),
361
+ searchSymbols(query, {
362
+ ...opts,
363
+ limit
364
+ })
365
+ ]);
366
+ const items = [];
367
+ for (const hit of filesRes.items) items.push({
368
+ kind: "file",
369
+ hit
370
+ });
371
+ for (const hit of symbolsRes.items) items.push({
372
+ kind: "symbol",
373
+ hit
374
+ });
375
+ for (const hit of contentRes.items) items.push({
376
+ kind: "text",
377
+ hit
378
+ });
379
+ const total = (filesRes.total || 0) + (contentRes.total || 0) + (symbolsRes.total || 0);
380
+ const indexing = filesRes.indexing || contentRes.indexing || symbolsRes.indexing;
381
+ const error = filesRes.error || contentRes.error || symbolsRes.error;
382
+ const regexError = contentRes.regexError;
383
+ return {
384
+ scope: "all",
385
+ items: items.slice(0, limit),
386
+ total,
387
+ indexing,
388
+ error,
389
+ regexError
390
+ };
391
+ }
392
+ async function getSearchStatus() {
393
+ const f = await getFinder();
394
+ if (!f) return {
395
+ available: false,
396
+ indexing: false,
397
+ indexedFiles: 0,
398
+ error: initError ?? "Search unavailable"
399
+ };
400
+ let indexedFiles = 0;
401
+ let indexing = false;
402
+ try {
403
+ const prog = f.getScanProgress();
404
+ if (prog.ok) {
405
+ indexedFiles = prog.value.scannedFilesCount;
406
+ indexing = prog.value.isScanning;
407
+ }
408
+ } catch {}
409
+ return {
410
+ available: true,
411
+ indexing,
412
+ indexedFiles
413
+ };
414
+ }
415
+ /** Record that the user opened `path` from a query, improving fff's frecency
416
+ * ranking for future searches. Best-effort, never throws. */
417
+ async function trackSelection(query, path) {
418
+ const f = await getFinder();
419
+ if (!f) return;
420
+ try {
421
+ f.trackQuery(query, path);
422
+ } catch {}
423
+ }
424
+ //#endregion
425
+ //#region src/lib/settings.ts
426
+ const CONFIG_DIR = join(homedir(), ".config", "diffing");
427
+ const SETTINGS_FILE = join(CONFIG_DIR, "settings.json");
428
+ const DEFAULTS = {
429
+ staged: true,
430
+ untracked: true,
431
+ diffStyle: "split",
432
+ defaultTabSize: 4,
433
+ theme: "nord",
434
+ editorIDE: "default",
435
+ lineDiffType: "word",
436
+ lineWrap: false,
437
+ diffIndicators: "classic",
438
+ showLineNumbers: true,
439
+ hunkSeparators: "line-info",
440
+ lineHoverHighlight: "both",
441
+ fontSize: 13,
442
+ expandContextByDefault: false,
443
+ collapsedContextThreshold: 10,
444
+ expansionLineCount: 20,
445
+ haptics: true,
446
+ sounds: true,
447
+ uiFont: null,
448
+ monoFont: null,
449
+ density: "comfortable",
450
+ autoCollapseLineThreshold: 400,
451
+ requireViewAllBeforeSend: false,
452
+ showStatusBar: true,
453
+ savedReplies: [],
454
+ ignoreSpaceChange: false,
455
+ ignoreAllSpace: false
456
+ };
457
+ function loadSettings() {
458
+ try {
459
+ const data = readFileSync(SETTINGS_FILE, "utf-8");
460
+ return {
461
+ ...DEFAULTS,
462
+ ...JSON.parse(data)
463
+ };
464
+ } catch {
465
+ return { ...DEFAULTS };
466
+ }
467
+ }
468
+ function saveSettings(settings) {
469
+ const merged = {
470
+ ...loadSettings(),
471
+ ...settings
472
+ };
473
+ mkdirSync(CONFIG_DIR, { recursive: true });
474
+ writeFileSync(SETTINGS_FILE, JSON.stringify(merged, null, 2));
475
+ return merged;
476
+ }
477
+ //#endregion
478
+ export { searchContent as a, trackSelection as c, searchAll as i, saveSettings as n, searchFiles as o, getSearchStatus as r, searchSymbols as s, loadSettings as t };