Skip to content

Commit 136b66d

Browse files
committed
claudetool: accept relative paths in patch tool
1 parent 04f16a5 commit 136b66d

File tree

3 files changed

+23
-16
lines changed

3 files changed

+23
-16
lines changed

claudetool/patch.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ type PatchCallback func(input PatchInput, output llm.ToolOut) llm.ToolOut
2828
// PatchTool specifies an llm.Tool for patching files.
2929
type PatchTool struct {
3030
Callback PatchCallback // may be nil
31+
// Pwd is the working directory for resolving relative paths
32+
Pwd string
3133
}
3234

3335
// Tool returns an llm.Tool based on p.
@@ -38,7 +40,7 @@ func (p *PatchTool) Tool() *llm.Tool {
3840
InputSchema: llm.MustSchema(PatchInputSchema),
3941
Run: func(ctx context.Context, m json.RawMessage) llm.ToolOut {
4042
var input PatchInput
41-
output := patchRun(ctx, m, &input)
43+
output := p.patchRun(ctx, m, &input)
4244
if p.Callback != nil {
4345
return p.Callback(input, output)
4446
}
@@ -71,7 +73,7 @@ Usage notes:
7173
"properties": {
7274
"path": {
7375
"type": "string",
74-
"description": "Absolute path to the file to patch"
76+
"description": "Path to the file to patch"
7577
},
7678
"patches": {
7779
"type": "array",
@@ -118,15 +120,19 @@ type PatchRequest struct {
118120

119121
// patchRun implements the guts of the patch tool.
120122
// It populates input from m.
121-
func patchRun(ctx context.Context, m json.RawMessage, input *PatchInput) llm.ToolOut {
123+
func (p *PatchTool) patchRun(ctx context.Context, m json.RawMessage, input *PatchInput) llm.ToolOut {
122124
if err := json.Unmarshal(m, &input); err != nil {
123125
return llm.ErrorfToolOut("failed to unmarshal user_patch input: %w", err)
124126
}
125127

126-
// Validate the input
128+
path := input.Path
127129
if !filepath.IsAbs(input.Path) {
128-
return llm.ErrorfToolOut("path %q is not absolute", input.Path)
130+
if p.Pwd == "" {
131+
return llm.ErrorfToolOut("path %q is not absolute and no working directory is set", input.Path)
132+
}
133+
path = filepath.Join(p.Pwd, input.Path)
129134
}
135+
input.Path = path
130136
if len(input.Patches) == 0 {
131137
return llm.ErrorToolOut(fmt.Errorf("no patches provided"))
132138
}

loop/agent.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,6 +1392,7 @@ func (a *Agent) initConvoWithUsage(usage *conversation.CumulativeUsage) *convers
13921392
}
13931393
patchTool := &claudetool.PatchTool{
13941394
Callback: a.patchCallback,
1395+
Pwd: a.workingDir,
13951396
}
13961397

13971398
// Register all tools with the conversation

loop/testdata/agent_loop.httprr

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
httprr trace v1
2-
18228 2477
2+
18219 2468
33
POST https://api.anthropic.com/v1/messages HTTP/1.1
44
Host: api.anthropic.com
55
User-Agent: Go-http-client/1.1
6-
Content-Length: 18030
6+
Content-Length: 18021
77
Anthropic-Version: 2023-06-01
88
Content-Type: application/json
99

@@ -85,7 +85,7 @@ Content-Type: application/json
8585
"properties": {
8686
"path": {
8787
"type": "string",
88-
"description": "Absolute path to the file to patch"
88+
"description": "Path to the file to patch"
8989
},
9090
"patches": {
9191
"type": "array",
@@ -557,24 +557,24 @@ Content-Type: application/json
557557
Anthropic-Organization-Id: 3c473a21-7208-450a-a9f8-80aebda45c1b
558558
Anthropic-Ratelimit-Input-Tokens-Limit: 2000000
559559
Anthropic-Ratelimit-Input-Tokens-Remaining: 2000000
560-
Anthropic-Ratelimit-Input-Tokens-Reset: 2025-07-30T18:29:45Z
560+
Anthropic-Ratelimit-Input-Tokens-Reset: 2025-07-30T19:14:22Z
561561
Anthropic-Ratelimit-Output-Tokens-Limit: 400000
562562
Anthropic-Ratelimit-Output-Tokens-Remaining: 400000
563-
Anthropic-Ratelimit-Output-Tokens-Reset: 2025-07-30T18:29:50Z
563+
Anthropic-Ratelimit-Output-Tokens-Reset: 2025-07-30T19:14:27Z
564564
Anthropic-Ratelimit-Requests-Limit: 4000
565565
Anthropic-Ratelimit-Requests-Remaining: 3999
566-
Anthropic-Ratelimit-Requests-Reset: 2025-07-30T18:29:44Z
566+
Anthropic-Ratelimit-Requests-Reset: 2025-07-30T19:14:21Z
567567
Anthropic-Ratelimit-Tokens-Limit: 2400000
568568
Anthropic-Ratelimit-Tokens-Remaining: 2400000
569-
Anthropic-Ratelimit-Tokens-Reset: 2025-07-30T18:29:45Z
569+
Anthropic-Ratelimit-Tokens-Reset: 2025-07-30T19:14:22Z
570570
Cf-Cache-Status: DYNAMIC
571-
Cf-Ray: 9676ec577ae3eb24-SJC
571+
Cf-Ray: 96772db40ff5cf27-SJC
572572
Content-Type: application/json
573-
Date: Wed, 30 Jul 2025 18:29:50 GMT
574-
Request-Id: req_011CRdgJS1bQMPR17iFwT9eP
573+
Date: Wed, 30 Jul 2025 19:14:27 GMT
574+
Request-Id: req_011CRdjhnD2HyANxHw5CqnXv
575575
Server: cloudflare
576576
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
577577
Via: 1.1 google
578578
X-Robots-Tag: none
579579

580-
{"id":"msg_01Gj3ha6GnJh81AQoF5vUENq","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[{"type":"text","text":"Here are the tools available to me:\n\n**File & Code Operations:**\n- `bash` - Execute shell commands\n- `patch` - Modify files with precise text edits\n- `keyword_search` - Search for files in unfamiliar codebases\n\n**Task Management:**\n- `todo_read` - Read current todo list\n- `todo_write` - Create and manage task lists\n- `think` - Think out loud, take notes, form plans\n\n**Development Workflow:**\n- `codereview` - Run automated code review\n- `done` - Mark work as complete with checklist verification\n\n**Browser Automation:**\n- `browser_navigate` - Navigate to URLs\n- `browser_click` - Click elements\n- `browser_type` - Type into input fields\n- `browser_wait_for` - Wait for elements\n- `browser_get_text` - Read page text\n- `browser_eval` - Execute JavaScript\n- `browser_scroll_into_view` - Scroll to elements\n- `browser_resize` - Resize browser window\n- `browser_take_screenshot` - Capture screenshots\n- `browser_recent_console_logs` - Get console logs\n- `browser_clear_console_logs` - Clear console logs\n\n**Utility:**\n- `about_sketch` - Get help with Sketch functionality\n- `read_image` - Read and encode image files"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4179,"cache_read_input_tokens":0,"output_tokens":314,"service_tier":"standard"}}
580+
{"id":"msg_01LPHcYnvczudVFZMnpNPsv2","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[{"type":"text","text":"Here are the tools available to me:\n\n**Code & File Management:**\n- `bash` - Execute shell commands\n- `patch` - Modify files with precise text edits\n- `keyword_search` - Search for files/code with conceptual queries\n\n**Task Management:**\n- `todo_read` - Read current task list\n- `todo_write` - Create/update structured task list\n- `think` - Internal note-taking and planning\n\n**Development Workflow:**\n- `codereview` - Run automated code review\n- `done` - Complete work with verification checklist\n\n**Browser Automation:**\n- `browser_navigate` - Navigate to URLs\n- `browser_click` - Click elements\n- `browser_type` - Type into inputs\n- `browser_wait_for` - Wait for elements\n- `browser_get_text` - Read element text\n- `browser_eval` - Execute JavaScript\n- `browser_scroll_into_view` - Scroll to elements\n- `browser_resize` - Resize browser window\n- `browser_recent_console_logs` - Get console logs\n- `browser_clear_console_logs` - Clear console logs\n- `browser_take_screenshot` - Capture screenshots\n\n**Utilities:**\n- `read_image` - Read and encode image files\n- `about_sketch` - Get help about Sketch itself"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4177,"cache_read_input_tokens":0,"output_tokens":308,"service_tier":"standard"}}

0 commit comments

Comments
 (0)