devchain-cli 0.6.1 → 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.
- package/dist/drizzle/0024_review_tables.sql +71 -0
- package/dist/drizzle/0025_reviews_mode_and_nullable_shas.sql +80 -0
- package/dist/drizzle/0026_review_comments_edited_at.sql +2 -0
- package/dist/drizzle/meta/_journal.json +22 -1
- package/dist/server/common/errors/error-types.d.ts +3 -0
- package/dist/server/common/errors/error-types.js +7 -1
- package/dist/server/common/errors/error-types.js.map +1 -1
- package/dist/server/common/validation/path-validation.d.ts +11 -0
- package/dist/server/common/validation/path-validation.js +197 -0
- package/dist/server/common/validation/path-validation.js.map +1 -0
- package/dist/server/modules/chat/dtos/chat.dto.d.ts +13 -13
- package/dist/server/modules/core/core.module.js +3 -2
- package/dist/server/modules/core/core.module.js.map +1 -1
- package/dist/server/modules/core/services/preflight.service.d.ts +1 -3
- package/dist/server/modules/core/services/preflight.service.js +2 -28
- package/dist/server/modules/core/services/preflight.service.js.map +1 -1
- package/dist/server/modules/core/services/provider-mcp-ensure.service.d.ts +26 -0
- package/dist/server/modules/core/services/provider-mcp-ensure.service.js +224 -0
- package/dist/server/modules/core/services/provider-mcp-ensure.service.js.map +1 -0
- package/dist/server/modules/epics/controllers/epics.controller.d.ts +3 -2
- package/dist/server/modules/epics/controllers/epics.controller.js +16 -6
- package/dist/server/modules/epics/controllers/epics.controller.js.map +1 -1
- package/dist/server/modules/events/catalog/epic.created.d.ts +2 -2
- package/dist/server/modules/events/catalog/index.d.ts +340 -2
- package/dist/server/modules/events/catalog/index.js +12 -0
- package/dist/server/modules/events/catalog/index.js.map +1 -1
- package/dist/server/modules/events/catalog/review.comment.created.d.ts +69 -0
- package/dist/server/modules/events/catalog/review.comment.created.js +30 -0
- package/dist/server/modules/events/catalog/review.comment.created.js.map +1 -0
- package/dist/server/modules/events/catalog/review.comment.deleted.d.ts +30 -0
- package/dist/server/modules/events/catalog/review.comment.deleted.js +17 -0
- package/dist/server/modules/events/catalog/review.comment.deleted.js.map +1 -0
- package/dist/server/modules/events/catalog/review.comment.resolved.d.ts +30 -0
- package/dist/server/modules/events/catalog/review.comment.resolved.js +17 -0
- package/dist/server/modules/events/catalog/review.comment.resolved.js.map +1 -0
- package/dist/server/modules/events/catalog/review.comment.updated.d.ts +39 -0
- package/dist/server/modules/events/catalog/review.comment.updated.js +20 -0
- package/dist/server/modules/events/catalog/review.comment.updated.js.map +1 -0
- package/dist/server/modules/events/catalog/review.created.d.ts +90 -0
- package/dist/server/modules/events/catalog/review.created.js +40 -0
- package/dist/server/modules/events/catalog/review.created.js.map +1 -0
- package/dist/server/modules/events/catalog/review.updated.d.ts +110 -0
- package/dist/server/modules/events/catalog/review.updated.js +35 -0
- package/dist/server/modules/events/catalog/review.updated.js.map +1 -0
- package/dist/server/modules/events/subscribers/index.js +4 -0
- package/dist/server/modules/events/subscribers/index.js.map +1 -1
- package/dist/server/modules/events/subscribers/review-broadcaster.subscriber.d.ts +16 -0
- package/dist/server/modules/events/subscribers/review-broadcaster.subscriber.js +163 -0
- package/dist/server/modules/events/subscribers/review-broadcaster.subscriber.js.map +1 -0
- package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.d.ts +23 -0
- package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.js +217 -0
- package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.js.map +1 -0
- package/dist/server/modules/git/controllers/git.controller.d.ts +24 -0
- package/dist/server/modules/git/controllers/git.controller.js +230 -0
- package/dist/server/modules/git/controllers/git.controller.js.map +1 -0
- package/dist/server/modules/git/dtos/git.dto.d.ts +86 -0
- package/dist/server/modules/git/dtos/git.dto.js +36 -0
- package/dist/server/modules/git/dtos/git.dto.js.map +1 -0
- package/dist/server/modules/git/git.module.d.ts +2 -0
- package/dist/server/modules/git/git.module.js +25 -0
- package/dist/server/modules/git/git.module.js.map +1 -0
- package/dist/server/modules/git/services/git.service.d.ts +71 -0
- package/dist/server/modules/git/services/git.service.js +550 -0
- package/dist/server/modules/git/services/git.service.js.map +1 -0
- package/dist/server/modules/mcp/controllers/mcp-http.controller.js +17 -562
- package/dist/server/modules/mcp/controllers/mcp-http.controller.js.map +1 -1
- package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js +9 -568
- package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js.map +1 -1
- package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +226 -36
- package/dist/server/modules/mcp/dtos/mcp.dto.js +158 -50
- package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -1
- package/dist/server/modules/mcp/dtos/schema-registry.d.ts +7 -0
- package/dist/server/modules/mcp/dtos/schema-registry.js +56 -0
- package/dist/server/modules/mcp/dtos/schema-registry.js.map +1 -0
- package/dist/server/modules/mcp/mcp.module.js +2 -0
- package/dist/server/modules/mcp/mcp.module.js.map +1 -1
- package/dist/server/modules/mcp/services/mcp.service.d.ts +9 -1
- package/dist/server/modules/mcp/services/mcp.service.js +702 -33
- package/dist/server/modules/mcp/services/mcp.service.js.map +1 -1
- package/dist/server/modules/mcp/tool-definitions.d.ts +2438 -0
- package/dist/server/modules/mcp/tool-definitions.js +727 -0
- package/dist/server/modules/mcp/tool-definitions.js.map +1 -0
- package/dist/server/modules/mcp/utils/param-suggestion.d.ts +3 -0
- package/dist/server/modules/mcp/utils/param-suggestion.js +95 -0
- package/dist/server/modules/mcp/utils/param-suggestion.js.map +1 -0
- package/dist/server/modules/providers/controllers/providers.controller.d.ts +7 -6
- package/dist/server/modules/providers/controllers/providers.controller.js +12 -107
- package/dist/server/modules/providers/controllers/providers.controller.js.map +1 -1
- package/dist/server/modules/reviews/controllers/reviews.controller.d.ts +28 -0
- package/dist/server/modules/reviews/controllers/reviews.controller.js +369 -0
- package/dist/server/modules/reviews/controllers/reviews.controller.js.map +1 -0
- package/dist/server/modules/reviews/dtos/review.dto.d.ts +195 -0
- package/dist/server/modules/reviews/dtos/review.dto.js +92 -0
- package/dist/server/modules/reviews/dtos/review.dto.js.map +1 -0
- package/dist/server/modules/reviews/reviews.module.d.ts +2 -0
- package/dist/server/modules/reviews/reviews.module.js +27 -0
- package/dist/server/modules/reviews/reviews.module.js.map +1 -0
- package/dist/server/modules/reviews/services/reviews.service.d.ts +86 -0
- package/dist/server/modules/reviews/services/reviews.service.js +464 -0
- package/dist/server/modules/reviews/services/reviews.service.js.map +1 -0
- package/dist/server/modules/reviews/utils/notification-formatter.d.ts +12 -0
- package/dist/server/modules/reviews/utils/notification-formatter.js +60 -0
- package/dist/server/modules/reviews/utils/notification-formatter.js.map +1 -0
- package/dist/server/modules/sessions/services/sessions.service.d.ts +3 -1
- package/dist/server/modules/sessions/services/sessions.service.js +32 -2
- package/dist/server/modules/sessions/services/sessions.service.js.map +1 -1
- package/dist/server/modules/storage/db/schema.d.ts +697 -0
- package/dist/server/modules/storage/db/schema.js +71 -1
- package/dist/server/modules/storage/db/schema.js.map +1 -1
- package/dist/server/modules/storage/interfaces/storage.interface.d.ts +23 -1
- package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
- package/dist/server/modules/storage/local/local-storage.service.d.ts +15 -2
- package/dist/server/modules/storage/local/local-storage.service.js +396 -7
- package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
- package/dist/server/modules/storage/models/domain.models.d.ts +62 -0
- package/dist/server/templates/claude-codex-advanced-swe.json +139 -0
- package/dist/server/templates/claude-codex-advanced.json +82 -82
- package/dist/server/templates/claude-opus.json +99 -99
- package/dist/server/templates/claude-swe-single.json +105 -0
- package/dist/server/templates/simple-codex.json +70 -70
- package/dist/server/test-setup.js +45 -0
- package/dist/server/test-setup.js.map +1 -1
- package/dist/server/tsconfig.tsbuildinfo +1 -1
- package/dist/server/ui/assets/ReviewDetailPage-I54h-2L-.js +6 -0
- package/dist/server/ui/assets/ReviewsPage-B4ua5hiX.js +19 -0
- package/dist/server/ui/assets/index-CqcmnFBh.css +32 -0
- package/dist/server/ui/assets/index-JbUMpbg7.js +858 -0
- package/dist/server/ui/assets/useReviewSubscription-C0GEsiRw.js +83 -0
- package/dist/server/ui/assets/useReviewSubscription-T3uj2-aP.css +1 -0
- package/dist/server/ui/index.html +2 -2
- package/dist/templates/claude-codex-advanced-swe.json +139 -0
- package/dist/templates/claude-codex-advanced.json +82 -82
- package/dist/templates/claude-opus.json +99 -99
- package/dist/templates/claude-swe-single.json +105 -0
- package/dist/templates/simple-codex.json +70 -70
- package/package.json +12 -2
- package/prebuilds/node-pty/darwin-arm64/pty.node +0 -0
- package/prebuilds/node-pty/darwin-arm64/spawn-helper +0 -0
- package/prebuilds/node-pty/darwin-x64/pty.node +0 -0
- package/prebuilds/node-pty/darwin-x64/spawn-helper +0 -0
- package/prebuilds/node-pty/linux-arm64/pty.node +0 -0
- package/prebuilds/node-pty/linux-x64/pty.node +0 -0
- package/prebuilds/node-pty/win32-arm64/conpty/OpenConsole.exe +0 -0
- package/prebuilds/node-pty/win32-arm64/conpty/conpty.dll +0 -0
- package/prebuilds/node-pty/win32-arm64/conpty.node +0 -0
- package/prebuilds/node-pty/win32-arm64/conpty.pdb +0 -0
- package/prebuilds/node-pty/win32-arm64/conpty_console_list.node +0 -0
- package/prebuilds/node-pty/win32-arm64/conpty_console_list.pdb +0 -0
- package/prebuilds/node-pty/win32-arm64/pty.node +0 -0
- package/prebuilds/node-pty/win32-arm64/pty.pdb +0 -0
- package/prebuilds/node-pty/win32-arm64/winpty-agent.exe +0 -0
- package/prebuilds/node-pty/win32-arm64/winpty-agent.pdb +0 -0
- package/prebuilds/node-pty/win32-arm64/winpty.dll +0 -0
- package/prebuilds/node-pty/win32-arm64/winpty.pdb +0 -0
- package/prebuilds/node-pty/win32-x64/conpty/OpenConsole.exe +0 -0
- package/prebuilds/node-pty/win32-x64/conpty/conpty.dll +0 -0
- package/prebuilds/node-pty/win32-x64/conpty.node +0 -0
- package/prebuilds/node-pty/win32-x64/conpty.pdb +0 -0
- package/prebuilds/node-pty/win32-x64/conpty_console_list.node +0 -0
- package/prebuilds/node-pty/win32-x64/conpty_console_list.pdb +0 -0
- package/prebuilds/node-pty/win32-x64/pty.node +0 -0
- package/prebuilds/node-pty/win32-x64/pty.pdb +0 -0
- package/prebuilds/node-pty/win32-x64/winpty-agent.exe +0 -0
- package/prebuilds/node-pty/win32-x64/winpty-agent.pdb +0 -0
- package/prebuilds/node-pty/win32-x64/winpty.dll +0 -0
- package/prebuilds/node-pty/win32-x64/winpty.pdb +0 -0
- package/scripts/postinstall.js +51 -1
- package/dist/server/ui/assets/index-BoDZOB7c.css +0 -32
- 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
|
|
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
|
|
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
|
-
|
|
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({
|
|
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
|
|
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(
|
|
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
|
|
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([
|