@diffohq/diffo 0.2.0 → 0.3.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 (142) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.mjs +111 -22
  3. package/dist/client/assets/{abnfDiagram-VCTEODGH-BK74b0Uj.js → abnfDiagram-O67JEVCF-DdDBSVZ4.js} +1 -1
  4. package/dist/client/assets/{arc-BZJjnFHJ.js → arc-BgNb6rn9.js} +1 -1
  5. package/dist/client/assets/architecture-7GRP2DOG-CYv-P09J.js +1 -0
  6. package/dist/client/assets/architectureDiagram-NJMV4G6O-HNLU6Z_V.js +36 -0
  7. package/dist/client/assets/{blockDiagram-I7D4REHJ-BWSjZ0YR.js → blockDiagram-BEXU5L5S-DO434Dqm.js} +15 -4
  8. package/dist/client/assets/c4Diagram-YGBWAQC7-CkhXDWcc.js +38 -0
  9. package/dist/client/assets/channel-eN698CVO.js +1 -0
  10. package/dist/client/assets/chunk-3FUC2YCW-DTfKImVL.js +2 -0
  11. package/dist/client/assets/{chunk-GVQU2GXP-3NWb-Kjs.js → chunk-5DYCD2WN-9Pj_Xuzv.js} +1 -1
  12. package/dist/client/assets/{chunk-PWAF6VOD-BRN4CzQ0.js → chunk-742MDFTN-DRQNNbBo.js} +1 -1
  13. package/dist/client/assets/chunk-7INBJB4K-BqnHF3P0.js +62 -0
  14. package/dist/client/assets/chunk-7M6MHVWA-NTwtPxj_.js +213 -0
  15. package/dist/client/assets/chunk-7PRAP22T-reFtaiUY.js +1 -0
  16. package/dist/client/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
  17. package/dist/client/assets/{chunk-FOHPRMQF-DHwB1DNv.js → chunk-FOHPRMQF-DzxwRta7.js} +9 -9
  18. package/dist/client/assets/{chunk-SVP7TREG-CsO-w42m.js → chunk-GTNCS2PH-BbSQqqTd.js} +1 -1
  19. package/dist/client/assets/{chunk-F27PBJKO-C6X74WsU.js → chunk-GWA4HPMP-B3OAiKhC.js} +1 -1
  20. package/dist/client/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
  21. package/dist/client/assets/chunk-MBY4JIJT-CNIWhzdY.js +72 -0
  22. package/dist/client/assets/chunk-NETBCI7D-Cu2GMg5A.js +1 -0
  23. package/dist/client/assets/chunk-O7XYJQB3-C616frX3.js +126 -0
  24. package/dist/client/assets/chunk-UA2S7LBM-_7jUrSu1.js +1 -0
  25. package/dist/client/assets/{chunk-POPQ4Y6H-DKwUAqbe.js → chunk-WEXAMYUT--WmuTjt8.js} +1 -1
  26. package/dist/client/assets/{chunk-XXDRQBXY-BikhrMBF.js → chunk-XXDRQBXY-CH68Fn6G.js} +1 -1
  27. package/dist/client/assets/{chunk-OSK3NFVY-BJ8i4cd-.js → chunk-Z7XXMR3K-CMGWkbdd.js} +3 -3
  28. package/dist/client/assets/{chunk-75Z2AOVW-B_bGdfJP.js → chunk-ZIGJFQKS-Bupo8UEA.js} +2 -2
  29. package/dist/client/assets/classDiagram-v2-NBCMYWYE-BlYlsN2U.js +217 -0
  30. package/dist/client/assets/{core-BJUznnCg.js → core-BuHBGQx-.js} +2 -2
  31. package/dist/client/assets/cose-bilkent-JH36ORCC-BtFdPq63.js +1 -0
  32. package/dist/client/assets/cynefin-OW5HDTMX-DQ0j2-fq.js +1 -0
  33. package/dist/client/assets/cynefinDiagram-VND7K2PF-DnctQE5u.js +62 -0
  34. package/dist/client/assets/cytoscape.esm-Yq6u8L66.js +321 -0
  35. package/dist/client/assets/dagre-6A5THRUB-u940AeXp.js +4 -0
  36. package/dist/client/assets/diagram-22UHCM2B-B1jzZKtP.js +200 -0
  37. package/dist/client/assets/{diagram-VX7I27RA-BmHtC5T2.js → diagram-3UASUU5V-BY_6wxmf.js} +2 -2
  38. package/dist/client/assets/{diagram-VSXAHHWV-BMNFierD.js → diagram-ATOU4E4O-CTsYtnJJ.js} +3 -3
  39. package/dist/client/assets/{diagram-S7CK7UJ4-C1jP2NX5.js → diagram-CDSNMT55-DuEJO9EQ.js} +3 -3
  40. package/dist/client/assets/{diagram-Z3DM3KII-CzCV6KsS.js → diagram-MLGK6HIB-CZ10U2Mk.js} +1 -1
  41. package/dist/client/assets/diagram-MPIPVDR6-CkO6cJlF.js +41 -0
  42. package/dist/client/assets/dist-DFkgQY1o.js +142 -0
  43. package/dist/client/assets/{dist-bii_eoxi.js → dist-DkqXbsqN.js} +1 -1
  44. package/dist/client/assets/{ebnfDiagram-PWID7BFC-DuVnjPWa.js → ebnfDiagram-ZINNZB2B-BJtnMhOp.js} +1 -1
  45. package/dist/client/assets/elk-276RUBZZ-CShSerV4.js +27 -0
  46. package/dist/client/assets/{engine-oniguruma-BlIEwGaN.js → engine-oniguruma-DDtqknzG.js} +1 -1
  47. package/dist/client/assets/{erDiagram-RLTQ6QDP-CtwYHyZu.js → erDiagram-OPXOYQCR-2VvQxgaY.js} +15 -15
  48. package/dist/client/assets/eventmodeling-NTZA5JFV-Ur4Uzv5y.js +1 -0
  49. package/dist/client/assets/flowDiagram-KWPJA3E3-BQS1w4s1.js +1 -0
  50. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-CR8Sa06t.js → ganttDiagram-FUAMR5RP-hQJ3ga2L.js} +2 -2
  51. package/dist/client/assets/gitGraph-4MIJSDKK-CVt38-tW.js +1 -0
  52. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-C36JIyKh.js → gitGraphDiagram-X574FWY7-X6_FAtp_.js} +4 -4
  53. package/dist/client/assets/index-t7j89PJp.js +91 -0
  54. package/dist/client/assets/info-A6RAGUB7-CsIS2T8y.js +1 -0
  55. package/dist/client/assets/infoDiagram-VRGFBTTK-CNesth4F.js +2 -0
  56. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-CsPYEqSH.js → ishikawaDiagram-OU5B5YK6-C3uowhy1.js} +2 -2
  57. package/dist/client/assets/{journeyDiagram-3NMN7TZE-DbHeKN0X.js → journeyDiagram-ZHPQQLJL-Bti7zdNB.js} +3 -3
  58. package/dist/client/assets/{kanban-definition-UXKFOSKX-CbYeTY2r.js → kanban-definition-PNTS6WVX-Co13mJCW.js} +10 -10
  59. package/dist/client/assets/{katex-CXMH3UgJ.js → katex-ZlcWpGUi.js} +2 -2
  60. package/dist/client/assets/{line-BeOqlVB1.js → line-CodMwzVg.js} +1 -1
  61. package/dist/client/assets/linear-D9CKVpWI.js +1 -0
  62. package/dist/client/assets/{mermaid-parser.core-BLSiQUXp.js → mermaid-parser.core-OY76TKUN.js} +3 -3
  63. package/dist/client/assets/mermaid.core-DWE2Tn2R.js +44 -0
  64. package/dist/client/assets/{mindmap-definition-YA3MSWOX-o49TTsg4.js → mindmap-definition-NLK3R4M7-DJ3MEvZg.js} +25 -25
  65. package/dist/client/assets/packet-AYTQ26CC-CW1j_hFQ.js +1 -0
  66. package/dist/client/assets/{pegDiagram-XKGWAZYB-CZxoewnI.js → pegDiagram-GJSIUBJH-hOvLvBiF.js} +1 -1
  67. package/dist/client/assets/pie-WAS4IAKB-D1KPtuKA.js +1 -0
  68. package/dist/client/assets/{pieDiagram-E7YTZNPT-dAiwtigh.js → pieDiagram-5QR66LMP-CJN1M2lG.js} +2 -2
  69. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-mefTaSBl.js → quadrantDiagram-O4NWA36T-DOysLQ6R.js} +3 -3
  70. package/dist/client/assets/radar-RG4KPBEZ-B7XCBnIS.js +1 -0
  71. package/dist/client/assets/railroad-74A4TZTK-0nmMIDEw.js +1 -0
  72. package/dist/client/assets/railroad-abnf-HS5TGJTU-DJU_abQP.js +1 -0
  73. package/dist/client/assets/railroad-ebnf-LZEXJU2U-_TwNbNw6.js +1 -0
  74. package/dist/client/assets/railroad-peg-WCYAUIDC-CAYsrf-p.js +1 -0
  75. package/dist/client/assets/{railroadDiagram-O6MQD6OU-DdhfhlaU.js → railroadDiagram-XR7U4H2S-BXbeU-WV.js} +1 -1
  76. package/dist/client/assets/{requirementDiagram-BXWQKSXE-BVrRfVMy.js → requirementDiagram-PLB6GJNP-D3G2HYv-.js} +11 -11
  77. package/dist/client/assets/sankeyDiagram-IPEJSGJF-CFSa-Gk4.js +40 -0
  78. package/dist/client/assets/sequenceDiagram-PO4LG4MO-BvmuQTlz.js +169 -0
  79. package/dist/client/assets/{src-CfhDi0bm.js → src-CplSIRuf.js} +1 -1
  80. package/dist/client/assets/stateDiagram-v2-GCMORJYK-C_ceCfHy.js +291 -0
  81. package/dist/client/assets/swimlanes-2SLR337P-D-n44lEC.js +1 -0
  82. package/dist/client/assets/swimlanesDiagram-TC7HE7FX-DrlgJMk7.js +8 -0
  83. package/dist/client/assets/timeline-definition-EJHVYXUP-DKYNs1Uj.js +120 -0
  84. package/dist/client/assets/treeView-Q6P3EWNA-COW2UiIS.js +1 -0
  85. package/dist/client/assets/treemap-WGGIJYW6-DJOuJZmF.js +1 -0
  86. package/dist/client/assets/usecaseDiagram-POWQR4AR-BTmpbeJZ.js +328 -0
  87. package/dist/client/assets/vennDiagram-UO4OBE2U-C37AKEaJ.js +34 -0
  88. package/dist/client/assets/wardley-WFR3VGLG-CpVTtNOC.js +1 -0
  89. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-CFaIuWQk.js → wardleyDiagram-VNRHLVJA-ja-hiiVC.js} +3 -3
  90. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-et5vk4QS.js → xychartDiagram-PMCCYNJV-BsDoacSf.js} +3 -3
  91. package/dist/client/index.html +1 -1
  92. package/package.json +4 -4
  93. package/plugin.json +1 -1
  94. package/skills/diffo/SKILL.md +17 -8
  95. package/dist/client/assets/architecture-7GRP2DOG-BMofp_0u.js +0 -1
  96. package/dist/client/assets/architectureDiagram-5GKGNRK7-BBjd1L77.js +0 -36
  97. package/dist/client/assets/c4Diagram-7LVT6UL2-z-xUGKCJ.js +0 -38
  98. package/dist/client/assets/channel-CmX7fcUa.js +0 -1
  99. package/dist/client/assets/chunk-4HAMMTFA-CAN-T2o-.js +0 -62
  100. package/dist/client/assets/chunk-DU6HZSFF-wyqPpFTy.js +0 -125
  101. package/dist/client/assets/chunk-GMAD6QVW-DBossm8a.js +0 -72
  102. package/dist/client/assets/chunk-IMKFNOWR-BCvjP66C.js +0 -231
  103. package/dist/client/assets/chunk-L3NEJ4N5-DppEbElX.js +0 -1
  104. package/dist/client/assets/chunk-P2QGCYS3-fvNVbdI1.js +0 -1
  105. package/dist/client/assets/chunk-SHT3W25Y-BfzjiqOe.js +0 -168
  106. package/dist/client/assets/chunk-TICWLB2K-DPzmokY4.js +0 -206
  107. package/dist/client/assets/classDiagram-ZZMXUADV-BSMgXeCj.js +0 -1
  108. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BSMgXeCj.js +0 -1
  109. package/dist/client/assets/cose-bilkent-JH36ORCC-CBu7-BXr.js +0 -1
  110. package/dist/client/assets/cynefin-OW5HDTMX-iJEmw--0.js +0 -1
  111. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-B4JnaLZu.js +0 -62
  112. package/dist/client/assets/cytoscape.esm-CGd-uY3x.js +0 -321
  113. package/dist/client/assets/dagre-GXQ25YYZ-bmDafESr.js +0 -4
  114. package/dist/client/assets/dagre-PrKaheQc.js +0 -1
  115. package/dist/client/assets/diagram-UQ7AKVKN-DssGkq6D.js +0 -41
  116. package/dist/client/assets/dist-BT7WB1vp.js +0 -142
  117. package/dist/client/assets/eventmodeling-NTZA5JFV-DBQsn9JF.js +0 -1
  118. package/dist/client/assets/flowDiagram-HODETNUW-C_zt1wER.js +0 -1
  119. package/dist/client/assets/gitGraph-4MIJSDKK-DoWc_DJ0.js +0 -1
  120. package/dist/client/assets/index-BUq9UmeC.js +0 -91
  121. package/dist/client/assets/info-A6RAGUB7-DULcTga7.js +0 -1
  122. package/dist/client/assets/infoDiagram-27XIBGKW-eQbUxJ9T.js +0 -2
  123. package/dist/client/assets/linear-D5d3UlAm.js +0 -1
  124. package/dist/client/assets/mermaid.core-BoXUi08V.js +0 -44
  125. package/dist/client/assets/packet-AYTQ26CC-Blxpg-nI.js +0 -1
  126. package/dist/client/assets/pie-WAS4IAKB-D-fqeiW7.js +0 -1
  127. package/dist/client/assets/radar-RG4KPBEZ-VaODqy4w.js +0 -1
  128. package/dist/client/assets/railroad-74A4TZTK-BwoigfOV.js +0 -1
  129. package/dist/client/assets/railroad-abnf-HS5TGJTU-BYYKbIBi.js +0 -1
  130. package/dist/client/assets/railroad-ebnf-LZEXJU2U-BFA23uel.js +0 -1
  131. package/dist/client/assets/railroad-peg-WCYAUIDC-Cq02Hm_P.js +0 -1
  132. package/dist/client/assets/sankeyDiagram-P5KCCOFB-BalSSH4U.js +0 -40
  133. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-B2KsE_Z_.js +0 -162
  134. package/dist/client/assets/stateDiagram-D77RDMKH-CoH6bKG5.js +0 -1
  135. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-rXsredo-.js +0 -1
  136. package/dist/client/assets/swimlanes-42K2YHIH-3IkzoE1X.js +0 -1
  137. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BeXrrDxh.js +0 -8
  138. package/dist/client/assets/timeline-definition-24CTP7MA-B2M9pP5h.js +0 -120
  139. package/dist/client/assets/treeView-Q6P3EWNA-Brp55dHV.js +0 -1
  140. package/dist/client/assets/treemap-WGGIJYW6-zRe9RDJV.js +0 -1
  141. package/dist/client/assets/vennDiagram-4TSXK5OY-PNF-rFET.js +0 -34
  142. package/dist/client/assets/wardley-WFR3VGLG-BSDDZIR6.js +0 -1
