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