Skip to content

Commit

Permalink
fix: more diff mode improvements (#81)
Browse files Browse the repository at this point in the history
* fix: more diff mode improvements

* fix: actually apply the changes

* fix: actually apply the changes
  • Loading branch information
gmickel authored Aug 6, 2024
1 parent 40a560b commit 2e3ceb3
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 75 deletions.
31 changes: 27 additions & 4 deletions src/git/apply-changes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'node:path';
import chalk from 'chalk';
import { applyPatch } from 'diff';
import { applyPatch, createPatch } from 'diff';
import fs from 'fs-extra';
import type { AIFileInfo, ApplyChangesOptions } from '../types';

Expand Down Expand Up @@ -70,10 +70,33 @@ async function applyFileChange(
} else {
if (file.diff) {
const currentContent = await fs.readFile(fullPath, 'utf-8');
const updatedContent = applyPatch(currentContent, file.diff);
if (updatedContent === false) {

// Generate the new content based on the diff
const newContent = file.diff.hunks.reduce((acc, hunk) => {
const lines = acc.split('\n');
const newLines = hunk.lines
.filter((line) => !line.startsWith('-'))
.map((line) => (line.startsWith('+') ? line.slice(1) : line));
lines.splice(hunk.newStart - 1, hunk.oldLines, ...newLines);
return lines.join('\n');
}, currentContent);

// Create the patch
const patchString = createPatch(
file.path,
currentContent,
newContent,
file.diff.oldFileName || file.path,
file.diff.newFileName || file.path,
{ context: 3 },
);

// Apply the patch
const updatedContent = applyPatch(currentContent, patchString);

if (typeof updatedContent === 'boolean') {
throw new Error(
`Failed to apply patch to file: ${file.path}\n A common cause is the the file was not sent to the LLM and it hallucinated the content. Try running the task again (task --redo) and selecting the problemtic file.`,
`Failed to apply patch to file: ${file.path}\nA common cause is that the file was not sent to the LLM and it hallucinated the content. Try running the task again (task --redo) and selecting the problematic file.`,
);
}
await fs.writeFile(fullPath, updatedContent);
Expand Down
88 changes: 74 additions & 14 deletions src/templates/codegen-diff-no-plan-prompt.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -114,28 +114,29 @@ FORMAT: Instructions for how to format your response.
EXPLANATION: Provide a brief explanation for your implementation choices, including any significant design decisions, alternatives considered, and reasons for your final decision. Address any non-obvious implementations or optimizations.

When creating diffs for modified files:
- Always include file header lines with the correct file paths.
- Always include hunk headers with the correct line numbers.
- Use '+' at the beginning of the line to indicate added lines, including new imports.
- Use '-' at the beginning of the line to indicate removed lines.
- Use ' ' (space) at the beginning of the line for unchanged lines (context).
- Ensure that new imports are marked with '+' at the beginning of the line.
- Include at least 3 lines of unchanged context before and after changes to help with patch application.

Example of a correct diff format:
<file_content language="typescript">
--- src/types/index.ts
+++ src/types/index.ts
@@ -1,4 +1,6 @@
import type { ParsedDiff } from 'diff';
+import type { LogLevel } from '../utils/logger';
+
export interface GitHubIssue {
--- src/types/index.ts
+++ src/types/index.ts
@@ -1,4 +1,6 @@
import type { ParsedDiff } from 'diff';
+import type { LogLevel } from '../utils/logger';
+
export interface GitHubIssue {
number: number;
title: string;
@@ -40,6 +42,7 @@
| 'noCodeblock'
> & {
@@ -40,6 +42,7 @@
| 'noCodeblock'
> & {
dryRun: boolean;
+ logLevel?: LogLevel;
+ logLevel?: LogLevel;
maxCostThreshold?: number;
task?: string;
description?: string;
Expand All @@ -147,14 +148,73 @@ FORMAT: Instructions for how to format your response.
1. Start with the original file content.
2. Make your changes, keeping track of line numbers.
3. Generate the diff by comparing the original and modified versions.
4. Include at least 3 lines of unchanged context before and after changes.
4. Include at least 3 lines of unchanged context before and after each change.
5. Verify that the diff accurately represents your intended changes.

After generating each diff:
- Verify that all new lines (including imports and blank lines) start with '+'.
- Ensure that the line numbers in the diff headers (@@ -old,oldlines +new,newlines @@) are correct and account for added/removed lines.
- Ensure that the line numbers in the diff headers (@@ -old,oldlines +new,newlines @@) are correct and account for
added/removed lines.
- Check that there are sufficient unchanged context lines around modifications.

Example for a new file:
<file>
<file_path>src/components/IssueList.tsx</file_path>
<file_status>new</file_status>
<file_content language="tsx">
import React from 'react';
import { Issue } from '../types';

interface IssueListProps {
issues: Issue[];
}

export const IssueList: React.FC<IssueListProps> = ({ issues }) => {
return (
<ul>
{issues.map((issue) => (
<li key={issue.id}>{issue.title}</li>
))}
</ul>
);
};
</file_content>
<explanation>
Created a new IssueList component to display a list of issues. Used React.FC for type-safety and map function for
efficient rendering of multiple issues.
</explanation>
</file>

Example for a modified file:
<file>
<file_path>src/components/App.tsx</file_path>
<file_status>modified</file_status>
<file_content language="tsx">
--- src/components/App.tsx
+++ src/components/App.tsx
@@ -1,5 +1,6 @@
import React from 'react';
import { Header } from './Header';
+import { IssueList } from './IssueList';

export const App: React.FC = () => {
return (
@@ -7,6 +8,7 @@
<div>
<Header />
<main>
- {/* TODO: Add issue list */}
+ <IssueList issues={[]} />
</main>
</div>
);
</file_content>
<explanation>
Updated App component to import and use the new IssueList component.
</explanation>
</file>


Ensure that:
- You have thoroughly analyzed the task and planned your implementation strategy.
- Everything specified in the task description and instructions is implemented.
Expand Down
107 changes: 57 additions & 50 deletions src/templates/codegen-diff-prompt.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,12 @@ FORMAT: Instructions for how to format your response.
FILE_CONTENT_OR_DIFF:
- For new files: Provide the complete file content, including all necessary imports, function definitions, and
exports.
- For modified files: Provide a unified diff format. Use '---' for removed lines and '+++' for added lines.
- For modified files: Provide a unified diff format. This MUST include:
1. File header lines (starting with "---" and "+++")
2. Hunk headers (starting with "@@")
3. Context lines (starting with a space)
4. Removed lines (starting with "-")
5. Added lines (starting with "+")
- For deleted files: Leave this section empty.
Ensure proper indentation and follow the project's coding standards.
Expand All @@ -125,28 +130,29 @@ FORMAT: Instructions for how to format your response.
EXPLANATION: Provide a brief explanation for any significant design decisions or non-obvious implementations.
When creating diffs for modified files:
- Always include file header lines with the correct file paths.
- Always include hunk headers with the correct line numbers.
- Use '+' at the beginning of the line to indicate added lines, including new imports.
- Use '-' at the beginning of the line to indicate removed lines.
- Use ' ' (space) at the beginning of the line for unchanged lines (context).
- Ensure that new imports are marked with '+' at the beginning of the line.
- Include at least 3 lines of unchanged context before and after changes to help with patch application.
Example of a correct diff format:
<file_content language="typescript">
--- src/types/index.ts
+++ src/types/index.ts
@@ -1,4 +1,6 @@
import type { ParsedDiff } from 'diff';
+import type { LogLevel } from '../utils/logger';
+
export interface GitHubIssue {
--- src/types/index.ts
+++ src/types/index.ts
@@ -1,4 +1,6 @@
import type { ParsedDiff } from 'diff';
+import type { LogLevel } from '../utils/logger';
+
export interface GitHubIssue {
number: number;
title: string;
@@ -40,6 +42,7 @@
| 'noCodeblock'
> & {
@@ -40,6 +42,7 @@
| 'noCodeblock'
> & {
dryRun: boolean;
+ logLevel?: LogLevel;
+ logLevel?: LogLevel;
maxCostThreshold?: number;
task?: string;
description?: string;
Expand All @@ -173,22 +179,22 @@ FORMAT: Instructions for how to format your response.
<file_path>src/components/IssueList.tsx</file_path>
<file_status>new</file_status>
<file_content language="tsx">
import React from 'react';
import { Issue } from '../types';
interface IssueListProps {
issues: Issue[];
}
export const IssueList: React.FC<IssueListProps> = ({ issues }) => {
return (
<ul>
{issues.map((issue) => (
<li key={issue.id}>{issue.title}</li>
))}
</ul>
);
};
import React from 'react';
import { Issue } from '../types';
interface IssueListProps {
issues: Issue[];
}
export const IssueList: React.FC<IssueListProps> = ({ issues }) => {
return (
<ul>
{issues.map((issue) => (
<li key={issue.id}>{issue.title}</li>
))}
</ul>
);
};
</file_content>
<explanation>
Created a new IssueList component to display a list of issues. Used React.FC for type-safety and map function for
Expand All @@ -201,24 +207,24 @@ FORMAT: Instructions for how to format your response.
<file_path>src/components/App.tsx</file_path>
<file_status>modified</file_status>
<file_content language="tsx">
--- src/components/App.tsx
+++ src/components/App.tsx
@@ -1,5 +1,6 @@
import React from 'react';
import { Header } from './Header';
+import { IssueList } from './IssueList';
export const App: React.FC = () => {
return (
@@ -7,6 +8,7 @@
<div>
<Header />
<main>
- {/* TODO: Add issue list */}
+ <IssueList issues={[]} />
</main>
</div>
);
--- src/components/App.tsx
+++ src/components/App.tsx
@@ -1,5 +1,6 @@
import React from 'react';
import { Header } from './Header';
+import { IssueList } from './IssueList';
export const App: React.FC = () => {
return (
@@ -7,6 +8,7 @@
<div>
<Header />
<main>
- {/* TODO: Add issue list */}
+ <IssueList issues={[]} />
</main>
</div>
);
</file_content>
<explanation>
Updated App component to import and use the new IssueList component.
Expand All @@ -229,8 +235,8 @@ FORMAT: Instructions for how to format your response.
- You have thoroughly analyzed the task and plan and have planned your implementation strategy.
- Everything specified in the task description and plan is implemented.
- All new files contain the full code.
- All modified files have accurate and clear diffs.
- Fiff formatting across all modified files is consistent.
- All modified files have accurate and clear diffs in the correct unified diff format.
- Diff formatting across all modified files is consistent and includes all required elements (file headers, hunk headers, context lines).
- The content includes all necessary imports, function definitions, and exports.
- The code is clean, maintainable, and efficient.
- The code is properly formatted and follows the project's coding standards.
Expand All @@ -243,6 +249,7 @@ FORMAT: Instructions for how to format your response.
- You complete all necessary work.
Note: The accuracy of the diff format is crucial for successful patch application. Even small errors in formatting can
cause the entire patch to fail. Pay extra attention to the correctness of your diff output.
cause the entire patch to fail. Pay extra attention to the correctness of your diff output, ensuring all required
elements (file headers, hunk headers, context lines) are present and correctly formatted.
</format>
Loading

0 comments on commit 2e3ceb3

Please sign in to comment.