diff --git a/emacs/feature/feature-km.el b/emacs/feature/feature-km.el index fcd1877..0cf955e 100644 --- a/emacs/feature/feature-km.el +++ b/emacs/feature/feature-km.el @@ -12,5 +12,7 @@ (require 'module-denote-multidir) (require 'module-denote-index) +(require 'module-collect) + (provide 'feature-km) ;;; feature-km.el ends here. diff --git a/emacs/module/module-assistant.el b/emacs/module/module-assistant.el index 435a18c..ced16e8 100644 --- a/emacs/module/module-assistant.el +++ b/emacs/module/module-assistant.el @@ -8,22 +8,5 @@ (require 'module-pkg) -(setq elisa-embeddings-provider - (make-llm-ollama :embedding-model "nomic-embed-text" - :host "eris.fritz.box")) -(setq elisa-chat-provider - (make-llm-ollama :chat-model "sskostyaev/openchat:8k-rag" - :embedding-model "nomic-embed-text" - :host "eris.fritz.box")) -(setq elisa-limit 5) - - -(pkg/require 'elisa) - -(defun my/elisa-setup () - "Perform initial elisa setup." - (interactive) - (elisa-download-sqlite-vss)) - (provide 'module-assistant) ;;; module-assistant.el ends here. diff --git a/emacs/module/module-collect.el b/emacs/module/module-collect.el new file mode 100644 index 0000000..049d0ad --- /dev/null +++ b/emacs/module/module-collect.el @@ -0,0 +1,163 @@ +;;; module-collect.el --- manage document collections +;;; +;;; Commentary: +;;; +;;; This module provides the ability to create, rename, and delete +;;; collections of documents. Each collection is a subdirectory of the +;;; path defined by the variable collect/store-path. +;;; +;;; Code: + + +;;; collect/store-path is a variable that stores a string defining the path to the directory under which collections are stored. +(defvar collect/store-path "~/collections") + +(defun collect/delete-collection-internal (collection-name) + "Delete an existing collection, moving its contents (except .collection-metadata) into the no-name collection." + (let ((source-dir (expand-file-name collection-name collect/store-path)) + (target-dir (expand-file-name "no-name" collect/store-path))) + (if (file-exists-p source-dir) + (progn + (unless (file-exists-p target-dir) + (make-directory target-dir t)) + ;; Move all contents, excluding .collection-metadata + (dolist (file (directory-files source-dir t "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\)")) + (let ((file-name (file-name-nondirectory file))) + (unless (string= file-name ".collection-metadata") + (let ((target-file (expand-file-name file-name target-dir))) + (if (file-directory-p file) + (copy-directory file target-file t t t) + (rename-file file target-file t)))))) + ;; Force remove the directory + (delete-directory source-dir t) + (message "Collection '%s' deleted and contents moved to 'no-name' collection." collection-name)) + (error "Collection '%s' does not exist" collection-name)))) + +(defun collect/create-collection () + "Interactively create a new collection." + (interactive) + (let ((collection-name (read-string "Enter collection name: "))) + (collect/create-collection-internal collection-name))) + +(defun collect/delete-collection () + "Interactively delete an existing collection." + (interactive) + (let* ((all-files (directory-files collect/store-path t "^[^.]")) + (collections (cl-remove-if + (lambda (dir) + (or (not (file-directory-p dir)) + (string= (file-name-nondirectory dir) "no-name"))) + all-files)) + (collection-names + (mapcar (lambda (dir) + (let ((metadata-file (expand-file-name ".collection-metadata" dir))) + (if (file-exists-p metadata-file) + (with-temp-buffer + (insert-file-contents metadata-file) + (if (re-search-forward "^name: \\(.*\\)$" nil t) + (match-string 1) + (file-name-nondirectory dir))) + (file-name-nondirectory dir)))) + collections)) + (choice (completing-read "Select collection to delete: " collection-names nil t)) + (dir-to-delete (nth (cl-position choice collection-names :test 'string=) collections))) + (when dir-to-delete + (collect/delete-collection-internal (file-name-nondirectory dir-to-delete))))) + +(defun collect/rename-collection-internal (old-name new-name) + "Rename collection from OLD-NAME to NEW-NAME." + (let ((old-dir (expand-file-name old-name collect/store-path)) + (new-dir (expand-file-name + (replace-regexp-in-string "[^[:alnum:]_-]" "-" + (downcase (string-trim new-name))) + collect/store-path))) + (when (file-exists-p old-dir) + ;; Create new collection + (collect/create-collection-internal new-name) + ;; Move contents + (dolist (file (directory-files old-dir t "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\)")) + (let ((file-name (file-name-nondirectory file))) + (unless (string= file-name ".collection-metadata") + (let ((target-file (expand-file-name file-name new-dir))) + (if (file-directory-p file) + (copy-directory file target-file t t t) + (rename-file file target-file t)))))) + ;; Update metadata file + (let ((old-metadata-file (expand-file-name ".collection-metadata" old-dir)) + (new-metadata-file (expand-file-name ".collection-metadata" new-dir))) + (when (file-exists-p old-metadata-file) + (with-temp-file new-metadata-file + (insert-file-contents old-metadata-file) + (goto-char (point-min)) + (when (re-search-forward "^name: .*$" nil t) + (replace-match (concat "name: " new-name))) + (goto-char (point-min)) + (when (re-search-forward "^updated: .*$" nil t) + (replace-match (concat "updated: " (format-time-string "%Y-%m-%d %H:%M:%S"))))))) + ;; Delete old collection directory + (delete-directory old-dir t) + (message "Collection '%s' renamed to '%s'." old-name new-name)))) + +(defun collect/rename-collection () + "Interactively rename an existing collection." + (interactive) + (let* ((all-files (directory-files collect/store-path t "^[^.]")) + (collections (cl-remove-if + (lambda (dir) + (or (not (file-directory-p dir)) + (string= (file-name-nondirectory dir) "no-name"))) + all-files)) + (collection-names + (mapcar (lambda (dir) + (let ((metadata-file (expand-file-name ".collection-metadata" dir))) + (if (file-exists-p metadata-file) + (with-temp-buffer + (insert-file-contents metadata-file) + (if (re-search-forward "^name: \\(.*\\)$" nil t) + (match-string 1) + (file-name-nondirectory dir))) + (file-name-nondirectory dir)))) + collections)) + (old-name (completing-read "Select collection to rename: " collection-names nil t)) + (new-name (read-string (format "Enter new name for collection '%s': " old-name)))) + (when (and old-name new-name) + (let ((dir-to-rename (nth (cl-position old-name collection-names :test 'string=) collections))) + (when dir-to-rename + (collect/rename-collection-internal (file-name-nondirectory dir-to-rename) new-name)))))) + +(defun collect/add-file-to-collection () + "Add a file to a specified collection." + (interactive) + (let* ((file-to-add (read-file-name "Select file to add: ")) + (all-files (directory-files collect/store-path t "^[^.]")) + (collections (cl-remove-if + (lambda (dir) + (or (not (file-directory-p dir)) + (string= (file-name-nondirectory dir) "no-name"))) + all-files)) + (collection-names + (mapcar (lambda (dir) + (let ((metadata-file (expand-file-name ".collection-metadata" dir))) + (if (file-exists-p metadata-file) + (with-temp-buffer + (insert-file-contents metadata-file) + (if (re-search-forward "^name: \\(.*\\)$" nil t) + (match-string 1) + (file-name-nondirectory dir))) + (file-name-nondirectory dir)))) + collections)) + (selected-collection (completing-read "Select collection to add to: " collection-names nil t)) + (target-dir (nth (cl-position selected-collection collection-names :test 'string=) collections))) + (when (and file-to-add target-dir) + (let ((target-file (expand-file-name (file-name-nondirectory file-to-add) target-dir))) + (if (file-exists-p target-file) + (if (y-or-n-p "File already exists in collection. Overwrite?") + (copy-file file-to-add target-file t) + (message "File not added to collection.")) + (copy-file file-to-add target-file)) + (message "File '%s' added to collection '%s'." + (file-name-nondirectory file-to-add) + selected-collection))))) + +(provide 'module-collect) +