devchain-cli 0.6.0 → 0.7.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 (170) hide show
  1. package/dist/drizzle/0024_review_tables.sql +71 -0
  2. package/dist/drizzle/0025_reviews_mode_and_nullable_shas.sql +80 -0
  3. package/dist/drizzle/0026_review_comments_edited_at.sql +2 -0
  4. package/dist/drizzle/meta/_journal.json +22 -1
  5. package/dist/node_modules/@devchain/shared/tsconfig.tsbuildinfo +1 -1
  6. package/dist/server/common/errors/error-types.d.ts +3 -0
  7. package/dist/server/common/errors/error-types.js +7 -1
  8. package/dist/server/common/errors/error-types.js.map +1 -1
  9. package/dist/server/common/validation/path-validation.d.ts +11 -0
  10. package/dist/server/common/validation/path-validation.js +197 -0
  11. package/dist/server/common/validation/path-validation.js.map +1 -0
  12. package/dist/server/modules/chat/dtos/chat.dto.d.ts +13 -13
  13. package/dist/server/modules/core/core.module.js +3 -2
  14. package/dist/server/modules/core/core.module.js.map +1 -1
  15. package/dist/server/modules/core/services/preflight.service.d.ts +1 -3
  16. package/dist/server/modules/core/services/preflight.service.js +2 -28
  17. package/dist/server/modules/core/services/preflight.service.js.map +1 -1
  18. package/dist/server/modules/core/services/provider-mcp-ensure.service.d.ts +26 -0
  19. package/dist/server/modules/core/services/provider-mcp-ensure.service.js +224 -0
  20. package/dist/server/modules/core/services/provider-mcp-ensure.service.js.map +1 -0
  21. package/dist/server/modules/epics/controllers/epics.controller.d.ts +3 -2
  22. package/dist/server/modules/epics/controllers/epics.controller.js +16 -6
  23. package/dist/server/modules/epics/controllers/epics.controller.js.map +1 -1
  24. package/dist/server/modules/events/catalog/epic.created.d.ts +2 -2
  25. package/dist/server/modules/events/catalog/index.d.ts +340 -2
  26. package/dist/server/modules/events/catalog/index.js +12 -0
  27. package/dist/server/modules/events/catalog/index.js.map +1 -1
  28. package/dist/server/modules/events/catalog/review.comment.created.d.ts +69 -0
  29. package/dist/server/modules/events/catalog/review.comment.created.js +30 -0
  30. package/dist/server/modules/events/catalog/review.comment.created.js.map +1 -0
  31. package/dist/server/modules/events/catalog/review.comment.deleted.d.ts +30 -0
  32. package/dist/server/modules/events/catalog/review.comment.deleted.js +17 -0
  33. package/dist/server/modules/events/catalog/review.comment.deleted.js.map +1 -0
  34. package/dist/server/modules/events/catalog/review.comment.resolved.d.ts +30 -0
  35. package/dist/server/modules/events/catalog/review.comment.resolved.js +17 -0
  36. package/dist/server/modules/events/catalog/review.comment.resolved.js.map +1 -0
  37. package/dist/server/modules/events/catalog/review.comment.updated.d.ts +39 -0
  38. package/dist/server/modules/events/catalog/review.comment.updated.js +20 -0
  39. package/dist/server/modules/events/catalog/review.comment.updated.js.map +1 -0
  40. package/dist/server/modules/events/catalog/review.created.d.ts +90 -0
  41. package/dist/server/modules/events/catalog/review.created.js +40 -0
  42. package/dist/server/modules/events/catalog/review.created.js.map +1 -0
  43. package/dist/server/modules/events/catalog/review.updated.d.ts +110 -0
  44. package/dist/server/modules/events/catalog/review.updated.js +35 -0
  45. package/dist/server/modules/events/catalog/review.updated.js.map +1 -0
  46. package/dist/server/modules/events/subscribers/index.js +4 -0
  47. package/dist/server/modules/events/subscribers/index.js.map +1 -1
  48. package/dist/server/modules/events/subscribers/review-broadcaster.subscriber.d.ts +16 -0
  49. package/dist/server/modules/events/subscribers/review-broadcaster.subscriber.js +163 -0
  50. package/dist/server/modules/events/subscribers/review-broadcaster.subscriber.js.map +1 -0
  51. package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.d.ts +23 -0
  52. package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.js +217 -0
  53. package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.js.map +1 -0
  54. package/dist/server/modules/git/controllers/git.controller.d.ts +24 -0
  55. package/dist/server/modules/git/controllers/git.controller.js +230 -0
  56. package/dist/server/modules/git/controllers/git.controller.js.map +1 -0
  57. package/dist/server/modules/git/dtos/git.dto.d.ts +86 -0
  58. package/dist/server/modules/git/dtos/git.dto.js +36 -0
  59. package/dist/server/modules/git/dtos/git.dto.js.map +1 -0
  60. package/dist/server/modules/git/git.module.d.ts +2 -0
  61. package/dist/server/modules/git/git.module.js +25 -0
  62. package/dist/server/modules/git/git.module.js.map +1 -0
  63. package/dist/server/modules/git/services/git.service.d.ts +71 -0
  64. package/dist/server/modules/git/services/git.service.js +550 -0
  65. package/dist/server/modules/git/services/git.service.js.map +1 -0
  66. package/dist/server/modules/mcp/controllers/mcp-http.controller.js +17 -562
  67. package/dist/server/modules/mcp/controllers/mcp-http.controller.js.map +1 -1
  68. package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js +9 -568
  69. package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js.map +1 -1
  70. package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +226 -36
  71. package/dist/server/modules/mcp/dtos/mcp.dto.js +158 -50
  72. package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -1
  73. package/dist/server/modules/mcp/dtos/schema-registry.d.ts +7 -0
  74. package/dist/server/modules/mcp/dtos/schema-registry.js +56 -0
  75. package/dist/server/modules/mcp/dtos/schema-registry.js.map +1 -0
  76. package/dist/server/modules/mcp/mcp.module.js +2 -0
  77. package/dist/server/modules/mcp/mcp.module.js.map +1 -1
  78. package/dist/server/modules/mcp/services/mcp.service.d.ts +9 -1
  79. package/dist/server/modules/mcp/services/mcp.service.js +702 -33
  80. package/dist/server/modules/mcp/services/mcp.service.js.map +1 -1
  81. package/dist/server/modules/mcp/tool-definitions.d.ts +2438 -0
  82. package/dist/server/modules/mcp/tool-definitions.js +727 -0
  83. package/dist/server/modules/mcp/tool-definitions.js.map +1 -0
  84. package/dist/server/modules/mcp/utils/param-suggestion.d.ts +3 -0
  85. package/dist/server/modules/mcp/utils/param-suggestion.js +95 -0
  86. package/dist/server/modules/mcp/utils/param-suggestion.js.map +1 -0
  87. package/dist/server/modules/providers/controllers/providers.controller.d.ts +7 -6
  88. package/dist/server/modules/providers/controllers/providers.controller.js +12 -107
  89. package/dist/server/modules/providers/controllers/providers.controller.js.map +1 -1
  90. package/dist/server/modules/reviews/controllers/reviews.controller.d.ts +28 -0
  91. package/dist/server/modules/reviews/controllers/reviews.controller.js +369 -0
  92. package/dist/server/modules/reviews/controllers/reviews.controller.js.map +1 -0
  93. package/dist/server/modules/reviews/dtos/review.dto.d.ts +195 -0
  94. package/dist/server/modules/reviews/dtos/review.dto.js +92 -0
  95. package/dist/server/modules/reviews/dtos/review.dto.js.map +1 -0
  96. package/dist/server/modules/reviews/reviews.module.d.ts +2 -0
  97. package/dist/server/modules/reviews/reviews.module.js +27 -0
  98. package/dist/server/modules/reviews/reviews.module.js.map +1 -0
  99. package/dist/server/modules/reviews/services/reviews.service.d.ts +86 -0
  100. package/dist/server/modules/reviews/services/reviews.service.js +464 -0
  101. package/dist/server/modules/reviews/services/reviews.service.js.map +1 -0
  102. package/dist/server/modules/reviews/utils/notification-formatter.d.ts +12 -0
  103. package/dist/server/modules/reviews/utils/notification-formatter.js +60 -0
  104. package/dist/server/modules/reviews/utils/notification-formatter.js.map +1 -0
  105. package/dist/server/modules/sessions/services/sessions.service.d.ts +3 -1
  106. package/dist/server/modules/sessions/services/sessions.service.js +32 -2
  107. package/dist/server/modules/sessions/services/sessions.service.js.map +1 -1
  108. package/dist/server/modules/storage/db/schema.d.ts +697 -0
  109. package/dist/server/modules/storage/db/schema.js +71 -1
  110. package/dist/server/modules/storage/db/schema.js.map +1 -1
  111. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +23 -1
  112. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  113. package/dist/server/modules/storage/local/local-storage.service.d.ts +15 -2
  114. package/dist/server/modules/storage/local/local-storage.service.js +396 -7
  115. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  116. package/dist/server/modules/storage/models/domain.models.d.ts +62 -0
  117. package/dist/server/templates/claude-codex-advanced-swe.json +139 -0
  118. package/dist/server/templates/claude-codex-advanced.json +82 -82
  119. package/dist/server/templates/claude-opus.json +99 -99
  120. package/dist/server/templates/claude-swe-single.json +105 -0
  121. package/dist/server/templates/simple-codex.json +70 -70
  122. package/dist/server/test-setup.js +45 -0
  123. package/dist/server/test-setup.js.map +1 -1
  124. package/dist/server/tsconfig.tsbuildinfo +1 -1
  125. package/dist/server/ui/assets/ReviewDetailPage-I54h-2L-.js +6 -0
  126. package/dist/server/ui/assets/ReviewsPage-B4ua5hiX.js +19 -0
  127. package/dist/server/ui/assets/index-CqcmnFBh.css +32 -0
  128. package/dist/server/ui/assets/index-JbUMpbg7.js +858 -0
  129. package/dist/server/ui/assets/useReviewSubscription-C0GEsiRw.js +83 -0
  130. package/dist/server/ui/assets/useReviewSubscription-T3uj2-aP.css +1 -0
  131. package/dist/server/ui/index.html +2 -2
  132. package/dist/templates/claude-codex-advanced-swe.json +139 -0
  133. package/dist/templates/claude-codex-advanced.json +82 -82
  134. package/dist/templates/claude-opus.json +99 -99
  135. package/dist/templates/claude-swe-single.json +105 -0
  136. package/dist/templates/simple-codex.json +70 -70
  137. package/package.json +16 -3
  138. package/prebuilds/node-pty/darwin-arm64/pty.node +0 -0
  139. package/prebuilds/node-pty/darwin-arm64/spawn-helper +0 -0
  140. package/prebuilds/node-pty/darwin-x64/pty.node +0 -0
  141. package/prebuilds/node-pty/darwin-x64/spawn-helper +0 -0
  142. package/prebuilds/node-pty/linux-arm64/pty.node +0 -0
  143. package/prebuilds/node-pty/linux-x64/pty.node +0 -0
  144. package/prebuilds/node-pty/win32-arm64/conpty/OpenConsole.exe +0 -0
  145. package/prebuilds/node-pty/win32-arm64/conpty/conpty.dll +0 -0
  146. package/prebuilds/node-pty/win32-arm64/conpty.node +0 -0
  147. package/prebuilds/node-pty/win32-arm64/conpty.pdb +0 -0
  148. package/prebuilds/node-pty/win32-arm64/conpty_console_list.node +0 -0
  149. package/prebuilds/node-pty/win32-arm64/conpty_console_list.pdb +0 -0
  150. package/prebuilds/node-pty/win32-arm64/pty.node +0 -0
  151. package/prebuilds/node-pty/win32-arm64/pty.pdb +0 -0
  152. package/prebuilds/node-pty/win32-arm64/winpty-agent.exe +0 -0
  153. package/prebuilds/node-pty/win32-arm64/winpty-agent.pdb +0 -0
  154. package/prebuilds/node-pty/win32-arm64/winpty.dll +0 -0
  155. package/prebuilds/node-pty/win32-arm64/winpty.pdb +0 -0
  156. package/prebuilds/node-pty/win32-x64/conpty/OpenConsole.exe +0 -0
  157. package/prebuilds/node-pty/win32-x64/conpty/conpty.dll +0 -0
  158. package/prebuilds/node-pty/win32-x64/conpty.node +0 -0
  159. package/prebuilds/node-pty/win32-x64/conpty.pdb +0 -0
  160. package/prebuilds/node-pty/win32-x64/conpty_console_list.node +0 -0
  161. package/prebuilds/node-pty/win32-x64/conpty_console_list.pdb +0 -0
  162. package/prebuilds/node-pty/win32-x64/pty.node +0 -0
  163. package/prebuilds/node-pty/win32-x64/pty.pdb +0 -0
  164. package/prebuilds/node-pty/win32-x64/winpty-agent.exe +0 -0
  165. package/prebuilds/node-pty/win32-x64/winpty-agent.pdb +0 -0
  166. package/prebuilds/node-pty/win32-x64/winpty.dll +0 -0
  167. package/prebuilds/node-pty/win32-x64/winpty.pdb +0 -0
  168. package/scripts/postinstall.js +51 -1
  169. package/dist/server/ui/assets/index-BoDZOB7c.css +0 -32
  170. package/dist/server/ui/assets/index-hB0e02VB.js +0 -735
