summaryrefslogtreecommitdiff
path: root/gptel-tools/write_text_file.el
blob: 03d64e5725e34e6437c300d34f0bfe894835c1e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
;;; write_text_file.el --- Write text files for gptel  -*- lexical-binding: t; -*-

;; Copyright (C) 2025

;; Author: gptel-tool-writer
;; Keywords: convenience, tools
;; Package-Requires: ((emacs "27.1") (gptel "0.9.0"))

;; This file is not part of GNU Emacs.

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;;; Commentary:

;; This file provides a gptel tool for writing text files to the filesystem.
;; The tool includes safety features like backup creation, size limits,
;; and restriction to the user's home directory.

;;; Code:

(require 'gptel)

(with-eval-after-load 'gptel
  (gptel-make-tool
   :name "write_text_file"
   :function (lambda (path content &optional overwrite)
               (let* ((full-path (expand-file-name path "~"))
                      (content (or content ""))
                      (content-size (length content))
                      (size-limit (* 1024 1024 1024))) ; 1 GB
                 ;; Check if path is within home directory
                 (unless (string-prefix-p (expand-file-name "~") full-path)
                   (error "Path must be within home directory"))
                 ;; Check size limit
                 (when (> content-size size-limit)
                   (unless (y-or-n-p (format "File is %s. Write anyway? "
                                            (file-size-human-readable content-size)))
                     (error "File write cancelled: size exceeds 1GB limit")))
                 ;; Check write permission on parent directory
                 (let ((parent-dir (file-name-directory full-path)))
                   (when parent-dir
                     ;; Create parent directories if needed
                     (unless (file-exists-p parent-dir)
                       (condition-case err
                           (make-directory parent-dir t)
                         (error (error "Cannot create directory %s: %s" 
                                      parent-dir (error-message-string err)))))
                     ;; Check write permission
                     (unless (file-writable-p parent-dir)
                       (error "No write permission for directory %s" parent-dir))))
                 ;; Handle existing file
                 (when (file-exists-p full-path)
                   (if overwrite
                       ;; Create backup with timestamp
                       (let* ((backup-name 
                              (format "%s-%s.bak" 
                                     full-path
                                     (format-time-string "%Y-%m-%d-%H%M%S"))))
                         (copy-file full-path backup-name t)
                         (message "Backed up existing file to %s" backup-name))
                     (error "File %s already exists. Set overwrite to true to replace it" full-path)))
                 ;; Write the file atomically
                 (with-temp-file full-path
                   (insert content))
                 (format "Successfully wrote %d bytes to %s" 
                         content-size full-path)))
   :description "Write text content to a file within the user's home directory. Creates parent directories if needed. Backs up existing files with timestamp when overwriting."
   :args (list '(:name "path"
                 :type string
                 :description "File path relative to home directory, e.g., 'documents/myfile.txt' or '~/documents/myfile.txt'")
               '(:name "content"
                 :type string
                 :description "The text content to write to the file")
               '(:name "overwrite"
                 :type boolean
                 :description "If true, backup and overwrite existing file. If false or omitted, error if file exists"
                 :optional t))
   :category "filesystem"
   :confirm t
   :include t)
  
  ;; Automatically add to gptel-tools on load
  (add-to-list 'gptel-tools (gptel-get-tool '("filesystem" "write_text_file"))))

(provide 'write_text_file)
;;; write_text_file.el ends here