diff options
| author | Kujtim Hoxha <[email protected]> | 2025-03-25 13:04:36 +0100 |
|---|---|---|
| committer | Kujtim Hoxha <[email protected]> | 2025-03-26 01:12:30 +0100 |
| commit | 904061c243f70696bfe781e97bf4e392e6954d07 (patch) | |
| tree | 4428f96d09968ee0cde44e6ebbaee4757f80050e /internal/llm/tools/write.go | |
| parent | 005b8ac16776512b2d4b1f22bd989da162ca1bad (diff) | |
| download | opencode-904061c243f70696bfe781e97bf4e392e6954d07.tar.gz opencode-904061c243f70696bfe781e97bf4e392e6954d07.zip | |
additional tools
Diffstat (limited to 'internal/llm/tools/write.go')
| -rw-r--r-- | internal/llm/tools/write.go | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/internal/llm/tools/write.go b/internal/llm/tools/write.go new file mode 100644 index 000000000..b3972cedf --- /dev/null +++ b/internal/llm/tools/write.go @@ -0,0 +1,165 @@ +package tools + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/cloudwego/eino/components/tool" + "github.com/cloudwego/eino/schema" + "github.com/kujtimiihoxha/termai/internal/permission" +) + +type writeTool struct { + workingDir string +} + +const ( + WriteToolName = "write" +) + +type WriteParams struct { + FilePath string `json:"file_path"` + Content string `json:"content"` +} + +func (b *writeTool) Info(ctx context.Context) (*schema.ToolInfo, error) { + return &schema.ToolInfo{ + Name: WriteToolName, + Desc: "Write a file to the local filesystem. Overwrites the existing file if there is one.\n\nBefore using this tool:\n\n1. Use the ReadFile tool to understand the file's contents and context\n\n2. Directory Verification (only applicable when creating new files):\n - Use the LS tool to verify the parent directory exists and is the correct location", + ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ + "file_path": { + Type: "string", + Desc: "The absolute path to the file to write (must be absolute, not relative)", + Required: true, + }, + "content": { + Type: "string", + Desc: "The content to write to the file", + Required: true, + }, + }), + }, nil +} + +func (b *writeTool) InvokableRun(ctx context.Context, args string, opts ...tool.Option) (string, error) { + var params WriteParams + if err := json.Unmarshal([]byte(args), ¶ms); err != nil { + return "", fmt.Errorf("failed to parse parameters: %w", err) + } + + if params.FilePath == "" { + return "file_path is required", nil + } + + if !filepath.IsAbs(params.FilePath) { + return fmt.Sprintf("file path must be absolute, got: %s", params.FilePath), nil + } + + // fileExists := false + // oldContent := "" + fileInfo, err := os.Stat(params.FilePath) + if err == nil { + if fileInfo.IsDir() { + return fmt.Sprintf("path is a directory, not a file: %s", params.FilePath), nil + } + + modTime := fileInfo.ModTime() + lastRead := getLastReadTime(params.FilePath) + if modTime.After(lastRead) { + return fmt.Sprintf("file %s has been modified since it was last read (mod time: %s, last read: %s)", + params.FilePath, modTime.Format(time.RFC3339), lastRead.Format(time.RFC3339)), nil + } + + // oldContentBytes, readErr := os.ReadFile(params.FilePath) + // if readErr != nil { + // oldContent = string(oldContentBytes) + // } + } else if !os.IsNotExist(err) { + return fmt.Sprintf("failed to access file: %s", err), nil + } + + p := permission.Default.Request( + permission.CreatePermissionRequest{ + Path: b.workingDir, + ToolName: WriteToolName, + Action: "write", + Description: fmt.Sprintf("Write to file %s", params.FilePath), + Params: map[string]interface{}{ + "file_path": params.FilePath, + "contnet": params.Content, + }, + }, + ) + if !p { + return "", fmt.Errorf("permission denied") + } + dir := filepath.Dir(params.FilePath) + if err = os.MkdirAll(dir, 0o755); err != nil { + return fmt.Sprintf("failed to create parent directories: %s", err), nil + } + + err = os.WriteFile(params.FilePath, []byte(params.Content), 0o644) + if err != nil { + return fmt.Sprintf("failed to write file: %s", err), nil + } + + recordFileWrite(params.FilePath) + + output := "File written: " + params.FilePath + + // if fileExists && oldContent != params.Content { + // output = generateSimpleDiff(oldContent, params.Content) + // } + + return output, nil +} + +func generateSimpleDiff(oldContent, newContent string) string { + if oldContent == newContent { + return "[No changes]" + } + + oldLines := strings.Split(oldContent, "\n") + newLines := strings.Split(newContent, "\n") + + var diffBuilder strings.Builder + diffBuilder.WriteString(fmt.Sprintf("@@ -%d,+%d @@\n", len(oldLines), len(newLines))) + + maxLines := max(len(oldLines), len(newLines)) + for i := range maxLines { + oldLine := "" + newLine := "" + + if i < len(oldLines) { + oldLine = oldLines[i] + } + + if i < len(newLines) { + newLine = newLines[i] + } + + if oldLine != newLine { + if i < len(oldLines) { + diffBuilder.WriteString(fmt.Sprintf("- %s\n", oldLine)) + } + if i < len(newLines) { + diffBuilder.WriteString(fmt.Sprintf("+ %s\n", newLine)) + } + } else { + diffBuilder.WriteString(fmt.Sprintf(" %s\n", oldLine)) + } + } + + return diffBuilder.String() +} + +func NewWriteTool(workingDir string) tool.InvokableTool { + return &writeTool{ + workingDir: workingDir, + } +} |
