diff --git a/apps/hook/server/index.ts b/apps/hook/server/index.ts index a0508cd..7f7a476 100644 --- a/apps/hook/server/index.ts +++ b/apps/hook/server/index.ts @@ -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 ); @@ -77,6 +77,7 @@ if (args[0] === "review") { const server = await startReviewServer({ rawPatch, gitRef, + error: diffError, origin: "claude-code", diffType: "uncommitted", gitContext, diff --git a/apps/opencode-plugin/index.ts b/apps/opencode-plugin/index.ts index dae038b..e4c4bf4 100644 --- a/apps/opencode-plugin/index.ts +++ b/apps/opencode-plugin/index.ts @@ -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 ); @@ -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, diff --git a/apps/pi-extension/server.ts b/apps/pi-extension/server.ts index 9ae7157..75795a2 100644 --- a/apps/pi-extension/server.ts +++ b/apps/pi-extension/server.ts @@ -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" }; } diff --git a/packages/review-editor/App.tsx b/packages/review-editor/App.tsx index 326df76..a8e1cdd 100644 --- a/packages/review-editor/App.tsx +++ b/packages/review-editor/App.tsx @@ -149,6 +149,7 @@ const ReviewApp: React.FC = () => { const [diffType, setDiffType] = useState('uncommitted'); const [gitContext, setGitContext] = useState(null); const [isLoadingDiff, setIsLoadingDiff] = useState(false); + const [diffError, setDiffError] = useState(null); const [isSendingFeedback, setIsSendingFeedback] = useState(false); const [isApproving, setIsApproving] = useState(false); const [submitted, setSubmitted] = useState<'approved' | 'feedback' | false>(false); @@ -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({ @@ -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 @@ -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); } @@ -763,20 +768,35 @@ const ReviewApp: React.FC = () => { ) : (
-
- - - +
+ {diffError ? ( + + + + ) : ( + + + + )}
-

No changes

-

- {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'}.`} -

+ {diffError ? ( + <> +

Failed to load diff

+

{diffError}

+ + ) : ( + <> +

No changes

+

+ {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'}.`} +

+ + )}
{gitContext?.diffOptions && gitContext.diffOptions.length > 1 && (

diff --git a/packages/server/git.ts b/packages/server/git.ts index f8aae32..7ba5349 100644 --- a/packages/server/git.ts +++ b/packages/server/git.ts @@ -28,6 +28,7 @@ export interface GitContext { export interface DiffResult { patch: string; label: string; + error?: string; } /** @@ -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; @@ -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 }; diff --git a/packages/server/review.ts b/packages/server/review.ts index 696617d..019bc53 100644 --- a/packages/server/review.ts +++ b/packages/server/review.ts @@ -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 */ @@ -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(); @@ -132,6 +135,7 @@ export async function startReviewServer( sharingEnabled, shareBaseUrl, repoInfo, + ...(currentError && { error: currentError }), }); } @@ -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 =