revert: AI 코드 검토 기능 제거 (85ae82f 되돌림)

통짜 코드 전송 방식이 토큰 비효율·부정확해, 검색 기반(grep으로 좁힌 뒤 관련
스니펫만 판정) 방식으로 재설계하기 위해 일괄 제거. 추후 개선안으로 재작업 예정.

This reverts commit 85ae82f.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-20 17:56:43 +09:00
parent e72e6aee83
commit 9bcbc1564f
14 changed files with 11 additions and 543 deletions
-13
View File
@@ -1,6 +1,5 @@
import { useApi } from './useApi'
import type {
ChecklistReviewResult,
ConversationDetail,
ConversationSummary,
SendResult,
@@ -56,17 +55,6 @@ export function useAi() {
})) as unknown as SuggestChecklistResult
}
// 코드 검토 — 체크리스트 구현 여부 판정(서버가 체크리스트에 반영). 코드 양에 따라 오래 걸림
async function reviewChecklist(payload: {
repoId: string
taskSeq: number
}): Promise<ChecklistReviewResult> {
return (await api.post('/ai/review-checklist', payload, {
silent: true,
timeout: 90_000,
})) as unknown as ChecklistReviewResult
}
return {
listConversations,
getConversation,
@@ -74,6 +62,5 @@ export function useAi() {
sendMessage,
deleteConversation,
suggestChecklist,
reviewChecklist,
}
}
-2
View File
@@ -36,8 +36,6 @@ export interface ChecklistItem {
id?: string
text: string
done: boolean
/** AI 코드 검토 판정 — 'done'(구현) | 'missing'(미구현) | null(미검토) */
reviewVerdict?: 'done' | 'missing' | null
}
/** 저장소 */
+4 -137
View File
@@ -21,7 +21,6 @@ import {
} from '@/mock/relay.mock'
import { useTaskStore } from '@/stores/task.store'
import { useAuthStore } from '@/stores/auth.store'
import { useAi } from '@/composables/useAi'
import UserAvatar from '@/components/UserAvatar.vue'
import { attachmentKindOf, fileBadge } from '@/shared/utils/format'
@@ -32,7 +31,6 @@ const taskId = computed(() => Number(route.params.taskId))
const taskStore = useTaskStore()
const authStore = useAuthStore()
const ai = useAi()
// 업무 상세 — API 연동
const task = ref<TaskDetail | null>(null)
@@ -171,33 +169,6 @@ async function sendApproval() {
}
}
// --- AI 코드 검토 ---
// 연결된 Gitea 코드를 검토해 체크리스트 항목별 구현 여부(✓/✗)를 채운다.
const aiReviewing = ref(false)
const aiReviewSummary = ref<string | null>(null)
const aiReviewTruncated = ref(false)
async function runAiReview() {
if (!task.value || aiReviewing.value) return
aiReviewing.value = true
aiReviewSummary.value = null
try {
const result = await ai.reviewChecklist({
repoId: repoId.value,
taskSeq: task.value.id,
})
aiReviewSummary.value = result.summary
aiReviewTruncated.value = result.truncated
// 서버가 체크리스트에 판정을 반영했으므로 상세를 다시 불러와 ✓/✗ 갱신
await loadTask()
} catch {
window.alert(
'AI 코드 검토에 실패했습니다. Gitea 연결 상태를 확인하거나 잠시 후 다시 시도해 주세요.',
)
} finally {
aiReviewing.value = false
}
}
/* ============================================================
* 댓글 / 활동 탭
* ============================================================ */
@@ -515,48 +486,20 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
</div>
<!--
체크리스트는 읽기 전용 사용자가 개별 토글/추가/삭제하지 않는다.
완료 처리는 승인 워크플로우(승인 완료 일괄 완료) 또는 AI 코드 검토로 채워진다.
완료 처리는 승인 워크플로우(승인 완료 일괄 완료)로만 이루어진다.
-->
<div
v-if="aiReviewSummary"
class="ai-review-summary"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path d="M12 3 13.4 8.6 19 10l-5.6 1.4L12 17l-1.4-5.6L5 10l5.6-1.4Z" /></svg>
<span>{{ aiReviewSummary }}<template v-if="aiReviewTruncated"> (코드 일부만 검토됨)</template></span>
</div>
<div class="checklist">
<div
v-for="(item, i) in task.checklist"
:key="item.id ?? i"
class="citem"
:class="{ done: item.done, missing: item.reviewVerdict === 'missing' }"
:title="item.reviewVerdict === 'missing' ? '미구현으로 판정됨' : undefined"
:class="{ done: item.done }"
>
<span
class="cbox"
:class="{
done: item.done,
missing: item.reviewVerdict === 'missing',
}"
:class="{ done: item.done }"
>
<svg
v-if="item.reviewVerdict === 'missing'"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="3.5"
stroke-linecap="round"
stroke-linejoin="round"
><path d="M18 6 6 18M6 6l12 12" /></svg>
<svg
v-else
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
@@ -921,23 +864,6 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
placeholder="승인 요청 메시지(선택) — 작업 내용이나 검토 포인트를 적어주세요."
/>
<div class="ap-card-actions">
<button
class="ap-btn ai-review"
type="button"
:disabled="aiReviewing"
title="연결된 Gitea 코드를 검토해 체크리스트 구현 여부를 채웁니다"
@click="runAiReview"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path d="M12 3 13.4 8.6 19 10l-5.6 1.4L12 17l-1.4-5.6L5 10l5.6-1.4Z" /></svg>
{{ aiReviewing ? '검토 중…' : 'AI 검토' }}
</button>
<button
class="ap-btn request-send"
type="button"
@@ -985,23 +911,6 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
placeholder="보완 내용 메시지(선택)"
/>
<div class="ap-card-actions">
<button
class="ap-btn ai-review"
type="button"
:disabled="aiReviewing"
title="연결된 Gitea 코드를 검토해 체크리스트 구현 여부를 채웁니다"
@click="runAiReview"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path d="M12 3 13.4 8.6 19 10l-5.6 1.4L12 17l-1.4-5.6L5 10l5.6-1.4Z" /></svg>
{{ aiReviewing ? '검토 중…' : 'AI 검토' }}
</button>
<button
class="ap-btn request-send"
type="button"
@@ -1588,18 +1497,13 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
background: var(--green);
border-color: var(--green);
}
.cbox.missing {
background: var(--red);
border-color: var(--red);
}
.cbox svg {
width: 0.75rem;
height: 0.75rem;
color: #fff;
opacity: 0;
}
.cbox.done svg,
.cbox.missing svg {
.cbox.done svg {
opacity: 1;
}
.citem .ctext {
@@ -1610,31 +1514,6 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
color: var(--text-3);
text-decoration: line-through;
}
.citem.missing .ctext {
color: var(--red);
}
/* AI 코드 검토 요약 배너 */
.ai-review-summary {
display: flex;
align-items: flex-start;
gap: 0.4375rem;
margin-bottom: 0.625rem;
padding: 0.5625rem 0.75rem;
background: var(--accent-weak);
border: 1px solid var(--accent-border);
border-radius: var(--radius);
font-size: 0.781rem;
line-height: 1.5;
color: var(--text);
}
.ai-review-summary svg {
width: 0.9375rem;
height: 0.9375rem;
color: var(--accent);
flex-shrink: 0;
margin-top: 0.125rem;
}
/* 댓글/활동 탭 */
.thread-tabs {
@@ -2370,18 +2249,6 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
.ap-btn.request-send:hover {
background: var(--accent-hover);
}
.ap-btn.ai-review {
color: var(--accent);
background: var(--accent-weak);
border-color: var(--accent-border);
}
.ap-btn.ai-review:hover:not(:disabled) {
background: #e7e4fb;
}
.ap-btn.ai-review:disabled {
opacity: 0.6;
cursor: default;
}
/* ===== 5단계: 첨부파일 ===== */
.files-head {
+1 -6
View File
@@ -176,12 +176,7 @@ function toTaskDetailView(api: ApiTaskDetail): TaskDetailView {
status: api.status,
statusLabel: taskStatusLabel(api.status),
content: api.content,
checklist: api.checklist.map((c) => ({
id: c.id,
text: c.text,
done: c.done,
reviewVerdict: c.reviewVerdict,
})),
checklist: api.checklist.map((c) => ({ id: c.id, text: c.text, done: c.done })),
comments: api.comments.map((c) => toCommentView(c, issuerId)),
activities: api.activities.map(toTaskActivityView),
assignees: api.assignees.map(toUserView),
-14
View File
@@ -33,17 +33,3 @@ export interface ChecklistSuggestionGroup {
export interface SuggestChecklistResult {
groups: ChecklistSuggestionGroup[]
}
// 코드 검토 — 항목별 판정 결과
export type ReviewVerdict = 'done' | 'missing'
export interface ChecklistReviewItem {
id: string
text: string
verdict: ReviewVerdict
reason: string
}
export interface ChecklistReviewResult {
items: ChecklistReviewItem[]
summary: string
truncated: boolean
}
-1
View File
@@ -22,7 +22,6 @@ export interface ApiChecklistItem {
id: string
text: string
done: boolean
reviewVerdict: 'done' | 'missing' | null // AI 코드 검토 판정(미검토면 null)
}
// 첨부파일(API 원시) — 백엔드 AttachmentResponse 와 1:1