persistence on auto PR

This commit is contained in:
AndyMik90
2026-01-10 19:02:31 +01:00
parent 125748e0fd
commit c3be86b080
5 changed files with 119 additions and 2 deletions
@@ -920,6 +920,91 @@ export function registerAutoFixHandlers(
abortController: AbortController;
}>();
/**
* Load persisted auto-PR review states on startup.
* This restores the in-memory state from disk so that active reviews
* are visible in the UI after app restart.
*/
function loadPersistedAutoPRReviewStates(): void {
const project = findProjectForAutoPRReview();
if (!project) {
debugLog('No project available for loading persisted Auto-PR-Review states');
return;
}
const stateDir = path.join(getGitHubDir(project), 'pr_review_state');
const indexFile = path.join(stateDir, 'index.json');
try {
const indexData = JSON.parse(fs.readFileSync(indexFile, 'utf-8'));
const reviews = indexData.reviews || [];
for (const entry of reviews) {
// Only load active (non-terminal) states
const terminalStatuses = ['completed', 'failed', 'cancelled', 'max_iterations', 'ready_to_merge'];
if (terminalStatuses.includes(entry.status)) {
continue;
}
// Load full state from individual file
const stateFile = path.join(stateDir, `pr_${entry.pr_number}.json`);
try {
const stateData = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
const key = `${stateData.repo}#${entry.pr_number}`;
// Map backend status to frontend status
const statusMap: Record<string, AutoPRReviewProgress['status']> = {
'pending': 'awaiting_checks',
'awaiting_checks': 'awaiting_checks',
'reviewing': 'pr_reviewing',
'fixing': 'pr_fixing',
'ready_to_merge': 'pr_ready_to_merge',
};
const progress: AutoPRReviewProgress = {
prNumber: entry.pr_number,
repository: stateData.repo,
status: statusMap[entry.status] || 'awaiting_checks',
currentIteration: entry.current_iteration || 0,
maxIterations: stateData.max_iterations || 5,
startedAt: entry.started_at || new Date().toISOString(),
elapsedMs: 0,
ciChecks: stateData.ci_checks || [],
ciSummary: { total: 0, passed: 0, failed: 0, pending: 0 },
externalBots: [],
fixedFindingsCount: stateData.resolved_finding_ids?.length || 0,
remainingFindingsCount: stateData.pending_finding_ids?.length || 0,
isCancellable: false, // Recovered states are not cancellable (no subprocess)
currentActivity: 'Recovered from previous session - restart review to continue',
};
activeAutoPRReviews.set(key, {
progress,
abortController: new AbortController(), // Placeholder, not connected to any subprocess
});
debugLog('Loaded persisted Auto-PR-Review state', {
key,
status: progress.status,
iteration: progress.currentIteration,
});
} catch {
// Skip invalid state files
}
}
debugLog('Loaded persisted Auto-PR-Review states', {
count: activeAutoPRReviews.size,
});
} catch {
// Index file doesn't exist or is invalid - no states to load
debugLog('No persisted Auto-PR-Review states to load');
}
}
// Load persisted states on startup
loadPersistedAutoPRReviewStates();
/**
* Get Auto-PR-Review configuration for a project
*/
@@ -3,6 +3,7 @@ import { GitPullRequest, RefreshCw, ExternalLink, Settings } from "lucide-react"
import { useTranslation } from "react-i18next";
import { useProjectStore } from "../../stores/project-store";
import { useGitHubPRs, usePRFiltering } from "./hooks";
import { useAutoPRReview } from "../github-issues/hooks/useAutoPRReview";
import { PRList, PRDetail, PRFilterBar } from "./components";
import { Button } from "../ui/button";
import { ResizablePanels } from "../ui/resizable-panels";
@@ -100,6 +101,12 @@ export function GitHubPRs({ onOpenSettings, isActive = false }: GitHubPRsProps)
hasActiveFilters,
} = usePRFiltering(prs, getReviewStateForPR);
// Auto-PR Review state for list indicator
const {
isReviewActive: isAutoPRReviewActive,
getReviewProgress: getAutoPRReviewProgress,
} = useAutoPRReview();
const handleRunReview = useCallback(() => {
if (selectedPRNumber) {
runReview(selectedPRNumber);
@@ -233,6 +240,9 @@ export function GitHubPRs({ onOpenSettings, isActive = false }: GitHubPRsProps)
getReviewStateForPR={getReviewStateForPR}
onSelectPR={selectPR}
onLoadMore={loadMore}
repoFullName={repoFullName}
isAutoPRReviewActive={isAutoPRReviewActive}
getAutoPRReviewProgress={getAutoPRReviewProgress}
/>
</div>
}
@@ -1,10 +1,11 @@
import { useRef, useEffect, useCallback } from 'react';
import { GitPullRequest, User, Clock, FileDiff, Loader2 } from 'lucide-react';
import { GitPullRequest, User, Clock, FileDiff, Loader2, Bot } from 'lucide-react';
import { ScrollArea } from '../../ui/scroll-area';
import { Badge } from '../../ui/badge';
import { cn } from '../../../lib/utils';
import type { PRData, PRReviewProgress, PRReviewResult } from '../hooks/useGitHubPRs';
import type { NewCommitsCheck } from '../../../../preload/api/modules/github-api';
import type { AutoPRReviewProgress } from '../../github-issues/types';
import { useTranslation } from 'react-i18next';
/**
@@ -173,6 +174,12 @@ interface PRListProps {
getReviewStateForPR: (prNumber: number) => PRReviewInfo | null;
onSelectPR: (prNumber: number) => void;
onLoadMore: () => void;
/** Repository full name for auto-PR review lookup */
repoFullName?: string | null;
/** Check if auto-PR review is active for a PR */
isAutoPRReviewActive?: (repoFullName: string, prNumber: number) => boolean;
/** Get auto-PR review progress for a PR */
getAutoPRReviewProgress?: (repoFullName: string, prNumber: number) => AutoPRReviewProgress | undefined;
}
function formatDate(dateString: string): string {
@@ -204,7 +211,10 @@ export function PRList({
error,
getReviewStateForPR,
onSelectPR,
onLoadMore
onLoadMore,
repoFullName,
isAutoPRReviewActive,
getAutoPRReviewProgress,
}: PRListProps) {
const { t } = useTranslation('common');
const scrollAreaRef = useRef<HTMLDivElement>(null);
@@ -315,6 +325,16 @@ export function PRList({
hasCommitsAfterPosting={reviewState?.newCommitsCheck?.hasCommitsAfterPosting ?? false}
t={t}
/>
{/* Auto-PR Review Active Indicator */}
{repoFullName && isAutoPRReviewActive && isAutoPRReviewActive(repoFullName, pr.number) && (
<Badge
variant="outline"
className="text-xs border-primary/50 text-primary bg-primary/10 animate-pulse"
>
<Bot className="h-3 w-3 mr-1" />
{t('prReview.autoReviewActive')}
</Badge>
)}
</div>
<h3 className="font-medium text-sm truncate">{pr.title}</h3>
<div className="flex items-center gap-3 mt-2 text-xs text-muted-foreground">
@@ -190,6 +190,7 @@
"runFollowup": "Run Follow-up",
"aiReviewInProgress": "AI Review in Progress",
"waitingForChanges": "Waiting for Changes",
"autoReviewActive": "Auto-Review",
"reviewComplete": "Review Complete",
"reviewStatus": "Review Status",
"files": "files",
@@ -190,6 +190,7 @@
"runFollowup": "Lancer le suivi",
"aiReviewInProgress": "Révision IA en cours",
"waitingForChanges": "En attente de modifications",
"autoReviewActive": "Auto-Révision",
"reviewComplete": "Révision terminée",
"reviewStatus": "Statut de révision",
"files": "fichiers",