diffing 0.18.1 → 0.18.3

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 (186) hide show
  1. package/dist/{cli-agent-Cf6SAnH4.mjs → cli-agent-DV5ZhMX1.mjs} +209 -25
  2. package/dist/{cli-gh-2NEumKQB.mjs → cli-gh-0hY7pvwJ.mjs} +2 -2
  3. package/dist/cli.mjs +13 -12
  4. package/dist/client/assets/{angular-html-BV3gPi7L.js → angular-html-DgLkj6cM.js} +1 -1
  5. package/dist/client/assets/{angular-ts-DuuSfKJK.js → angular-ts-S7IdfqOh.js} +1 -1
  6. package/dist/client/assets/{apl-BMc69egk.js → apl-dC851k0e.js} +1 -1
  7. package/dist/client/assets/{arc-CEC1cz1S.js → arc-v8u_6qG_.js} +1 -1
  8. package/dist/client/assets/{architectureDiagram-3BPJPVTR-BmJ5OHgd.js → architectureDiagram-3BPJPVTR-CtfNNxPZ.js} +1 -1
  9. package/dist/client/assets/{astro-Bg_CYwmB.js → astro-DgnNzfoZ.js} +1 -1
  10. package/dist/client/assets/{blade-Mca0XoRC.js → blade-By7nZeyV.js} +1 -1
  11. package/dist/client/assets/{blockDiagram-GPEHLZMM--XrThRLW.js → blockDiagram-GPEHLZMM-cfmYosKW.js} +1 -1
  12. package/dist/client/assets/{c-B2Uqi6oI.js → c-C6I1CPq_.js} +1 -1
  13. package/dist/client/assets/{c4Diagram-AAUBKEIU-W8w5oiRi.js → c4Diagram-AAUBKEIU-L52oIoFq.js} +1 -1
  14. package/dist/client/assets/channel-B8Y5aSHX.js +1 -0
  15. package/dist/client/assets/{chunk-2J33WTMH-Bpr3MqKf.js → chunk-2J33WTMH-Dpy-98nI.js} +1 -1
  16. package/dist/client/assets/{chunk-3OPIFGDE-DdMV_3DM.js → chunk-3OPIFGDE-DsFmltuQ.js} +1 -1
  17. package/dist/client/assets/{chunk-4BX2VUAB-BkfeLLsz.js → chunk-4BX2VUAB-B1PXeD30.js} +1 -1
  18. package/dist/client/assets/{chunk-55IACEB6-pCwXbxR0.js → chunk-55IACEB6-BkfaPY5R.js} +1 -1
  19. package/dist/client/assets/{chunk-5ZQYHXKU-BGFzCgvs.js → chunk-5ZQYHXKU-kHj4q0HC.js} +1 -1
  20. package/dist/client/assets/{chunk-727SXJPM-DU7oVWfJ.js → chunk-727SXJPM-DYndNEF7.js} +1 -1
  21. package/dist/client/assets/{chunk-AQP2D5EJ-DaVYG1qm.js → chunk-AQP2D5EJ-CGgu3cJd.js} +1 -1
  22. package/dist/client/assets/{chunk-BSJP7CBP-DAvnzgjY.js → chunk-BSJP7CBP-B85q9YnN.js} +1 -1
  23. package/dist/client/assets/{chunk-CSCIHK7Q-D6ZDvMsi.js → chunk-CSCIHK7Q-D4izdgab.js} +1 -1
  24. package/dist/client/assets/{chunk-FMBD7UC4-BFDCYCqF.js → chunk-FMBD7UC4-CwH6kGrM.js} +1 -1
  25. package/dist/client/assets/{chunk-KSCS5N6A-BmD6XY52.js → chunk-KSCS5N6A-D6WNG6Qv.js} +1 -1
  26. package/dist/client/assets/{chunk-L5ZTLDWV-Dl9iUIBf.js → chunk-L5ZTLDWV-BFzgANjL.js} +1 -1
  27. package/dist/client/assets/{chunk-LZXEDZCA-rOOJDxdB.js → chunk-LZXEDZCA-CXVdeXQA.js} +2 -2
  28. package/dist/client/assets/{chunk-ND2GUHAM-DBDu-JeD.js → chunk-ND2GUHAM-nez7hw5f.js} +1 -1
  29. package/dist/client/assets/{chunk-NZK2D7GU-CRyk4F3R.js → chunk-NZK2D7GU-DQxrGm1r.js} +1 -1
  30. package/dist/client/assets/{chunk-O5CBEL6O-DcKB07iP.js → chunk-O5CBEL6O-BAYDbzO8.js} +1 -1
  31. package/dist/client/assets/chunk-QZHKN3VN-DPZNzASS.js +1 -0
  32. package/dist/client/assets/chunk-WU5MYG2G-DOKX6dPU.js +1 -0
  33. package/dist/client/assets/{chunk-XPW4576I-WuzWMSrp.js → chunk-XPW4576I-DdtioiQK.js} +1 -1
  34. package/dist/client/assets/classDiagram-4FO5ZUOK-CkitIGos.js +1 -0
  35. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BD7zcbFa.js +1 -0
  36. package/dist/client/assets/{cobol-oQ1i-YtO.js → cobol-DVRUz845.js} +1 -1
  37. package/dist/client/assets/{coffee-s9XlmHbB.js → coffee-ywp-ztcj.js} +1 -1
  38. package/dist/client/assets/{cose-bilkent-S5V4N54A-CymUm7fn.js → cose-bilkent-S5V4N54A-YAV-XlD-.js} +1 -1
  39. package/dist/client/assets/{cpp-D60LxTox.js → cpp-lGJg5yP8.js} +1 -1
  40. package/dist/client/assets/{crystal-CHrCczDb.js → crystal-C255DQNq.js} +1 -1
  41. package/dist/client/assets/{css-1PQO32Hw.js → css-D0fueLBG.js} +1 -1
  42. package/dist/client/assets/{dagre-BM42HDAG-DOHZc0tO.js → dagre-BM42HDAG-C-nRRJ_h.js} +1 -1
  43. package/dist/client/assets/{diagram-2AECGRRQ-D8O2cleS.js → diagram-2AECGRRQ-Cy08G2GT.js} +1 -1
  44. package/dist/client/assets/{diagram-5GNKFQAL-CO-Ej4UD.js → diagram-5GNKFQAL-B5KxlMfb.js} +1 -1
  45. package/dist/client/assets/{diagram-KO2AKTUF-DCUr4wzS.js → diagram-KO2AKTUF-BtlklsuV.js} +1 -1
  46. package/dist/client/assets/{diagram-LMA3HP47-D9HnA0Qg.js → diagram-LMA3HP47-DwJhvsVf.js} +1 -1
  47. package/dist/client/assets/{diagram-OG6HWLK6-BxAitO2n.js → diagram-OG6HWLK6-e6mkflzE.js} +1 -1
  48. package/dist/client/assets/{dist-N-ROBnkL.js → dist-8nkFH_K6.js} +1 -1
  49. package/dist/client/assets/{edge-Ck54Fn7y.js → edge-Cy4Xn6qQ.js} +1 -1
  50. package/dist/client/assets/{edit-B8RwtrDs.js → edit-C3wvBNC7.js} +1 -1
  51. package/dist/client/assets/{elixir-xXxdfsfU.js → elixir-CHz66mgQ.js} +1 -1
  52. package/dist/client/assets/{elm-g0vLRWbF.js → elm-CuSKJX5P.js} +1 -1
  53. package/dist/client/assets/{erDiagram-TEJ5UH35-1ZMg7g98.js → erDiagram-TEJ5UH35-U7lAAznz.js} +1 -1
  54. package/dist/client/assets/{erb-CGFnxOST.js → erb-BbtQ1hep.js} +1 -1
  55. package/dist/client/assets/{flowDiagram-I6XJVG4X-D5u9lhJ-.js → flowDiagram-I6XJVG4X-jIylSBs9.js} +1 -1
  56. package/dist/client/assets/{ganttDiagram-6RSMTGT7-BbVKEzs8.js → ganttDiagram-6RSMTGT7-DW3d8a36.js} +1 -1
  57. package/dist/client/assets/{git-rebase-B3s6bE5_.js → git-rebase-R-XkmJUY.js} +1 -1
  58. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-sAottiiz.js → gitGraphDiagram-PVQCEYII-BHzkJu8s.js} +1 -1
  59. package/dist/client/assets/{glimmer-js-C2Xff18_.js → glimmer-js-C_yhese4.js} +1 -1
  60. package/dist/client/assets/{glimmer-ts-CRBS8ftd.js → glimmer-ts-DKD-Bn0M.js} +1 -1
  61. package/dist/client/assets/{glsl-aOY_t6dG.js → glsl-CJScdeHB.js} +1 -1
  62. package/dist/client/assets/{graphql-Cucpdpc6.js → graphql-BUP7lnbX.js} +1 -1
  63. package/dist/client/assets/{hack-Bw0KkVza.js → hack-Uu2tSM5_.js} +1 -1
  64. package/dist/client/assets/{haml-DnyaPoa2.js → haml-BKKDeeb3.js} +1 -1
  65. package/dist/client/assets/{handlebars-AedEof-m.js → handlebars-DXZJVvuj.js} +1 -1
  66. package/dist/client/assets/{html-Bu5h_Kgf.js → html-BNpfcyGi.js} +1 -1
  67. package/dist/client/assets/{html-derivative-CNiVLld1.js → html-derivative-D2Zj9RHU.js} +1 -1
  68. package/dist/client/assets/{http-B9-oOYPw.js → http-BDjsRgLN.js} +1 -1
  69. package/dist/client/assets/{hurl-Bo6NUnnd.js → hurl-D_YxHbnH.js} +1 -1
  70. package/dist/client/assets/{index-BkwV-g6J.js → index-CpN9wq1L.js} +142 -142
  71. package/dist/client/assets/{index-C73yr1tm.css → index-EoTRSuBL.css} +1 -1
  72. package/dist/client/assets/{infoDiagram-5YYISTIA-CfVELF4R.js → infoDiagram-5YYISTIA-oGpzG0Af.js} +1 -1
  73. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-DztjIGfb.js → ishikawaDiagram-YF4QCWOH-066RGSaQ.js} +1 -1
  74. package/dist/client/assets/{java-JJ3dl_jz.js → java-DeOKsdfJ.js} +1 -1
  75. package/dist/client/assets/{javascript-QgUuQNTw.js → javascript-oM5JhL2b.js} +1 -1
  76. package/dist/client/assets/{jinja-GbeEm2-u.js → jinja-CpW87uSV.js} +1 -1
  77. package/dist/client/assets/{jison-BXZbHsO3.js → jison-Du6rTe9r.js} +1 -1
  78. package/dist/client/assets/{journeyDiagram-JHISSGLW-Lg8CkxeI.js → journeyDiagram-JHISSGLW-BB4Aymt4.js} +1 -1
  79. package/dist/client/assets/{json-D02YvQSa.js → json-Cl5vEJGM.js} +1 -1
  80. package/dist/client/assets/{jsx-eZde6NZS.js → jsx-D3lCkT_f.js} +1 -1
  81. package/dist/client/assets/{julia-CfcuUD-A.js → julia-P1bWdzXQ.js} +1 -1
  82. package/dist/client/assets/{just-hQr8WcQp.js → just-BQHIj4wn.js} +1 -1
  83. package/dist/client/assets/{kanban-definition-UN3LZRKU-BWIeZIf6.js → kanban-definition-UN3LZRKU-CveGMVSw.js} +1 -1
  84. package/dist/client/assets/{latex-BxffZwv_.js → latex-CzeBoguz.js} +1 -1
  85. package/dist/client/assets/{line-Cy1AFxf0.js → line-CjLRf0wl.js} +1 -1
  86. package/dist/client/assets/{linear-Kk5IbGHr.js → linear-GmrB0HMZ.js} +1 -1
  87. package/dist/client/assets/{liquid-BvKnpUyR.js → liquid-BNh96Tca.js} +1 -1
  88. package/dist/client/assets/{lua-Bcds2vY9.js → lua-CNQoqqQP.js} +1 -1
  89. package/dist/client/assets/{marko-CZJQLjYc.js → marko-C_xevFIb.js} +1 -1
  90. package/dist/client/assets/{mdc-BRigtixg.js → mdc-XPBTv6ak.js} +1 -1
  91. package/dist/client/assets/{mermaid-parser.core-Dh2uTVxt.js → mermaid-parser.core-HsXe3oD9.js} +1 -1
  92. package/dist/client/assets/{mermaid.core-CXIGVQ74.js → mermaid.core-BukRjtV2.js} +3 -3
  93. package/dist/client/assets/{mindmap-definition-RKZ34NQL-DQNQJuAB.js → mindmap-definition-RKZ34NQL-VTmEFYO4.js} +1 -1
  94. package/dist/client/assets/{nginx-BI3-LI9U.js → nginx-C6cZlyey.js} +1 -1
  95. package/dist/client/assets/{nim-PEd8ZOME.js → nim-Cd9xainG.js} +1 -1
  96. package/dist/client/assets/{perl-BPTGdP4p.js → perl-DUfKG3XX.js} +1 -1
  97. package/dist/client/assets/{php-C8BKL0yL.js → php-DqMFPxyM.js} +1 -1
  98. package/dist/client/assets/{pieDiagram-4H26LBE5-Dz7gE0t5.js → pieDiagram-4H26LBE5-Bwlobw-Z.js} +1 -1
  99. package/dist/client/assets/{pug-H5z9rtbz.js → pug-GUix1iYH.js} +1 -1
  100. package/dist/client/assets/{qml-EzSpo2aM.js → qml-CqF34de7.js} +1 -1
  101. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-pTnmYdEJ.js → quadrantDiagram-W4KKPZXB-CMujQAS2.js} +1 -1
  102. package/dist/client/assets/{r-VMeUwMIt.js → r-BxeiO8R8.js} +1 -1
  103. package/dist/client/assets/{razor-Ne05zcaj.js → razor-Cu8elnMU.js} +1 -1
  104. package/dist/client/assets/{regexp-i5_jUZAj.js → regexp-DxNPsA-x.js} +1 -1
  105. package/dist/client/assets/{requirementDiagram-4Y6WPE33-GVw3NOxE.js → requirementDiagram-4Y6WPE33-CrrnxwFz.js} +1 -1
  106. package/dist/client/assets/{review-export-B4TOUDUE.js → review-export-Bd6rUYIi.js} +1 -1
  107. package/dist/client/assets/{rst-BZWuei_6.js → rst-2Xzw7kAb.js} +1 -1
  108. package/dist/client/assets/{ruby-CApICFTD.js → ruby-tZt1Nxb8.js} +1 -1
  109. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CwWzwmqj.js → sankeyDiagram-5OEKKPKP-ihur8vgn.js} +1 -1
  110. package/dist/client/assets/{sas-BzEPX6GL.js → sas-DjpUVtXs.js} +1 -1
  111. package/dist/client/assets/{scss-B6Ld2NRm.js → scss-CZffX3V1.js} +1 -1
  112. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-Bz_Q76Kt.js → sequenceDiagram-3UESZ5HK-Bg7fvZIu.js} +1 -1
  113. package/dist/client/assets/{shellscript-KmoqGVVu.js → shellscript-B7bmiOyR.js} +1 -1
  114. package/dist/client/assets/{shellsession-CsSh_KTo.js → shellsession-CS7D1mSi.js} +1 -1
  115. package/dist/client/assets/{soy-CLgOu1If.js → soy-B9Nii6ks.js} +1 -1
  116. package/dist/client/assets/{sql-BdCh56kU.js → sql-CzQPwj1z.js} +1 -1
  117. package/dist/client/assets/{src-D7QQkGEc.js → src-DNsvZq9f.js} +1 -1
  118. package/dist/client/assets/{stata-C3fo8EYS.js → stata-BjQgQlmT.js} +1 -1
  119. package/dist/client/assets/{stateDiagram-AJRCARHV-D0pZtzLu.js → stateDiagram-AJRCARHV-DsgTVCaj.js} +1 -1
  120. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CwRm1eh7.js +1 -0
  121. package/dist/client/assets/{surrealql-qzihsJw_.js → surrealql-D6DnLnn1.js} +1 -1
  122. package/dist/client/assets/{svelte-B4tbn_KI.js → svelte-DRgP3jXp.js} +1 -1
  123. package/dist/client/assets/{templ-Ddev3BvY.js → templ-Cwgx1vj-.js} +1 -1
  124. package/dist/client/assets/{tex-DJLtgNQ6.js → tex-RzUmM9ei.js} +1 -1
  125. package/dist/client/assets/{timeline-definition-PNZ67QCA-CWTWgYi0.js → timeline-definition-PNZ67QCA-rG2Mo1Zq.js} +1 -1
  126. package/dist/client/assets/{ts-tags-qqxigRtr.js → ts-tags-gdlLe8LD.js} +1 -1
  127. package/dist/client/assets/{tsx-Pkf9BaPv.js → tsx-C3y6O1l0.js} +1 -1
  128. package/dist/client/assets/{twig-BiScb10p.js → twig-DLMzUdS6.js} +1 -1
  129. package/dist/client/assets/{typescript-Bhy3UXig.js → typescript-T8PJwqaO.js} +1 -1
  130. package/dist/client/assets/{vennDiagram-CIIHVFJN-DwB89v9I.js → vennDiagram-CIIHVFJN-B-eDjTJG.js} +1 -1
  131. package/dist/client/assets/{vue-CANMWBRv.js → vue-BMIaruOV.js} +1 -1
  132. package/dist/client/assets/{vue-html-DtS6f62r.js → vue-html-D4e4SuHW.js} +1 -1
  133. package/dist/client/assets/{vue-vine-BpQ9kwSB.js → vue-vine-DU2nprLA.js} +1 -1
  134. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DNwwPk4s.js → wardleyDiagram-YWT4CUSO-BkcHoefB.js} +1 -1
  135. package/dist/client/assets/{xml-CD5eIXsm.js → xml-DD7circ2.js} +1 -1
  136. package/dist/client/assets/{xsl-DGNZQAUu.js → xsl-Bu-TQ7Ag.js} +1 -1
  137. package/dist/client/assets/{xychartDiagram-2RQKCTM6-L7M4YSvw.js → xychartDiagram-2RQKCTM6-BGa1-yRO.js} +1 -1
  138. package/dist/client/assets/{yaml-Cm8mwIB7.js → yaml-C12ZIXNt.js} +1 -1
  139. package/dist/client/index.html +2 -2
  140. package/dist/completions-C0ERX2wC.mjs +2 -0
  141. package/dist/design-system-types-QR9xS7o3.mjs +2 -0
  142. package/dist/design-system-types-uiP9W2uj.mjs +75 -0
  143. package/dist/{doctor-BxTaLqAF.mjs → doctor-Dg61wsBc.mjs} +6 -6
  144. package/dist/{doctor-BdDB6p27.mjs → doctor-Y8l1LEDM.mjs} +1 -1
  145. package/dist/{github-CToLsvPu.mjs → github-BtABlwOZ.mjs} +3 -3
  146. package/dist/{github--rpPZ3vK.mjs → github-BuBcy-co.mjs} +1 -1
  147. package/dist/{github-attachments-DEvAeo5P.mjs → github-attachments-Bwr25ItV.mjs} +1 -1
  148. package/dist/{mcp-DoNBucix.mjs → mcp-45VPdWIr.mjs} +136 -17
  149. package/dist/{mockup-format-D4Ln0WxJ.mjs → mockup-lint-BRjhxXSw.mjs} +160 -7
  150. package/dist/native/tui-darwin-arm64/diffing-tui +0 -0
  151. package/dist/native/tui-darwin-x64/diffing-tui +0 -0
  152. package/dist/native/tui-linux-arm64-gnu/diffing-tui +0 -0
  153. package/dist/native/tui-linux-arm64-musl/diffing-tui +0 -0
  154. package/dist/native/tui-linux-x64-gnu/diffing-tui +0 -0
  155. package/dist/native/tui-linux-x64-musl/diffing-tui +0 -0
  156. package/dist/native/tui-win32-x64-msvc/diffing-tui.exe +0 -0
  157. package/dist/{review-export-DoST2oPt.mjs → review-export-BXCeRNjk.mjs} +1 -1
  158. package/dist/{server-BRQdb64n.mjs → server-CH-UPGFR.mjs} +1145 -127
  159. package/dist/{server-lock-CdaUNZyX.mjs → server-lock-ACw2cKDI.mjs} +22 -5
  160. package/dist/{session-conflict-BVgyyerQ.mjs → session-conflict-oUPEv3YT.mjs} +3 -3
  161. package/dist/{session-manager-CUqF8hkP.mjs → session-manager-C9KjVkzq.mjs} +2 -2
  162. package/dist/{session-url-B9FOnF3F.mjs → session-url-CyBpcLAH.mjs} +1 -1
  163. package/dist/{settings-DkPDB_hS.mjs → settings-DIa-3C8o.mjs} +1 -1
  164. package/dist/{setup-BwWZ9Fzq.mjs → setup-B6bOeWQl.mjs} +6 -6
  165. package/dist/{setup-first-run-i1o7DBki.mjs → setup-first-run-CAjtqBal.mjs} +2 -2
  166. package/dist/{setup-first-run-DyT0PlOZ.mjs → setup-first-run-DPXLAMqa.mjs} +1 -1
  167. package/extensions/pi/index.ts +128 -60
  168. package/extensions/pi/skill-heal.ts +73 -0
  169. package/extensions/pi/tsconfig.json +2 -1
  170. package/package.json +1 -1
  171. package/dist/client/assets/channel-C9URWRe4.js +0 -1
  172. package/dist/client/assets/chunk-QZHKN3VN-BBNO2-yv.js +0 -1
  173. package/dist/client/assets/chunk-WU5MYG2G-HP2Om5f9.js +0 -1
  174. package/dist/client/assets/classDiagram-4FO5ZUOK-BW_ZPHe7.js +0 -1
  175. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DOd628kk.js +0 -1
  176. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-P0vTqsyH.js +0 -1
  177. package/dist/completions-CYa5usLb.mjs +0 -2
  178. /package/dist/{apply-suggestion-BsR_8FXW.mjs → apply-suggestion-tLsWREyb.mjs} +0 -0
  179. /package/dist/{comment-format-D4mCqr7T.mjs → comment-format-DS1wOWj_.mjs} +0 -0
  180. /package/dist/{completions-2aHVTmiJ.mjs → completions-ClqvbDYF.mjs} +0 -0
  181. /package/dist/{diff-fingerprint-BL5jNHnP.mjs → diff-fingerprint-PnC8Fs1-.mjs} +0 -0
  182. /package/dist/{find-tui-binary-DGspwyvd.mjs → find-tui-binary-DvADkBkP.mjs} +0 -0
  183. /package/dist/{handoff-DcShCxWJ.mjs → handoff-B85WLpcN.mjs} +0 -0
  184. /package/dist/{terminal-BroplOnw.mjs → terminal-Ce7OL1A-.mjs} +0 -0
  185. /package/dist/{tui-search-bridge-fFKqX4Fi.mjs → tui-search-bridge-N33QmB0q.mjs} +0 -0
  186. /package/dist/{update-check-ISaw61qS.mjs → update-check-D0yDl7sZ.mjs} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { f as getProjectStorageDir, h as getRepoRoot } from "./git-BGj31GX-.mjs";
