summaryrefslogtreecommitdiffhomepage
path: root/internal/llm/tools/write.go
diff options
context:
space:
mode:
authorKujtim Hoxha <[email protected]>2025-03-25 13:04:36 +0100
committerKujtim Hoxha <[email protected]>2025-03-26 01:12:30 +0100
commit904061c243f70696bfe781e97bf4e392e6954d07 (patch)
tree4428f96d09968ee0cde44e6ebbaee4757f80050e /internal/llm/tools/write.go
parent005b8ac16776512b2d4b1f22bd989da162ca1bad (diff)
downloadopencode-904061c243f70696bfe781e97bf4e392e6954d07.tar.gz
opencode-904061c243f70696bfe781e97bf4e392e6954d07.zip
additional tools
Diffstat (limited to 'internal/llm/tools/write.go')
-rw-r--r--internal/llm/tools/write.go165
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), &params); 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,
+ }
+}