@a9n-shoji/rvw 0.1.1 → 0.2.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 (105) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +39 -12
  3. package/dist/cli.mjs +486 -54
  4. package/dist/cli.mjs.map +4 -4
  5. package/dist/web/assets/DocumentViewer-CNgHpCuL.js +1560 -0
  6. package/dist/web/assets/WalkthroughViewer-BVB3ETHG.js +1 -0
  7. package/dist/web/assets/{abnfDiagram-N423BO3Z-Ck9r3vgI.js → abnfDiagram-N423BO3Z-0v-ndtqQ.js} +1 -1
  8. package/dist/web/assets/architecture-TIHT7OUA-BjGWtjPU.js +1 -0
  9. package/dist/web/assets/{architectureDiagram-T3A2C74G-CuKnSDyu.js → architectureDiagram-T3A2C74G-WSNJ-tZE.js} +1 -1
  10. package/dist/web/assets/{blockDiagram-VBNYF7ZC-BQg6daf-.js → blockDiagram-VBNYF7ZC-lmMbEUwI.js} +1 -1
  11. package/dist/web/assets/{c4Diagram-5PPSVZJV-DlH9a39M.js → c4Diagram-5PPSVZJV-BrN5YVV8.js} +1 -1
  12. package/dist/web/assets/channel-DkH22XO0.js +1 -0
  13. package/dist/web/assets/{chunk-2GRJ4B5K-BVLXBbte.js → chunk-2GRJ4B5K-gt2JPyrH.js} +1 -1
  14. package/dist/web/assets/{chunk-4I5QYGJK-DiwTdMc_.js → chunk-4I5QYGJK-Cg5GZ_-7.js} +1 -1
  15. package/dist/web/assets/{chunk-5RXB4S5H-DxlG3rKg.js → chunk-5RXB4S5H-BdhRGvxW.js} +1 -1
  16. package/dist/web/assets/{chunk-6Q2QTUOP-DOnYF_EB.js → chunk-6Q2QTUOP-B1DYWNVJ.js} +1 -1
  17. package/dist/web/assets/{chunk-7Z6QIM7H-e6n3pq6e.js → chunk-7Z6QIM7H-D2YImamZ.js} +1 -1
  18. package/dist/web/assets/{chunk-GF5L2VYU-FUD5NaJd.js → chunk-GF5L2VYU-CWtwL_CM.js} +1 -1
  19. package/dist/web/assets/{chunk-I66GZJ75-CgtIbVp9.js → chunk-I66GZJ75-DdoGlxMt.js} +1 -1
  20. package/dist/web/assets/{chunk-JQJVKLGR-m8efG8h-.js → chunk-JQJVKLGR-CVWAp3XP.js} +1 -1
  21. package/dist/web/assets/{chunk-KBJHAD2P-C0ildSCF.js → chunk-KBJHAD2P-BBacwg3J.js} +1 -1
  22. package/dist/web/assets/{chunk-NSK5VX7P-DJim9vRL.js → chunk-NSK5VX7P-1AsXiBV3.js} +1 -1
  23. package/dist/web/assets/{chunk-QR6OTTB3-D3ng5jSq.js → chunk-QR6OTTB3-DL2s5pNp.js} +1 -1
  24. package/dist/web/assets/{chunk-UBXNYLIW-r4FNgMUm.js → chunk-UBXNYLIW-mu3fkNQI.js} +1 -1
  25. package/dist/web/assets/{chunk-W5SLKNZC-BxwOtp_h.js → chunk-W5SLKNZC-BAwhMBBb.js} +1 -1
  26. package/dist/web/assets/{chunk-WRU74C26-DAa9M2Ku.js → chunk-WRU74C26-C7IeXZaP.js} +1 -1
  27. package/dist/web/assets/classDiagram-JCYQIIEL-C6Z4rmZR.js +1 -0
  28. package/dist/web/assets/classDiagram-v2-OCEON4UE-C6Z4rmZR.js +1 -0
  29. package/dist/web/assets/{cynefin-VYW2F7L2-D_MdPIfm.js → cynefin-VYW2F7L2-DNisGvNv.js} +1 -1
  30. package/dist/web/assets/{cynefinDiagram-MW4NZA55-BQ16VR2b.js → cynefinDiagram-MW4NZA55-BA27gOTz.js} +1 -1
  31. package/dist/web/assets/{dagre-VZM6K2ZE-BcYktfC7.js → dagre-VZM6K2ZE-vo_1YAuM.js} +1 -1
  32. package/dist/web/assets/{diagram-7IWD3JNH-BQ-4OKZa.js → diagram-7IWD3JNH-DLLdUj0_.js} +1 -1
  33. package/dist/web/assets/{diagram-B4RE2ZJO-C24yZ4GQ.js → diagram-B4RE2ZJO-BV0WyNA-.js} +1 -1
  34. package/dist/web/assets/{diagram-LBJQPF4R-wMs9ij7l.js → diagram-LBJQPF4R-DXxKSx2U.js} +1 -1
  35. package/dist/web/assets/{diagram-Q27KOJAE-CQR8qV1D.js → diagram-Q27KOJAE-DsQ2GzIK.js} +1 -1
  36. package/dist/web/assets/{diagram-UB23O5K3-D8Fm2CWy.js → diagram-UB23O5K3-DfjElFP1.js} +1 -1
  37. package/dist/web/assets/{ebnfDiagram-BXEA7PRR-DctbWdRA.js → ebnfDiagram-BXEA7PRR-cYu0XV1a.js} +1 -1
  38. package/dist/web/assets/{erDiagram-JOGREHBK-CMXJzItT.js → erDiagram-JOGREHBK-DnsLBHaV.js} +1 -1
  39. package/dist/web/assets/eventmodeling-45OFAUF4-BUAcQ5NU.js +1 -0
  40. package/dist/web/assets/flowDiagram-UKHOOZJN-hAe9ojjP.js +1 -0
  41. package/dist/web/assets/{ganttDiagram-PKOTCBZU-B6UdI6h5.js → ganttDiagram-PKOTCBZU-CVD5gq3E.js} +1 -1
  42. package/dist/web/assets/{gitGraph-TEB2WS4Q-C7YUJfov.js → gitGraph-TEB2WS4Q-C_j0n-Kw.js} +1 -1
  43. package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-CKIazrRX.js → gitGraphDiagram-DS77QQ5N-qACevJmW.js} +1 -1
  44. package/dist/web/assets/index-Coye1BY5.js +384 -0
  45. package/dist/web/assets/{index-B8_aim9s.css → index-DFdYNMZE.css} +1 -1
  46. package/dist/web/assets/{info-DKCQHKI2-Dj4QbgxZ.js → info-DKCQHKI2-Cmkbz3Yi.js} +1 -1
  47. package/dist/web/assets/{infoDiagram-6WML65LV-CkPZHoSH.js → infoDiagram-6WML65LV-CDrAem7M.js} +1 -1
  48. package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-C1U-aqaM.js → ishikawaDiagram-WSZJBQD7-DXgCnFqW.js} +1 -1
  49. package/dist/web/assets/{journeyDiagram-NVQOT4AX-BFmnFTx5.js → journeyDiagram-NVQOT4AX-1KW-I7DV.js} +1 -1
  50. package/dist/web/assets/{kanban-definition-27J2QSJJ-C5KXffho.js → kanban-definition-27J2QSJJ-CCt75OuF.js} +1 -1
  51. package/dist/web/assets/{line-CPlnGb39.js → line-DQfkDi_r.js} +1 -1
  52. package/dist/web/assets/markdown-source-map-_dS1q5Dg.js +7 -0
  53. package/dist/web/assets/{mermaid-parser.core-CQYajAMQ.js → mermaid-parser.core-B7TMMtY0.js} +3 -3
  54. package/dist/web/assets/{mermaid.core-SFGI5XV8.js → mermaid.core-B93x-7Mk.js} +4 -4
  55. package/dist/web/assets/{mindmap-definition-FAOFIHXS-Dxek7WcS.js → mindmap-definition-FAOFIHXS-Dyt28V6S.js} +1 -1
  56. package/dist/web/assets/{packet-7NZHBO7P-CAR0tDyo.js → packet-7NZHBO7P-zQ0u35s3.js} +1 -1
  57. package/dist/web/assets/{pegDiagram-VL7TDLO6-CN_CBGXn.js → pegDiagram-VL7TDLO6-et0KHGIE.js} +1 -1
  58. package/dist/web/assets/{pie-RZYD4A2V-CAy1ZkLF.js → pie-RZYD4A2V-uur86LkQ.js} +1 -1
  59. package/dist/web/assets/{pieDiagram-7S7Q4E2Y-Do--6qr_.js → pieDiagram-7S7Q4E2Y-c027WPTB.js} +1 -1
  60. package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-_d72nP_y.js → quadrantDiagram-CIZ2JOQS-BtSV4E-0.js} +1 -1
  61. package/dist/web/assets/{radar-I7S5WNFK-D_NREiMx.js → radar-I7S5WNFK-B_g0VZMF.js} +1 -1
  62. package/dist/web/assets/{railroad-3IZDKUUU-DU7RDpQt.js → railroad-3IZDKUUU-VFLsM_Em.js} +1 -1
  63. package/dist/web/assets/railroad-abnf-AHOZXSZD-TVRi9_2L.js +1 -0
  64. package/dist/web/assets/railroad-ebnf-EBAXGLYW-B5B-JrS4.js +1 -0
  65. package/dist/web/assets/railroad-peg-LSFZ7HO6-CfIpa6ba.js +1 -0
  66. package/dist/web/assets/{railroadDiagram-AXF67PYL-DNZ982Bc.js → railroadDiagram-AXF67PYL-uOtnxRbQ.js} +1 -1
  67. package/dist/web/assets/{requirementDiagram-LRYGKXZP-BNEqDsk3.js → requirementDiagram-LRYGKXZP-DGSZAsj4.js} +1 -1
  68. package/dist/web/assets/{sankeyDiagram-W5VNT64P-BBdI8stM.js → sankeyDiagram-W5VNT64P-CCDk8Vxt.js} +1 -1
  69. package/dist/web/assets/{sequenceDiagram-SI44F4Z6-3zzNyCI7.js → sequenceDiagram-SI44F4Z6-Ce-Jzdk5.js} +1 -1
  70. package/dist/web/assets/{stateDiagram-OKZ733FA-PtqkKvef.js → stateDiagram-OKZ733FA-BYpYzyEa.js} +1 -1
  71. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-BJFG9cb2.js +1 -0
  72. package/dist/web/assets/{swimlanes-SLNWSIFB-CPWjF8YR.js → swimlanes-SLNWSIFB-BL-CUc6d.js} +1 -1
  73. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-BS8L8fb1.js +8 -0
  74. package/dist/web/assets/{timeline-definition-Z64GVDOM-Cyh2AdbF.js → timeline-definition-Z64GVDOM-p9pkof_K.js} +1 -1
  75. package/dist/web/assets/{treeView-QDETBFTQ-zPmED5io.js → treeView-QDETBFTQ-dRhXTU9p.js} +1 -1
  76. package/dist/web/assets/{treemap-6X3UGDF4-Cd2gjC8X.js → treemap-6X3UGDF4-CZHe3dIh.js} +1 -1
  77. package/dist/web/assets/{vennDiagram-T6HMQDX7-BaNEKHnw.js → vennDiagram-T6HMQDX7-ipODJegx.js} +1 -1
  78. package/dist/web/assets/{wardley-OPB4EBWU-B5ry0Fo7.js → wardley-OPB4EBWU-CxHlVjkF.js} +1 -1
  79. package/dist/web/assets/{wardleyDiagram-T6FBY63Y-BHvM4Wb0.js → wardleyDiagram-T6FBY63Y-YarkIncH.js} +1 -1
  80. package/dist/web/assets/{xychartDiagram-ELKLHX3M-Qgza2Rjo.js → xychartDiagram-ELKLHX3M-Cw1M-7a4.js} +1 -1
  81. package/dist/web/index.html +2 -2
  82. package/migrations/009_comment_watch.sql +30 -0
  83. package/package.json +1 -1
  84. package/skills/rvw/SKILL.md +84 -5
  85. package/skills/rvw/agents/openai.yaml +2 -2
  86. package/skills/rvw-walkthrough/SKILL.md +17 -8
  87. package/skills/rvw-walkthrough/references/walkthrough-authoring.md +105 -0
  88. package/skills/rvw-watch-comments/SKILL.md +175 -0
  89. package/skills/rvw-watch-comments/agents/openai.yaml +4 -0
  90. package/skills/rvw-watch-comments/scripts/watch-state.mjs +724 -0
  91. package/dist/web/assets/DocumentViewer-ERTovlNY.js +0 -1560
  92. package/dist/web/assets/MarkdownImagePlaceholder-BseUb-vG.js +0 -36
  93. package/dist/web/assets/WalkthroughViewer-4HaKxbeO.js +0 -2
  94. package/dist/web/assets/architecture-TIHT7OUA-9PYQORPB.js +0 -1
  95. package/dist/web/assets/channel-zAJ9N7Xu.js +0 -1
  96. package/dist/web/assets/classDiagram-JCYQIIEL-DW7PwlYE.js +0 -1
  97. package/dist/web/assets/classDiagram-v2-OCEON4UE-DW7PwlYE.js +0 -1
  98. package/dist/web/assets/eventmodeling-45OFAUF4-mByhu-2v.js +0 -1
  99. package/dist/web/assets/flowDiagram-UKHOOZJN-DTgITdYW.js +0 -1
  100. package/dist/web/assets/index-DAM-rdgA.js +0 -355
  101. package/dist/web/assets/railroad-abnf-AHOZXSZD-D0hTyA1H.js +0 -1
  102. package/dist/web/assets/railroad-ebnf-EBAXGLYW-DzkmoqWU.js +0 -1
  103. package/dist/web/assets/railroad-peg-LSFZ7HO6-u3hJ9iID.js +0 -1
  104. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-BY3gjuqo.js +0 -1
  105. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-DK-u00Uf.js +0 -8
