WriteFile writes content to path, creating parent dirs. Errors return as part of the output string (bash convention), never as a Go error, so the model sees a write failure the way it sees a non-zero bash exit.
(path, content string)
| 10 | // appends instead of overwriting, which is how a file too large for one |
| 11 | // streamed tool call gets built: first part plain, every later part appended. |
| 12 | // Errors return as part of the output string (bash convention), never as a Go |
| 13 | // error, so the model sees a write failure the way it sees a non-zero bash exit. |
| 14 | func WriteFile(path, content string, appendMode bool) string { |
| 15 | if path == "" { |
| 16 | return "(empty path)" |
| 17 | } |
| 18 | // Refuse an existing non-regular target: open(2) with O_WRONLY on a FIFO |
| 19 | // with no reader blocks forever, leaking the tool goroutine past Ctrl+C |
| 20 | // (which cancels the turn but can't unblock the open). Stat never blocks; |
| 21 | // directories fall through to os.WriteFile's immediate EISDIR. |
| 22 | if info, err := os.Stat(path); err == nil && !info.Mode().IsRegular() && !info.IsDir() { |
| 23 | return fmt.Sprintf("(write error: %s is not a regular file)", path) |
| 24 | } |
| 25 | if dir := filepath.Dir(path); dir != "." { |
| 26 | if err := os.MkdirAll(dir, 0o755); err != nil { |
| 27 | return fmt.Sprintf("(mkdir error: %v)", err) |
| 28 | } |
| 29 | } |
| 30 | if appendMode { |
| 31 | f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) |
| 32 | if err != nil { |
| 33 | return fmt.Sprintf("(write error: %v)", err) |
| 34 | } |
| 35 | defer f.Close() |
no outgoing calls