package/README.md CHANGED
@@ -21,7 +21,7 @@ come back as fixes.
21
21
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
22
22
  [![Node](https://img.shields.io/badge/node-%E2%89%A5%2024-brightgreen)](#quick-start)
23
23
  [![Docs](https://img.shields.io/badge/docs-diffo-8b5cf6)](https://diffohq.github.io/diffo/)
24
- [![Status](https://img.shields.io/badge/status-pre--release-orange)](#status)
24
+ [![Tests](https://img.shields.io/badge/tests-1000-brightgreen)](#contributing)
25
25
 
26
26
  </div>
27
27
 
@@ -214,7 +214,7 @@ cheapest to change.
214
214
  TypeScript on Node >= 24: a [Hono](https://hono.dev) server over loopback serving a React 19
215
215
  UI, live updates over server-sent events from one recursive filesystem watch, and state in a
216
216
  single SQLite file at `~/.diffo/diffo.db` through the runtime's built-in `node:sqlite`, so
217
- there is no database to install. **Zero network calls.** 859 tests across 54 files.
217
+ there is no database to install. **Zero network calls.** 1,000 tests across 57 files.
218
218
 
219
219
  Reviews are scoped per repo **and branch**, and the server is loopback-only, rejecting
220
220
  non-loopback `Host` and `Origin` headers so a web page can't reach into your repo through
package/dist/cli.mjs CHANGED
@@ -94,6 +94,18 @@ function untouchedAgentVoice(thread) {
94
94
  function undeliveredThreadIds(threads) {
95
95
  return threads.filter((t) => (t.state === "sent" || t.state === "addressed") && t.unanswered !== true && t.withheld !== true && t.messages.at(-1)?.author === "reviewer").sort((a, b) => (a.sentAt ?? "").localeCompare(b.sentAt ?? "")).map((t) => t.id);
96
96
  }
97
+ /**
98
+ * Whatever the agent sent, made fit to be a tab's name: controls stripped,
99
+ * whitespace collapsed to one line, capped. Null for anything that isn't a
100
+ * usable title — the caller then leaves the existing name alone rather than
101
+ * blanking it.
102
+ */
103
+ function normalizeTitle(raw) {
104
+ if (typeof raw !== "string") return null;
105
+ const line = raw.replace(/[\u0000-\u001f\u007f-\u009f]/g, " ").replace(/\s+/g, " ").trim();
106
+ if (line === "") return null;
107
+ return line.length > 40 ? `${line.slice(0, 39).trimEnd()}…` : line;
108
+ }
97
109
  const EMPTY_REVIEW = {
98
110
  version: 1,
99
111
  threads: []
@@ -193,6 +205,7 @@ function buildCliCommands(cli) {
193
205
  return {
194
206
  open: cli,
195
207
  poll: `${cli} poll`,
208
+ firstPoll: `${cli} poll --title "<the change, in 2-3 words>"`,
196
209
  reply: `${cli} reply <threadId> --message "<your reply>"`,
197
210
  comment: `${cli} comment [<file>] [--line <line>] --message "<comment>"`,
198
211
  guide: `${cli} comment --message "<what the change does>"`,
@@ -217,9 +230,31 @@ const INSTALL_SKILL = {
217
230
  * restates how to hold the poll — attended, never detached. Mirrors the skill.
218
231
  */
219
232
  const POLL_STANCE = "a tracked background task if your harness has one, the foreground if not — never a detached process";
220
- const JOIN_PROMPT = `join the diffo review: run \`${CLI_COMMANDS.poll}\` (${POLL_STANCE}) and follow the JSON payload it prints — each payload carries its own instructions`;
233
+ /**
234
+ * The tab-title doctrine — the few words an agent hands its first poll, which
235
+ * become the browser tab's name. Stated once and interpolated into every
236
+ * surface that teaches it (the skill, `help agent`, the open-time next step),
237
+ * the same way GUIDE and POLL_STANCE keep their rules from drifting apart.
238
+ *
239
+ * It exists because a reviewer keeps several reviews open and every tab reads
240
+ * "Diffo": the agent is the only party that knows which is which at the moment
241
+ * it starts listening.
242
+ */
243
+ const TAB_TITLE = {
244
+ /** What to write — and how little room there is to write it in. */
245
+ what: "two or three words naming what the change IS, not what you did to it — about 20 characters, because that is all a browser tab shows",
246
+ /** Why it is worth a flag at all — the reviewer's problem, stated once. */
247
+ why: "it becomes the name of the reviewer's browser tab, and a reviewer with several reviews open sees every one of them titled \"Diffo\"",
248
+ /** How to order the words, given where they are read. */
249
+ shape: "the tab cuts off everything past that, so the word telling this review apart from another goes first, not last",
250
+ /** When to send it — and when not to bother. */
251
+ when: "on your first poll of a review; on a later one only if the changeset has become something the old title no longer names",
252
+ /** Two to copy the register from — both fit a tab whole. */
253
+ examples: "\"tab titles\", \"flaky upload retries\""
254
+ };
255
+ const JOIN_PROMPT = `join the diffo review: run \`${CLI_COMMANDS.firstPoll}\` (${POLL_STANCE}; the title is ${TAB_TITLE.what} — ${TAB_TITLE.why}) and follow the JSON payload it prints — each payload carries its own instructions`;
221
256
  function nextStepFor(kind, actionable) {
222
- if (kind === "cleared") return `Post the guide if this changeset warrants one, then run \`${CLI_COMMANDS.poll}\` again to keep listening (${POLL_STANCE}).`;
257
+ if (kind === "cleared") return `Post the guide if this changeset warrants one, then run \`${CLI_COMMANDS.firstPoll}\` again to keep listening (${POLL_STANCE}) — this is a fresh round, so give it a fresh title.`;
223
258
  if (kind === "finish" && actionable === 0) return `Nothing to act on — run \`${CLI_COMMANDS.poll}\` again to keep listening (${POLL_STANCE}); the reviewer may follow up.`;
224
259
  return `${kind === "finish" ? "The reviewer is done reading — work the whole batch. " : ""}Act on each thread, reply with \`${CLI_COMMANDS.reply}\`, then run \`${CLI_COMMANDS.poll}\` again to keep listening.`;
225
260
  }
@@ -251,9 +286,15 @@ const GUIDE = {
251
286
  * The skill teaches the same step, but this line is what an agent WITHOUT the
252
287
  * skill sees — payloads and command output must stand alone (see POLL_STANCE).
253
288
  */
289
+ /**
290
+ * The URL goes out before the guide, never after: composing a guide is the
291
+ * slowest step of an open (a diagram takes real thought), and a reviewer who
292
+ * is waiting for a link should not wait on it. The guide lands live at the top
293
+ * of their review while they are still opening the page.
294
+ */
254
295
  function guideNudge(hasGuide) {
255
296
  if (hasGuide) return null;
256
- return `orient the reviewer before sharing the URL, if this changeset needs it (${GUIDE.when}): post a guide — one comment on the whole changeset: ${GUIDE.what}: \`${CLI_COMMANDS.guide}\` (no file, so it anchors to the changeset). ${GUIDE.stance}.`;
297
+ return `share the URL above with the user right now, as a message line, before anything else. Then, while they open it, orient them if this changeset needs it (${GUIDE.when}): post a guide — one comment on the whole changeset: ${GUIDE.what}: \`${CLI_COMMANDS.guide}\` (no file, so it anchors to the changeset; it appears live at the top of their review). ${GUIDE.stance}.`;
257
298
  }
258
299
  /**
259
300
  * Printed to stderr when a poll takes the review over and the review already
@@ -588,6 +629,8 @@ For the reviewer:
588
629
  For the agent (the AI that wrote the change):
589
630
  poll Wait for the reviewer's feedback (blocking long-poll;
590
631
  prints one JSON payload; safe to re-run any time)
632
+ (--title "<what the change is>" on the first poll names
633
+ the reviewer's browser tab)
591
634
  reply <threadId> Post a reply to a review thread
592
635
  (--message "<text>", or pipe the text on stdin)
593
636
  comment [<file>] Start a comment thread as the agent — on a line (--line),
@@ -627,20 +670,26 @@ The loop:
627
670
 
628
671
  1. Open: run \`diffo --no-open\` from inside the repo. It returns straight
629
672
  away, leaving a background server watching the working tree. Never open a
630
- browser at the reviewer — end your message with the printed URL instead,
631
- and keep ending every message with it while you stay attached.
673
+ browser at the reviewer — share the printed URL instead, the moment it
674
+ prints: a message line right after this command, before the guide, the
675
+ poll, or anything else. Then end your message with it too, and keep
676
+ ending every message with it while you stay attached.
632
677
  Attached without ever seeing the URL (a takeover, a fresh session)?
633
678
  \`diffo status\` prints it.
634
679
  2. Guide — post one only when the changeset needs orientation:
635
680
  ${GUIDE.when}.
636
- Before sharing the URL, post ONE comment on the whole changeset
637
- (\`diffo comment -m "…"\`, no file), containing:
638
- ${GUIDE.what}.
681
+ Right after sharing the URL, while the reviewer opens the page, post ONE
682
+ comment on the whole changeset (\`diffo comment -m "…"\`, no file),
683
+ containing: ${GUIDE.what}.
639
684
  ${GUIDE.stance}.
685
+ It lands live at the top of their review — never hold the URL back for it.
640
686
  If the changeset later shifts under the guide, ${GUIDE.update}.
641
- 3. Listen: run \`diffo poll\` — it blocks until the reviewer acts, then prints
642
- one JSON payload naming the threads to act on. Run it attended:
643
- ${POLL_STANCE}.
687
+ 3. Listen: run \`${CLI_COMMANDS.firstPoll}\` — it blocks until the reviewer
688
+ acts, then prints one JSON payload naming the threads to act on. Run it
689
+ attended: ${POLL_STANCE}.
690
+ The title is ${TAB_TITLE.what}: ${TAB_TITLE.why}. Write it the way it is
691
+ read — ${TAB_TITLE.shape} (${TAB_TITLE.examples}). Send it ${TAB_TITLE.when};
692
+ every other poll is a plain \`diffo poll\`.
644
693
  Killed or timed out? Re-run it; feedback is held in the review, not the
645
694
  poll.
646
695
  4. Act: \`[issue]\` threads want a code change; \`[question]\` threads want an
@@ -667,7 +716,7 @@ Rules:
667
716
  - Resolving a thread is the reviewer's call, never yours.`,
668
717
  poll: `diffo poll — wait for the reviewer's feedback
669
718
 
670
- Usage: diffo poll
719
+ Usage: diffo poll [--title "<what the change is>"]
671
720
 
672
721
  Blocks (streaming whitespace heartbeats) until the reviewer acts, then prints
673
722
  one JSON payload naming the review threads to act on, and exits. Run it
@@ -676,10 +725,15 @@ payload that reaches a process nobody is listening to never reaches you.
676
725
  Safe to re-run any time: feedback is held in the review itself, so
677
726
  nothing is lost when a poll is killed or times out — the next poll gets it.
678
727
 
728
+ --title is ${TAB_TITLE.what}: ${TAB_TITLE.why}. Write it the way it is read —
729
+ ${TAB_TITLE.shape} (${TAB_TITLE.examples}). Send it ${TAB_TITLE.when}; the
730
+ newest title wins, and a poll without one leaves the name it finds alone.
731
+
679
732
  Output: one JSON object, e.g.
680
733
  {"status":"feedback","threadIds":["t-3"],"prompt":"…what to do…"}
681
734
 
682
- Example:
735
+ Examples:
736
+ diffo poll --title "tab titles from the agent"
683
737
  diffo poll`,
684
738
  reply: `diffo reply — post a reply to a review thread
685
739
 
@@ -930,6 +984,7 @@ function parseVerb(verb, rest) {
930
984
  short: "m"
931
985
  },
932
986
  line: { type: "string" },
987
+ title: { type: "string" },
933
988
  more: { type: "boolean" },
934
989
  json: { type: "boolean" },
935
990
  help: {
@@ -955,6 +1010,10 @@ function parseVerb(verb, rest) {
955
1010
  kind: "error",
956
1011
  message: `'${verb}' takes no --more`
957
1012
  };
1013
+ if (values.title !== void 0 && verb !== "poll") return {
1014
+ kind: "error",
1015
+ message: `'${verb}' takes no --title`
1016
+ };
958
1017
  if (verb === "poll" || verb === "end" || verb === "setup" || verb === "status" || verb === "stop") {
959
1018
  if (positionals.length > 0) return {
960
1019
  kind: "error",
@@ -968,6 +1027,17 @@ function parseVerb(verb, rest) {
968
1027
  kind: "status",
969
1028
  json: values.json === true
970
1029
  };
1030
+ if (verb === "poll") {
1031
+ const title = normalizeTitle(values.title);
1032
+ if (values.title !== void 0 && title === null) return {
1033
+ kind: "error",
1034
+ message: "--title needs a few words naming the change"
1035
+ };
1036
+ return {
1037
+ kind: "poll",
1038
+ title
1039
+ };
1040
+ }
971
1041
  return { kind: verb };
972
1042
  }
973
1043
  if (verb === "reply") {
@@ -2515,16 +2585,18 @@ var ReviewStore = class {
2515
2585
  return true;
2516
2586
  }
2517
2587
  /**
2518
- * Start the review over: threads, the last-finish record, and the landed
2519
- * marker all go. Not just the threads — a kept `lastFinish` would carry hunk
2520
- * ids from the dead changeset into the next review's "since last review"
2521
- * lens, reporting everything as new. `seenHead` survives: it describes the
2588
+ * Start the review over: threads, the last-finish record, the landed marker,
2589
+ * and the tab title all go. Not just the threads — a kept `lastFinish` would
2590
+ * carry hunk ids from the dead changeset into the next review's "since last
2591
+ * review" lens, reporting everything as new, and a kept title would name the
2592
+ * work that just ended. The cleared agent is woken (see the DELETE route), so
2593
+ * its next poll names the new round. `seenHead` survives: it describes the
2522
2594
  * repo, not the review being discarded.
2523
2595
  */
2524
2596
  reset() {
2525
2597
  const ids = this.state.threads.map((t) => t.id);
2526
- if (ids.length === 0 && !this.state.lastFinish && !this.state.landed) return [];
2527
- const { lastFinish: _finish, landed: _landed, ...rest } = this.state;
2598
+ if (ids.length === 0 && !this.state.lastFinish && !this.state.landed && !this.state.title) return [];
2599
+ const { lastFinish: _finish, landed: _landed, title: _title, ...rest } = this.state;
2528
2600
  this.state = {
2529
2601
  ...rest,
2530
2602
  threads: []
@@ -2532,6 +2604,16 @@ var ReviewStore = class {
2532
2604
  this.commit();
2533
2605
  return ids;
2534
2606
  }
2607
+ /** The agent's name for this changeset, carried by its poll. The newest one
2608
+ * wins: a change that grows a second subject renames its own tab. */
2609
+ setTitle(title) {
2610
+ if (this.state.title === title) return;
2611
+ this.state = {
2612
+ ...this.state,
2613
+ title
2614
+ };
2615
+ this.commit();
2616
+ }
2535
2617
  /** The base the work under review sits on — see `ReviewState.seenHead` for
2536
2618
  * when the caller must NOT move it. */
2537
2619
  noteHead(sha) {
@@ -2702,9 +2784,11 @@ function parseReview(raw) {
2702
2784
  const migrated = Array.isArray(parsed.suggestions) ? parsed.suggestions.map((s) => migrateLegacySuggestion(s, now)).filter((t) => t !== null) : [];
2703
2785
  const lastFinish = normalizeLastFinish(parsed.lastFinish, now);
2704
2786
  const landed = normalizeLanded(parsed.landed, now);
2787
+ const title = normalizeTitle(parsed.title);
2705
2788
  return {
2706
2789
  version: 1,
2707
2790
  threads: [...valid, ...migrated],
2791
+ ...title ? { title } : {},
2708
2792
  ...lastFinish ? { lastFinish } : {},
2709
2793
  ...typeof parsed.seenHead === "string" && parsed.seenHead !== "" ? { seenHead: parsed.seenHead } : {},
2710
2794
  ...landed ? { landed } : {}
@@ -3425,6 +3509,7 @@ function createApp(ctx, store, review, queue) {
3425
3509
  const deliver = body?.deliver !== false;
3426
3510
  let thread = review.addMessage(c.req.param("id"), "reviewer", text, !deliver);
3427
3511
  if (!thread) return c.json({ error: "no such thread" }, 404);
3512
+ if (deliver && thread.state === "open" && startedByAgent(thread)) thread = review.send(thread.id) ?? thread;
3428
3513
  let delivered = false;
3429
3514
  if (deliver && (thread.state === "sent" || thread.state === "addressed")) {
3430
3515
  delivered = deliverThreads([thread.id]);
@@ -3474,8 +3559,9 @@ function createApp(ctx, store, review, queue) {
3474
3559
  const handedOver = thread.state === "sent" || thread.state === "addressed" && thread.withheld === true;
3475
3560
  const delivered = handedOver ? deliverThreads([thread.id]) : false;
3476
3561
  if (handedOver) review.clearWithheld([thread.id]);
3562
+ const current = review.get().threads.find((t) => t.id === thread.id) ?? thread;
3477
3563
  return c.json({
3478
- thread,
3564
+ thread: current,
3479
3565
  prompt,
3480
3566
  delivered,
3481
3567
  presence
@@ -3636,6 +3722,8 @@ function createApp(ctx, store, review, queue) {
3636
3722
  if (!c.req.header("x-diffo-agent") && !c.req.header("x-diffo-session-pid")) return c.json({ error: "agent polls must send the x-diffo-agent header — use `diffo poll`" }, 403);
3637
3723
  const tookOverFrom = queue.claimSession(parseSessionPid(c.req.header("x-diffo-session-pid")));
3638
3724
  if (tookOverFrom !== null) c.header("x-diffo-took-over-from", String(tookOverFrom));
3725
+ const title = normalizeTitle(c.req.query("title"));
3726
+ if (title) review.setTitle(title);
3639
3727
  const heartbeatMs = ctx.pollHeartbeatMs ?? POLL_HEARTBEAT_MS;
3640
3728
  return stream(c, async (s) => {
3641
3729
  let aborted = false;
@@ -4762,7 +4850,8 @@ if (command.kind === "poll") {
4762
4850
  const port = await requireServer();
4763
4851
  process.stderr.write("diffo: waiting for the reviewer — keep this process attended: a tracked\nbackground task or the foreground, never detached. If it dies, just\nre-run it — feedback is held in the review and survives.\n");
4764
4852
  try {
4765
- const res = await fetch(apiUrl(port, "/api/agent/poll"), { headers: sessionHeaders() });
4853
+ const path = command.title === null ? "/api/agent/poll" : `/api/agent/poll?title=${encodeURIComponent(command.title)}`;
4854
+ const res = await fetch(apiUrl(port, path), { headers: sessionHeaders() });
4766
4855
  if (!res.ok) fail(`poll failed (${res.status})`);
4767
4856
  const tookOverFrom = res.headers.get("x-diffo-took-over-from");
4768
4857
  if (tookOverFrom) {
@@ -4841,7 +4930,7 @@ async function printAgentNextStep(port) {
4841
4930
  const review = await fetchReviewState(port);
4842
4931
  const nudge = review ? guideNudge(findGuideThread(review) !== void 0) : null;
4843
4932
  if (nudge) console.log(`first: ${nudge}`);
4844
- console.log("next: run `diffo poll` to receive the reviewer's feedback (`diffo help agent` prints the whole loop)");
4933
+ console.log(`next: run \`${CLI_COMMANDS.firstPoll}\` to receive the reviewer's feedback — the title is ${TAB_TITLE.what} (${TAB_TITLE.examples}): ${TAB_TITLE.why} (\`diffo help agent\` prints the whole loop)`);
4845
4934
  }
4846
4935
  /**
4847
4936
  * Dev builds serve the client from the checkout's `dist/client`, which nothing
@@ -1 +1 @@
1
- import{m as e,t}from"./mermaid-parser.core-BLSiQUXp.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-CfhDi0bm.js";import"./chunk-DU6HZSFF-wyqPpFTy.js";import{n as i,r as a,t as o}from"./chunk-SVP7TREG-CsO-w42m.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-BoXUi08V.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
1
+ import{m as e,t}from"./mermaid-parser.core-OY76TKUN.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-CplSIRuf.js";import"./chunk-O7XYJQB3-C616frX3.js";import{n as i,r as a,t as o}from"./chunk-GTNCS2PH-BbSQqqTd.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-DWE2Tn2R.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
@@ -1 +1 @@
1
- import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-bii_eoxi.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i){if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
1
+ import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-DkqXbsqN.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i){if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
@@ -0,0 +1 @@
1
+ import"./chunk-FOHPRMQF-DzxwRta7.js";import{M as e}from"./mermaid-parser.core-OY76TKUN.js";export{e as createArchitectureServices};