@@ -696,7 +696,13 @@ let LocalStorageService = class LocalStorageService {
696
696
  .where(eq(epics.projectId, projectId))
697
697
  .limit(limit)
698
698
  .offset(offset);
699
- const itemsWithTags = await Promise.all(items.map((item) => this.getEpic(item.id)));
699
+ const epicIds = items.map((item) => item.id);
700
+ const tagsMap = await this.batchFetchTags(epicIds);
701
+ const itemsWithTags = items.map((item) => ({
702
+ ...item,
703
+ data: item.data,
704
+ tags: tagsMap.get(item.id) ?? [],
705
+ }));
700
706
  return {
701
707
  items: itemsWithTags,
702
708
  total: items.length,
@@ -715,7 +721,13 @@ let LocalStorageService = class LocalStorageService {
715
721
  .where(eq(epics.statusId, statusId))
716
722
  .limit(limit)
717
723
  .offset(offset);
718
- const itemsWithTags = await Promise.all(items.map((item) => this.getEpic(item.id)));
724
+ const epicIds = items.map((item) => item.id);
725
+ const tagsMap = await this.batchFetchTags(epicIds);
726
+ const itemsWithTags = items.map((item) => ({
727
+ ...item,
728
+ data: item.data,
729
+ tags: tagsMap.get(item.id) ?? [],
730
+ }));
719
731
  return {
720
732
  items: itemsWithTags,
721
733
  total: items.length,
@@ -733,7 +745,14 @@ let LocalStorageService = class LocalStorageService {
733
745
  const search = options.q.trim().toLowerCase();
734
746
  if (search.length) {
735
747
  const pattern = `%${search}%`;
736
- conditions.push(sql `(lower(${epics.title}) LIKE ${pattern} OR lower(ifnull(${epics.description}, '')) LIKE ${pattern})`);
748
+ const isUuidPrefix = search.length >= 8 && /^[a-f0-9-]+$/.test(search);
749
+ if (isUuidPrefix) {
750
+ const idPrefixPattern = `${search}%`;
751
+ conditions.push(sql `(lower(${epics.title}) LIKE ${pattern} OR lower(ifnull(${epics.description}, '')) LIKE ${pattern} OR ${epics.id} LIKE ${idPrefixPattern})`);
752
+ }
753
+ else {
754
+ conditions.push(sql `(lower(${epics.title}) LIKE ${pattern} OR lower(ifnull(${epics.description}, '')) LIKE ${pattern})`);
755
+ }
737
756
  }
738
757
  }
739
758
  if (options.statusId) {
@@ -764,14 +783,20 @@ let LocalStorageService = class LocalStorageService {
764
783
  .where(whereClause);
765
784
  const total = Number(totalResult[0]?.count ?? 0);
766
785
  const rows = await this.db
767
- .select({ id: epics.id })
786
+ .select({ epic: epics })
768
787
  .from(epics)
769
788
  .innerJoin(statuses, eq(statuses.id, epics.statusId))
770
789
  .where(whereClause)
771
790
  .orderBy(desc(epics.updatedAt))
772
791
  .limit(limit)
773
792
  .offset(offset);
774
- const items = await Promise.all(rows.map((row) => this.getEpic(row.id)));
793
+ const epicIds = rows.map((row) => row.epic.id);
794
+ const tagsMap = await this.batchFetchTags(epicIds);
795
+ const items = rows.map((row) => ({
796
+ ...row.epic,
797
+ data: row.epic.data,
798
+ tags: tagsMap.get(row.epic.id) ?? [],
799
+ }));
775
800
  return {
776
801
  items,
777
802
  total,
@@ -804,13 +829,19 @@ let LocalStorageService = class LocalStorageService {
804
829
  .where(whereClause);
805
830
  const total = Number(totalResult[0]?.count ?? 0);
806
831
  const rows = await this.db
807
- .select({ id: epics.id })
832
+ .select()
808
833
  .from(epics)
809
834
  .where(whereClause)
810
835
  .orderBy(desc(epics.updatedAt))
811
836
  .limit(limit)
812
837
  .offset(offset);
813
- const items = await Promise.all(rows.map((row) => this.getEpic(row.id)));
838
+ const epicIds = rows.map((row) => row.id);
839
+ const tagsMap = await this.batchFetchTags(epicIds);
840
+ const items = rows.map((row) => ({
841
+ ...row,
842
+ data: row.data,
843
+ tags: tagsMap.get(row.id) ?? [],
844
+ }));
814
845
  return {
815
846
  items,
816
847
  total,
@@ -2690,6 +2721,364 @@ let LocalStorageService = class LocalStorageService {
2690
2721
  updatedAt: now,
2691
2722
  };
2692
2723
  }
2724
+ async createReview(data) {
2725
+ const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
2726
+ const now = new Date().toISOString();
2727
+ const { reviews } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
2728
+ const review = {
2729
+ id: randomUUID(),
2730
+ projectId: data.projectId,
2731
+ epicId: data.epicId,
2732
+ title: data.title,
2733
+ description: data.description,
2734
+ status: data.status,
2735
+ mode: data.mode,
2736
+ baseRef: data.baseRef,
2737
+ headRef: data.headRef,
2738
+ baseSha: data.baseSha,
2739
+ headSha: data.headSha,
2740
+ createdBy: data.createdBy,
2741
+ createdByAgentId: data.createdByAgentId,
2742
+ version: 1,
2743
+ createdAt: now,
2744
+ updatedAt: now,
2745
+ };
2746
+ await this.db.insert(reviews).values({
2747
+ id: review.id,
2748
+ projectId: review.projectId,
2749
+ epicId: review.epicId,
2750
+ title: review.title,
2751
+ description: review.description,
2752
+ status: review.status,
2753
+ mode: review.mode,
2754
+ baseRef: review.baseRef,
2755
+ headRef: review.headRef,
2756
+ baseSha: review.baseSha,
2757
+ headSha: review.headSha,
2758
+ createdBy: review.createdBy,
2759
+ createdByAgentId: review.createdByAgentId,
2760
+ version: review.version,
2761
+ createdAt: review.createdAt,
2762
+ updatedAt: review.updatedAt,
2763
+ });
2764
+ logger.info({ reviewId: review.id, projectId: review.projectId }, 'Created review');
2765
+ return review;
2766
+ }
2767
+ async getReview(id) {
2768
+ const { reviews, reviewComments } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
2769
+ const { eq, count } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
2770
+ const result = await this.db.select().from(reviews).where(eq(reviews.id, id)).limit(1);
2771
+ if (!result[0]) {
2772
+ throw new error_types_1.NotFoundError('Review', id);
2773
+ }
2774
+ const countResult = await this.db
2775
+ .select({ count: count() })
2776
+ .from(reviewComments)
2777
+ .where(eq(reviewComments.reviewId, id));
2778
+ return {
2779
+ ...result[0],
2780
+ commentCount: countResult[0]?.count ?? 0,
2781
+ };
2782
+ }
2783
+ async updateReview(id, data, expectedVersion) {
2784
+ const { reviews } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
2785
+ const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
2786
+ const now = new Date().toISOString();
2787
+ const current = await this.getReview(id);
2788
+ if (current.version !== expectedVersion) {
2789
+ throw new error_types_1.OptimisticLockError('Review', id, {
2790
+ expectedVersion,
2791
+ actualVersion: current.version,
2792
+ });
2793
+ }
2794
+ const updateData = {};
2795
+ if (data.title !== undefined)
2796
+ updateData.title = data.title;
2797
+ if (data.description !== undefined)
2798
+ updateData.description = data.description;
2799
+ if (data.status !== undefined)
2800
+ updateData.status = data.status;
2801
+ if (data.headSha !== undefined)
2802
+ updateData.headSha = data.headSha;
2803
+ await this.db
2804
+ .update(reviews)
2805
+ .set({ ...updateData, version: expectedVersion + 1, updatedAt: now })
2806
+ .where(eq(reviews.id, id));
2807
+ logger.info({ reviewId: id }, 'Updated review');
2808
+ return this.getReview(id);
2809
+ }
2810
+ async deleteReview(id) {
2811
+ const { reviews } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
2812
+ const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
2813
+ await this.db.delete(reviews).where(eq(reviews.id, id));
2814
+ logger.info({ reviewId: id }, 'Deleted review');
2815
+ }
2816
+ async listReviews(projectId, options = {}) {
2817
+ const { reviews, reviewComments } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
2818
+ const { eq, and, count, desc } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
2819
+ const limit = options.limit ?? 100;
2820
+ const offset = options.offset ?? 0;
2821
+ const conditions = [eq(reviews.projectId, projectId)];
2822
+ if (options.status) {
2823
+ conditions.push(eq(reviews.status, options.status));
2824
+ }
2825
+ if (options.epicId) {
2826
+ conditions.push(eq(reviews.epicId, options.epicId));
2827
+ }
2828
+ const rows = await this.db
2829
+ .select()
2830
+ .from(reviews)
2831
+ .where(and(...conditions))
2832
+ .orderBy(desc(reviews.createdAt))
2833
+ .limit(limit)
2834
+ .offset(offset);
2835
+ const reviewIds = rows.map((r) => r.id);
2836
+ let commentCountMap = new Map();
2837
+ if (reviewIds.length > 0) {
2838
+ const { inArray } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
2839
+ const countRows = await this.db
2840
+ .select({
2841
+ reviewId: reviewComments.reviewId,
2842
+ count: count(),
2843
+ })
2844
+ .from(reviewComments)
2845
+ .where(inArray(reviewComments.reviewId, reviewIds))
2846
+ .groupBy(reviewComments.reviewId);
2847
+ commentCountMap = new Map(countRows.map((r) => [r.reviewId, r.count]));
2848
+ }
2849
+ const items = rows.map((row) => ({
2850
+ ...row,
2851
+ commentCount: commentCountMap.get(row.id) ?? 0,
2852
+ }));
2853
+ return {
2854
+ items,
2855
+ total: items.length,
2856
+ limit,
2857
+ offset,
2858
+ };
2859
+ }
2860
+ async createReviewComment(data, targetAgentIds) {
2861
+ const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
2862
+ const now = new Date().toISOString();
2863
+ const { reviewComments, reviewCommentTargets } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
2864
+ const sqlite = (0, sqlite_raw_1.getRawSqliteClient)(this.db);
2865
+ if (!sqlite || typeof sqlite.exec !== 'function') {
2866
+ throw new error_types_1.StorageError('Unable to access underlying SQLite client');
2867
+ }
2868
+ sqlite.exec('BEGIN IMMEDIATE TRANSACTION');
2869
+ try {
2870
+ const comment = {
2871
+ id: randomUUID(),
2872
+ reviewId: data.reviewId,
2873
+ filePath: data.filePath,
2874
+ parentId: data.parentId,
2875
+ lineStart: data.lineStart,
2876
+ lineEnd: data.lineEnd,
2877
+ side: data.side,
2878
+ content: data.content,
2879
+ commentType: data.commentType,
2880
+ status: data.status,
2881
+ authorType: data.authorType,
2882
+ authorAgentId: data.authorAgentId,
2883
+ version: 1,
2884
+ editedAt: null,
2885
+ createdAt: now,
2886
+ updatedAt: now,
2887
+ };
2888
+ await this.db.insert(reviewComments).values({
2889
+ id: comment.id,
2890
+ reviewId: comment.reviewId,
2891
+ filePath: comment.filePath,
2892
+ parentId: comment.parentId,
2893
+ lineStart: comment.lineStart,
2894
+ lineEnd: comment.lineEnd,
2895
+ side: comment.side,
2896
+ content: comment.content,
2897
+ commentType: comment.commentType,
2898
+ status: comment.status,
2899
+ authorType: comment.authorType,
2900
+ authorAgentId: comment.authorAgentId,
2901
+ version: comment.version,
2902
+ editedAt: comment.editedAt,
2903
+ createdAt: comment.createdAt,
2904
+ updatedAt: comment.updatedAt,
2905
+ });
2906
+ if (targetAgentIds && targetAgentIds.length > 0) {
2907
+ for (const agentId of targetAgentIds) {
2908
+ await this.db.insert(reviewCommentTargets).values({
2909
+ id: randomUUID(),
2910
+ commentId: comment.id,
2911
+ agentId,
2912
+ createdAt: now,
2913
+ });
2914
+ }
2915
+ }
2916
+ sqlite.exec('COMMIT');
2917
+ logger.info({ commentId: comment.id, reviewId: comment.reviewId, targets: targetAgentIds?.length ?? 0 }, 'Created review comment');
2918
+ return comment;
2919
+ }
2920
+ catch (error) {
2921
+ try {
2922
+ sqlite.exec('ROLLBACK');
2923
+ logger.info('Transaction rolled back successfully');
2924
+ }
2925
+ catch (rollbackError) {
2926
+ logger.error({ rollbackError }, 'Failed to rollback transaction');
2927
+ }
2928
+ throw error;
2929
+ }
2930
+ }
2931
+ async getReviewComment(id) {
2932
+ const { reviewComments } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
2933
+ const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
2934
+ const result = await this.db
2935
+ .select()
2936
+ .from(reviewComments)
2937
+ .where(eq(reviewComments.id, id))
2938
+ .limit(1);
2939
+ if (!result[0]) {
2940
+ throw new error_types_1.NotFoundError('ReviewComment', id);
2941
+ }
2942
+ return result[0];
2943
+ }
2944
+ async updateReviewComment(id, data, expectedVersion) {
2945
+ const { reviewComments } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
2946
+ const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
2947
+ const now = new Date().toISOString();
2948
+ const current = await this.getReviewComment(id);
2949
+ if (current.version !== expectedVersion) {
2950
+ throw new error_types_1.OptimisticLockError('ReviewComment', id, {
2951
+ expectedVersion,
2952
+ actualVersion: current.version,
2953
+ });
2954
+ }
2955
+ const updateData = {};
2956
+ if (data.content !== undefined && data.content !== current.content) {
2957
+ updateData.content = data.content;
2958
+ updateData.editedAt = now;
2959
+ }
2960
+ if (data.status !== undefined && data.status !== current.status) {
2961
+ updateData.status = data.status;
2962
+ }
2963
+ if (Object.keys(updateData).length === 0) {
2964
+ logger.info({ commentId: id }, 'Skipped review comment update (no changes)');
2965
+ return current;
2966
+ }
2967
+ await this.db
2968
+ .update(reviewComments)
2969
+ .set({ ...updateData, version: expectedVersion + 1, updatedAt: now })
2970
+ .where(eq(reviewComments.id, id));
2971
+ logger.info({ commentId: id }, 'Updated review comment');
2972
+ return this.getReviewComment(id);
2973
+ }
2974
+ async listReviewComments(reviewId, options = {}) {
2975
+ const { reviewComments, agents, reviewCommentTargets } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
2976
+ const { eq, and, isNull, desc, inArray } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
2977
+ const limit = options.limit ?? 100;
2978
+ const offset = options.offset ?? 0;
2979
+ const conditions = [eq(reviewComments.reviewId, reviewId)];
2980
+ if (options.status) {
2981
+ conditions.push(eq(reviewComments.status, options.status));
2982
+ }
2983
+ if (options.filePath) {
2984
+ conditions.push(eq(reviewComments.filePath, options.filePath));
2985
+ }
2986
+ if (options.parentId === null) {
2987
+ conditions.push(isNull(reviewComments.parentId));
2988
+ }
2989
+ else if (options.parentId !== undefined) {
2990
+ conditions.push(eq(reviewComments.parentId, options.parentId));
2991
+ }
2992
+ const rows = await this.db
2993
+ .select()
2994
+ .from(reviewComments)
2995
+ .where(and(...conditions))
2996
+ .orderBy(desc(reviewComments.createdAt))
2997
+ .limit(limit)
2998
+ .offset(offset);
2999
+ if (rows.length === 0) {
3000
+ return { items: [], total: 0, limit, offset };
3001
+ }
3002
+ const authorAgentIds = [
3003
+ ...new Set(rows.map((r) => r.authorAgentId).filter((id) => typeof id === 'string')),
3004
+ ];
3005
+ const agentNameMap = new Map();
3006
+ if (authorAgentIds.length > 0) {
3007
+ const authorAgents = await this.db
3008
+ .select({ id: agents.id, name: agents.name })
3009
+ .from(agents)
3010
+ .where(inArray(agents.id, authorAgentIds));
3011
+ authorAgents.forEach((a) => agentNameMap.set(a.id, a.name));
3012
+ }
3013
+ const commentIds = rows.map((r) => r.id);
3014
+ const targetsWithNames = await this.db
3015
+ .select({
3016
+ commentId: reviewCommentTargets.commentId,
3017
+ agentId: reviewCommentTargets.agentId,
3018
+ agentName: agents.name,
3019
+ })
3020
+ .from(reviewCommentTargets)
3021
+ .leftJoin(agents, eq(reviewCommentTargets.agentId, agents.id))
3022
+ .where(inArray(reviewCommentTargets.commentId, commentIds));
3023
+ const targetsByCommentId = new Map();
3024
+ targetsWithNames.forEach((t) => {
3025
+ const list = targetsByCommentId.get(t.commentId) ?? [];
3026
+ list.push({ agentId: t.agentId, name: t.agentName ?? 'Unknown' });
3027
+ targetsByCommentId.set(t.commentId, list);
3028
+ });
3029
+ const enrichedItems = rows.map((row) => ({
3030
+ ...row,
3031
+ authorAgentName: row.authorAgentId ? (agentNameMap.get(row.authorAgentId) ?? null) : null,
3032
+ targetAgents: targetsByCommentId.get(row.id) ?? [],
3033
+ }));
3034
+ return {
3035
+ items: enrichedItems,
3036
+ total: rows.length,
3037
+ limit,
3038
+ offset,
3039
+ };
3040
+ }
3041
+ async addReviewCommentTargets(commentId, agentIds) {
3042
+ const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
3043
+ const now = new Date().toISOString();
3044
+ const { reviewCommentTargets } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
3045
+ await this.getReviewComment(commentId);
3046
+ const targets = [];
3047
+ for (const agentId of agentIds) {
3048
+ const target = {
3049
+ id: randomUUID(),
3050
+ commentId,
3051
+ agentId,
3052
+ createdAt: now,
3053
+ };
3054
+ await this.db.insert(reviewCommentTargets).values(target);
3055
+ targets.push(target);
3056
+ }
3057
+ logger.info({ commentId, count: targets.length }, 'Added review comment targets');
3058
+ return targets;
3059
+ }
3060
+ async getReviewCommentTargets(commentId) {
3061
+ const { reviewCommentTargets } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
3062
+ const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
3063
+ const rows = await this.db
3064
+ .select()
3065
+ .from(reviewCommentTargets)
3066
+ .where(eq(reviewCommentTargets.commentId, commentId));
3067
+ return rows;
3068
+ }
3069
+ async deleteReviewComment(id) {
3070
+ const { reviewComments } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
3071
+ const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
3072
+ await this.db.delete(reviewComments).where(eq(reviewComments.id, id));
3073
+ }
3074
+ async deleteNonResolvedComments(reviewId) {
3075
+ const { reviewComments } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
3076
+ const { eq, and, notInArray } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
3077
+ const result = await this.db
3078
+ .delete(reviewComments)
3079
+ .where(and(eq(reviewComments.reviewId, reviewId), notInArray(reviewComments.status, ['resolved', 'wont_fix'])));
3080
+ return result.changes ?? 0;
3081
+ }
2693
3082
  };
2694
3083
  exports.LocalStorageService = LocalStorageService;
2695
3084
  exports.LocalStorageService = LocalStorageService = __decorate([