2024-06-12 02:35:07 -06:00
|
|
|
package toolbox
|
2024-02-21 21:55:38 -07:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
2024-06-23 12:57:08 -06:00
|
|
|
toolutil "git.mlow.ca/mlow/lmcli/pkg/agents/toolbox/util"
|
2024-06-12 02:35:07 -06:00
|
|
|
"git.mlow.ca/mlow/lmcli/pkg/api"
|
2024-02-21 21:55:38 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
const FILE_REPLACE_LINES_DESCRIPTION = `Replace or remove a range of lines within a file, must specify path.
|
|
|
|
|
|
|
|
Useful for re-writing snippets/blocks of code or entire functions.
|
|
|
|
|
|
|
|
Plan your edits carefully and ensure any new content matches the flow and indentation of surrounding text.`
|
|
|
|
|
2024-06-12 02:35:07 -06:00
|
|
|
var FileReplaceLinesTool = api.ToolSpec{
|
2024-02-21 21:55:38 -07:00
|
|
|
Name: "file_replace_lines",
|
|
|
|
Description: FILE_REPLACE_LINES_DESCRIPTION,
|
2024-06-12 02:35:07 -06:00
|
|
|
Parameters: []api.ToolParameter{
|
2024-02-21 21:55:38 -07:00
|
|
|
{
|
|
|
|
Name: "path",
|
|
|
|
Type: "string",
|
|
|
|
Description: "Path of the file to be modified, relative to the current working directory.",
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "start_line",
|
|
|
|
Type: "integer",
|
|
|
|
Description: `Line number which specifies the start of the replacement range (inclusive).`,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "end_line",
|
|
|
|
Type: "integer",
|
|
|
|
Description: `Line number which specifies the end of the replacement range (inclusive). If unset, range extends to end of file.`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "content",
|
|
|
|
Type: "string",
|
|
|
|
Description: `Content to replace specified range. Omit to remove the specified range.`,
|
|
|
|
},
|
|
|
|
},
|
2024-06-12 02:35:07 -06:00
|
|
|
Impl: func(tool *api.ToolSpec, args map[string]interface{}) (string, error) {
|
2024-02-21 21:55:38 -07:00
|
|
|
tmp, ok := args["path"]
|
|
|
|
if !ok {
|
|
|
|
return "", fmt.Errorf("path parameter to write_file was not included.")
|
|
|
|
}
|
|
|
|
path, ok := tmp.(string)
|
|
|
|
if !ok {
|
|
|
|
return "", fmt.Errorf("Invalid path in function arguments: %v", tmp)
|
|
|
|
}
|
|
|
|
var start_line int
|
|
|
|
tmp, ok = args["start_line"]
|
|
|
|
if ok {
|
|
|
|
tmp, ok := tmp.(float64)
|
|
|
|
if !ok {
|
|
|
|
return "", fmt.Errorf("Invalid start_line in function arguments: %v", tmp)
|
|
|
|
}
|
|
|
|
start_line = int(tmp)
|
|
|
|
}
|
|
|
|
var end_line int
|
|
|
|
tmp, ok = args["end_line"]
|
|
|
|
if ok {
|
|
|
|
tmp, ok := tmp.(float64)
|
|
|
|
if !ok {
|
|
|
|
return "", fmt.Errorf("Invalid end_line in function arguments: %v", tmp)
|
|
|
|
}
|
|
|
|
end_line = int(tmp)
|
|
|
|
}
|
|
|
|
var content string
|
|
|
|
tmp, ok = args["content"]
|
|
|
|
if ok {
|
|
|
|
content, ok = tmp.(string)
|
|
|
|
if !ok {
|
|
|
|
return "", fmt.Errorf("Invalid content in function arguments: %v", tmp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result := fileReplaceLines(path, start_line, end_line, content)
|
|
|
|
ret, err := result.ToJson()
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("Could not serialize result: %v", err)
|
|
|
|
}
|
|
|
|
return ret, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2024-06-12 02:35:07 -06:00
|
|
|
func fileReplaceLines(path string, startLine int, endLine int, content string) api.CallResult {
|
2024-02-21 21:55:38 -07:00
|
|
|
ok, reason := toolutil.IsPathWithinCWD(path)
|
|
|
|
if !ok {
|
2024-06-12 02:35:07 -06:00
|
|
|
return api.CallResult{Message: reason}
|
2024-02-21 21:55:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Read the existing file's content
|
|
|
|
data, err := os.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
2024-06-12 02:35:07 -06:00
|
|
|
return api.CallResult{Message: fmt.Sprintf("Could not read path: %s", err.Error())}
|
2024-02-21 21:55:38 -07:00
|
|
|
}
|
|
|
|
_, err = os.Create(path)
|
|
|
|
if err != nil {
|
2024-06-12 02:35:07 -06:00
|
|
|
return api.CallResult{Message: fmt.Sprintf("Could not create new file: %s", err.Error())}
|
2024-02-21 21:55:38 -07:00
|
|
|
}
|
|
|
|
data = []byte{}
|
|
|
|
}
|
|
|
|
|
|
|
|
if startLine < 1 {
|
2024-06-12 02:35:07 -06:00
|
|
|
return api.CallResult{Message: "start_line cannot be less than 1"}
|
2024-02-21 21:55:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
lines := strings.Split(string(data), "\n")
|
|
|
|
contentLines := strings.Split(strings.Trim(content, "\n"), "\n")
|
|
|
|
|
|
|
|
if endLine == 0 || endLine > len(lines) {
|
|
|
|
endLine = len(lines)
|
|
|
|
}
|
|
|
|
|
|
|
|
before := lines[:startLine-1]
|
|
|
|
after := lines[endLine:]
|
|
|
|
|
|
|
|
lines = append(before, append(contentLines, after...)...)
|
|
|
|
newContent := strings.Join(lines, "\n")
|
|
|
|
|
|
|
|
// Join the lines and write back to the file
|
|
|
|
err = os.WriteFile(path, []byte(newContent), 0644)
|
|
|
|
if err != nil {
|
2024-06-12 02:35:07 -06:00
|
|
|
return api.CallResult{Message: fmt.Sprintf("Could not write to path: %s", err.Error())}
|
2024-02-21 21:55:38 -07:00
|
|
|
}
|
|
|
|
|
2024-06-12 02:35:07 -06:00
|
|
|
return api.CallResult{Result: newContent}
|
2024-02-21 21:55:38 -07:00
|
|
|
}
|