@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.
- package/README.md +2 -2
- package/dist/cli.mjs +111 -22
- package/dist/client/assets/{abnfDiagram-VCTEODGH-BK74b0Uj.js → abnfDiagram-O67JEVCF-DdDBSVZ4.js} +1 -1
- package/dist/client/assets/{arc-BZJjnFHJ.js → arc-BgNb6rn9.js} +1 -1
- package/dist/client/assets/architecture-7GRP2DOG-CYv-P09J.js +1 -0
- package/dist/client/assets/architectureDiagram-NJMV4G6O-HNLU6Z_V.js +36 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-BWSjZ0YR.js → blockDiagram-BEXU5L5S-DO434Dqm.js} +15 -4
- package/dist/client/assets/c4Diagram-YGBWAQC7-CkhXDWcc.js +38 -0
- package/dist/client/assets/channel-eN698CVO.js +1 -0
- package/dist/client/assets/chunk-3FUC2YCW-DTfKImVL.js +2 -0
- package/dist/client/assets/{chunk-GVQU2GXP-3NWb-Kjs.js → chunk-5DYCD2WN-9Pj_Xuzv.js} +1 -1
- package/dist/client/assets/{chunk-PWAF6VOD-BRN4CzQ0.js → chunk-742MDFTN-DRQNNbBo.js} +1 -1
- package/dist/client/assets/chunk-7INBJB4K-BqnHF3P0.js +62 -0
- package/dist/client/assets/chunk-7M6MHVWA-NTwtPxj_.js +213 -0
- package/dist/client/assets/chunk-7PRAP22T-reFtaiUY.js +1 -0
- package/dist/client/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
- package/dist/client/assets/{chunk-FOHPRMQF-DHwB1DNv.js → chunk-FOHPRMQF-DzxwRta7.js} +9 -9
- package/dist/client/assets/{chunk-SVP7TREG-CsO-w42m.js → chunk-GTNCS2PH-BbSQqqTd.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-C6X74WsU.js → chunk-GWA4HPMP-B3OAiKhC.js} +1 -1
- package/dist/client/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
- package/dist/client/assets/chunk-MBY4JIJT-CNIWhzdY.js +72 -0
- package/dist/client/assets/chunk-NETBCI7D-Cu2GMg5A.js +1 -0
- package/dist/client/assets/chunk-O7XYJQB3-C616frX3.js +126 -0
- package/dist/client/assets/chunk-UA2S7LBM-_7jUrSu1.js +1 -0
- package/dist/client/assets/{chunk-POPQ4Y6H-DKwUAqbe.js → chunk-WEXAMYUT--WmuTjt8.js} +1 -1
- package/dist/client/assets/{chunk-XXDRQBXY-BikhrMBF.js → chunk-XXDRQBXY-CH68Fn6G.js} +1 -1
- package/dist/client/assets/{chunk-OSK3NFVY-BJ8i4cd-.js → chunk-Z7XXMR3K-CMGWkbdd.js} +3 -3
- package/dist/client/assets/{chunk-75Z2AOVW-B_bGdfJP.js → chunk-ZIGJFQKS-Bupo8UEA.js} +2 -2
- package/dist/client/assets/classDiagram-v2-NBCMYWYE-BlYlsN2U.js +217 -0
- package/dist/client/assets/{core-BJUznnCg.js → core-BuHBGQx-.js} +2 -2
- package/dist/client/assets/cose-bilkent-JH36ORCC-BtFdPq63.js +1 -0
- package/dist/client/assets/cynefin-OW5HDTMX-DQ0j2-fq.js +1 -0
- package/dist/client/assets/cynefinDiagram-VND7K2PF-DnctQE5u.js +62 -0
- package/dist/client/assets/cytoscape.esm-Yq6u8L66.js +321 -0
- package/dist/client/assets/dagre-6A5THRUB-u940AeXp.js +4 -0
- package/dist/client/assets/diagram-22UHCM2B-B1jzZKtP.js +200 -0
- package/dist/client/assets/{diagram-VX7I27RA-BmHtC5T2.js → diagram-3UASUU5V-BY_6wxmf.js} +2 -2
- package/dist/client/assets/{diagram-VSXAHHWV-BMNFierD.js → diagram-ATOU4E4O-CTsYtnJJ.js} +3 -3
- package/dist/client/assets/{diagram-S7CK7UJ4-C1jP2NX5.js → diagram-CDSNMT55-DuEJO9EQ.js} +3 -3
- package/dist/client/assets/{diagram-Z3DM3KII-CzCV6KsS.js → diagram-MLGK6HIB-CZ10U2Mk.js} +1 -1
- package/dist/client/assets/diagram-MPIPVDR6-CkO6cJlF.js +41 -0
- package/dist/client/assets/dist-DFkgQY1o.js +142 -0
- package/dist/client/assets/{dist-bii_eoxi.js → dist-DkqXbsqN.js} +1 -1
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-DuVnjPWa.js → ebnfDiagram-ZINNZB2B-BJtnMhOp.js} +1 -1
- package/dist/client/assets/elk-276RUBZZ-CShSerV4.js +27 -0
- package/dist/client/assets/{engine-oniguruma-BlIEwGaN.js → engine-oniguruma-DDtqknzG.js} +1 -1
- package/dist/client/assets/{erDiagram-RLTQ6QDP-CtwYHyZu.js → erDiagram-OPXOYQCR-2VvQxgaY.js} +15 -15
- package/dist/client/assets/eventmodeling-NTZA5JFV-Ur4Uzv5y.js +1 -0
- package/dist/client/assets/flowDiagram-KWPJA3E3-BQS1w4s1.js +1 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-CR8Sa06t.js → ganttDiagram-FUAMR5RP-hQJ3ga2L.js} +2 -2
- package/dist/client/assets/gitGraph-4MIJSDKK-CVt38-tW.js +1 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-C36JIyKh.js → gitGraphDiagram-X574FWY7-X6_FAtp_.js} +4 -4
- package/dist/client/assets/index-t7j89PJp.js +91 -0
- package/dist/client/assets/info-A6RAGUB7-CsIS2T8y.js +1 -0
- package/dist/client/assets/infoDiagram-VRGFBTTK-CNesth4F.js +2 -0
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-CsPYEqSH.js → ishikawaDiagram-OU5B5YK6-C3uowhy1.js} +2 -2
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-DbHeKN0X.js → journeyDiagram-ZHPQQLJL-Bti7zdNB.js} +3 -3
- package/dist/client/assets/{kanban-definition-UXKFOSKX-CbYeTY2r.js → kanban-definition-PNTS6WVX-Co13mJCW.js} +10 -10
- package/dist/client/assets/{katex-CXMH3UgJ.js → katex-ZlcWpGUi.js} +2 -2
- package/dist/client/assets/{line-BeOqlVB1.js → line-CodMwzVg.js} +1 -1
- package/dist/client/assets/linear-D9CKVpWI.js +1 -0
- package/dist/client/assets/{mermaid-parser.core-BLSiQUXp.js → mermaid-parser.core-OY76TKUN.js} +3 -3
- package/dist/client/assets/mermaid.core-DWE2Tn2R.js +44 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-o49TTsg4.js → mindmap-definition-NLK3R4M7-DJ3MEvZg.js} +25 -25
- package/dist/client/assets/packet-AYTQ26CC-CW1j_hFQ.js +1 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-CZxoewnI.js → pegDiagram-GJSIUBJH-hOvLvBiF.js} +1 -1
- package/dist/client/assets/pie-WAS4IAKB-D1KPtuKA.js +1 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-dAiwtigh.js → pieDiagram-5QR66LMP-CJN1M2lG.js} +2 -2
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-mefTaSBl.js → quadrantDiagram-O4NWA36T-DOysLQ6R.js} +3 -3
- package/dist/client/assets/radar-RG4KPBEZ-B7XCBnIS.js +1 -0
- package/dist/client/assets/railroad-74A4TZTK-0nmMIDEw.js +1 -0
- package/dist/client/assets/railroad-abnf-HS5TGJTU-DJU_abQP.js +1 -0
- package/dist/client/assets/railroad-ebnf-LZEXJU2U-_TwNbNw6.js +1 -0
- package/dist/client/assets/railroad-peg-WCYAUIDC-CAYsrf-p.js +1 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-DdhfhlaU.js → railroadDiagram-XR7U4H2S-BXbeU-WV.js} +1 -1
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-BVrRfVMy.js → requirementDiagram-PLB6GJNP-D3G2HYv-.js} +11 -11
- package/dist/client/assets/sankeyDiagram-IPEJSGJF-CFSa-Gk4.js +40 -0
- package/dist/client/assets/sequenceDiagram-PO4LG4MO-BvmuQTlz.js +169 -0
- package/dist/client/assets/{src-CfhDi0bm.js → src-CplSIRuf.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-GCMORJYK-C_ceCfHy.js +291 -0
- package/dist/client/assets/swimlanes-2SLR337P-D-n44lEC.js +1 -0
- package/dist/client/assets/swimlanesDiagram-TC7HE7FX-DrlgJMk7.js +8 -0
- package/dist/client/assets/timeline-definition-EJHVYXUP-DKYNs1Uj.js +120 -0
- package/dist/client/assets/treeView-Q6P3EWNA-COW2UiIS.js +1 -0
- package/dist/client/assets/treemap-WGGIJYW6-DJOuJZmF.js +1 -0
- package/dist/client/assets/usecaseDiagram-POWQR4AR-BTmpbeJZ.js +328 -0
- package/dist/client/assets/vennDiagram-UO4OBE2U-C37AKEaJ.js +34 -0
- package/dist/client/assets/wardley-WFR3VGLG-CpVTtNOC.js +1 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-CFaIuWQk.js → wardleyDiagram-VNRHLVJA-ja-hiiVC.js} +3 -3
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-et5vk4QS.js → xychartDiagram-PMCCYNJV-BsDoacSf.js} +3 -3
- package/dist/client/index.html +1 -1
- package/package.json +4 -4
- package/plugin.json +1 -1
- package/skills/diffo/SKILL.md +17 -8
- package/dist/client/assets/architecture-7GRP2DOG-BMofp_0u.js +0 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-BBjd1L77.js +0 -36
- package/dist/client/assets/c4Diagram-7LVT6UL2-z-xUGKCJ.js +0 -38
- package/dist/client/assets/channel-CmX7fcUa.js +0 -1
- package/dist/client/assets/chunk-4HAMMTFA-CAN-T2o-.js +0 -62
- package/dist/client/assets/chunk-DU6HZSFF-wyqPpFTy.js +0 -125
- package/dist/client/assets/chunk-GMAD6QVW-DBossm8a.js +0 -72
- package/dist/client/assets/chunk-IMKFNOWR-BCvjP66C.js +0 -231
- package/dist/client/assets/chunk-L3NEJ4N5-DppEbElX.js +0 -1
- package/dist/client/assets/chunk-P2QGCYS3-fvNVbdI1.js +0 -1
- package/dist/client/assets/chunk-SHT3W25Y-BfzjiqOe.js +0 -168
- package/dist/client/assets/chunk-TICWLB2K-DPzmokY4.js +0 -206
- package/dist/client/assets/classDiagram-ZZMXUADV-BSMgXeCj.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-BSMgXeCj.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-CBu7-BXr.js +0 -1
- package/dist/client/assets/cynefin-OW5HDTMX-iJEmw--0.js +0 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-B4JnaLZu.js +0 -62
- package/dist/client/assets/cytoscape.esm-CGd-uY3x.js +0 -321
- package/dist/client/assets/dagre-GXQ25YYZ-bmDafESr.js +0 -4
- package/dist/client/assets/dagre-PrKaheQc.js +0 -1
- package/dist/client/assets/diagram-UQ7AKVKN-DssGkq6D.js +0 -41
- package/dist/client/assets/dist-BT7WB1vp.js +0 -142
- package/dist/client/assets/eventmodeling-NTZA5JFV-DBQsn9JF.js +0 -1
- package/dist/client/assets/flowDiagram-HODETNUW-C_zt1wER.js +0 -1
- package/dist/client/assets/gitGraph-4MIJSDKK-DoWc_DJ0.js +0 -1
- package/dist/client/assets/index-BUq9UmeC.js +0 -91
- package/dist/client/assets/info-A6RAGUB7-DULcTga7.js +0 -1
- package/dist/client/assets/infoDiagram-27XIBGKW-eQbUxJ9T.js +0 -2
- package/dist/client/assets/linear-D5d3UlAm.js +0 -1
- package/dist/client/assets/mermaid.core-BoXUi08V.js +0 -44
- package/dist/client/assets/packet-AYTQ26CC-Blxpg-nI.js +0 -1
- package/dist/client/assets/pie-WAS4IAKB-D-fqeiW7.js +0 -1
- package/dist/client/assets/radar-RG4KPBEZ-VaODqy4w.js +0 -1
- package/dist/client/assets/railroad-74A4TZTK-BwoigfOV.js +0 -1
- package/dist/client/assets/railroad-abnf-HS5TGJTU-BYYKbIBi.js +0 -1
- package/dist/client/assets/railroad-ebnf-LZEXJU2U-BFA23uel.js +0 -1
- package/dist/client/assets/railroad-peg-WCYAUIDC-Cq02Hm_P.js +0 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-BalSSH4U.js +0 -40
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-B2KsE_Z_.js +0 -162
- package/dist/client/assets/stateDiagram-D77RDMKH-CoH6bKG5.js +0 -1
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-rXsredo-.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-3IkzoE1X.js +0 -1
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BeXrrDxh.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-B2M9pP5h.js +0 -120
- package/dist/client/assets/treeView-Q6P3EWNA-Brp55dHV.js +0 -1
- package/dist/client/assets/treemap-WGGIJYW6-zRe9RDJV.js +0 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-PNF-rFET.js +0 -34
- 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)
|
|
22
22
|
[](#quick-start)
|
|
23
23
|
[](https://diffohq.github.io/diffo/)
|
|
24
|
-
[](#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.**
|
|
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
|
-
|
|
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.
|
|
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 `
|
|
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 —
|
|
631
|
-
|
|
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
|
-
|
|
637
|
-
(\`diffo comment -m "…"\`, no file),
|
|
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 \`
|
|
642
|
-
one JSON payload naming the threads to act on. Run it
|
|
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
|
-
|
|
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,
|
|
2519
|
-
*
|
|
2520
|
-
* ids from the dead changeset into the next review's "since last
|
|
2521
|
-
* lens, reporting everything as new
|
|
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
|
|
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(
|
|
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
|
package/dist/client/assets/{abnfDiagram-VCTEODGH-BK74b0Uj.js → abnfDiagram-O67JEVCF-DdDBSVZ4.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e,t}from"./mermaid-parser.core-
|
|
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-
|
|
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};
|