diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-23 20:12:58 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-23 20:12:58 -0400 |
| commit | 10fa6f4e2e7150ad99827721ada1ae4badcc5e90 (patch) | |
| tree | 960065c8e69f1e7a4150ecf522e2f813c239b5ee /archive/gptel/gptel-tools/write_text_file.el | |
| parent | f4cc70c69e7707dd4a686637e14885f5443fcca6 (diff) | |
| download | dotemacs-10fa6f4e2e7150ad99827721ada1ae4badcc5e90.tar.gz dotemacs-10fa6f4e2e7150ad99827721ada1ae4badcc5e90.zip | |
chore(ai): archive gptel and remove it from the live config
I archived gptel to archive/gptel/ since I rarely use it. Moved there: the six gptel modules (ai-config, ai-conversations, ai-conversations-browser, ai-mcp, ai-quick-ask, ai-rewrite), the gptel-tools/ directory, custom/gptel-prompts.el, their test files and utilities, and the four gptel-only specs.
Scrubbed from the live config: the ai-config require in init.el, which also drops the whole C-; a keymap; the gptel-mode emojify hook in font-config.el; the gptel-tools entries in the Makefile clean target and the coverage runner; and the gptel feature notes in README. Cancelled the open gptel tasks in todo.org (the AI Open Work issues, the feature-extension brainstorm, the velox gptel-magit bug).
ai-term stays. It is the ghostel Claude launcher, independent of gptel.
Verified: every module loads, a batch init launch reaches completion clean, and the full test suite shows only pre-existing coverage failures unrelated to this change.
Diffstat (limited to 'archive/gptel/gptel-tools/write_text_file.el')
| -rw-r--r-- | archive/gptel/gptel-tools/write_text_file.el | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/archive/gptel/gptel-tools/write_text_file.el b/archive/gptel/gptel-tools/write_text_file.el new file mode 100644 index 00000000..1bda5446 --- /dev/null +++ b/archive/gptel/gptel-tools/write_text_file.el @@ -0,0 +1,107 @@ +;;; write_text_file.el --- Write text files for gptel -*- lexical-binding: t; -*- + +;; Author: Craig Jennings <c@cjennings.net> +;; Keywords: convenience, tools + +;; This file is not part of GNU Emacs. + +;;; Commentary: + +;; Gptel tool for writing a text file under the user's home directory. +;; Creates parent directories as needed, optionally overwrites an +;; existing file (with a timestamped backup), and rejects writes +;; larger than 1 GB unless the user confirms. + +;;; Code: + +(require 'gptel) + +(defconst cj/write-text-file--size-limit (* 1024 1024 1024) + "Soft cap for new-file writes (1 GB). Above this size a confirm is required.") + +(defun cj/write-text-file--validate-path (path) + "Validate PATH for write. Return the expanded path on success. +PATH must resolve inside the user's home directory." + (let* ((home (file-name-as-directory (file-truename (expand-file-name "~")))) + (full (expand-file-name path "~")) + (existing (and (file-exists-p full) (file-truename full))) + (parent (file-name-directory full)) + (resolved-parent (and parent + (file-exists-p parent) + (file-truename parent)))) + (unless (string-prefix-p (expand-file-name "~") full) + (error "Path must be within home directory: %s" path)) + (when (and existing + (not (string-prefix-p home existing))) + (error "Resolved path must be within home directory: %s" path)) + (when (and resolved-parent + (not (or (string= resolved-parent (directory-file-name home)) + (string-prefix-p home resolved-parent)))) + (error "Resolved parent must be within home directory: %s" path)) + full)) + +(defun cj/write-text-file--backup-name (path) + "Return a timestamped backup filename for PATH." + (format "%s-%s.bak" + path + (format-time-string "%Y-%m-%d-%H%M%S"))) + +(defun cj/write-text-file--ensure-parent (path) + "Ensure the parent directory of PATH exists and is writable. +Create missing parents. Signal on failure." + (let ((parent (file-name-directory path))) + (when parent + (unless (file-exists-p parent) + (condition-case err + (make-directory parent t) + (error (error "Cannot create directory %s: %s" + parent (error-message-string err))))) + (unless (file-writable-p parent) + (error "No write permission for directory %s" parent))))) + +(defun cj/write-text-file--run (path content &optional overwrite) + "Write CONTENT to PATH. Return a status string. +PATH must be inside the user's home directory. If the file exists +and OVERWRITE is non-nil, make a timestamped backup before writing; +otherwise signal." + (let* ((full (cj/write-text-file--validate-path path)) + (content (or content "")) + (size (length content))) + (when (> size cj/write-text-file--size-limit) + (unless (y-or-n-p (format "File is %s. Write anyway? " + (file-size-human-readable size))) + (error "File write cancelled: size exceeds 1GB limit"))) + (cj/write-text-file--ensure-parent full) + (when (file-exists-p full) + (if overwrite + (let ((backup (cj/write-text-file--backup-name full))) + (copy-file full backup t) + (message "Backed up existing file to %s" backup)) + (error "File %s already exists. Set overwrite to true to replace it" full))) + (with-temp-file full (insert content)) + (format "Successfully wrote %d bytes to %s" size full))) + +(with-eval-after-load 'gptel + (gptel-make-tool + :name "write_text_file" + :function (lambda (path content &optional overwrite) + (cj/write-text-file--run path content overwrite)) + :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) + + (add-to-list 'gptel-tools (gptel-get-tool '("filesystem" "write_text_file")))) + +(provide 'write_text_file) +;;; write_text_file.el ends here |
