@a9n-shoji/rvw 0.2.0 → 0.2.1

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 (104) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +10 -6
  3. package/dist/cli.mjs +492 -307
  4. package/dist/cli.mjs.map +3 -3
  5. package/dist/web/assets/{DocumentViewer-CNgHpCuL.js → DocumentViewer-DvrDlIpR.js} +33 -33
  6. package/dist/web/assets/{markdown-source-map-_dS1q5Dg.js → MarkdownTable-DbDc9VoP.js} +2 -2
  7. package/dist/web/assets/WalkthroughViewer-aDUeKC3B.js +1 -0
  8. package/dist/web/assets/{abnfDiagram-N423BO3Z-0v-ndtqQ.js → abnfDiagram-N423BO3Z-fQDP4qYL.js} +1 -1
  9. package/dist/web/assets/architecture-TIHT7OUA-CuFS9GkN.js +1 -0
  10. package/dist/web/assets/{architectureDiagram-T3A2C74G-WSNJ-tZE.js → architectureDiagram-T3A2C74G-D3Aon3Nd.js} +1 -1
  11. package/dist/web/assets/{blockDiagram-VBNYF7ZC-lmMbEUwI.js → blockDiagram-VBNYF7ZC-DS1DcAqy.js} +1 -1
  12. package/dist/web/assets/{c4Diagram-5PPSVZJV-BrN5YVV8.js → c4Diagram-5PPSVZJV-BHQWz7l2.js} +1 -1
  13. package/dist/web/assets/channel-CTWz7tRI.js +1 -0
  14. package/dist/web/assets/{chunk-2GRJ4B5K-gt2JPyrH.js → chunk-2GRJ4B5K-CaahxDVT.js} +1 -1
  15. package/dist/web/assets/{chunk-4I5QYGJK-Cg5GZ_-7.js → chunk-4I5QYGJK-Bat5hdXD.js} +1 -1
  16. package/dist/web/assets/{chunk-5RXB4S5H-BdhRGvxW.js → chunk-5RXB4S5H-jyonN41f.js} +1 -1
  17. package/dist/web/assets/{chunk-6Q2QTUOP-B1DYWNVJ.js → chunk-6Q2QTUOP-BBo9TYzL.js} +1 -1
  18. package/dist/web/assets/{chunk-7Z6QIM7H-D2YImamZ.js → chunk-7Z6QIM7H-CGSwsKhC.js} +1 -1
  19. package/dist/web/assets/{chunk-GF5L2VYU-CWtwL_CM.js → chunk-GF5L2VYU-Cuca3bAY.js} +1 -1
  20. package/dist/web/assets/{chunk-I66GZJ75-DdoGlxMt.js → chunk-I66GZJ75-DRdqLSyH.js} +1 -1
  21. package/dist/web/assets/{chunk-JQJVKLGR-CVWAp3XP.js → chunk-JQJVKLGR-8xhLkhs2.js} +1 -1
  22. package/dist/web/assets/{chunk-KBJHAD2P-BBacwg3J.js → chunk-KBJHAD2P-oOV-3e5-.js} +1 -1
  23. package/dist/web/assets/{chunk-NSK5VX7P-1AsXiBV3.js → chunk-NSK5VX7P-CXit1l3q.js} +1 -1
  24. package/dist/web/assets/{chunk-QR6OTTB3-DL2s5pNp.js → chunk-QR6OTTB3-DcdCU-01.js} +1 -1
  25. package/dist/web/assets/{chunk-UBXNYLIW-mu3fkNQI.js → chunk-UBXNYLIW-i72Hgljr.js} +1 -1
  26. package/dist/web/assets/{chunk-W5SLKNZC-BAwhMBBb.js → chunk-W5SLKNZC-DetAQo7M.js} +1 -1
  27. package/dist/web/assets/{chunk-WRU74C26-C7IeXZaP.js → chunk-WRU74C26-DDUvJFNs.js} +1 -1
  28. package/dist/web/assets/classDiagram-JCYQIIEL-BJQv9QuO.js +1 -0
  29. package/dist/web/assets/classDiagram-v2-OCEON4UE-BJQv9QuO.js +1 -0
  30. package/dist/web/assets/{cynefin-VYW2F7L2-DNisGvNv.js → cynefin-VYW2F7L2-fd-v6ani.js} +1 -1
  31. package/dist/web/assets/{cynefinDiagram-MW4NZA55-BA27gOTz.js → cynefinDiagram-MW4NZA55-DRsVpAC3.js} +1 -1
  32. package/dist/web/assets/{dagre-VZM6K2ZE-vo_1YAuM.js → dagre-VZM6K2ZE-BnRSs_fe.js} +1 -1
  33. package/dist/web/assets/{diagram-7IWD3JNH-DLLdUj0_.js → diagram-7IWD3JNH-D1g0PR4y.js} +1 -1
  34. package/dist/web/assets/{diagram-B4RE2ZJO-BV0WyNA-.js → diagram-B4RE2ZJO-DuDKsQLp.js} +1 -1
  35. package/dist/web/assets/{diagram-LBJQPF4R-DXxKSx2U.js → diagram-LBJQPF4R-COpzgGe7.js} +1 -1
  36. package/dist/web/assets/{diagram-Q27KOJAE-DsQ2GzIK.js → diagram-Q27KOJAE-DR2vRbPE.js} +1 -1
  37. package/dist/web/assets/{diagram-UB23O5K3-DfjElFP1.js → diagram-UB23O5K3-hs4VGPP7.js} +1 -1
  38. package/dist/web/assets/{ebnfDiagram-BXEA7PRR-cYu0XV1a.js → ebnfDiagram-BXEA7PRR-B_4NlK9q.js} +1 -1
  39. package/dist/web/assets/{erDiagram-JOGREHBK-DnsLBHaV.js → erDiagram-JOGREHBK-JyoZ3U-o.js} +1 -1
  40. package/dist/web/assets/eventmodeling-45OFAUF4-CfUhQK5g.js +1 -0
  41. package/dist/web/assets/flowDiagram-UKHOOZJN-FFZ82SPa.js +1 -0
  42. package/dist/web/assets/{ganttDiagram-PKOTCBZU-CVD5gq3E.js → ganttDiagram-PKOTCBZU-f37I0Twm.js} +1 -1
  43. package/dist/web/assets/{gitGraph-TEB2WS4Q-C_j0n-Kw.js → gitGraph-TEB2WS4Q-B_QWjr7j.js} +1 -1
  44. package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-qACevJmW.js → gitGraphDiagram-DS77QQ5N-AZsaC0hu.js} +1 -1
  45. package/dist/web/assets/{index-Coye1BY5.js → index-CY_JoUL3.js} +98 -98
  46. package/dist/web/assets/index-kK3hngnz.css +1 -0
  47. package/dist/web/assets/{info-DKCQHKI2-Cmkbz3Yi.js → info-DKCQHKI2-BRfisbEI.js} +1 -1
  48. package/dist/web/assets/{infoDiagram-6WML65LV-CDrAem7M.js → infoDiagram-6WML65LV-CaIb64N5.js} +1 -1
  49. package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-DXgCnFqW.js → ishikawaDiagram-WSZJBQD7-BoJrcTja.js} +1 -1
  50. package/dist/web/assets/{journeyDiagram-NVQOT4AX-1KW-I7DV.js → journeyDiagram-NVQOT4AX-CRR1dZy8.js} +1 -1
  51. package/dist/web/assets/{kanban-definition-27J2QSJJ-CCt75OuF.js → kanban-definition-27J2QSJJ-D5skDuP4.js} +1 -1
  52. package/dist/web/assets/{line-DQfkDi_r.js → line-BKKx0vSh.js} +1 -1
  53. package/dist/web/assets/{mermaid-parser.core-B7TMMtY0.js → mermaid-parser.core-DvZnIth0.js} +3 -3
  54. package/dist/web/assets/{mermaid.core-B93x-7Mk.js → mermaid.core-hykiOF_o.js} +4 -4
  55. package/dist/web/assets/{mindmap-definition-FAOFIHXS-Dyt28V6S.js → mindmap-definition-FAOFIHXS-BN2LQ4Yn.js} +1 -1
  56. package/dist/web/assets/{packet-7NZHBO7P-zQ0u35s3.js → packet-7NZHBO7P-CS5I0NlV.js} +1 -1
  57. package/dist/web/assets/{pegDiagram-VL7TDLO6-et0KHGIE.js → pegDiagram-VL7TDLO6-WvOdhbxo.js} +1 -1
  58. package/dist/web/assets/{pie-RZYD4A2V-uur86LkQ.js → pie-RZYD4A2V-BwNAZrQ1.js} +1 -1
  59. package/dist/web/assets/{pieDiagram-7S7Q4E2Y-c027WPTB.js → pieDiagram-7S7Q4E2Y-CbyrZjm7.js} +1 -1
  60. package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-BtSV4E-0.js → quadrantDiagram-CIZ2JOQS-CW9YBxAO.js} +1 -1
  61. package/dist/web/assets/{radar-I7S5WNFK-B_g0VZMF.js → radar-I7S5WNFK-DBoCLDNH.js} +1 -1
  62. package/dist/web/assets/{railroad-3IZDKUUU-VFLsM_Em.js → railroad-3IZDKUUU-BOjpYyDz.js} +1 -1
  63. package/dist/web/assets/railroad-abnf-AHOZXSZD-DQhDS2pf.js +1 -0
  64. package/dist/web/assets/railroad-ebnf-EBAXGLYW-Dtt2kOaN.js +1 -0
  65. package/dist/web/assets/railroad-peg-LSFZ7HO6-nfH8zno6.js +1 -0
  66. package/dist/web/assets/{railroadDiagram-AXF67PYL-uOtnxRbQ.js → railroadDiagram-AXF67PYL-BdOOOq8o.js} +1 -1
  67. package/dist/web/assets/{requirementDiagram-LRYGKXZP-DGSZAsj4.js → requirementDiagram-LRYGKXZP-DIYlSrTb.js} +1 -1
  68. package/dist/web/assets/{sankeyDiagram-W5VNT64P-CCDk8Vxt.js → sankeyDiagram-W5VNT64P-Cc1IGxp_.js} +1 -1
  69. package/dist/web/assets/{sequenceDiagram-SI44F4Z6-Ce-Jzdk5.js → sequenceDiagram-SI44F4Z6-CJsPO9Z9.js} +1 -1
  70. package/dist/web/assets/{stateDiagram-OKZ733FA-BYpYzyEa.js → stateDiagram-OKZ733FA-B033gpHZ.js} +1 -1
  71. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-wS01qj3t.js +1 -0
  72. package/dist/web/assets/{swimlanes-SLNWSIFB-BL-CUc6d.js → swimlanes-SLNWSIFB-C0Tstg7e.js} +1 -1
  73. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-DoAqfWAd.js +8 -0
  74. package/dist/web/assets/{timeline-definition-Z64GVDOM-p9pkof_K.js → timeline-definition-Z64GVDOM-BA3y-16W.js} +1 -1
  75. package/dist/web/assets/{treeView-QDETBFTQ-dRhXTU9p.js → treeView-QDETBFTQ-BUNq-JXr.js} +1 -1
  76. package/dist/web/assets/{treemap-6X3UGDF4-CZHe3dIh.js → treemap-6X3UGDF4-FIR_NbUo.js} +1 -1
  77. package/dist/web/assets/{vennDiagram-T6HMQDX7-ipODJegx.js → vennDiagram-T6HMQDX7-IeFFjnlq.js} +1 -1
  78. package/dist/web/assets/{wardley-OPB4EBWU-CxHlVjkF.js → wardley-OPB4EBWU-DQAX4LKo.js} +1 -1
  79. package/dist/web/assets/{wardleyDiagram-T6FBY63Y-YarkIncH.js → wardleyDiagram-T6FBY63Y-guGPPMAG.js} +1 -1
  80. package/dist/web/assets/{xychartDiagram-ELKLHX3M-Cw1M-7a4.js → xychartDiagram-ELKLHX3M-Cj0v359_.js} +1 -1
  81. package/dist/web/index.html +2 -2
  82. package/migrations/010_comment_post_references.sql +17 -0
  83. package/package.json +1 -1
  84. package/skills/rvw/SKILL.md +35 -3
  85. package/skills/rvw-walkthrough/SKILL.md +1 -1
  86. package/skills/rvw-watch-comments/SKILL.md +186 -67
  87. package/skills/rvw-watch-comments/scripts/auto-ack.mjs +211 -0
  88. package/skills/rvw-watch-comments/scripts/preflight.mjs +108 -0
  89. package/skills/rvw-watch-comments/scripts/rvw-command.mjs +80 -0
  90. package/skills/rvw-watch-comments/scripts/watch-driver.mjs +272 -0
  91. package/skills/rvw-watch-comments/scripts/watch-state.mjs +111 -8
  92. package/dist/web/assets/WalkthroughViewer-BVB3ETHG.js +0 -1
  93. package/dist/web/assets/architecture-TIHT7OUA-BjGWtjPU.js +0 -1
  94. package/dist/web/assets/channel-DkH22XO0.js +0 -1
  95. package/dist/web/assets/classDiagram-JCYQIIEL-C6Z4rmZR.js +0 -1
  96. package/dist/web/assets/classDiagram-v2-OCEON4UE-C6Z4rmZR.js +0 -1
  97. package/dist/web/assets/eventmodeling-45OFAUF4-BUAcQ5NU.js +0 -1
  98. package/dist/web/assets/flowDiagram-UKHOOZJN-hAe9ojjP.js +0 -1
  99. package/dist/web/assets/index-DFdYNMZE.css +0 -1
  100. package/dist/web/assets/railroad-abnf-AHOZXSZD-TVRi9_2L.js +0 -1
  101. package/dist/web/assets/railroad-ebnf-EBAXGLYW-B5B-JrS4.js +0 -1
  102. package/dist/web/assets/railroad-peg-LSFZ7HO6-CfIpa6ba.js +0 -1
  103. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-BJFG9cb2.js +0 -1
  104. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-BS8L8fb1.js +0 -8
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runRvw } from "./rvw-command.mjs";
4
+
5
+ const REQUIRED_NODE = [24, 15, 0];
6
+ const REQUIRED_PROTOCOL = 3;
7
+ const REQUIRED_CAPABILITIES = [
8
+ "agent.transport",
9
+ "comment.watch",
10
+ "comment.read",
11
+ "comment.reply",
12
+ "comment.edit",
13
+ "comment.codeReferences",
14
+ "pullRequest.sync",
15
+ ];
16
+
17
+ function nodeIsSupported(version) {
18
+ const actual = version.split(".").map(Number);
19
+ for (let index = 0; index < REQUIRED_NODE.length; index += 1) {
20
+ if ((actual[index] ?? 0) > REQUIRED_NODE[index]) return true;
21
+ if ((actual[index] ?? 0) < REQUIRED_NODE[index]) return false;
22
+ }
23
+ return true;
24
+ }
25
+
26
+ function commandSummary(result) {
27
+ return {
28
+ exitCode: result.code,
29
+ signal: result.signal,
30
+ output: result.json,
31
+ ...(result.stderr.trim() ? { stderr: result.stderr.trim() } : {}),
32
+ ...(!result.json && result.stdout.trim() ? { stdout: result.stdout.trim() } : {}),
33
+ };
34
+ }
35
+
36
+ async function main() {
37
+ const settled = await Promise.allSettled([
38
+ runRvw(["protocol", "--json"]),
39
+ runRvw(["agent", "status", "--json"]),
40
+ runRvw(["agent", "ping", "--json"]),
41
+ ]);
42
+ const [protocolResult, statusResult, pingResult] = settled.map((result) =>
43
+ result.status === "fulfilled" ? result.value : null,
44
+ );
45
+ const protocol = protocolResult?.json;
46
+ const agentStatus = statusResult?.json;
47
+ const agentPing = pingResult?.json;
48
+ const capabilities = Array.isArray(protocol?.capabilities) ? protocol.capabilities : [];
49
+ const missingCapabilities = REQUIRED_CAPABILITIES.filter(
50
+ (capability) => !capabilities.includes(capability),
51
+ );
52
+ const checks = {
53
+ nodeVersion: nodeIsSupported(process.versions.node),
54
+ rvwBinary: protocolResult !== null,
55
+ appVersion: typeof protocol?.appVersion === "string" && protocol.appVersion.length > 0,
56
+ protocol: protocol?.protocolVersion === REQUIRED_PROTOCOL,
57
+ capabilities: missingCapabilities.length === 0,
58
+ agentStatus: agentStatus?.ok === true && agentStatus.selectedTransport !== "unavailable",
59
+ agentPingInspected: agentPing !== null && typeof agentPing === "object",
60
+ agentPingConnected: agentPing?.ok === true && agentPing.connected === true,
61
+ };
62
+ const ok =
63
+ checks.nodeVersion &&
64
+ checks.rvwBinary &&
65
+ checks.appVersion &&
66
+ checks.protocol &&
67
+ checks.capabilities &&
68
+ checks.agentStatus &&
69
+ checks.agentPingInspected;
70
+ const output = {
71
+ ok,
72
+ node: {
73
+ version: process.versions.node,
74
+ required: ">=24.15.0",
75
+ ok: checks.nodeVersion,
76
+ },
77
+ rvw: {
78
+ binary: protocolResult?.binary ?? process.env.RVW_BIN ?? "rvw",
79
+ appVersion: typeof protocol?.appVersion === "string" ? protocol.appVersion : null,
80
+ protocolVersion:
81
+ typeof protocol?.protocolVersion === "number" ? protocol.protocolVersion : null,
82
+ requiredProtocolVersion: REQUIRED_PROTOCOL,
83
+ capabilities,
84
+ requiredCapabilities: REQUIRED_CAPABILITIES,
85
+ missingCapabilities,
86
+ },
87
+ agent: {
88
+ status: statusResult ? commandSummary(statusResult) : null,
89
+ ping: pingResult ? commandSummary(pingResult) : null,
90
+ pingRequired: false,
91
+ },
92
+ checks,
93
+ errors: settled.flatMap((result, index) =>
94
+ result.status === "rejected"
95
+ ? [
96
+ {
97
+ command: ["protocol", "agent status", "agent ping"][index],
98
+ error: String(result.reason),
99
+ },
100
+ ]
101
+ : [],
102
+ ),
103
+ };
104
+ process.stdout.write(`${JSON.stringify(output)}\n`);
105
+ if (!ok) process.exitCode = 1;
106
+ }
107
+
108
+ await main();
@@ -0,0 +1,80 @@
1
+ import { spawn } from "node:child_process";
2
+
3
+ const MAX_CAPTURE_BYTES = 40 * 1024 * 1024;
4
+
5
+ function commandPrefix() {
6
+ const binary = process.env.RVW_BIN?.trim() || "rvw";
7
+ const rawPrefix = process.env.RVW_BIN_ARGS_JSON;
8
+ if (!rawPrefix) return { binary, prefix: [] };
9
+ let parsed;
10
+ try {
11
+ parsed = JSON.parse(rawPrefix);
12
+ } catch (error) {
13
+ throw new Error(`RVW_BIN_ARGS_JSON must be a JSON string array: ${String(error)}`, {
14
+ cause: error,
15
+ });
16
+ }
17
+ if (!Array.isArray(parsed) || parsed.some((value) => typeof value !== "string")) {
18
+ throw new Error("RVW_BIN_ARGS_JSON must be a JSON string array");
19
+ }
20
+ return { binary, prefix: parsed };
21
+ }
22
+
23
+ export function spawnRvw(args, options = {}) {
24
+ const { binary, prefix } = commandPrefix();
25
+ return spawn(binary, [...prefix, ...args], {
26
+ stdio: options.stdio ?? ["pipe", "pipe", "pipe"],
27
+ env: process.env,
28
+ });
29
+ }
30
+
31
+ export async function runRvw(args, options = {}) {
32
+ const child = spawnRvw(args);
33
+ const stdout = [];
34
+ const stderr = [];
35
+ let stdoutBytes = 0;
36
+ let stderrBytes = 0;
37
+ child.stdout.on("data", (chunk) => {
38
+ stdoutBytes += chunk.length;
39
+ if (stdoutBytes <= MAX_CAPTURE_BYTES) stdout.push(chunk);
40
+ });
41
+ child.stderr.on("data", (chunk) => {
42
+ stderrBytes += chunk.length;
43
+ if (stderrBytes <= MAX_CAPTURE_BYTES) stderr.push(chunk);
44
+ });
45
+ if (options.input === undefined) child.stdin.end();
46
+ else
47
+ child.stdin.end(
48
+ typeof options.input === "string" ? options.input : JSON.stringify(options.input),
49
+ );
50
+ const status = await new Promise((resolve, reject) => {
51
+ child.once("error", reject);
52
+ child.once("close", (code, signal) => resolve({ code, signal }));
53
+ });
54
+ if (stdoutBytes > MAX_CAPTURE_BYTES || stderrBytes > MAX_CAPTURE_BYTES) {
55
+ throw new Error("rvw command output exceeded 40 MiB");
56
+ }
57
+ const stdoutText = Buffer.concat(stdout).toString("utf8");
58
+ const stderrText = Buffer.concat(stderr).toString("utf8");
59
+ let json = null;
60
+ if (stdoutText.trim()) {
61
+ try {
62
+ json = JSON.parse(stdoutText);
63
+ } catch {
64
+ // The caller reports malformed output with the original stdout attached.
65
+ }
66
+ }
67
+ return {
68
+ args,
69
+ binary: commandPrefix().binary,
70
+ code: status.code,
71
+ signal: status.signal,
72
+ stdout: stdoutText,
73
+ stderr: stderrText,
74
+ json,
75
+ };
76
+ }
77
+
78
+ export function successfulJson(result) {
79
+ return result.code === 0 && result.json && result.json.ok !== false;
80
+ }
@@ -0,0 +1,272 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from "node:child_process";
4
+ import path from "node:path";
5
+ import { StringDecoder } from "node:string_decoder";
6
+ import { fileURLToPath } from "node:url";
7
+ import { spawnRvw } from "./rvw-command.mjs";
8
+
9
+ const EXIT_WATCH = 20;
10
+ const EXIT_SEQUENCE = 21;
11
+ const EXIT_INGEST = 22;
12
+ const EXIT_AUTO_ACK = 23;
13
+ const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
14
+ const stateScript = path.join(scriptDirectory, "watch-state.mjs");
15
+ const autoAckScript = path.join(scriptDirectory, "auto-ack.mjs");
16
+
17
+ class DriverError extends Error {
18
+ constructor(message, exitCode, details = null) {
19
+ super(message);
20
+ this.exitCode = exitCode;
21
+ this.details = details;
22
+ }
23
+ }
24
+
25
+ function parseArguments(values) {
26
+ let state = null;
27
+ let autoAck = false;
28
+ for (let index = 0; index < values.length; index += 1) {
29
+ const value = values[index];
30
+ if (value === "--auto-ack") {
31
+ autoAck = true;
32
+ continue;
33
+ }
34
+ if (value === "--state") {
35
+ const next = values[index + 1];
36
+ if (!next || next.startsWith("--")) {
37
+ throw new DriverError("--state requires a path", EXIT_WATCH);
38
+ }
39
+ state = next;
40
+ index += 1;
41
+ continue;
42
+ }
43
+ if (!value.startsWith("--") && state === null) {
44
+ state = value;
45
+ continue;
46
+ }
47
+ throw new DriverError(`Unexpected argument: ${value}`, EXIT_WATCH);
48
+ }
49
+ if (!state) throw new DriverError("Pass the task state database path", EXIT_WATCH);
50
+ return { state: path.resolve(state), autoAck };
51
+ }
52
+
53
+ async function runNodeScript(script, args, input) {
54
+ const child = spawn(process.execPath, [script, ...args], {
55
+ stdio: ["pipe", "pipe", "pipe"],
56
+ });
57
+ const stdout = [];
58
+ const stderr = [];
59
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
60
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
61
+ child.stdin.end(input === undefined ? undefined : JSON.stringify(input));
62
+ const status = await new Promise((resolve, reject) => {
63
+ child.once("error", reject);
64
+ child.once("close", (code, signal) => resolve({ code, signal }));
65
+ });
66
+ const stdoutText = Buffer.concat(stdout).toString("utf8");
67
+ const stderrText = Buffer.concat(stderr).toString("utf8");
68
+ let json = null;
69
+ try {
70
+ json = JSON.parse(stdoutText);
71
+ } catch {
72
+ // The caller selects the appropriate driver exit contract.
73
+ }
74
+ return { ...status, stdout: stdoutText, stderr: stderrText, json };
75
+ }
76
+
77
+ async function stateCommand(state, command, args = [], input) {
78
+ const result = await runNodeScript(stateScript, [command, "--state", state, ...args], input);
79
+ if (result.code !== 0 || !result.json?.ok) {
80
+ throw new DriverError(`watch-state ${command} failed`, EXIT_INGEST, result);
81
+ }
82
+ return result.json;
83
+ }
84
+
85
+ function write(value) {
86
+ process.stdout.write(`${JSON.stringify(value)}\n`);
87
+ }
88
+
89
+ async function dispatchAutoAck(state, pullRequest) {
90
+ const pending = await stateCommand(state, "list");
91
+ if (!pending.pending.some((batch) => batch.pullRequest === pullRequest)) {
92
+ write({ ok: true, type: "queued", pullRequest, reason: "batch-not-yet-eligible" });
93
+ return;
94
+ }
95
+ const result = await runNodeScript(autoAckScript, [
96
+ "--state",
97
+ state,
98
+ "--pull-request",
99
+ pullRequest,
100
+ ]);
101
+ if (result.code !== 0 || !result.json?.ok) {
102
+ throw new DriverError(`auto-ack failed for ${pullRequest}`, EXIT_AUTO_ACK, result);
103
+ }
104
+ write({ ...result.json, type: "batch-acknowledged" });
105
+ }
106
+
107
+ async function dispatchPendingAutoAcks(state) {
108
+ const pending = await stateCommand(state, "list");
109
+ for (const batch of pending.pending) {
110
+ await dispatchAutoAck(state, batch.pullRequest);
111
+ }
112
+ }
113
+
114
+ async function processFrame(state, frame, autoAck, pullRequests) {
115
+ if (!frame || typeof frame !== "object" || typeof frame.type !== "string") {
116
+ throw new DriverError("Invalid RFC 7464 frame", EXIT_SEQUENCE, frame);
117
+ }
118
+ if (frame.type === "error") {
119
+ throw new DriverError("rvw comment watch returned an error frame", EXIT_WATCH, frame);
120
+ }
121
+ const ingested = await stateCommand(state, "ingest", [], frame);
122
+ if (frame.type === "ready") {
123
+ write({
124
+ ok: true,
125
+ type: "watch-ready",
126
+ cursor: ingested.cursor,
127
+ databaseId: frame.databaseId,
128
+ anchoredAtCurrent: frame.anchoredAtCurrent,
129
+ autoAck,
130
+ });
131
+ } else if (frame.type === "comment-posted" && ingested.status === "queued") {
132
+ if (autoAck) pullRequests.add(frame.event.pullRequestUrl);
133
+ else {
134
+ write({
135
+ ok: true,
136
+ type: "pending",
137
+ pullRequest: frame.event.pullRequestUrl,
138
+ commentRef: frame.event.commentRef,
139
+ cursor: ingested.cursor,
140
+ });
141
+ }
142
+ }
143
+ }
144
+
145
+ async function runWatchOnce(state, autoAck, stopping) {
146
+ const current = await stateCommand(state, "status");
147
+ const args = ["comment", "watch"];
148
+ if (current.cursor) args.push("--after", current.cursor);
149
+ args.push("--interval", "1", "--json-seq");
150
+ const child = spawnRvw(args);
151
+ stopping.child = child;
152
+ let stderr = "";
153
+ child.stderr.setEncoding("utf8");
154
+ child.stderr.on("data", (chunk) => {
155
+ stderr += chunk;
156
+ process.stderr.write(chunk);
157
+ });
158
+ const statusPromise = new Promise((resolve, reject) => {
159
+ child.once("error", reject);
160
+ child.once("close", (code, signal) => resolve({ code, signal }));
161
+ });
162
+ child.stdin.end();
163
+ const decoder = new StringDecoder("utf8");
164
+ let buffered = "";
165
+ let readySeen = false;
166
+ let stoppedSeen = false;
167
+ try {
168
+ for await (const chunk of child.stdout) {
169
+ buffered += decoder.write(chunk);
170
+ const lines = buffered.split("\n");
171
+ buffered = lines.pop() ?? "";
172
+ const pullRequests = new Set();
173
+ for (const rawLine of lines) {
174
+ if (!rawLine) continue;
175
+ if (!rawLine.startsWith("\u001e")) {
176
+ throw new DriverError("RFC 7464 frame does not start with RS", EXIT_SEQUENCE, rawLine);
177
+ }
178
+ let frame;
179
+ try {
180
+ frame = JSON.parse(rawLine.slice(1));
181
+ } catch (error) {
182
+ throw new DriverError("RFC 7464 frame is not valid JSON", EXIT_SEQUENCE, String(error));
183
+ }
184
+ readySeen ||= frame.type === "ready";
185
+ stoppedSeen ||= frame.type === "stopped";
186
+ await processFrame(state, frame, autoAck, pullRequests);
187
+ }
188
+ for (const pullRequest of pullRequests) await dispatchAutoAck(state, pullRequest);
189
+ }
190
+ buffered += decoder.end();
191
+ if (buffered.trim()) {
192
+ throw new DriverError(
193
+ "rvw comment watch ended with a truncated frame",
194
+ EXIT_SEQUENCE,
195
+ buffered,
196
+ );
197
+ }
198
+ const status = await statusPromise;
199
+ stopping.child = null;
200
+ return { ...status, stderr, readySeen, stoppedSeen };
201
+ } catch (error) {
202
+ child.kill("SIGTERM");
203
+ await statusPromise.catch(() => undefined);
204
+ stopping.child = null;
205
+ throw error;
206
+ }
207
+ }
208
+
209
+ function retryDelay(attempt) {
210
+ const base = Number(process.env.RVW_WATCH_DRIVER_RETRY_MS ?? "1000");
211
+ const safeBase = Number.isFinite(base) && base >= 0 ? base : 1000;
212
+ return Math.min(safeBase * 2 ** Math.max(0, attempt - 1), 30_000);
213
+ }
214
+
215
+ function delay(milliseconds) {
216
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
217
+ }
218
+
219
+ async function main() {
220
+ const { state, autoAck } = parseArguments(process.argv.slice(2));
221
+ const stopping = { requested: false, child: null };
222
+ const stop = () => {
223
+ stopping.requested = true;
224
+ stopping.child?.kill("SIGTERM");
225
+ };
226
+ process.once("SIGINT", stop);
227
+ process.once("SIGTERM", stop);
228
+ const maxRestarts = Number(process.env.RVW_WATCH_DRIVER_MAX_RESTARTS ?? "5");
229
+ let restarts = 0;
230
+ try {
231
+ while (!stopping.requested) {
232
+ const startedAt = Date.now();
233
+ if (autoAck) await dispatchPendingAutoAcks(state);
234
+ const result = await runWatchOnce(state, autoAck, stopping);
235
+ if (stopping.requested) return;
236
+ if (Date.now() - startedAt >= 30_000) restarts = 0;
237
+ restarts += 1;
238
+ if (restarts > maxRestarts) {
239
+ throw new DriverError("rvw comment watch exceeded its reconnect limit", EXIT_WATCH, result);
240
+ }
241
+ const delayMs = retryDelay(restarts);
242
+ write({
243
+ ok: true,
244
+ type: "reconnecting",
245
+ attempt: restarts,
246
+ delayMs,
247
+ lastExitCode: result.code,
248
+ lastSignal: result.signal,
249
+ readySeen: result.readySeen,
250
+ });
251
+ await delay(delayMs);
252
+ }
253
+ } finally {
254
+ process.off("SIGINT", stop);
255
+ process.off("SIGTERM", stop);
256
+ }
257
+ }
258
+
259
+ try {
260
+ await main();
261
+ } catch (error) {
262
+ const exitCode = error instanceof DriverError ? error.exitCode : EXIT_WATCH;
263
+ process.stderr.write(
264
+ `${JSON.stringify({
265
+ ok: false,
266
+ error: error instanceof Error ? error.message : String(error),
267
+ details: error?.details ?? null,
268
+ exitCode,
269
+ })}\n`,
270
+ );
271
+ process.exitCode = exitCode;
272
+ }
@@ -11,20 +11,37 @@ function fail(message) {
11
11
 
12
12
  function parseOptions(values) {
13
13
  const result = {};
14
+ const flags = new Set(["follow"]);
14
15
  for (let index = 0; index < values.length; index += 1) {
15
16
  const key = values[index];
16
17
  if (!key?.startsWith("--")) fail(`Unexpected argument: ${key ?? ""}`);
18
+ const name = key.slice(2);
17
19
  const value = values[index + 1];
18
- if (value === undefined || value.startsWith("--")) fail(`${key} requires a value`);
19
- result[key.slice(2)] = value;
20
- index += 1;
20
+ if (value === undefined || value.startsWith("--")) {
21
+ if (!flags.has(name)) fail(`${key} requires a value`);
22
+ result[name] = true;
23
+ } else {
24
+ result[name] = value;
25
+ index += 1;
26
+ }
21
27
  }
22
28
  return result;
23
29
  }
24
30
 
25
31
  function required(options, key) {
26
32
  const value = options[key];
27
- if (!value) fail(`--${key} is required`);
33
+ if (typeof value !== "string" || value.length === 0) fail(`--${key} is required`);
34
+ return value;
35
+ }
36
+
37
+ function positiveIntegerOption(options, key, defaultValue) {
38
+ const raw = options[key];
39
+ if (raw === undefined) return defaultValue;
40
+ if (typeof raw !== "string" || !/^\d+$/.test(raw)) {
41
+ fail(`--${key} must be a positive integer`);
42
+ }
43
+ const value = Number(raw);
44
+ if (!Number.isSafeInteger(value) || value < 1) fail(`--${key} must be a positive integer`);
28
45
  return value;
29
46
  }
30
47
 
@@ -39,6 +56,10 @@ function write(value) {
39
56
  process.stdout.write(`${JSON.stringify(value)}\n`);
40
57
  }
41
58
 
59
+ function waitFor(milliseconds) {
60
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
61
+ }
62
+
42
63
  function openState(statePath, create) {
43
64
  const absolute = path.resolve(statePath);
44
65
  if (create) {
@@ -376,6 +397,34 @@ function listPending(database) {
376
397
  });
377
398
  }
378
399
 
400
+ async function waitForPending(database, options) {
401
+ const intervalMs = positiveIntegerOption(options, "interval-ms", 250);
402
+ const follow = options.follow === true;
403
+ let wasNonEmpty = false;
404
+ do {
405
+ const pending = listPending(database);
406
+ const isNonEmpty = pending.length > 0;
407
+ if (isNonEmpty && !wasNonEmpty) {
408
+ write({
409
+ ok: true,
410
+ type: "pending",
411
+ pullRequests: pending.map((batch) => batch.pullRequest),
412
+ pending,
413
+ });
414
+ if (!follow) return;
415
+ }
416
+ wasNonEmpty = isNonEmpty;
417
+ await waitFor(intervalMs);
418
+ } while (follow || !wasNonEmpty);
419
+ }
420
+
421
+ function normalizeWriteKey(writeKey) {
422
+ if (typeof writeKey !== "string" || !/^[^/\s]+\/[^/\s]+$/.test(writeKey)) {
423
+ fail("--write-key must be owner/repository");
424
+ }
425
+ return writeKey.toLowerCase();
426
+ }
427
+
379
428
  function createBatch(database, pullRequestUrl, now) {
380
429
  const batchId = randomUUID();
381
430
  const events = database
@@ -405,10 +454,7 @@ function createBatch(database, pullRequestUrl, now) {
405
454
  }
406
455
 
407
456
  function claim(database, pullRequestUrl, writeKey) {
408
- if (writeKey !== undefined && !/^[^/\s]+\/[^/\s]+$/.test(writeKey)) {
409
- fail("--write-key must be owner/repository");
410
- }
411
- const canonicalWriteKey = writeKey?.toLowerCase();
457
+ const canonicalWriteKey = writeKey === undefined ? undefined : normalizeWriteKey(writeKey);
412
458
  return transaction(database, () => {
413
459
  const now = new Date().toISOString();
414
460
  const active = database
@@ -481,6 +527,36 @@ function claim(database, pullRequestUrl, writeKey) {
481
527
  });
482
528
  }
483
529
 
530
+ function reserveWrite(database, leaseId, writeKey) {
531
+ const canonicalWriteKey = normalizeWriteKey(writeKey);
532
+ return transaction(database, () => {
533
+ const batch = database
534
+ .prepare("SELECT * FROM batches WHERE lease_id = ? AND status = 'in_flight'")
535
+ .get(leaseId);
536
+ if (!batch) fail("Active lease was not found");
537
+ if (batch.write_key !== null && batch.write_key !== canonicalWriteKey) {
538
+ fail(`Active lease already owns repository ${batch.write_key}`);
539
+ }
540
+ try {
541
+ database
542
+ .prepare("UPDATE batches SET write_key = ?, updated_at = ? WHERE id = ?")
543
+ .run(canonicalWriteKey, new Date().toISOString(), batch.id);
544
+ } catch (error) {
545
+ if (String(error).includes("UNIQUE constraint failed")) {
546
+ fail(`Another write-capable batch owns repository ${canonicalWriteKey}`);
547
+ }
548
+ throw error;
549
+ }
550
+ return {
551
+ leaseId,
552
+ batchId: batch.id,
553
+ pullRequest: batch.pull_request_url,
554
+ writeKey: canonicalWriteKey,
555
+ status: batch.write_key === canonicalWriteKey ? "existing" : "reserved",
556
+ };
557
+ });
558
+ }
559
+
484
560
  function acknowledge(database, leaseId, input) {
485
561
  if (typeof input.commentRef !== "string" || input.commentRef.length === 0) {
486
562
  fail("commentRef is required");
@@ -635,6 +711,21 @@ function status(database) {
635
711
  const unbatched = database
636
712
  .prepare("SELECT count(*) AS count FROM events WHERE status = 'pending' AND batch_id IS NULL")
637
713
  .get();
714
+ const inFlightBatches = database
715
+ .prepare(
716
+ `SELECT id, pull_request_url, attempts, lease_id, write_key, updated_at
717
+ FROM batches WHERE status = 'in_flight' ORDER BY created_at`,
718
+ )
719
+ .all()
720
+ .map((batch) => ({
721
+ batchId: batch.id,
722
+ leaseId: batch.lease_id,
723
+ pullRequest: batch.pull_request_url,
724
+ attempts: Number(batch.attempts),
725
+ writeKey: batch.write_key,
726
+ updatedAt: batch.updated_at,
727
+ operations: batchStatusOperations(database, batch.id),
728
+ }));
638
729
  return {
639
730
  taskId: getMeta(database, "task_id"),
640
731
  databaseId: getMeta(database, "database_id"),
@@ -649,6 +740,7 @@ function status(database) {
649
740
  quarantined: batchCounts.quarantined ?? 0,
650
741
  unbatchedEvents: Number(unbatched.count),
651
742
  },
743
+ inFlightBatches,
652
744
  quarantinedBatches: quarantinedBatches(database),
653
745
  };
654
746
  }
@@ -683,6 +775,10 @@ async function main() {
683
775
  write({ ok: true, pending: listPending(database) });
684
776
  return;
685
777
  }
778
+ if (command === "wait") {
779
+ await waitForPending(database, options);
780
+ return;
781
+ }
686
782
  if (command === "claim") {
687
783
  write({
688
784
  ok: true,
@@ -690,6 +786,13 @@ async function main() {
690
786
  });
691
787
  return;
692
788
  }
789
+ if (command === "reserve-write") {
790
+ write({
791
+ ok: true,
792
+ ...reserveWrite(database, required(options, "lease"), required(options, "write-key")),
793
+ });
794
+ return;
795
+ }
693
796
  if (command === "complete") {
694
797
  write({ ok: true, ...complete(database, required(options, "lease"), await readInput()) });
695
798
  return;
@@ -1 +0,0 @@
1
- import{r as e}from"./rolldown-runtime-hePW80VL.js";import{B as t,F as n,H as r,L as i,M as a,N as o,P as s,T as c,U as l,V as u,b as d,c as f,d as p,g as m,l as h,o as g,s as _,w as v,x as y,y as b,z as x}from"./index-Coye1BY5.js";import{a as S,i as C,n as w,r as T,t as E}from"./markdown-source-map-_dS1q5Dg.js";var D=e(l(),1),O=r(),k=2400,A={...d,protocols:{...d.protocols,href:[...d.protocols?.href??[],`rvw-ref`]}};function j(e){return e?.startsWith(`rvw-ref:`)?e.slice(8):null}function M(e){return D.Children.toArray(e).map(e=>typeof e==`string`||typeof e==`number`?String(e):(0,D.isValidElement)(e)?M(e.props.children):``).join(``)}function N(e){return e.startLine===null||e.endLine===null?null:e.startLine===e.endLine?`L${e.startLine}`:`L${e.startLine}–${e.endLine}`}function P(e){let t=N(e);return t?`${e.path}:${t}`:e.path}function F(e,t){return!!(e&&t&&e.startLine===t.startLine&&e.endLine===t.endLine)}function I(){return(0,O.jsx)(`svg`,{"aria-hidden":`true`,viewBox:`0 0 16 16`,width:`16`,height:`16`,children:(0,O.jsx)(`path`,{fill:`currentColor`,d:`M5.5 1.75A1.75 1.75 0 0 1 7.25 0h1.5a1.75 1.75 0 0 1 1.75 1.75V2h3a.75.75 0 0 1 0 1.5h-.64l-.68 10.03A2.5 2.5 0 0 1 9.69 16H6.31a2.5 2.5 0 0 1-2.49-2.47L3.14 3.5H2.5a.75.75 0 0 1 0-1.5h3v-.25Zm1.5 0V2h2v-.25a.25.25 0 0 0-.25-.25h-1.5a.25.25 0 0 0-.25.25ZM4.65 3.5l.67 9.96c.02.58.46 1.04.99 1.04h3.38c.53 0 .97-.46.99-1.04l.67-9.96h-6.7Z`})})}function L({source:e,sourceRange:t,commented:n,bindings:r,references:i,themePreference:o,onOpenReference:s,onCommentRange:c,commentComposer:l}){let u=(0,D.useRef)(null),d=!!l;(0,D.useLayoutEffect)(()=>{if(!d)return;let e=window.requestAnimationFrame(()=>{u.current?.scrollIntoView({block:`nearest`,inline:`nearest`})});return()=>window.cancelAnimationFrame(e)},[d]);let f=(0,D.useCallback)(e=>{if(!(e instanceof Element))return;let t=e.closest(`[data-walkthrough-reference-id]`)?.dataset.walkthroughReferenceId;return t?i.get(t):void 0},[i]),p=(0,D.useCallback)(e=>{for(let t of e.querySelectorAll(`g.node, g.classGroup, g[data-id], g[id^='classId-']`)){let e=t.dataset.id??Object.keys(r).find(e=>t.id===e||t.id.includes(`-${e}`)||t.id.includes(`-${e}-`)||t.id.endsWith(`-${e}`));if(!e)continue;let n=r[e],a=n?i.get(n):void 0;a&&(t.classList.add(`walkthrough-diagram-node--linked`),t.setAttribute(`role`,`button`),t.setAttribute(`tabindex`,`0`),t.setAttribute(`aria-label`,`${a.label}をコードで開く`),t.dataset.walkthroughReferenceId=a.id)}},[r,i]);return(0,O.jsxs)(`div`,{className:`walkthrough-diagram-shell${n?` has-comment`:``}`,...t?{"data-rvw-navigation-start-line":t.startLine,"data-rvw-navigation-end-line":t.endLine}:{},children:[(0,O.jsxs)(`div`,{className:`walkthrough-diagram-toolbar`,children:[(0,O.jsx)(`span`,{children:`Mermaid diagram`}),(0,O.jsx)(`span`,{children:`nodeを選択して開く · Cmd/Ctrlで反対のペイン`}),t&&(0,O.jsxs)(`button`,{className:`button--quiet walkthrough-diagram-comment`,onPointerDown:e=>{e.button===0&&(e.preventDefault(),c(t))},onClick:e=>{e.detail===0&&c(t)},children:[(0,O.jsx)(a,{}),`図全体へコメント`]})]}),(0,O.jsx)(h,{className:`walkthrough-diagram`,source:e,themePreference:o,renderIdPrefix:`rvwWalkthrough`,errorClassName:`walkthrough-diagram-error`,onRendered:p,onPointerDown:e=>{let t=f(e.target);t&&(e.preventDefault(),e.stopPropagation(),s(t,e.metaKey||e.ctrlKey))},onKeyDown:e=>{if(e.key!==`Enter`&&e.key!==` `)return;let t=f(e.target);t&&(e.preventDefault(),s(t,!1))}}),l&&(0,O.jsx)(`div`,{className:`walkthrough-diagram-comment-composer`,ref:u,children:l})]})}var R=(0,D.createContext)(null);function z({range:e,pending:t,error:n,onCancel:r,onSubmit:i}){let[a,s]=(0,D.useState)(``),c=e.startLine===e.endLine?`L${e.startLine}へコメント`:`L${e.startLine}–${e.endLine}へコメント`;return(0,O.jsx)(o,{body:a,label:c,pending:t,error:n,validationError:void 0,placement:`line`,onBodyChange:s,onCancel:r,onSubmit:()=>i(a)})}var B=({children:e,node:t,...n})=>{let r=(0,D.useContext)(R),i=D.Children.count(e)===1?D.Children.only(e):null;if(!r||!(0,D.isValidElement)(i)||i.props.className!==`language-mermaid`)return(0,O.jsx)(`pre`,{...n,children:e});let a=T(t);return(0,O.jsx)(L,{source:M(i.props.children).trim(),sourceRange:a,commented:!!(a&&r.activeCommentId&&r.placedComments.some(({comment:e,placement:t})=>e.id===r.activeCommentId&&t.range&&t.range.startLine<=a.endLine&&t.range.endLine>=a.startLine)),bindings:r.diagramBindings,references:r.references,themePreference:r.themePreference,onOpenReference:r.onOpenReference,onCommentRange:r.onCommentRange,commentComposer:a&&F(a,r.diagramCommentRange)?(0,O.jsx)(z,{range:a,pending:r.diagramCommentPending,error:r.diagramCommentError,onCancel:r.onCancelDiagramComment,onSubmit:e=>r.onSubmitDiagramComment(a,e)},`${a.startLine}:${a.endLine}`):null})},V=(0,D.memo)(function({body:e,diagramBindings:t,references:n,placedComments:r,activeCommentId:i,selectedRange:a,selectionComposerOpen:o,diagramCommentRange:s,markdownSourceOid:l,themePreference:u,onOpenReference:d,onOpenRepositoryLink:h,onCommentActiveChange:g,onCommentRange:x,diagramCommentPending:T,diagramCommentError:E,onCancelDiagramComment:k,onSubmitDiagramComment:M}){let F=(0,D.useRef)(null),I=(0,D.useMemo)(()=>r.map(({comment:e,placement:t})=>({id:e.id,range:t.range})),[r]),L=(0,D.useMemo)(()=>new Map(r.map(({comment:e,placement:t})=>[e.id,{comment:e,placement:t}])),[r]),z=(0,D.useCallback)(({node:e,children:t,...n})=>{let r=w(e);return r.length===0?(0,O.jsx)(`div`,{...n,children:t}):(0,O.jsx)(`div`,{className:`markdown-inline-comments`,children:r.map(e=>{let t=L.get(e);return t?(0,O.jsx)(f,{comment:t.comment,variant:`inline`,placement:t.placement,markdownSourceOid:l,themePreference:u,onActiveChange:g,onOpenRepositoryLink:h},e):null})})},[L,l,g,h,u]);return(0,O.jsx)(R.Provider,{value:{diagramBindings:t,references:n,placedComments:r,activeCommentId:i,diagramCommentRange:s,themePreference:u,onOpenReference:d,onCommentRange:x,diagramCommentPending:T,diagramCommentError:E,onCancelDiagramComment:k,onSubmitDiagramComment:M},children:(0,O.jsx)(`article`,{className:`walkthrough-markdown`,children:(0,O.jsx)(v,{rehypePlugins:[y,[b,A],[S,{annotations:I,activeCommentId:i,selectedRange:a,composerOpen:o}]],remarkPlugins:[m],urlTransform:e=>e.startsWith(`rvw-ref:`)?e:c(e),components:{div:z,table:({children:e,node:t,...n})=>(0,O.jsx)(`div`,{className:`markdown-table-scroll`,children:(0,O.jsx)(`table`,{...C(t),...n,children:e})}),a:({href:e,children:t,node:r,...i})=>{let a=j(e),o=a?n.get(a):void 0;return o?(0,O.jsxs)(`button`,{className:`walkthrough-inline-reference`,title:P(o),onPointerDown:e=>{e.button===0&&(F.current={x:e.clientX,y:e.clientY})},onPointerUp:e=>{let t=F.current;F.current=null,t&&(Math.hypot(e.clientX-t.x,e.clientY-t.y)>4||(e.preventDefault(),e.stopPropagation(),d(o,e.metaKey||e.ctrlKey)))},onPointerCancel:()=>{F.current=null},onClick:e=>{e.preventDefault(),e.detail===0&&d(o,!1)},onContextMenu:e=>{(e.ctrlKey||e.metaKey)&&e.preventDefault()},children:[(0,O.jsx)(_,{path:o.path,kind:`file`}),(0,O.jsx)(`span`,{children:t}),(0,O.jsx)(`small`,{children:N(o)??`File`})]}):(0,O.jsx)(`a`,{...C(r),...i,href:e,children:t})},img:({alt:e,title:t,node:n})=>(0,O.jsx)(p,{alt:e,title:t,sourceAttributes:C(n)}),pre:B},children:e})})})});function H({walkthrough:e,comments:r,activeCommentId:c,navigationTarget:l,onNavigationApplied:d,themePreference:p,onCommentActiveChange:m,onOpenReference:h,onOpenRepositoryLink:_,onDeleted:v}){let y=u(),b=(0,D.useRef)(null),S=(0,D.useRef)(null),C=(0,D.useRef)(d);C.current=d;let[w,T]=(0,D.useState)(!1),[A,j]=(0,D.useState)(null),[M,N]=(0,D.useState)(null),[P,F]=(0,D.useState)(null),[L,R]=(0,D.useState)(``),[z,B]=(0,D.useState)(null),H=(0,D.useRef)(0),U=(0,D.useRef)(null),ee=(0,D.useMemo)(()=>new Map(e.references.map(e=>[e.id,e])),[e.references]);(0,D.useLayoutEffect)(()=>{if(!l||S.current===l.requestId)return;let e=l.requestId,t=window.requestAnimationFrame(()=>{let t=b.current;if(!t)return;if(l.line===null){t.scrollIntoView({behavior:`auto`,block:`start`,inline:`nearest`}),S.current=e,C.current(e);return}let n=String(l.line),r=t.querySelector(`[data-rvw-navigation-start-line="${n}"]`)??t.querySelector(`[data-rvw-source-start-line="${n}"]`),i=r?.closest(`details:not([open])`);i&&(i.open=!0),r&&(r.scrollIntoView({behavior:`auto`,block:`center`,inline:`nearest`}),S.current=e,C.current(e))});return()=>window.cancelAnimationFrame(t)},[l]);let W=(0,D.useCallback)((t,n)=>{H.current+=1;let r=H.current;U.current!==null&&(window.clearTimeout(U.current),U.current=null),B(null),h(e,t,n).then(e=>{r!==H.current||!e||(B(e),U.current=window.setTimeout(()=>{B(null),U.current=null},k))})},[h,e]);(0,D.useEffect)(()=>()=>{H.current+=1,U.current!==null&&window.clearTimeout(U.current)},[]);let G=r.filter(t=>t.target.kind===`walkthrough`&&t.target.walkthroughId===e.id),K=G.reduce((e,t)=>e+t.posts.length,0),q=t({queryKey:[`walkthrough-comment-placements`,e.id,e.body,G.map(e=>`${e.id}:${e.updatedAt}`)],queryFn:async()=>{let t=new URLSearchParams({kind:`walkthrough`,pullRequestId:e.pullRequestId,walkthroughId:e.id});return await Promise.all(G.map(async e=>({comment:e,placement:(await n(`/api/comments/${e.id}/placement?${t.toString()}`)).placement})))},enabled:G.length>0}),J=(0,D.useMemo)(()=>q.data??[],[q.data]),te=(0,D.useMemo)(()=>J.filter(({placement:e})=>!e.outdated&&e.range!==null),[J]),Y=(0,D.useMemo)(()=>J.filter(({comment:e,placement:t})=>e.target.kind===`walkthrough`&&e.target.startLine===null||t.outdated),[J]),X=x({mutationFn:async({range:t,body:r})=>await n(`/api/comments`,i({pullRequestId:e.pullRequestId,target:{kind:`walkthrough`,walkthroughId:e.id,startLine:t?.startLine??null,endLine:t?.endLine??null},body:r,authorLabel:`You`})),onSuccess:async()=>{window.getSelection()?.removeAllRanges(),R(``),T(!1),j(null),N(null),F(null),await y.invalidateQueries({queryKey:[`comments`]}),await y.invalidateQueries({queryKey:[`change-sequence`]})}}),Z=x({mutationFn:async()=>await n(`/api/pull-requests/${e.pullRequestId}/walkthroughs/${e.id}`,{...i({}),method:`DELETE`}),onSuccess:async()=>{v(e),await Promise.all([y.invalidateQueries({queryKey:[`walkthroughs`]}),y.invalidateQueries({queryKey:[`walkthrough`]}),y.invalidateQueries({queryKey:[`comments`]}),y.invalidateQueries({queryKey:[`change-sequence`]})])}}),Q=G.filter(e=>e.resolvedAt===null).length,ne=()=>{let e=G.length===0?`紐づくコメントはありません。`:`紐づくコメント ${G.length}件と投稿 ${K}件も削除されます。`;window.confirm(`このウォークスルーを削除します。\n${e}\nコピー済みの参照は無効になります。\n\nこの操作は元に戻せません。`)&&Z.mutate()},re=A?A.startLine===A.endLine?`L${A.startLine}`:`L${A.startLine}–${A.endLine}`:null,$=()=>{window.getSelection()?.removeAllRanges(),X.reset(),R(``),j(null),N(null),F(null)},ie=e=>{X.reset(),R(``),T(!1),j(null),N(e),F(`diagram`)},ae=A?(0,O.jsx)(o,{body:L,label:`${re??`選択範囲`}へコメント`,pending:X.isPending,error:X.error,validationError:void 0,placement:`line`,onBodyChange:R,onCancel:$,onSubmit:()=>X.mutate({range:A,body:L})}):null,oe=(0,O.jsx)(V,{body:e.body,diagramBindings:e.diagramBindings,references:ee,placedComments:te,activeCommentId:c,selectedRange:P===`selection`?A:null,selectionComposerOpen:P===`selection`,diagramCommentRange:M,markdownSourceOid:e.sourceOid,themePreference:p,onOpenReference:W,onOpenRepositoryLink:_,onCommentActiveChange:m,onCommentRange:ie,diagramCommentPending:X.isPending,diagramCommentError:X.error,onCancelDiagramComment:$,onSubmitDiagramComment:(e,t)=>X.mutate({range:e,body:t})});return(0,O.jsxs)(`div`,{className:`walkthrough-viewer`,ref:b,children:[(0,O.jsxs)(`header`,{className:`walkthrough-viewer-header`,children:[(0,O.jsxs)(`div`,{className:`walkthrough-viewer-title`,children:[(0,O.jsx)(`span`,{className:`walkthrough-viewer-icon`,children:(0,O.jsx)(g,{})}),(0,O.jsxs)(`div`,{className:`walkthrough-viewer-heading-copy`,children:[(0,O.jsx)(`span`,{className:`walkthrough-kicker`,children:`Agent-provided walkthrough`}),(0,O.jsx)(`h2`,{title:e.title,children:e.title})]})]}),(0,O.jsxs)(`div`,{className:`walkthrough-viewer-actions`,children:[(0,O.jsxs)(`button`,{className:`comment-icon-button walkthrough-comment-button`,"aria-label":`ウォークスルー全体へコメント`,title:`ウォークスルー全体へコメント`,"aria-pressed":w,onClick:()=>{X.reset(),R(``),j(null),N(null),F(null),T(e=>!e)},children:[(0,O.jsx)(a,{}),Q>0&&(0,O.jsx)(`span`,{children:Q})]}),(0,O.jsx)(`button`,{className:`comment-icon-button walkthrough-delete-button`,"aria-label":`ウォークスルーを削除`,title:`ウォークスルーを削除`,disabled:Z.isPending,onClick:ne,children:(0,O.jsx)(I,{})})]}),z&&(0,O.jsx)(`div`,{className:`walkthrough-reference-notice`,role:`status`,"aria-live":`polite`,children:z})]}),(0,O.jsx)(s,{error:Z.error}),(0,O.jsx)(s,{error:q.error}),(w||Y.length>0)&&(0,O.jsxs)(`div`,{className:`walkthrough-comment-area`,children:[w&&(0,O.jsx)(o,{body:L,label:`ウォークスルー全体へコメント`,pending:X.isPending,error:X.error,validationError:void 0,placement:`file`,onBodyChange:R,onCancel:()=>{X.reset(),R(``),T(!1)},onSubmit:()=>X.mutate({range:null,body:L})}),Y.map(({comment:t,placement:n})=>(0,O.jsx)(f,{comment:t,variant:`inline`,placement:n,markdownSourceOid:e.sourceOid,themePreference:p,onActiveChange:m,onOpenRepositoryLink:_},t.id))]}),(0,O.jsx)(E,{className:`walkthrough-markdown-surface`,selectedRange:A,composerOpen:P===`selection`,onSelection:e=>{if(!e){j(null),F(e=>e===`selection`?null:e);return}X.reset(),R(``),T(!1),j(e),N(null),F(null)},onOpenComposer:()=>F(`selection`),composer:ae,children:oe})]})}export{H as WalkthroughViewer};
@@ -1 +0,0 @@
1
- import"./chunk-KEIR6QF5-BICK3FdT.js";import{M as e}from"./mermaid-parser.core-B7TMMtY0.js";export{e as createArchitectureServices};
@@ -1 +0,0 @@
1
- import{at as e,ot as t}from"./chunk-I66GZJ75-DdoGlxMt.js";var n=(n,r)=>t.lang.round(e.parse(n)[r]);export{n as t};
@@ -1 +0,0 @@
1
- import{n as e}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import"./src-B6xuSHsQ.js";import"./chunk-I66GZJ75-DdoGlxMt.js";import"./chunk-NSK5VX7P-1AsXiBV3.js";import"./chunk-4I5QYGJK-Cg5GZ_-7.js";import"./chunk-WRU74C26-C7IeXZaP.js";import"./chunk-7BUUIJ7U-Bb538aSH.js";import"./chunk-UBXNYLIW-mu3fkNQI.js";import"./chunk-2GRJ4B5K-gt2JPyrH.js";import"./chunk-XXDRQBXY-DqAYeljN.js";import"./chunk-KBJHAD2P-BBacwg3J.js";import"./chunk-W5SLKNZC-BAwhMBBb.js";import"./chunk-QR6OTTB3-DL2s5pNp.js";import"./chunk-7Z6QIM7H-D2YImamZ.js";import{i as t,n,r,t as i}from"./chunk-GF5L2VYU-CWtwL_CM.js";import"./mermaid.core-B93x-7Mk.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};