Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/hook/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ if (args[0] === "review") {
const gitContext = await getGitContext();

// Run git diff HEAD (uncommitted changes - default)
const { patch: rawPatch, label: gitRef } = await runGitDiff(
const { patch: rawPatch, label: gitRef, error: diffError } = await runGitDiff(
"uncommitted",
gitContext.defaultBranch
);
Expand All @@ -77,6 +77,7 @@ if (args[0] === "review") {
const server = await startReviewServer({
rawPatch,
gitRef,
error: diffError,
origin: "claude-code",
diffType: "uncommitted",
gitContext,
Expand Down
3 changes: 2 additions & 1 deletion apps/opencode-plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Do NOT proceed with implementation until your plan is approved.
const gitContext = await getGitContext();

// Run git diff HEAD (uncommitted changes - default)
const { patch: rawPatch, label: gitRef } = await runGitDiff(
const { patch: rawPatch, label: gitRef, error: diffError } = await runGitDiff(
"uncommitted",
gitContext.defaultBranch
);
Expand All @@ -169,6 +169,7 @@ Do NOT proceed with implementation until your plan is approved.
const server = await startReviewServer({
rawPatch,
gitRef,
error: diffError,
origin: "opencode",
diffType: "uncommitted",
gitContext,
Expand Down
10 changes: 5 additions & 5 deletions apps/pi-extension/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,15 +389,15 @@ export function getGitContext(): GitContext {
export function runGitDiff(diffType: DiffType, defaultBranch = "main"): { patch: string; label: string } {
switch (diffType) {
case "uncommitted":
return { patch: git("diff HEAD"), label: "Uncommitted changes" };
return { patch: git("diff HEAD --src-prefix=a/ --dst-prefix=b/"), label: "Uncommitted changes" };
case "staged":
return { patch: git("diff --staged"), label: "Staged changes" };
return { patch: git("diff --staged --src-prefix=a/ --dst-prefix=b/"), label: "Staged changes" };
case "unstaged":
return { patch: git("diff"), label: "Unstaged changes" };
return { patch: git("diff --src-prefix=a/ --dst-prefix=b/"), label: "Unstaged changes" };
case "last-commit":
return { patch: git("diff HEAD~1..HEAD"), label: "Last commit" };
return { patch: git("diff HEAD~1..HEAD --src-prefix=a/ --dst-prefix=b/"), label: "Last commit" };
case "branch":
return { patch: git(`diff ${defaultBranch}..HEAD`), label: `Changes vs ${defaultBranch}` };
return { patch: git(`diff ${defaultBranch}..HEAD --src-prefix=a/ --dst-prefix=b/`), label: `Changes vs ${defaultBranch}` };
default:
return { patch: "", label: "Unknown diff type" };
}
Expand Down
44 changes: 32 additions & 12 deletions packages/review-editor/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ const ReviewApp: React.FC = () => {
const [diffType, setDiffType] = useState<string>('uncommitted');
const [gitContext, setGitContext] = useState<GitContext | null>(null);
const [isLoadingDiff, setIsLoadingDiff] = useState(false);
const [diffError, setDiffError] = useState<string | null>(null);
const [isSendingFeedback, setIsSendingFeedback] = useState(false);
const [isApproving, setIsApproving] = useState(false);
const [submitted, setSubmitted] = useState<'approved' | 'feedback' | false>(false);
Expand Down Expand Up @@ -209,6 +210,7 @@ const ReviewApp: React.FC = () => {
gitContext?: GitContext;
sharingEnabled?: boolean;
repoInfo?: { display: string; branch?: string };
error?: string;
}) => {
const apiFiles = parseDiffToFiles(data.rawPatch);
setDiffData({
Expand All @@ -226,6 +228,7 @@ const ReviewApp: React.FC = () => {
if (data.gitContext) setGitContext(data.gitContext);
if (data.sharingEnabled !== undefined) setSharingEnabled(data.sharingEnabled);
if (data.repoInfo) setRepoInfo(data.repoInfo);
if (data.error) setDiffError(data.error);
})
.catch(() => {
// Not in API mode - use demo content
Expand Down Expand Up @@ -340,10 +343,12 @@ const ReviewApp: React.FC = () => {
setDiffType(data.diffType);
setActiveFileIndex(0);
setPendingSelection(null);
setDiffError((data as { error?: string }).error || null);
// Note: We keep existing annotations - they may still be relevant
// or user can clear them manually
} catch (err) {
console.error('Failed to switch diff:', err);
setDiffError(err instanceof Error ? err.message : 'Failed to switch diff');
} finally {
setIsLoadingDiff(false);
}
Expand Down Expand Up @@ -763,20 +768,35 @@ const ReviewApp: React.FC = () => {
) : (
<div className="h-full flex items-center justify-center">
<div className="text-center space-y-3 max-w-md px-8">
<div className="mx-auto w-12 h-12 rounded-full bg-muted/50 flex items-center justify-center">
<svg className="w-6 h-6 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
</svg>
<div className={`mx-auto w-12 h-12 rounded-full flex items-center justify-center ${diffError ? 'bg-destructive/10' : 'bg-muted/50'}`}>
{diffError ? (
<svg className="w-6 h-6 text-destructive" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
</svg>
) : (
<svg className="w-6 h-6 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
</svg>
)}
</div>
<div>
<h3 className="text-sm font-medium text-foreground">No changes</h3>
<p className="text-xs text-muted-foreground mt-1">
{diffType === 'uncommitted' && "No uncommitted changes to review."}
{diffType === 'staged' && "No staged changes. Stage some files with git add."}
{diffType === 'unstaged' && "No unstaged changes. All changes are staged."}
{diffType === 'last-commit' && "No changes in the last commit."}
{diffType === 'branch' && `No changes between this branch and ${gitContext?.defaultBranch || 'main'}.`}
</p>
{diffError ? (
<>
<h3 className="text-sm font-medium text-destructive">Failed to load diff</h3>
<p className="text-xs text-muted-foreground mt-1">{diffError}</p>
</>
) : (
<>
<h3 className="text-sm font-medium text-foreground">No changes</h3>
<p className="text-xs text-muted-foreground mt-1">
{diffType === 'uncommitted' && "No uncommitted changes to review."}
{diffType === 'staged' && "No staged changes. Stage some files with git add."}
{diffType === 'unstaged' && "No unstaged changes. All changes are staged."}
{diffType === 'last-commit' && "No changes in the last commit."}
{diffType === 'branch' && `No changes between this branch and ${gitContext?.defaultBranch || 'main'}.`}
</p>
</>
)}
</div>
{gitContext?.diffOptions && gitContext.diffOptions.length > 1 && (
<p className="text-xs text-muted-foreground/60">
Expand Down
13 changes: 8 additions & 5 deletions packages/server/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface GitContext {
export interface DiffResult {
patch: string;
label: string;
error?: string;
}

/**
Expand Down Expand Up @@ -108,27 +109,27 @@ export async function runGitDiff(
try {
switch (diffType) {
case "uncommitted":
patch = (await $`git diff HEAD`.quiet()).text();
patch = (await $`git diff HEAD --src-prefix=a/ --dst-prefix=b/`.quiet()).text();
label = "Uncommitted changes";
break;

case "staged":
patch = (await $`git diff --staged`.quiet()).text();
patch = (await $`git diff --staged --src-prefix=a/ --dst-prefix=b/`.quiet()).text();
label = "Staged changes";
break;

case "unstaged":
patch = (await $`git diff`.quiet()).text();
patch = (await $`git diff --src-prefix=a/ --dst-prefix=b/`.quiet()).text();
label = "Unstaged changes";
break;

case "last-commit":
patch = (await $`git diff HEAD~1..HEAD`.quiet()).text();
patch = (await $`git diff HEAD~1..HEAD --src-prefix=a/ --dst-prefix=b/`.quiet()).text();
label = "Last commit";
break;

case "branch":
patch = (await $`git diff ${defaultBranch}..HEAD`.quiet()).text();
patch = (await $`git diff ${defaultBranch}..HEAD --src-prefix=a/ --dst-prefix=b/`.quiet()).text();
label = `Changes vs ${defaultBranch}`;
break;

Expand All @@ -139,8 +140,10 @@ export async function runGitDiff(
} catch (error) {
// Handle errors gracefully (e.g., no commits yet, invalid ref)
console.error(`Git diff error for ${diffType}:`, error);
const errorMessage = error instanceof Error ? error.message : String(error);
patch = "";
label = `Error: ${diffType}`;
return { patch, label, error: errorMessage };
}

return { patch, label };
Expand Down
6 changes: 6 additions & 0 deletions packages/server/review.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export interface ReviewServerOptions {
rawPatch: string;
/** Git ref used for the diff (e.g., "HEAD", "main..HEAD", "--staged") */
gitRef: string;
/** Error message if git diff failed */
error?: string;
/** HTML content to serve for the UI */
htmlContent: string;
/** Origin identifier for UI customization */
Expand Down Expand Up @@ -89,6 +91,7 @@ export async function startReviewServer(
let currentPatch = options.rawPatch;
let currentGitRef = options.gitRef;
let currentDiffType: DiffType = options.diffType || "uncommitted";
let currentError = options.error;

const isRemote = isRemoteSession();
const configuredPort = getServerPort();
Expand Down Expand Up @@ -132,6 +135,7 @@ export async function startReviewServer(
sharingEnabled,
shareBaseUrl,
repoInfo,
...(currentError && { error: currentError }),
});
}

Expand All @@ -156,11 +160,13 @@ export async function startReviewServer(
currentPatch = result.patch;
currentGitRef = result.label;
currentDiffType = newDiffType;
currentError = result.error;

return Response.json({
rawPatch: currentPatch,
gitRef: currentGitRef,
diffType: currentDiffType,
...(currentError && { error: currentError }),
});
} catch (err) {
const message =
Expand Down