@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added a standard unified patch to edit tool result details for SDK consumers ([#4821](https://github.com/earendil-works/pi/issues/4821)).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed final bash tool cards to avoid rendering duplicate full-output truncation paths ([#4819](https://github.com/earendil-works/pi/issues/4819)).
|
||||
|
||||
@@ -473,6 +473,8 @@ Specify which built-in tools to enable:
|
||||
- `noTools: "all"` disables all tools
|
||||
- `noTools: "builtin"` disables default built-ins while keeping extension and custom tools enabled
|
||||
|
||||
The `edit` tool returns `details.diff` for Pi's TUI display and `details.patch` as a standard unified patch for SDK consumers.
|
||||
|
||||
```typescript
|
||||
import { createAgentSession } from "@earendil-works/pi-coding-agent";
|
||||
|
||||
|
||||
@@ -259,8 +259,16 @@ export function applyEditsToNormalizedContent(
|
||||
return { baseContent, newContent };
|
||||
}
|
||||
|
||||
/** Generate a standard unified patch. */
|
||||
export function generateUnifiedPatch(path: string, oldContent: string, newContent: string, contextLines = 4): string {
|
||||
return Diff.createTwoFilesPatch(path, path, oldContent, newContent, undefined, undefined, {
|
||||
context: contextLines,
|
||||
headerOptions: Diff.FILE_HEADERS_ONLY,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unified diff string with line numbers and context.
|
||||
* Generate a display-oriented diff string with line numbers and context.
|
||||
* Returns both the diff string and the first changed line number (in the new file).
|
||||
*/
|
||||
export function generateDiffString(
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
type EditDiffError,
|
||||
type EditDiffResult,
|
||||
generateDiffString,
|
||||
generateUnifiedPatch,
|
||||
normalizeToLF,
|
||||
restoreLineEndings,
|
||||
stripBom,
|
||||
@@ -57,8 +58,10 @@ type LegacyEditToolInput = EditToolInput & {
|
||||
};
|
||||
|
||||
export interface EditToolDetails {
|
||||
/** Unified diff of the changes made */
|
||||
/** Display-oriented diff of the changes made */
|
||||
diff: string;
|
||||
/** Standard unified patch of the changes made */
|
||||
patch: string;
|
||||
/** Line number of the first change in the new file (for editor navigation) */
|
||||
firstChangedLine?: number;
|
||||
}
|
||||
@@ -394,6 +397,7 @@ export function createEditToolDefinition(
|
||||
}
|
||||
|
||||
const diffResult = generateDiffString(baseContent, newContent);
|
||||
const patch = generateUnifiedPatch(path, baseContent, newContent);
|
||||
resolve({
|
||||
content: [
|
||||
{
|
||||
@@ -401,7 +405,7 @@ export function createEditToolDefinition(
|
||||
text: `Successfully replaced ${edits.length} block(s) in ${path}.`,
|
||||
},
|
||||
],
|
||||
details: { diff: diffResult.diff, firstChangedLine: diffResult.firstChangedLine },
|
||||
details: { diff: diffResult.diff, patch, firstChangedLine: diffResult.firstChangedLine },
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
// Clean up abort handler.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { applyPatch } from "diff";
|
||||
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
@@ -239,6 +240,12 @@ describe("Coding Agent Tools", () => {
|
||||
expect(result.details.diff).toBeDefined();
|
||||
expect(typeof result.details.diff).toBe("string");
|
||||
expect(result.details.diff).toContain("testing");
|
||||
expect(result.details.patch).toContain("--- ");
|
||||
expect(result.details.patch).toContain("+++ ");
|
||||
expect(result.details.patch).toContain("@@");
|
||||
expect(result.details.patch).toContain("-Hello, world!");
|
||||
expect(result.details.patch).toContain("+Hello, testing!");
|
||||
expect(applyPatch(originalContent, result.details.patch)).toBe("Hello, testing!");
|
||||
});
|
||||
|
||||
it("should fail if text not found", async () => {
|
||||
|
||||
Reference in New Issue
Block a user