2
2
  import { spawnSync } from "node:child_process";
3
- import { randomBytes, randomUUID } from "node:crypto";
3
+ import { randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
4
4
  import { join } from "node:path";
5
5
  import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
6
6
  import { getCookie } from "hono/cookie";
@@ -12,7 +12,7 @@ import { getCookie } from "hono/cookie";
12
12
  * is safe to bundle into the browser UI. Server-only helpers live in
13
13
  * `server-auth.ts` and re-export these values for existing Node importers.
14
14
  */
15
- /** Header, HttpOnly cookie, or legacy query param carrying the per-session review API token. */
15
+ /** Header, HttpOnly cookie, or SSE query param carrying the per-session review API token. */
16
16
  const SESSION_TOKEN_HEADER = "x-diffing-token";
17
17
  const SESSION_TOKEN_QUERY = "token";
18
18
  const SESSION_TOKEN_COOKIE = "diffing-token";
@@ -52,12 +52,29 @@ function isAllowedRequestHost(hostHeader, bindHost) {
52
52
  if (!host) return true;
53
53
  return isLoopbackHost(host);
54
54
  }
55
- function readSessionToken(c) {
55
+ /**
56
+ * Read the session token from the header or HttpOnly cookie.
57
+ *
58
+ * The query param is only honored when `allowQuery` is set, which is reserved
59
+ * for the SSE `/api/live` endpoint (`EventSource` cannot send headers). Tokens
60
+ * in URLs leak into history, referrers, and logs, so every other API route
61
+ * requires the header or cookie.
62
+ */
63
+ function readSessionToken(c, opts) {
56
64
  const header = c.req.header(SESSION_TOKEN_HEADER);
57
65
  if (header) return header;
58
66
  const cookie = getCookie(c, SESSION_TOKEN_COOKIE);
59
67
  if (cookie) return cookie;
60
- return c.req.query("token") || null;
68
+ if (opts?.allowQuery) return c.req.query("token") || null;
69
+ return null;
70
+ }
71
+ /** Constant-time comparison of a provided token against the expected session token. */
72
+ function tokenMatches(provided, expected) {
73
+ if (!provided) return false;
74
+ const providedBuf = Buffer.from(provided);
75
+ const expectedBuf = Buffer.from(expected);
76
+ if (providedBuf.length !== expectedBuf.length) return false;
77
+ return timingSafeEqual(providedBuf, expectedBuf);
61
78
  }
62
79
  /** `Set-Cookie` value for the review session token (loopback http — no Secure). */
63
80
  function buildSessionTokenSetCookieValue(token) {
@@ -82,7 +99,7 @@ function createServerAuthMiddleware(config) {
82
99
  }
83
100
  if (!isAllowedRequestHost(c.req.header("host"), config.bindHost)) return c.json({ error: "request Host header is not allowed for this bind address" }, 403);
84
101
  if (!config.insecureNoAuth && config.authToken) {
85
- if (readSessionToken(c) !== config.authToken) return c.json({ error: "invalid or missing review session token" }, 401);
102
+ if (!tokenMatches(readSessionToken(c, { allowQuery: c.req.path === "/api/live" }), config.authToken)) return c.json({ error: "invalid or missing review session token" }, 401);
86
103
  }
87
104
  await next();
88
105
  };
@@ -1,6 +1,6 @@
1
- import { i as loadSettings } from "./settings-DkPDB_hS.mjs";
2
- import { l as removeServerSession, m as probeLockServerSync } from "./server-lock-CdaUNZyX.mjs";
3
- import { i as reviewSessionUrl } from "./session-url-B9FOnF3F.mjs";
1
+ import { i as loadSettings } from "./settings-DIa-3C8o.mjs";
2
+ import { l as removeServerSession, m as probeLockServerSync } from "./server-lock-ACw2cKDI.mjs";
3
+ import { i as reviewSessionUrl } from "./session-url-CyBpcLAH.mjs";
4
4
  //#region src/lib/session-conflict.ts
5
5
  function sleepMs(ms) {
6
6
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -1,5 +1,5 @@
1
- import { a as listServerLocks, d as sameServerSession, f as serverSessionId, n as activateServerLock, u as resolveActiveServerLock } from "./server-lock-CdaUNZyX.mjs";
2
- import { n as openExistingSession, r as stopLockOwner, t as existingSessionUrl } from "./session-conflict-BVgyyerQ.mjs";
1
+ import { a as listServerLocks, d as sameServerSession, f as serverSessionId, n as activateServerLock, u as resolveActiveServerLock } from "./server-lock-ACw2cKDI.mjs";
2
+ import { n as openExistingSession, r as stopLockOwner, t as existingSessionUrl } from "./session-conflict-oUPEv3YT.mjs";
3
3
  //#region src/lib/session-manager.ts
4
4
  const EXIT_OK = 0;
5
5
  const EXIT_NOT_FOUND = 4;
@@ -1,4 +1,4 @@
1
- import { S as SESSION_TOKEN_QUERY, y as isLoopbackHost } from "./server-lock-CdaUNZyX.mjs";
1
+ import { S as SESSION_TOKEN_QUERY, y as isLoopbackHost } from "./server-lock-ACw2cKDI.mjs";
2
2
  //#region src/lib/session-url.ts
3
3
  /** Loopback-safe review URL for web / gh-pr locks; null for TUI or invalid ports. */
4
4
  function reviewSessionBaseUrl(lock) {
@@ -85,4 +85,4 @@ function resetSetupCompleted() {
85
85
  return saveSettings({ setupCompletedAt: null });
86
86
  }
87
87
  //#endregion
88
- export { markSetupCompleted as a, loadSettings as i, formatModeLabel as n, resetSetupCompleted as o, isSetupCompleted as r, saveSettings as s, ensureConfigDir as t };
88
+ export { markSetupCompleted as a, writeJsonAtomically as c, loadSettings as i, formatModeLabel as n, resetSetupCompleted as o, isSetupCompleted as r, saveSettings as s, ensureConfigDir as t };
@@ -1,9 +1,9 @@
1
1
  import { S as isGitRepo, h as getRepoRoot } from "./git-BGj31GX-.mjs";
2
- import { a as markSetupCompleted, i as loadSettings, n as formatModeLabel, o as resetSetupCompleted, r as isSetupCompleted, s as saveSettings, t as ensureConfigDir } from "./settings-DkPDB_hS.mjs";
3
- import { n as completionFor } from "./completions-2aHVTmiJ.mjs";
4
- import { a as hintLine, c as stepHeader, i as dim, l as tone, n as box, o as isColorEnabled, r as copyBlock, s as rule, t as bold } from "./terminal-BroplOnw.mjs";
5
- import { n as runDoctor, t as formatDoctorReport } from "./doctor-BxTaLqAF.mjs";
6
- import { t as DOCS_GETTING_STARTED_URL } from "./setup-first-run-i1o7DBki.mjs";
2
+ import { a as markSetupCompleted, i as loadSettings, n as formatModeLabel, o as resetSetupCompleted, r as isSetupCompleted, s as saveSettings, t as ensureConfigDir } from "./settings-DIa-3C8o.mjs";
3
+ import { n as completionFor } from "./completions-ClqvbDYF.mjs";
4
+ import { a as hintLine, c as stepHeader, i as dim, l as tone, n as box, o as isColorEnabled, r as copyBlock, s as rule, t as bold } from "./terminal-Ce7OL1A-.mjs";
5
+ import { n as runDoctor, t as formatDoctorReport } from "./doctor-Dg61wsBc.mjs";
6
+ import { t as DOCS_GETTING_STARTED_URL } from "./setup-first-run-CAjtqBal.mjs";
7
7
  import { execFileSync, spawn } from "node:child_process";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import { dirname, join, resolve } from "node:path";
@@ -425,7 +425,7 @@ function printDoneChecklist(inGitRepo, cwd, color) {
425
425
  ], { color }));
426
426
  }
427
427
  async function handleFirstRunGate(options) {
428
- const { shouldOfferFirstRunSetup, promptFirstRunSetup, DOCS_GETTING_STARTED_URL } = await import("./setup-first-run-DyT0PlOZ.mjs");
428
+ const { shouldOfferFirstRunSetup, promptFirstRunSetup, DOCS_GETTING_STARTED_URL } = await import("./setup-first-run-DPXLAMqa.mjs");
429
429
  if (!shouldOfferFirstRunSetup({ skipSetup: options.skipSetup })) return;
430
430
  while (true) {
431
431
  const choice = await promptFirstRunSetup();
@@ -1,5 +1,5 @@
1
- import { r as isSetupCompleted } from "./settings-DkPDB_hS.mjs";
2
- import { a as hintLine, i as dim, n as box, o as isColorEnabled } from "./terminal-BroplOnw.mjs";
1
+ import { r as isSetupCompleted } from "./settings-DIa-3C8o.mjs";
2
+ import { a as hintLine, i as dim, n as box, o as isColorEnabled } from "./terminal-Ce7OL1A-.mjs";
3
3
  import { stdin, stdout } from "node:process";
4
4
  import { createInterface } from "node:readline/promises";
5
5
  //#region src/lib/setup-first-run.ts
@@ -1,2 +1,2 @@
1
- import { a as shouldOfferFirstRunSetup, i as promptFirstRunSetup, t as DOCS_GETTING_STARTED_URL } from "./setup-first-run-i1o7DBki.mjs";
1
+ import { a as shouldOfferFirstRunSetup, i as promptFirstRunSetup, t as DOCS_GETTING_STARTED_URL } from "./setup-first-run-CAjtqBal.mjs";
2
2
  export { DOCS_GETTING_STARTED_URL, promptFirstRunSetup, shouldOfferFirstRunSetup };
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Bridges the local-first diffing review loop into pi:
5
5
  * - Structured LLM tools mirroring the diffing MCP surface (status, review
6
- * start, comments, reply/resolve, plan loop, mockup loop, progress,
7
- * sessions, GH PR).
6
+ * start, comments, reply/resolve, plan loop, mockup loop, design system,
7
+ * progress, sessions, GH PR).
8
8
  * - `/diffing` command to open/reuse the review UI for the current repo.
9
9
  * - Footer status showing the active review session.
10
10
  * - Skill self-heal: keeps `~/.agents/skills/diffing*` as symlinks to the
@@ -20,12 +20,9 @@
20
20
  import { spawn } from "node:child_process";
21
21
  import {
22
22
  existsSync,
23
- lstatSync,
24
23
  mkdtempSync,
25
24
  readFileSync,
26
- realpathSync,
27
25
  rmSync,
28
- symlinkSync,
29
26
  writeFileSync,
30
27
  } from "node:fs";
31
28
  import { homedir, tmpdir } from "node:os";
@@ -37,29 +34,19 @@ import type {
37
34
  ExtensionContext,
38
35
  } from "@earendil-works/pi-coding-agent";
39
36
  import { Type } from "typebox";
37
+ import {
38
+ selfHealSkillLinks,
39
+ SKILLS_REL,
40
+ } from "./skill-heal.ts";
40
41
 
41
42
  // ────────────────────────────────────────────────────────────────────────────
42
43
  // Constants
43
44
  // ────────────────────────────────────────────────────────────────────────────
44
45
 
45
- const SKILL_NAMES = [
46
- "diffing",
47
- "diffing-finish-review",
48
- "diffing-mockup-review",
49
- "diffing-plan-review",
50
- "diffing-pr-address",
51
- "diffing-pr-read",
52
- "diffing-release",
53
- "diffing-review",
54
- "diffing-start-review",
55
- ] as const;
56
-
57
46
  const MAX_OUTPUT_BYTES = 48 * 1024;
58
47
  const REVIEW_START_TIMEOUT_MS = 15_000;
59
48
  const REVIEW_START_POLL_MS = 400;
60
49
 
61
- const SKILLS_REL = join(".agents", "skills");
62
-
63
50
  // ────────────────────────────────────────────────────────────────────────────
64
51
  // CLI runner
65
52
  // ────────────────────────────────────────────────────────────────────────────
@@ -247,35 +234,6 @@ function findCanonicalRoot(cwd: string): string | null {
247
234
  return null;
248
235
  }
249
236
 
250
- /**
251
- * Keep the `~/.agents/skills/diffing*` entries as symlinks to the canonical
252
- * checkout's `.agents/skills`. pi dedupes skills by canonical realpath, so
253
- * symlinked home entries merge silently with the repo's project skills and no
254
- * `[Skill conflicts]` banner appears. Repairs stale real copies left behind by
255
- * `diffing setup skills` / `npx skills add --copy`. Only touches the known
256
- * names in SKILL_NAMES; never deletes anything else.
257
- */
258
- function selfHealSkillLinks(canonical: string): void {
259
- const homeSkills = join(homedir(), ".agents", "skills");
260
- if (!existsSync(homeSkills)) return;
261
- for (const name of SKILL_NAMES) {
262
- const target = join(canonical, SKILLS_REL, name);
263
- if (!existsSync(target)) continue;
264
- const link = join(homeSkills, name);
265
- try {
266
- if (existsSync(link) || lstatSync(link)) {
267
- if (lstatSync(link).isSymbolicLink()) {
268
- const real = realpathSync(link);
269
- if (real === realpathSync(target)) continue; // already correct
270
- }
271
- rmSync(link, { recursive: true, force: true });
272
- }
273
- symlinkSync(target, link, "dir");
274
- } catch {
275
- // best-effort; a failed heal must not break the session
276
- }
277
- }
278
- }
279
237
 
280
238
  // ────────────────────────────────────────────────────────────────────────────
281
239
  // Detached review server start
@@ -339,7 +297,9 @@ export default function (pi: ExtensionAPI) {
339
297
  // Keep the home skill links canonical for every session (idempotent).
340
298
  pi.on("session_start", async (_event, ctx) => {
341
299
  const canonical = findCanonicalRoot(ctx.cwd);
342
- if (canonical) selfHealSkillLinks(canonical);
300
+ if (canonical) {
301
+ selfHealSkillLinks(canonical, join(homedir(), ".agents", "skills"));
302
+ }
343
303
  await refreshStatus(ctx);
344
304
  });
345
305
 
@@ -824,7 +784,7 @@ export default function (pi: ExtensionAPI) {
824
784
  name: "diffing_mockup_submit",
825
785
  label: "Diffing Mockup Submit",
826
786
  description:
827
- "Submit HTML mockup screen(s) for visual review. Pass a single html body, a file/dir path, or an inline screens array [{id, html}]. Share the URL and park unless asked to wait. Resubmit with mockupId to bump the version and reset the verdict.",
787
+ "Submit HTML mockup screen(s) for visual review. HARD RULE one state per screen: never tabs/accordions/toggles/modals/JS swaps. Pass html, a file/dir already under ~/.diffing/…/mockup-sources/, or screens [{id, html}]. Call diffing_design show first. Share the URL and park unless asked to wait. Resubmit with mockupId to bump the version. Fix any in-page-state or generic-style hints before parking.",
828
788
  parameters: Type.Object({
829
789
  html: Type.Optional(
830
790
  Type.String({ description: "Single-screen HTML body." }),
@@ -853,6 +813,23 @@ export default function (pi: ExtensionAPI) {
853
813
  ),
854
814
  model: Type.Optional(Type.String()),
855
815
  source: Type.Optional(Type.String()),
816
+ mode: Type.Optional(
817
+ StringEnum(["fragment", "document"] as const, {
818
+ description:
819
+ "fragment = body contents wrapped by the published design-system shell. document = full HTML, no wrap. Default: server default.",
820
+ }),
821
+ ),
822
+ designSystem: Type.Optional(
823
+ Type.String({
824
+ description:
825
+ "Design-system id to bind (default system if omitted).",
826
+ }),
827
+ ),
828
+ planId: Type.Optional(
829
+ Type.String({
830
+ description: "Optional plan id this mockup illustrates.",
831
+ }),
832
+ ),
856
833
  }),
857
834
  async execute(_id, params, _signal, _onUpdate, ctx) {
858
835
  const args = ["mockup", "submit"];
@@ -895,6 +872,9 @@ export default function (pi: ExtensionAPI) {
895
872
  if (params.mockupId) args.push("--id", params.mockupId);
896
873
  if (params.model) args.push("--model", params.model);
897
874
  if (params.source) args.push("--source", params.source);
875
+ if (params.mode) args.push("--mode", params.mode);
876
+ if (params.designSystem) args.push("--system", params.designSystem);
877
+ if (params.planId) args.push("--plan-id", params.planId);
898
878
  try {
899
879
  const result = await runDiffing(args, ctx.cwd, { stdin });
900
880
  const stdout = result.stdout.trim();
@@ -1078,12 +1058,15 @@ export default function (pi: ExtensionAPI) {
1078
1058
  name: "diffing_mockup_inspect",
1079
1059
  label: "Diffing Mockup Inspect",
1080
1060
  description:
1081
- "Read compact, bounded mockup data without transferring screen HTML. view=summary (headline stats), comments (paged comment list), comment (one thread), screen (screen source). Filter by comment scope: status, screenId, viewport (desktop|tablet|mobile), version. context=none|anchor|source controls anchor detail. Prefer this over diffing_mockup_show.",
1061
+ "Read compact, bounded mockup data without transferring screen HTML. view=summary (headline stats), comments (paged comment list), comment (one thread), screen (screen source), preview (rendered preview metadata). Filter by comment scope: status, screenId, viewport (desktop|tablet|mobile), version. context=none|anchor|source controls anchor detail. Prefer this over diffing_mockup_show.",
1082
1062
  parameters: Type.Object({
1083
- view: StringEnum(["summary", "comments", "comment", "screen"] as const, {
1084
- description: "What to read. Default: summary.",
1085
- default: "summary",
1086
- }),
1063
+ view: StringEnum(
1064
+ ["summary", "comments", "comment", "screen", "preview"] as const,
1065
+ {
1066
+ description: "What to read. Default: summary.",
1067
+ default: "summary",
1068
+ },
1069
+ ),
1087
1070
  mockupId: Type.Optional(
1088
1071
  Type.String({ description: "Mockup id. Default: latest." }),
1089
1072
  ),
@@ -1151,11 +1134,12 @@ export default function (pi: ExtensionAPI) {
1151
1134
  name: "diffing_mockup_screen",
1152
1135
  label: "Diffing Mockup Screen",
1153
1136
  description:
1154
- "One-screen revision of an HTML mockup. action=upsert adds/replaces a screen (html or file), remove deletes a screen, patch replaces the first exact occurrence of text with replacement. Every success bumps the mockup version; pass expectedVersion to guard racing edits (409 version-mismatch, nothing applied). For multi-screen revisions, resubmit via diffing_mockup_submit with the same mockupId.",
1137
+ "One-screen revision of an HTML mockup. action=upsert adds/replaces a screen (html or file), remove deletes a screen, patch replaces the first exact occurrence of text with replacement, replace-region replaces the inner HTML of the first [data-diffing=region] element (prefer this when the comment has a data-diffing target). Every success bumps the mockup version; pass expectedVersion to guard racing edits (409 version-mismatch, nothing applied). For multi-screen revisions, resubmit via diffing_mockup_submit with the same mockupId.",
1155
1138
  parameters: Type.Object({
1156
- action: StringEnum(["upsert", "remove", "patch"] as const, {
1157
- description: "Revision op.",
1158
- }),
1139
+ action: StringEnum(
1140
+ ["upsert", "remove", "patch", "replace-region"] as const,
1141
+ { description: "Revision op." },
1142
+ ),
1159
1143
  mockupId: Type.String({ description: "Mockup id." }),
1160
1144
  screenId: Type.String({ description: "Screen id." }),
1161
1145
  html: Type.Optional(
@@ -1176,8 +1160,17 @@ export default function (pi: ExtensionAPI) {
1176
1160
  description: "For patch: exact text to replace (first occurrence).",
1177
1161
  }),
1178
1162
  ),
1163
+ region: Type.Optional(
1164
+ Type.String({
1165
+ description:
1166
+ "For replace-region: data-diffing attribute value of the block to replace.",
1167
+ }),
1168
+ ),
1179
1169
  replacement: Type.Optional(
1180
- Type.String({ description: "For patch: replacement text." }),
1170
+ Type.String({
1171
+ description:
1172
+ "For patch: replacement text. For replace-region: new inner HTML.",
1173
+ }),
1181
1174
  ),
1182
1175
  expectedVersion: Type.Optional(
1183
1176
  Type.Number({
@@ -1201,6 +1194,10 @@ export default function (pi: ExtensionAPI) {
1201
1194
  stdin = params.html;
1202
1195
  }
1203
1196
  if (params.label) args.push("--label", params.label);
1197
+ } else if (params.action === "replace-region") {
1198
+ if (params.region) args.push("--region", params.region);
1199
+ if (params.replacement !== undefined)
1200
+ args.push("--replacement", params.replacement);
1204
1201
  } else if (params.action === "patch") {
1205
1202
  if (params.text !== undefined) args.push("--text", params.text);
1206
1203
  if (params.replacement !== undefined)
@@ -1263,6 +1260,77 @@ export default function (pi: ExtensionAPI) {
1263
1260
  },
1264
1261
  });
1265
1262
 
1263
+ pi.registerTool({
1264
+ name: "diffing_mockup_handoff",
1265
+ label: "Diffing Mockup Handoff",
1266
+ description:
1267
+ "Compact implementation handoff after a mockup is approved: tokens, screens with intent, components used, leftover nits. Prefer this over dumping every screen's HTML. Call after decision=approved, before writing product UI.",
1268
+ parameters: Type.Object({
1269
+ mockupId: Type.Optional(
1270
+ Type.String({ description: "Mockup id. Default: latest." }),
1271
+ ),
1272
+ json: Type.Optional(
1273
+ Type.Boolean({ description: "Emit raw JSON. Default: XML handoff." }),
1274
+ ),
1275
+ }),
1276
+ async execute(_id, params, _signal, _onUpdate, ctx) {
1277
+ const args = ["mockup", "handoff"];
1278
+ if (params.mockupId) args.push(params.mockupId);
1279
+ if (params.json) args.push("--json");
1280
+ const result = await runDiffing(args, ctx.cwd);
1281
+ return textResult(describe(result, "diffing mockup handoff"), {
1282
+ exitCode: result.exitCode,
1283
+ stderr: result.stderr.trim() || undefined,
1284
+ });
1285
+ },
1286
+ });
1287
+
1288
+ pi.registerTool({
1289
+ name: "diffing_design",
1290
+ label: "Diffing Design System",
1291
+ description:
1292
+ "Per-repo design system used before authoring mockup HTML. show/list: read tokens, guidelines, components. extract: scan the consumer repo and write a draft (does not publish). propose: update the draft. publish: human action only — do not publish unless the human asked. Omit id for the default system.",
1293
+ parameters: Type.Object({
1294
+ action: StringEnum(
1295
+ ["show", "list", "extract", "propose", "publish"] as const,
1296
+ { description: "Design-system op. Default: show." },
1297
+ ),
1298
+ id: Type.Optional(
1299
+ Type.String({ description: "System id. Default: default." }),
1300
+ ),
1301
+ json: Type.Optional(
1302
+ Type.Boolean({ description: "Emit raw JSON. Default: false." }),
1303
+ ),
1304
+ from: Type.Optional(
1305
+ StringEnum(["css", "text"] as const, {
1306
+ description: "For extract: scan source. Default: css.",
1307
+ }),
1308
+ ),
1309
+ title: Type.Optional(
1310
+ Type.String({ description: "For extract/propose: system title." }),
1311
+ ),
1312
+ guidelines: Type.Optional(
1313
+ Type.String({
1314
+ description: "For propose: markdown guidelines to write on the draft.",
1315
+ }),
1316
+ ),
1317
+ }),
1318
+ async execute(_id, params, _signal, _onUpdate, ctx) {
1319
+ const action = params.action ?? "show";
1320
+ const args = ["design", action];
1321
+ if (params.id) args.push(params.id);
1322
+ if (params.json) args.push("--json");
1323
+ if (params.from) args.push("--from", params.from);
1324
+ if (params.title) args.push("--title", params.title);
1325
+ if (params.guidelines) args.push("--guidelines", params.guidelines);
1326
+ const result = await runDiffing(args, ctx.cwd);
1327
+ return textResult(describe(result, `diffing design ${action}`), {
1328
+ exitCode: result.exitCode,
1329
+ stderr: result.stderr.trim() || undefined,
1330
+ });
1331
+ },
1332
+ });
1333
+
1266
1334
  pi.registerTool({
1267
1335
  name: "diffing_url",
1268
1336
  label: "Diffing URL",
@@ -0,0 +1,73 @@
1
+ import {
2
+ existsSync,
3
+ lstatSync,
4
+ realpathSync,
5
+ rmSync,
6
+ symlinkSync,
7
+ } from "node:fs";
8
+ import { join } from "node:path";
9
+
10
+ export const DIFFING_SKILL_NAMES = [
11
+ "diffing",
12
+ "diffing-finish-review",
13
+ "diffing-mockup-author",
14
+ "diffing-mockup-review",
15
+ "diffing-plan-review",
16
+ "diffing-pr-address",
17
+ "diffing-pr-read",
18
+ "diffing-release",
19
+ "diffing-review",
20
+ "diffing-start-review",
21
+ ] as const;
22
+
23
+ export const SKILLS_REL = join(".agents", "skills");
24
+
25
+ /**
26
+ * Point `link` at `target`. Creates a missing link, repairs a stale/dangling
27
+ * one, and leaves a correct symlink alone. `existsSync` is false for both
28
+ * missing paths and dangling symlinks, so this uses `lstat` and treats ENOENT
29
+ * as "create".
30
+ */
31
+ export function healSkillLink(
32
+ link: string,
33
+ target: string,
34
+ ): "ok" | "created" | "repaired" {
35
+ if (!existsSync(target)) return "ok";
36
+ let st: ReturnType<typeof lstatSync> | null = null;
37
+ try {
38
+ st = lstatSync(link);
39
+ } catch {
40
+ st = null;
41
+ }
42
+ if (st) {
43
+ if (st.isSymbolicLink()) {
44
+ try {
45
+ if (realpathSync(link) === realpathSync(target)) return "ok";
46
+ } catch {
47
+ // dangling or unreadable — fall through and replace
48
+ }
49
+ }
50
+ rmSync(link, { recursive: true, force: true });
51
+ symlinkSync(target, link, "dir");
52
+ return "repaired";
53
+ }
54
+ symlinkSync(target, link, "dir");
55
+ return "created";
56
+ }
57
+
58
+ /** Keep `~/.agents/skills/diffing*` as symlinks to the canonical checkout. */
59
+ export function selfHealSkillLinks(
60
+ canonical: string,
61
+ homeSkills: string,
62
+ ): void {
63
+ if (!existsSync(homeSkills)) return;
64
+ for (const name of DIFFING_SKILL_NAMES) {
65
+ const target = join(canonical, SKILLS_REL, name);
66
+ if (!existsSync(target)) continue;
67
+ try {
68
+ healSkillLink(join(homeSkills, name), target);
69
+ } catch {
70
+ // best-effort; a failed heal must not break the session
71
+ }
72
+ }
73
+ }
@@ -5,10 +5,11 @@
5
5
  "moduleResolution": "bundler",
6
6
  "strict": true,
7
7
  "noEmit": true,
8
+ "allowImportingTsExtensions": true,
8
9
  "skipLibCheck": true,
9
10
  "esModuleInterop": true,
10
11
  "forceConsistentCasingInFileNames": true,
11
12
  "types": ["node"]
12
13
  },
13
- "include": ["index.ts", "pi-modules.d.ts"]
14
+ "include": ["index.ts", "skill-heal.ts", "pi-modules.d.ts"]
14
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diffing",
3
- "version": "0.18.1",
3
+ "version": "0.18.3",
4
4
  "description": "local-first CLI for reviewing, navigating, and discussing git diffs with AI",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- import{at as e,ot as t}from"./chunk-CSCIHK7Q-D6ZDvMsi.js";var n=(n,r)=>t.lang.round(e.parse(n)[r]);export{n as t};
@@ -1 +0,0 @@
1
- import{h as e}from"./src-D7QQkGEc.js";var t=class{constructor(e){this.init=e,this.records=this.init()}static{e(this,`ImperativeState`)}reset(){this.records=this.init()}};export{t};
@@ -1 +0,0 @@
1
- import{h as e,p as t}from"./src-D7QQkGEc.js";import{x as n}from"./chunk-CSCIHK7Q-D6ZDvMsi.js";var r=e(e=>{let{securityLevel:r}=n(),i=t(`body`);return r===`sandbox`&&(i=t((t(`#i${e}`).node()?.contentDocument??document).body)),i.select(`#${e}`)},`selectSvgElement`);export{r as t};
@@ -1 +0,0 @@
1
- import{h as e}from"./src-D7QQkGEc.js";import"./chunk-CSCIHK7Q-D6ZDvMsi.js";import"./chunk-5ZQYHXKU-BGFzCgvs.js";import"./chunk-O5CBEL6O-DcKB07iP.js";import"./chunk-FMBD7UC4-BFDCYCqF.js";import"./chunk-BSJP7CBP-DAvnzgjY.js";import"./chunk-L5ZTLDWV-Dl9iUIBf.js";import"./chunk-ND2GUHAM-DBDu-JeD.js";import"./chunk-55IACEB6-pCwXbxR0.js";import"./chunk-2J33WTMH-Bpr3MqKf.js";import"./chunk-NZK2D7GU-CRyk4F3R.js";import"./chunk-3OPIFGDE-DdMV_3DM.js";import"./chunk-KSCS5N6A-BmD6XY52.js";import"./chunk-LZXEDZCA-rOOJDxdB.js";import{i as t,n,r,t as i}from"./chunk-727SXJPM-DU7oVWfJ.js";var a={parser:n,get db(){return new i},renderer:r,styles:t,init:e(e=>{e.class||={},e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
@@ -1 +0,0 @@
1
- import{h as e}from"./src-D7QQkGEc.js";import"./chunk-CSCIHK7Q-D6ZDvMsi.js";import"./chunk-5ZQYHXKU-BGFzCgvs.js";import"./chunk-O5CBEL6O-DcKB07iP.js";import"./chunk-FMBD7UC4-BFDCYCqF.js";import"./chunk-BSJP7CBP-DAvnzgjY.js";import"./chunk-L5ZTLDWV-Dl9iUIBf.js";import"./chunk-ND2GUHAM-DBDu-JeD.js";import"./chunk-55IACEB6-pCwXbxR0.js";import"./chunk-2J33WTMH-Bpr3MqKf.js";import"./chunk-NZK2D7GU-CRyk4F3R.js";import"./chunk-3OPIFGDE-DdMV_3DM.js";import"./chunk-KSCS5N6A-BmD6XY52.js";import"./chunk-LZXEDZCA-rOOJDxdB.js";import{i as t,n,r,t as i}from"./chunk-727SXJPM-DU7oVWfJ.js";var a={parser:n,get db(){return new i},renderer:r,styles:t,init:e(e=>{e.class||={},e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
@@ -1 +0,0 @@
1
- import{h as e}from"./src-D7QQkGEc.js";import"./chunk-CSCIHK7Q-D6ZDvMsi.js";import"./chunk-5ZQYHXKU-BGFzCgvs.js";import"./chunk-O5CBEL6O-DcKB07iP.js";import"./chunk-BSJP7CBP-DAvnzgjY.js";import"./chunk-L5ZTLDWV-Dl9iUIBf.js";import"./chunk-55IACEB6-pCwXbxR0.js";import"./chunk-2J33WTMH-Bpr3MqKf.js";import"./chunk-NZK2D7GU-CRyk4F3R.js";import"./chunk-3OPIFGDE-DdMV_3DM.js";import"./chunk-KSCS5N6A-BmD6XY52.js";import"./chunk-LZXEDZCA-rOOJDxdB.js";import{i as t,n,r,t as i}from"./chunk-AQP2D5EJ-DaVYG1qm.js";var a={parser:n,get db(){return new i(2)},renderer:r,styles:t,init:e(e=>{e.state||={},e.state.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
@@ -1,2 +0,0 @@
1
- import { n as completionFor } from "./completions-2aHVTmiJ.mjs";
2
- export { completionFor };