package/dist/cli.mjs CHANGED
@@ -18512,7 +18512,7 @@ function date4(params) {
18512
18512
  config(en_default());
18513
18513
 
18514
18514
  // src/infrastructure/db/database.ts
18515
- import { randomUUID } from "node:crypto";
18515
+ import { createHash, randomUUID } from "node:crypto";
18516
18516
  import {
18517
18517
  chmodSync,
18518
18518
  closeSync,
@@ -18736,6 +18736,9 @@ function mapPullRequest(row) {
18736
18736
  repository: stringValue(row, "repository"),
18737
18737
  number: numberValue(row, "number"),
18738
18738
  url: stringValue(row, "github_url"),
18739
+ latestAuthorLogin: nullableString(row, "latest_author_login"),
18740
+ latestHeadRepositoryOwner: nullableString(row, "latest_head_repository_owner"),
18741
+ latestHeadRepositoryName: nullableString(row, "latest_head_repository_name"),
18739
18742
  localRepositoryPath: stringValue(row, "local_repository_path"),
18740
18743
  gitCommonDir: stringValue(row, "git_common_dir"),
18741
18744
  latestTitle: stringValue(row, "latest_title"),
@@ -18751,6 +18754,21 @@ function mapPullRequest(row) {
18751
18754
  updatedAt: stringValue(row, "updated_at")
18752
18755
  };
18753
18756
  }
18757
+ function mapCommentPost(row) {
18758
+ return {
18759
+ id: stringValue(row, "id"),
18760
+ commentId: stringValue(row, "comment_id"),
18761
+ body: stringValue(row, "body"),
18762
+ relatedCommitOid: nullableString(row, "related_commit_oid"),
18763
+ authorLabel: nullableString(row, "author_label"),
18764
+ isRoot: numberValue(row, "is_root") === 1,
18765
+ createdAt: stringValue(row, "created_at"),
18766
+ updatedAt: stringValue(row, "updated_at")
18767
+ };
18768
+ }
18769
+ function hashIdempotencyKey(value) {
18770
+ return createHash("sha256").update(value, "utf8").digest("hex");
18771
+ }
18754
18772
  function findMigrationsDirectory(explicit) {
18755
18773
  if (explicit) return explicit;
18756
18774
  const moduleDirectory = path4.dirname(fileURLToPath2(import.meta.url));
@@ -18982,6 +19000,33 @@ var RvwDatabase = class {
18982
19000
  const row = this.database.prepare("SELECT value FROM app_meta WHERE key = 'change_sequence'").get();
18983
19001
  return Number(stringValue(row, "value"));
18984
19002
  }
19003
+ getCommentWatchDatabaseId() {
19004
+ const row = this.database.prepare("SELECT value FROM app_meta WHERE key = 'comment_watch_database_id'").get();
19005
+ if (!row) throw new RvwError("DATABASE_ERROR", "comment watch\u306Edatabase ID\u304C\u3042\u308A\u307E\u305B\u3093\u3002");
19006
+ return stringValue(row, "value");
19007
+ }
19008
+ getLatestCommentPostEventSequence() {
19009
+ const row = this.database.prepare("SELECT COALESCE(MAX(sequence), 0) AS sequence FROM comment_post_events").get();
19010
+ return numberValue(row, "sequence");
19011
+ }
19012
+ listCommentPostEvents(afterSequence, limit) {
19013
+ const rows = this.database.prepare(
19014
+ `SELECT e.*, CASE WHEN p.id IS NULL THEN 1 ELSE 0 END AS deleted
19015
+ FROM comment_post_events e
19016
+ LEFT JOIN comment_posts p ON p.id = e.post_id
19017
+ WHERE e.sequence > ?
19018
+ ORDER BY e.sequence ASC
19019
+ LIMIT ?`
19020
+ ).all(afterSequence, limit);
19021
+ return rows.map((row) => ({
19022
+ sequence: numberValue(row, "sequence"),
19023
+ createdAt: stringValue(row, "created_at"),
19024
+ postId: stringValue(row, "post_id"),
19025
+ commentRef: stringValue(row, "comment_ref"),
19026
+ pullRequestUrl: stringValue(row, "pull_request_url"),
19027
+ deleted: numberValue(row, "deleted") === 1
19028
+ }));
19029
+ }
18985
19030
  getThemePreference() {
18986
19031
  const row = this.database.prepare("SELECT value FROM app_meta WHERE key = 'theme_preference'").get();
18987
19032
  if (!row) {
@@ -19037,14 +19082,18 @@ var RvwDatabase = class {
19037
19082
  `INSERT INTO pull_requests(
19038
19083
  id, host, owner, repository, number, github_url,
19039
19084
  local_repository_path, git_common_dir,
19085
+ latest_author_login, latest_head_repository_owner, latest_head_repository_name,
19040
19086
  latest_title, latest_body, latest_base_ref_name, latest_head_ref_name,
19041
19087
  latest_base_oid, latest_head_oid, github_updated_at, fetched_at,
19042
19088
  created_at, updated_at, latest_comparison_base_oid
19043
- ) VALUES (?, 'github.com', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
19089
+ ) VALUES (?, 'github.com', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
19044
19090
  ON CONFLICT(host, owner, repository, number) DO UPDATE SET
19045
19091
  github_url = excluded.github_url,
19046
19092
  local_repository_path = excluded.local_repository_path,
19047
19093
  git_common_dir = excluded.git_common_dir,
19094
+ latest_author_login = excluded.latest_author_login,
19095
+ latest_head_repository_owner = excluded.latest_head_repository_owner,
19096
+ latest_head_repository_name = excluded.latest_head_repository_name,
19048
19097
  latest_title = excluded.latest_title,
19049
19098
  latest_body = excluded.latest_body,
19050
19099
  latest_base_ref_name = excluded.latest_base_ref_name,
@@ -19063,6 +19112,9 @@ var RvwDatabase = class {
19063
19112
  github.url,
19064
19113
  repository.localRepositoryPath,
19065
19114
  repository.gitCommonDir,
19115
+ github.authorLogin,
19116
+ github.headRepositoryOwner,
19117
+ github.headRepositoryName,
19066
19118
  github.title,
19067
19119
  github.body,
19068
19120
  github.baseRefName,
@@ -19303,6 +19355,8 @@ var RvwDatabase = class {
19303
19355
  const now = (/* @__PURE__ */ new Date()).toISOString();
19304
19356
  const id = randomUUID();
19305
19357
  const postId = randomUUID();
19358
+ const pullRequest = this.getPullRequest(input.pullRequestId);
19359
+ if (!pullRequest) throw new RvwError("PR_NOT_FOUND", "Pull Request\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002");
19306
19360
  this.immediateTransaction(() => {
19307
19361
  this.database.prepare(
19308
19362
  "INSERT INTO comments(id, pull_request_id, created_head_oid, resolved_at, created_at, updated_at) VALUES (?, ?, ?, NULL, ?, ?)"
@@ -19311,6 +19365,11 @@ var RvwDatabase = class {
19311
19365
  this.database.prepare(
19312
19366
  "INSERT INTO comment_posts(id, comment_id, body, related_commit_oid, author_label, is_root, created_at, updated_at) VALUES (?, ?, ?, NULL, ?, 1, ?, ?)"
19313
19367
  ).run(postId, id, input.body, input.authorLabel ?? null, now, now);
19368
+ this.database.prepare(
19369
+ `INSERT INTO comment_post_events(
19370
+ post_id, comment_ref, pull_request_url, created_at
19371
+ ) VALUES (?, ?, ?, ?)`
19372
+ ).run(postId, formatCommentUri(id), pullRequest.url, now);
19314
19373
  this.incrementChangeSequence();
19315
19374
  });
19316
19375
  const comment = this.getComment(id);
@@ -19474,56 +19533,94 @@ var RvwDatabase = class {
19474
19533
  listCommentPosts(commentId) {
19475
19534
  return this.database.prepare(
19476
19535
  "SELECT * FROM comment_posts WHERE comment_id = ? ORDER BY is_root DESC, created_at ASC, id ASC"
19477
- ).all(commentId).map((row) => ({
19478
- id: stringValue(row, "id"),
19479
- commentId: stringValue(row, "comment_id"),
19480
- body: stringValue(row, "body"),
19481
- relatedCommitOid: nullableString(row, "related_commit_oid"),
19482
- authorLabel: nullableString(row, "author_label"),
19483
- isRoot: numberValue(row, "is_root") === 1,
19484
- createdAt: stringValue(row, "created_at"),
19485
- updatedAt: stringValue(row, "updated_at")
19486
- }));
19536
+ ).all(commentId).map(mapCommentPost);
19487
19537
  }
19488
19538
  insertReply(commentId, input, incrementSequence = true) {
19489
- if (!this.getComment(commentId))
19490
- throw new RvwError("COMMENT_NOT_FOUND", "\u30B3\u30E1\u30F3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002");
19491
- const id = randomUUID();
19492
- const now = (/* @__PURE__ */ new Date()).toISOString();
19539
+ let result;
19493
19540
  const write = () => {
19541
+ const comment = this.getComment(commentId);
19542
+ if (!comment) {
19543
+ throw new RvwError("COMMENT_NOT_FOUND", "\u30B3\u30E1\u30F3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002");
19544
+ }
19545
+ const pullRequest = this.getPullRequest(comment.pullRequestId);
19546
+ if (!pullRequest) throw new RvwError("PR_NOT_FOUND", "Pull Request\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002");
19547
+ const relatedCommitOid = input.relatedCommitOid ?? null;
19548
+ const authorLabel = input.authorLabel ?? null;
19549
+ const idempotencyKeyHash = input.idempotencyKey === void 0 ? null : hashIdempotencyKey(input.idempotencyKey);
19550
+ const idempotencyRequestHash2 = idempotencyKeyHash === null ? null : input.idempotencyRequestHash ?? hashIdempotencyKey(
19551
+ JSON.stringify({
19552
+ operation: "comment.reply",
19553
+ commentId,
19554
+ body: input.body,
19555
+ relatedCommitOid,
19556
+ authorLabel
19557
+ })
19558
+ );
19559
+ if (idempotencyKeyHash !== null) {
19560
+ const existingRow = this.database.prepare("SELECT * FROM comment_reply_idempotency WHERE key_hash = ?").get(idempotencyKeyHash);
19561
+ if (existingRow) {
19562
+ if (stringValue(existingRow, "request_hash") !== idempotencyRequestHash2) {
19563
+ throw new RvwError(
19564
+ "IDEMPOTENCY_CONFLICT",
19565
+ "\u540C\u3058idempotencyKey\u304C\u5225\u306Ecomment reply\u306B\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059\u3002"
19566
+ );
19567
+ }
19568
+ const postId = stringValue(existingRow, "post_id");
19569
+ const postRow = this.database.prepare("SELECT * FROM comment_posts WHERE id = ?").get(postId);
19570
+ if (!postRow) {
19571
+ throw new RvwError(
19572
+ "IDEMPOTENCY_RESULT_DELETED",
19573
+ "\u3053\u306EidempotencyKey\u3067\u4F5C\u6210\u3057\u305Fcomment reply\u306F\u65E2\u306B\u524A\u9664\u3055\u308C\u3066\u3044\u307E\u3059\u3002",
19574
+ { details: { postId } }
19575
+ );
19576
+ }
19577
+ const existing = mapCommentPost(postRow);
19578
+ result = existing;
19579
+ return;
19580
+ }
19581
+ }
19582
+ const id = randomUUID();
19583
+ const now = (/* @__PURE__ */ new Date()).toISOString();
19494
19584
  this.database.prepare(
19495
19585
  "INSERT INTO comment_posts(id, comment_id, body, related_commit_oid, author_label, is_root, created_at, updated_at) VALUES (?, ?, ?, ?, ?, 0, ?, ?)"
19496
- ).run(
19497
- id,
19498
- commentId,
19499
- input.body,
19500
- input.relatedCommitOid ?? null,
19501
- input.authorLabel ?? null,
19502
- now,
19503
- now
19504
- );
19586
+ ).run(id, commentId, input.body, relatedCommitOid, authorLabel, now, now);
19587
+ this.database.prepare(
19588
+ `INSERT INTO comment_post_events(
19589
+ post_id, comment_ref, pull_request_url, created_at
19590
+ ) VALUES (?, ?, ?, ?)`
19591
+ ).run(id, comment.ref, pullRequest.url, now);
19592
+ if (idempotencyKeyHash !== null && idempotencyRequestHash2 !== null) {
19593
+ this.database.prepare(
19594
+ "INSERT INTO comment_reply_idempotency(key_hash, request_hash, post_id, created_at) VALUES (?, ?, ?, ?)"
19595
+ ).run(idempotencyKeyHash, idempotencyRequestHash2, id, now);
19596
+ }
19505
19597
  this.database.prepare("UPDATE comments SET updated_at = ? WHERE id = ?").run(now, commentId);
19506
19598
  if (incrementSequence) this.incrementChangeSequence();
19599
+ result = {
19600
+ id,
19601
+ commentId,
19602
+ body: input.body,
19603
+ relatedCommitOid,
19604
+ authorLabel,
19605
+ isRoot: false,
19606
+ createdAt: now,
19607
+ updatedAt: now
19608
+ };
19507
19609
  };
19508
19610
  if (incrementSequence) this.immediateTransaction(write);
19509
19611
  else write();
19510
- return {
19511
- id,
19512
- commentId,
19513
- body: input.body,
19514
- relatedCommitOid: input.relatedCommitOid ?? null,
19515
- authorLabel: input.authorLabel ?? null,
19516
- isRoot: false,
19517
- createdAt: now,
19518
- updatedAt: now
19519
- };
19612
+ if (!result) throw new RvwError("DATABASE_ERROR", "\u8FD4\u4FE1\u7D50\u679C\u3092\u8AAD\u307F\u51FA\u305B\u307E\u305B\u3093\u3002");
19613
+ return result;
19520
19614
  }
19521
- updateCommentPost(commentId, postId, body) {
19615
+ updateCommentPost(commentId, postId, body, relatedCommitOid) {
19522
19616
  const now = (/* @__PURE__ */ new Date()).toISOString();
19523
19617
  this.immediateTransaction(() => {
19524
- const result = this.database.prepare(
19618
+ const result = relatedCommitOid === void 0 ? this.database.prepare(
19525
19619
  "UPDATE comment_posts SET body = ?, updated_at = ? WHERE id = ? AND comment_id = ?"
19526
- ).run(body, now, postId, commentId);
19620
+ ).run(body, now, postId, commentId) : this.database.prepare(
19621
+ `UPDATE comment_posts SET body = ?, related_commit_oid = ?, updated_at = ?
19622
+ WHERE id = ? AND comment_id = ?`
19623
+ ).run(body, relatedCommitOid, now, postId, commentId);
19527
19624
  if (Number(result.changes) === 0) {
19528
19625
  throw new RvwError("COMMENT_POST_NOT_FOUND", "\u30B3\u30E1\u30F3\u30C8\u6295\u7A3F\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002", {
19529
19626
  status: 404
@@ -19599,7 +19696,9 @@ var RvwDatabase = class {
19599
19696
  {
19600
19697
  body: update.reply,
19601
19698
  relatedCommitOid,
19602
- authorLabel: update.authorLabel ?? "Agent"
19699
+ authorLabel: update.authorLabel ?? "Agent",
19700
+ ...update.idempotencyKey === void 0 ? {} : { idempotencyKey: update.idempotencyKey },
19701
+ ...update.idempotencyRequestHash === void 0 ? {} : { idempotencyRequestHash: update.idempotencyRequestHash }
19603
19702
  },
19604
19703
  false
19605
19704
  );
@@ -19617,7 +19716,7 @@ import path5 from "node:path";
19617
19716
  import { TextDecoder as TextDecoder2 } from "node:util";
19618
19717
 
19619
19718
  // src/shared/constants.ts
19620
- var APP_VERSION = "0.1.1";
19719
+ var APP_VERSION = "0.2.0";
19621
19720
  var PROTOCOL_VERSION = 2;
19622
19721
  var VIEWER_ID_HEADER = "x-rvw-viewer-id";
19623
19722
  var GIT_OBJECT_ID_PATTERN = /^[0-9a-f]{40,64}$/i;
@@ -19625,8 +19724,13 @@ var MAX_TEXT_DOCUMENT_BYTES = 1024 * 1024;
19625
19724
  var MAX_MARKDOWN_ASSET_BYTES = 5 * 1024 * 1024;
19626
19725
  var MAX_COMMENT_BODY_BYTES = 64 * 1024;
19627
19726
  var MAX_AUTHOR_LABEL_CHARACTERS = 100;
19727
+ var MAX_IDEMPOTENCY_KEY_CHARACTERS = 200;
19628
19728
  var DEFAULT_COMMENT_LIST_LIMIT = 50;
19629
19729
  var MAX_COMMENT_LIST_LIMIT = 100;
19730
+ var DEFAULT_COMMENT_WATCH_LIMIT = 100;
19731
+ var MAX_COMMENT_WATCH_LIMIT = 100;
19732
+ var DEFAULT_COMMENT_WATCH_INTERVAL_SECONDS = 10;
19733
+ var MAX_COMMENT_WATCH_INTERVAL_SECONDS = 300;
19630
19734
  var MAX_COMMENT_LIST_BODY_PREVIEW_BYTES = 512;
19631
19735
  var MAX_WALKTHROUGH_BODY_BYTES = 256 * 1024;
19632
19736
  var MAX_WALKTHROUGH_REFERENCES = 200;
@@ -20269,6 +20373,9 @@ var GitClient = class {
20269
20373
 
20270
20374
  // src/infrastructure/github/github-client.ts
20271
20375
  var ghPullRequestSchema = external_exports.object({
20376
+ author: external_exports.object({ login: external_exports.string().min(1) }).nullable(),
20377
+ headRepository: external_exports.object({ name: external_exports.string().min(1) }).nullable(),
20378
+ headRepositoryOwner: external_exports.object({ login: external_exports.string().min(1) }).nullable(),
20272
20379
  number: external_exports.number().int().positive(),
20273
20380
  url: external_exports.url(),
20274
20381
  title: external_exports.string(),
@@ -20309,6 +20416,7 @@ var GitHubClient = class {
20309
20416
  async getPullRequest(reference, cwd) {
20310
20417
  await this.assertAuthenticated();
20311
20418
  const fields = [
20419
+ "author",
20312
20420
  "number",
20313
20421
  "url",
20314
20422
  "title",
@@ -20358,6 +20466,9 @@ var GitHubClient = class {
20358
20466
  repository: identity.repository,
20359
20467
  number: parsed.data.number,
20360
20468
  url: parsed.data.url,
20469
+ authorLogin: parsed.data.author?.login ?? null,
20470
+ headRepositoryOwner: parsed.data.headRepositoryOwner?.login ?? null,
20471
+ headRepositoryName: parsed.data.headRepository?.name ?? null,
20361
20472
  title: parsed.data.title,
20362
20473
  body: parsed.data.body ?? "",
20363
20474
  baseRefName: parsed.data.baseRefName,
@@ -20372,6 +20483,7 @@ var GitHubClient = class {
20372
20483
  };
20373
20484
 
20374
20485
  // src/application/rvw-service.ts
20486
+ import { createHash as createHash3 } from "node:crypto";
20375
20487
  import path6 from "node:path";
20376
20488
 
20377
20489
  // node_modules/.pnpm/mdast-util-to-string@4.0.0/node_modules/mdast-util-to-string/lib/index.js
@@ -27307,6 +27419,28 @@ function defaultOnError(left, right) {
27307
27419
  }
27308
27420
  }
27309
27421
 
27422
+ // src/domain/comment-watch-cursor.ts
27423
+ var CURSOR_VERSION = 1;
27424
+ var DATABASE_ID_PATTERN = /^[0-9a-f]{32}$/;
27425
+ function formatCommentWatchCursor(cursor) {
27426
+ return Buffer.from(
27427
+ JSON.stringify({ v: CURSOR_VERSION, d: cursor.databaseId, s: cursor.sequence }),
27428
+ "utf8"
27429
+ ).toString("base64url");
27430
+ }
27431
+ function parseCommentWatchCursor(value) {
27432
+ try {
27433
+ if (value.length === 0 || value.length > 512) throw new Error("cursor length");
27434
+ const decoded = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
27435
+ if (decoded.v !== CURSOR_VERSION || typeof decoded.d !== "string" || !DATABASE_ID_PATTERN.test(decoded.d) || typeof decoded.s !== "number" || !Number.isSafeInteger(decoded.s) || decoded.s < 0) {
27436
+ throw new Error("cursor shape");
27437
+ }
27438
+ return { databaseId: decoded.d, sequence: decoded.s };
27439
+ } catch (error51) {
27440
+ throw new RvwError("INVALID_INPUT", "comment watch cursor\u304C\u4E0D\u6B63\u3067\u3059\u3002", { cause: error51 });
27441
+ }
27442
+ }
27443
+
27310
27444
  // node_modules/.pnpm/diff@9.0.0/node_modules/diff/libesm/diff/base.js
27311
27445
  var Diff = class {
27312
27446
  diff(oldStr, newStr, options = {}) {
@@ -27527,7 +27661,7 @@ function diffArrays(oldArr, newArr, options) {
27527
27661
  }
27528
27662
 
27529
27663
  // src/domain/pr-markdown.ts
27530
- import { createHash } from "node:crypto";
27664
+ import { createHash as createHash2 } from "node:crypto";
27531
27665
  function normalizeLf(value) {
27532
27666
  return value.replace(/\r\n?/g, "\n");
27533
27667
  }
@@ -27537,7 +27671,7 @@ function buildPullRequestMarkdown(title, body) {
27537
27671
  ${normalizeLf(body)}`;
27538
27672
  }
27539
27673
  function hashDocument(value) {
27540
- return createHash("sha256").update(normalizeLf(value), "utf8").digest("hex");
27674
+ return createHash2("sha256").update(normalizeLf(value), "utf8").digest("hex");
27541
27675
  }
27542
27676
  function selectedLineText(value, startLine, endLine) {
27543
27677
  const lines2 = normalizeLf(value).split("\n");
@@ -27686,6 +27820,18 @@ function assertAuthorLabel(authorLabel) {
27686
27820
  );
27687
27821
  }
27688
27822
  }
27823
+ function assertIdempotencyKey(idempotencyKey2) {
27824
+ if (idempotencyKey2 === void 0) return;
27825
+ if (idempotencyKey2.length === 0 || idempotencyKey2.length > MAX_IDEMPOTENCY_KEY_CHARACTERS) {
27826
+ throw new RvwError(
27827
+ "INVALID_INPUT",
27828
+ `idempotencyKey\u306F1\u301C${MAX_IDEMPOTENCY_KEY_CHARACTERS}\u6587\u5B57\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
27829
+ );
27830
+ }
27831
+ }
27832
+ function idempotencyRequestHash(value) {
27833
+ return createHash3("sha256").update(JSON.stringify(value), "utf8").digest("hex");
27834
+ }
27689
27835
  function assertLinePair(startLine, endLine) {
27690
27836
  if (startLine === null !== (endLine === null)) {
27691
27837
  throw new RvwError("INVALID_INPUT", "\u884C\u7BC4\u56F2\u306F\u958B\u59CB\u884C\u3068\u7D42\u4E86\u884C\u3092\u4E21\u65B9\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
@@ -27971,11 +28117,23 @@ var RvwService = class {
27971
28117
  }
27972
28118
  if (update.reply.trim()) assertTextBody(update.reply);
27973
28119
  assertAuthorLabel(update.authorLabel);
28120
+ assertIdempotencyKey(update.idempotencyKey);
28121
+ const authorLabel = update.authorLabel ?? "Agent";
27974
28122
  return {
27975
28123
  commentId,
27976
28124
  reply: update.reply,
27977
28125
  resolve: update.resolve,
27978
- ...update.authorLabel === void 0 ? {} : { authorLabel: update.authorLabel }
28126
+ ...update.authorLabel === void 0 ? {} : { authorLabel: update.authorLabel },
28127
+ ...update.idempotencyKey === void 0 ? {} : { idempotencyKey: update.idempotencyKey },
28128
+ ...update.idempotencyKey === void 0 ? {} : {
28129
+ idempotencyRequestHash: idempotencyRequestHash({
28130
+ operation: "pr.sync.comment-update",
28131
+ commentId,
28132
+ reply: update.reply,
28133
+ resolve: update.resolve,
28134
+ authorLabel
28135
+ })
28136
+ }
27979
28137
  };
27980
28138
  });
27981
28139
  }
@@ -28304,6 +28462,15 @@ var RvwService = class {
28304
28462
  ...input.authorLabel === void 0 ? {} : { authorLabel: input.authorLabel }
28305
28463
  });
28306
28464
  }
28465
+ async createCommentForReference(input) {
28466
+ const pullRequest = this.resolveStoredPullRequest(input.pullRequest);
28467
+ return await this.createComment({
28468
+ pullRequestId: pullRequest.id,
28469
+ target: input.target,
28470
+ body: input.body,
28471
+ ...input.authorLabel === void 0 ? {} : { authorLabel: input.authorLabel }
28472
+ });
28473
+ }
28307
28474
  getCommentByUri(uri) {
28308
28475
  const id = parseCommentUri(uri);
28309
28476
  const comment = this.database.getComment(id);
@@ -28340,7 +28507,7 @@ var RvwService = class {
28340
28507
  result.pullRequest.url,
28341
28508
  result.pullRequest.localRepositoryPath
28342
28509
  ) : null;
28343
- const staleAgainstGitHub = live ? live.title !== result.pullRequest.latestTitle || live.body !== result.pullRequest.latestBody || live.baseOid !== result.pullRequest.latestBaseOid || live.headOid !== result.pullRequest.latestHeadOid || live.updatedAt !== result.pullRequest.githubUpdatedAt : null;
28510
+ const staleAgainstGitHub = live ? live.title !== result.pullRequest.latestTitle || live.body !== result.pullRequest.latestBody || live.headRepositoryOwner !== result.pullRequest.latestHeadRepositoryOwner || live.headRepositoryName !== result.pullRequest.latestHeadRepositoryName || live.baseOid !== result.pullRequest.latestBaseOid || live.headOid !== result.pullRequest.latestHeadOid || live.updatedAt !== result.pullRequest.githubUpdatedAt : null;
28344
28511
  return {
28345
28512
  ...result,
28346
28513
  latestPlacement,
@@ -28389,6 +28556,39 @@ var RvwService = class {
28389
28556
  }
28390
28557
  };
28391
28558
  }
28559
+ listCommentPostEvents(cursor, limit = DEFAULT_COMMENT_WATCH_LIMIT) {
28560
+ if (!Number.isInteger(limit) || limit < 1 || limit > MAX_COMMENT_WATCH_LIMIT) {
28561
+ throw new RvwError(
28562
+ "INVALID_INPUT",
28563
+ `comment watch\u306Elimit\u306F1\u301C${MAX_COMMENT_WATCH_LIMIT}\u306E\u6574\u6570\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
28564
+ );
28565
+ }
28566
+ const databaseId = this.database.getCommentWatchDatabaseId();
28567
+ const parsed = cursor === void 0 ? null : parseCommentWatchCursor(cursor);
28568
+ if (parsed !== null && parsed.databaseId !== databaseId) {
28569
+ throw new RvwError("INVALID_INPUT", "comment watch cursor\u306F\u5225\u306Ervw database\u7528\u3067\u3059\u3002");
28570
+ }
28571
+ const latestAtStart = this.database.getLatestCommentPostEventSequence();
28572
+ if (parsed !== null && parsed.sequence > latestAtStart) {
28573
+ throw new RvwError("INVALID_INPUT", "comment watch cursor\u306Fdatabase\u306E\u6700\u65B0event\u3088\u308A\u5148\u3067\u3059\u3002");
28574
+ }
28575
+ const afterSequence = parsed?.sequence ?? latestAtStart;
28576
+ const rawEvents = this.database.listCommentPostEvents(afterSequence, limit);
28577
+ const events = rawEvents.map((event) => ({
28578
+ cursor: formatCommentWatchCursor({ databaseId, sequence: event.sequence }),
28579
+ event
28580
+ }));
28581
+ const lastSequence = rawEvents.at(-1)?.sequence ?? afterSequence;
28582
+ const latestSequence = this.database.getLatestCommentPostEventSequence();
28583
+ return {
28584
+ databaseId,
28585
+ startCursor: formatCommentWatchCursor({ databaseId, sequence: afterSequence }),
28586
+ cursor: formatCommentWatchCursor({ databaseId, sequence: lastSequence }),
28587
+ anchoredAtCurrent: parsed === null,
28588
+ hasMore: lastSequence < latestSequence,
28589
+ events
28590
+ };
28591
+ }
28392
28592
  listComments(pullRequestId, resolved) {
28393
28593
  this.getPullRequest(pullRequestId);
28394
28594
  return this.database.listComments(pullRequestId, resolved);
@@ -28598,13 +28798,27 @@ var RvwService = class {
28598
28798
  if (!comment)
28599
28799
  throw new RvwError("COMMENT_NOT_FOUND", "\u30B3\u30E1\u30F3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002", { status: 404 });
28600
28800
  assertAuthorLabel(input.authorLabel);
28801
+ assertIdempotencyKey(input.idempotencyKey);
28601
28802
  if (input.relatedCommitOid) {
28602
28803
  await this.assertCommitAvailable(
28603
28804
  this.getPullRequest(comment.pullRequestId),
28604
28805
  input.relatedCommitOid
28605
28806
  );
28606
28807
  }
28607
- return this.database.insertReply(id, { ...input, body: assertTextBody(input.body) });
28808
+ const body = assertTextBody(input.body);
28809
+ return this.database.insertReply(id, {
28810
+ ...input,
28811
+ body,
28812
+ ...input.idempotencyKey === void 0 ? {} : {
28813
+ idempotencyRequestHash: idempotencyRequestHash({
28814
+ operation: "comment.reply",
28815
+ commentId: id,
28816
+ body,
28817
+ relatedCommitOid: input.relatedCommitOid ?? null,
28818
+ authorLabel: input.authorLabel ?? null
28819
+ })
28820
+ }
28821
+ });
28608
28822
  }
28609
28823
  setCommentResolved(uriOrId, resolved) {
28610
28824
  const id = uriOrId.startsWith("rvw://") ? parseCommentUri(uriOrId) : uriOrId;
@@ -28617,6 +28831,25 @@ var RvwService = class {
28617
28831
  updateCommentPost(commentId, postId, body) {
28618
28832
  return this.database.updateCommentPost(commentId, postId, assertTextBody(body));
28619
28833
  }
28834
+ async editCommentPost(uriOrId, postId, input) {
28835
+ const commentId = uriOrId.startsWith("rvw://") ? parseCommentUri(uriOrId) : uriOrId;
28836
+ const comment = this.database.getComment(commentId);
28837
+ if (!comment) {
28838
+ throw new RvwError("COMMENT_NOT_FOUND", "\u30B3\u30E1\u30F3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002", { status: 404 });
28839
+ }
28840
+ if (input.relatedCommitOid) {
28841
+ await this.assertCommitAvailable(
28842
+ this.getPullRequest(comment.pullRequestId),
28843
+ input.relatedCommitOid
28844
+ );
28845
+ }
28846
+ return this.database.updateCommentPost(
28847
+ commentId,
28848
+ postId,
28849
+ assertTextBody(input.body),
28850
+ input.relatedCommitOid
28851
+ );
28852
+ }
28620
28853
  deleteReply(commentId, postId) {
28621
28854
  return this.database.deleteReply(commentId, postId);
28622
28855
  }
@@ -28813,7 +29046,7 @@ function createRuntime(options = {}) {
28813
29046
  }
28814
29047
 
28815
29048
  // src/infrastructure/skills/skill-installer.ts
28816
- import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
29049
+ import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
28817
29050
  import {
28818
29051
  cpSync,
28819
29052
  existsSync as existsSync2,
@@ -28829,7 +29062,11 @@ import {
28829
29062
  import os3 from "node:os";
28830
29063
  import path7 from "node:path";
28831
29064
  import { fileURLToPath as fileURLToPath3 } from "node:url";
28832
- var skillNames = ["rvw", "rvw-walkthrough"];
29065
+ var skillNames = [
29066
+ "rvw",
29067
+ "rvw-walkthrough",
29068
+ "rvw-watch-comments"
29069
+ ];
28833
29070
  var INSTALL_METADATA_FILE = ".rvw-install.json";
28834
29071
  var INSTALL_METADATA_SCHEMA_VERSION = 1;
28835
29072
  var MAX_DIGEST_ENTRIES = 1e4;
@@ -28843,7 +29080,7 @@ var adapters = {
28843
29080
  }
28844
29081
  };
28845
29082
  function directoryDigest(directory) {
28846
- const hash2 = createHash2("sha256");
29083
+ const hash2 = createHash4("sha256");
28847
29084
  let entriesSeen = 0;
28848
29085
  let bytesSeen = 0;
28849
29086
  const walk = (current, relative) => {
@@ -33179,7 +33416,7 @@ async function startServer(service, options = {}) {
33179
33416
  }
33180
33417
 
33181
33418
  // src/server/agent-socket.ts
33182
- import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
33419
+ import { createHash as createHash5, randomUUID as randomUUID4 } from "node:crypto";
33183
33420
  import {
33184
33421
  chmodSync as chmodSync2,
33185
33422
  existsSync as existsSync5,
@@ -33198,18 +33435,67 @@ import path9 from "node:path";
33198
33435
  var nonEmptyString = external_exports.string().min(1);
33199
33436
  var commentUri = external_exports.string().regex(/^rvw:\/\/comment\//);
33200
33437
  var walkthroughUri = external_exports.string().regex(/^rvw:\/\/walkthrough\//);
33438
+ var nullableCommentLine = external_exports.number().int().positive().nullable().optional().default(null);
33439
+ var idempotencyKey = external_exports.string().min(1).max(MAX_IDEMPOTENCY_KEY_CHARACTERS).optional();
33440
+ var commentTargetInputSchema = external_exports.union([
33441
+ external_exports.object({ kind: external_exports.literal("pull-request") }).strict(),
33442
+ external_exports.object({
33443
+ kind: external_exports.literal("walkthrough"),
33444
+ walkthroughId: external_exports.uuid(),
33445
+ startLine: nullableCommentLine,
33446
+ endLine: nullableCommentLine
33447
+ }).strict(),
33448
+ external_exports.object({
33449
+ kind: external_exports.literal("document"),
33450
+ documentKind: external_exports.literal("pull-request-markdown"),
33451
+ startLine: nullableCommentLine,
33452
+ endLine: nullableCommentLine
33453
+ }).strict(),
33454
+ external_exports.object({
33455
+ kind: external_exports.literal("document"),
33456
+ documentKind: external_exports.literal("repository-file"),
33457
+ sourceOid: external_exports.string().regex(GIT_OBJECT_ID_PATTERN),
33458
+ path: nonEmptyString,
33459
+ startLine: nullableCommentLine,
33460
+ endLine: nullableCommentLine
33461
+ }).strict()
33462
+ ]).superRefine((target, context) => {
33463
+ if (target.kind === "pull-request") return;
33464
+ if (target.startLine === null !== (target.endLine === null)) {
33465
+ context.addIssue({
33466
+ code: "custom",
33467
+ message: "startLine\u3068endLine\u306F\u4E21\u65B9\u6307\u5B9A\u3059\u308B\u304B\u3001\u4E21\u65B9\u7701\u7565\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
33468
+ });
33469
+ return;
33470
+ }
33471
+ if (target.startLine !== null && target.endLine !== null && target.endLine < target.startLine) {
33472
+ context.addIssue({
33473
+ code: "custom",
33474
+ message: "endLine\u306FstartLine\u4EE5\u4E0A\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
33475
+ });
33476
+ }
33477
+ });
33478
+ var commentCreateInputSchema = external_exports.object({
33479
+ pullRequest: nonEmptyString,
33480
+ target: commentTargetInputSchema,
33481
+ body: external_exports.string().min(1).refine((value) => value.trim().length > 0).refine((value) => Buffer.byteLength(value, "utf8") <= MAX_COMMENT_BODY_BYTES),
33482
+ authorLabel: external_exports.string().max(MAX_AUTHOR_LABEL_CHARACTERS).nullable().optional()
33483
+ }).strict();
33201
33484
  var commentReplyInputSchema = external_exports.object({
33202
33485
  body: external_exports.string().min(1).refine((value) => Buffer.byteLength(value, "utf8") <= MAX_COMMENT_BODY_BYTES),
33203
33486
  authorLabel: external_exports.string().max(MAX_AUTHOR_LABEL_CHARACTERS).nullable().optional(),
33204
- relatedCommitOid: external_exports.string().regex(GIT_OBJECT_ID_PATTERN).nullable().optional()
33487
+ relatedCommitOid: external_exports.string().regex(GIT_OBJECT_ID_PATTERN).nullable().optional(),
33488
+ idempotencyKey
33205
33489
  }).strict();
33490
+ var commentPostEditInputSchema = commentReplyInputSchema.pick({ body: true, relatedCommitOid: true }).strict();
33206
33491
  var pullRequestSyncInputSchema = external_exports.object({
33207
33492
  pullRequest: nonEmptyString,
33208
33493
  commentUpdates: external_exports.array(
33209
33494
  external_exports.object({
33210
33495
  commentRef: commentUri,
33211
33496
  reply: external_exports.string().refine((value) => Buffer.byteLength(value, "utf8") <= MAX_COMMENT_BODY_BYTES),
33212
- resolve: external_exports.boolean()
33497
+ resolve: external_exports.boolean(),
33498
+ idempotencyKey
33213
33499
  }).strict()
33214
33500
  ).max(500).optional()
33215
33501
  }).strict();
@@ -33263,8 +33549,14 @@ var agentCommandInputSchemas = {
33263
33549
  limit: external_exports.number().int().min(1).max(MAX_COMMENT_LIST_LIMIT).default(DEFAULT_COMMENT_LIST_LIMIT),
33264
33550
  offset: external_exports.number().int().min(0).default(0)
33265
33551
  }).strict(),
33552
+ "comment.watch": external_exports.object({
33553
+ cursor: external_exports.string().min(1).max(512).optional(),
33554
+ limit: external_exports.number().int().min(1).max(MAX_COMMENT_WATCH_LIMIT)
33555
+ }).strict(),
33556
+ "comment.create": commentCreateInputSchema,
33266
33557
  "comment.get": external_exports.object({ uri: commentUri, live: external_exports.boolean().default(false) }).strict(),
33267
33558
  "comment.reply": external_exports.object({ uri: commentUri, reply: commentReplyInputSchema }).strict(),
33559
+ "comment.edit": external_exports.object({ uri: commentUri, postId: nonEmptyString, edit: commentPostEditInputSchema }).strict(),
33268
33560
  "comment.resolve": external_exports.object({ uri: commentUri }).strict(),
33269
33561
  "comment.reopen": external_exports.object({ uri: commentUri }).strict(),
33270
33562
  "walkthrough.get": external_exports.object({ uri: walkthroughUri }).strict(),
@@ -33302,7 +33594,7 @@ function agentSocketPath(databaseFilePath) {
33302
33594
  if (configuredPath) return configuredPath;
33303
33595
  const user = process.getuid?.() ?? os4.userInfo().username.replace(/[^A-Za-z0-9_-]/g, "_");
33304
33596
  const databasePath = path9.resolve(databaseFilePath ?? databasePathConfiguration().filePath);
33305
- const databaseKey = createHash3("sha256").update(databasePath).digest("hex").slice(0, 16);
33597
+ const databaseKey = createHash5("sha256").update(databasePath).digest("hex").slice(0, 16);
33306
33598
  return path9.join(os4.tmpdir(), `rvw-agent-${user}`, `${databaseKey}.sock`);
33307
33599
  }
33308
33600
  function ensurePrivateSocketDirectory(socketPath) {
@@ -33407,6 +33699,16 @@ async function dispatchAgentSocketRequest(service, rawRequest) {
33407
33699
  offset: input.offset
33408
33700
  });
33409
33701
  }
33702
+ case "comment.watch": {
33703
+ const input = parseOperationInput("comment.watch", request.input);
33704
+ return service.listCommentPostEvents(input.cursor, input.limit);
33705
+ }
33706
+ case "comment.create": {
33707
+ const input = parseOperationInput("comment.create", request.input);
33708
+ return await service.createCommentForReference(
33709
+ input
33710
+ );
33711
+ }
33410
33712
  case "comment.get": {
33411
33713
  const input = parseOperationInput("comment.get", request.input);
33412
33714
  return await service.getCommentReviewContext(input.uri, { live: input.live });
@@ -33418,6 +33720,13 @@ async function dispatchAgentSocketRequest(service, rawRequest) {
33418
33720
  input.reply
33419
33721
  );
33420
33722
  }
33723
+ case "comment.edit": {
33724
+ const input = parseOperationInput("comment.edit", request.input);
33725
+ return await service.editCommentPost(input.uri, input.postId, {
33726
+ body: input.edit.body,
33727
+ ...input.edit.relatedCommitOid === void 0 ? {} : { relatedCommitOid: input.edit.relatedCommitOid }
33728
+ });
33729
+ }
33421
33730
  case "comment.resolve": {
33422
33731
  const input = parseOperationInput("comment.resolve", request.input);
33423
33732
  return service.setCommentResolved(input.uri, true);
@@ -33972,11 +34281,20 @@ var commentListOptionsSchema = external_exports.object({
33972
34281
  limit: external_exports.coerce.number().int().min(1).max(MAX_COMMENT_LIST_LIMIT).default(DEFAULT_COMMENT_LIST_LIMIT),
33973
34282
  offset: external_exports.coerce.number().int().min(0).default(0)
33974
34283
  });
34284
+ var commentWatchOptionsSchema = external_exports.object({
34285
+ after: external_exports.string().min(1).max(512).optional(),
34286
+ interval: external_exports.coerce.number().int().min(1).max(MAX_COMMENT_WATCH_INTERVAL_SECONDS).default(DEFAULT_COMMENT_WATCH_INTERVAL_SECONDS),
34287
+ limit: external_exports.coerce.number().int().min(1).max(MAX_COMMENT_WATCH_LIMIT).default(DEFAULT_COMMENT_WATCH_LIMIT),
34288
+ once: external_exports.boolean().optional().default(false),
34289
+ jsonSeq: external_exports.literal(true)
34290
+ });
33975
34291
  var commentPullRequestOutputSchema = external_exports.object({
33976
34292
  url: external_exports.string(),
33977
34293
  owner: external_exports.string(),
33978
34294
  repository: external_exports.string(),
33979
34295
  number: external_exports.number().int(),
34296
+ authorLogin: external_exports.string().nullable(),
34297
+ headRepository: external_exports.object({ owner: external_exports.string(), name: external_exports.string(), url: external_exports.string() }).strict().nullable(),
33980
34298
  title: external_exports.string(),
33981
34299
  body: external_exports.string().optional(),
33982
34300
  baseRefName: external_exports.string(),
@@ -34124,6 +34442,8 @@ var commentGetOutputSchema = external_exports.object({
34124
34442
  liveCheckedAt: external_exports.string().nullable(),
34125
34443
  staleAgainstGitHub: external_exports.boolean().nullable(),
34126
34444
  live: external_exports.object({
34445
+ authorLogin: external_exports.string().nullable(),
34446
+ headRepository: external_exports.object({ owner: external_exports.string(), name: external_exports.string(), url: external_exports.string() }).strict().nullable(),
34127
34447
  title: external_exports.string(),
34128
34448
  body: external_exports.string().optional(),
34129
34449
  baseRefName: external_exports.string(),
@@ -34183,11 +34503,18 @@ function truncateUtf82(value, maxBytes) {
34183
34503
  return { value: result, truncated: true };
34184
34504
  }
34185
34505
  function formatPullRequest(pullRequest, options = {}) {
34506
+ const headRepository = pullRequest.latestHeadRepositoryOwner && pullRequest.latestHeadRepositoryName ? {
34507
+ owner: pullRequest.latestHeadRepositoryOwner,
34508
+ name: pullRequest.latestHeadRepositoryName,
34509
+ url: `https://github.com/${pullRequest.latestHeadRepositoryOwner}/${pullRequest.latestHeadRepositoryName}`
34510
+ } : null;
34186
34511
  return {
34187
34512
  url: pullRequest.url,
34188
34513
  owner: pullRequest.owner,
34189
34514
  repository: pullRequest.repository,
34190
34515
  number: pullRequest.number,
34516
+ authorLogin: pullRequest.latestAuthorLogin,
34517
+ headRepository,
34191
34518
  title: pullRequest.latestTitle,
34192
34519
  ...options.includeBody ? { body: pullRequest.latestBody } : {},
34193
34520
  baseRefName: pullRequest.latestBaseRefName,
@@ -34245,6 +34572,12 @@ function formatCommentGetOutput(result, options = {}) {
34245
34572
  liveCheckedAt: result.githubState.liveCheckedAt,
34246
34573
  staleAgainstGitHub: result.githubState.staleAgainstGitHub,
34247
34574
  live: result.githubState.live ? {
34575
+ authorLogin: result.githubState.live.authorLogin,
34576
+ headRepository: result.githubState.live.headRepositoryOwner && result.githubState.live.headRepositoryName ? {
34577
+ owner: result.githubState.live.headRepositoryOwner,
34578
+ name: result.githubState.live.headRepositoryName,
34579
+ url: `https://github.com/${result.githubState.live.headRepositoryOwner}/${result.githubState.live.headRepositoryName}`
34580
+ } : null,
34248
34581
  title: result.githubState.live.title,
34249
34582
  ...options.includePrBody ? { body: result.githubState.live.body } : {},
34250
34583
  baseRefName: result.githubState.live.baseRefName,
@@ -34256,6 +34589,12 @@ function formatCommentGetOutput(result, options = {}) {
34256
34589
  }
34257
34590
  });
34258
34591
  }
34592
+ function formatCommentWatchEvent(item) {
34593
+ return {
34594
+ cursor: item.cursor,
34595
+ event: item.event
34596
+ };
34597
+ }
34259
34598
  function formatCommentListOutput(result, state) {
34260
34599
  return commentListOutputSchema.parse({
34261
34600
  ok: true,
@@ -34295,6 +34634,13 @@ function writeJson(value) {
34295
34634
  process.stdout.write(`${JSON.stringify(value)}
34296
34635
  `);
34297
34636
  }
34637
+ function writeJsonSequence(value) {
34638
+ process.stdout.write(`${JSON.stringify(value)}
34639
+ `);
34640
+ }
34641
+ async function waitForCommentWatchInterval(milliseconds) {
34642
+ await new Promise((resolve) => setTimeout(resolve, milliseconds));
34643
+ }
34298
34644
  function writeOutput(options, value, human) {
34299
34645
  if (options.json) writeJson(value);
34300
34646
  else process.stdout.write(`${human}
@@ -34668,9 +35014,12 @@ function createProgram(runtimeFactory = defaultRuntimeFactory) {
34668
35014
  appVersion: APP_VERSION,
34669
35015
  capabilities: [
34670
35016
  "agent.transport",
35017
+ "comment.create",
34671
35018
  "comment.list",
35019
+ "comment.watch",
34672
35020
  "comment.read",
34673
35021
  "comment.reply",
35022
+ "comment.edit",
34674
35023
  "comment.resolve",
34675
35024
  "comment.reopen",
34676
35025
  "pullRequest.sync",
@@ -34870,6 +35219,73 @@ function createProgram(runtimeFactory = defaultRuntimeFactory) {
34870
35219
  );
34871
35220
  writeJson({ ok: true, deleted });
34872
35221
  });
35222
+ comment.command("watch").description("\u5168\u767B\u9332PR\u3067\u8D77\u52D5\u5F8C\u306B\u4F5C\u6210\u3055\u308C\u305Froot comment\u3068reply\u3092\u76E3\u8996").option("--after <cursor>", "\u4EE5\u524D\u306Ewatch cursor\u304B\u3089\u518D\u958B").option(
35223
+ "--interval <seconds>",
35224
+ `poll\u9593\u9694\uFF08\u65E2\u5B9A: ${DEFAULT_COMMENT_WATCH_INTERVAL_SECONDS}\u79D2\uFF09`,
35225
+ String(DEFAULT_COMMENT_WATCH_INTERVAL_SECONDS)
35226
+ ).option(
35227
+ "--limit <limit>",
35228
+ `\u4E00\u5EA6\u306B\u53D6\u5F97\u3059\u308B\u6700\u5927event\u6570\uFF08\u65E2\u5B9A: ${DEFAULT_COMMENT_WATCH_LIMIT}\uFF09`,
35229
+ String(DEFAULT_COMMENT_WATCH_LIMIT)
35230
+ ).option("--once", "\u73FE\u5728\u53D6\u5F97\u53EF\u80FD\u306Aevent\u3092\u51FA\u529B\u3057\u3066\u7D42\u4E86").requiredOption("--json-seq", "RFC 7464 JSON text sequence\u3067\u51FA\u529B").action(async (rawOptions) => {
35231
+ const options = commentWatchOptionsSchema.parse(rawOptions);
35232
+ let cursor = options.after;
35233
+ let first = true;
35234
+ let stopping = false;
35235
+ const stop = () => {
35236
+ stopping = true;
35237
+ };
35238
+ process.once("SIGINT", stop);
35239
+ process.once("SIGTERM", stop);
35240
+ try {
35241
+ do {
35242
+ const result = await callService(
35243
+ "comment.watch",
35244
+ {
35245
+ ...cursor === void 0 ? {} : { cursor },
35246
+ limit: options.limit
35247
+ },
35248
+ () => getRuntime().service.listCommentPostEvents(cursor, options.limit)
35249
+ );
35250
+ if (first) {
35251
+ writeJsonSequence({
35252
+ type: "ready",
35253
+ databaseId: result.databaseId,
35254
+ cursor: result.startCursor,
35255
+ anchoredAtCurrent: result.anchoredAtCurrent
35256
+ });
35257
+ }
35258
+ for (const item of result.events) {
35259
+ writeJsonSequence({ type: "comment-posted", ...formatCommentWatchEvent(item) });
35260
+ }
35261
+ cursor = result.cursor;
35262
+ first = false;
35263
+ if (options.once || stopping) break;
35264
+ if (!result.hasMore) {
35265
+ await waitForCommentWatchInterval(options.interval * 1e3);
35266
+ }
35267
+ } while (!stopping);
35268
+ writeJsonSequence({ type: "stopped", cursor });
35269
+ } finally {
35270
+ process.off("SIGINT", stop);
35271
+ process.off("SIGTERM", stop);
35272
+ }
35273
+ });
35274
+ comment.command("create").requiredOption("--stdin", "stdin\u304B\u3089JSON\u3092\u8AAD\u3080").requiredOption("--json", "JSON\u3067\u51FA\u529B").description("\u767B\u9332\u6E08\u307FPR\u3078\u672A\u89E3\u6C7A\u30B3\u30E1\u30F3\u30C8\u3092\u4E00\u4EF6\u4F5C\u6210").action(async () => {
35275
+ const input = commentCreateInputSchema.parse(await readStdinJson());
35276
+ const request = {
35277
+ pullRequest: input.pullRequest,
35278
+ target: input.target,
35279
+ body: input.body,
35280
+ ...input.authorLabel === void 0 ? {} : { authorLabel: input.authorLabel }
35281
+ };
35282
+ const created = await callService(
35283
+ "comment.create",
35284
+ request,
35285
+ async () => await getRuntime().service.createCommentForReference(request)
35286
+ );
35287
+ writeJson({ ok: true, comment: created });
35288
+ });
34873
35289
  comment.command("list").argument("<pull-request>", "\u767B\u9332\u6E08\u307FPR URL\u307E\u305F\u306F\u756A\u53F7").option("--state <state>", "unresolved\u3001resolved\u3001all\uFF08\u65E2\u5B9A: unresolved\uFF09", "unresolved").option(
34874
35290
  "--limit <limit>",
34875
35291
  `1\u30DA\u30FC\u30B8\u306E\u6700\u5927\u4EF6\u6570\uFF08\u65E2\u5B9A: ${DEFAULT_COMMENT_LIST_LIMIT}\uFF09`,
@@ -34902,7 +35318,8 @@ function createProgram(runtimeFactory = defaultRuntimeFactory) {
34902
35318
  const reply = {
34903
35319
  body: input.body,
34904
35320
  ...input.authorLabel === void 0 ? {} : { authorLabel: input.authorLabel },
34905
- ...input.relatedCommitOid === void 0 ? {} : { relatedCommitOid: input.relatedCommitOid }
35321
+ ...input.relatedCommitOid === void 0 ? {} : { relatedCommitOid: input.relatedCommitOid },
35322
+ ...input.idempotencyKey === void 0 ? {} : { idempotencyKey: input.idempotencyKey }
34906
35323
  };
34907
35324
  const post = await callService(
34908
35325
  "comment.reply",
@@ -34911,6 +35328,19 @@ function createProgram(runtimeFactory = defaultRuntimeFactory) {
34911
35328
  );
34912
35329
  writeJson({ ok: true, post });
34913
35330
  });
35331
+ comment.command("edit").argument("<comment-uri>").requiredOption("--post <post-id>", "\u7DE8\u96C6\u3059\u308Bpost ID").requiredOption("--stdin", "stdin\u304B\u3089JSON\u3092\u8AAD\u3080").requiredOption("--json", "JSON\u3067\u51FA\u529B").action(async (uri, options) => {
35332
+ const input = commentPostEditInputSchema.parse(await readStdinJson());
35333
+ const edit = {
35334
+ body: input.body,
35335
+ ...input.relatedCommitOid === void 0 ? {} : { relatedCommitOid: input.relatedCommitOid }
35336
+ };
35337
+ const post = await callService(
35338
+ "comment.edit",
35339
+ { uri, postId: options.post, edit },
35340
+ async () => await getRuntime().service.editCommentPost(uri, options.post, edit)
35341
+ );
35342
+ writeJson({ ok: true, post });
35343
+ });
34914
35344
  comment.command("resolve").argument("<comment-uri>").requiredOption("--json", "JSON\u3067\u51FA\u529B").action(async (uri) => {
34915
35345
  const comment2 = await callService(
34916
35346
  "comment.resolve",
@@ -34958,13 +35388,15 @@ function createProgram(runtimeFactory = defaultRuntimeFactory) {
34958
35388
  }
34959
35389
  async function runCli(argv = process.argv) {
34960
35390
  const json3 = argv.includes("--json");
35391
+ const jsonSequence = argv.includes("--json-seq");
34961
35392
  try {
34962
35393
  await createProgram().parseAsync(argv);
34963
35394
  } catch (error51) {
34964
35395
  const rvwError = error51 instanceof external_exports.ZodError ? new RvwError("INVALID_INPUT", "\u5165\u529B\u304CCLI schema\u306B\u9069\u5408\u3057\u307E\u305B\u3093\u3002", {
34965
35396
  details: external_exports.treeifyError(error51)
34966
35397
  }) : asRvwError(error51);
34967
- if (json3) writeJson({ ok: false, error: rvwError.toJSON() });
35398
+ if (jsonSequence) writeJsonSequence({ type: "error", error: rvwError.toJSON() });
35399
+ else if (json3) writeJson({ ok: false, error: rvwError.toJSON() });
34968
35400
  else process.stderr.write(`rvw: ${rvwError.message}
34969
35401
  ${rvwError.suggestions.join("\n")}
34970
35402
  `);