Commit aa02df039570
Changed files (6)
dots
.config
claude
skills
Notes
dots/.config/claude/skills/Notes/SKILL.md
@@ -129,13 +129,46 @@ Additional content...
## Creating Notes
-### Generate Timestamp
+### Using org-manager (Recommended)
+
+The `org-manager` tool provides batch mode denote integration for creating notes programmatically:
+
+```bash
+# Create a simple note
+org-manager denote-create "Note Title" "tag1,tag2,tag3" \
+ --category=homelab --directory=~/desktop/org/notes
+
+# Create with signature (for automated notes)
+org-manager denote-create "Session Summary" "history,session" \
+ --signature=pkai --category=history
+
+# Create with initial content from file
+echo "* Custom Content" > /tmp/content.org
+org-manager denote-create "My Note" "nixos,config" \
+ --content=/tmp/content.org
+```
+
+**Output**: Returns JSON with created file path:
+```json
+{
+ "success": true,
+ "filepath": "/path/to/20251205T140049--note-title__tag1_tag2_tag3.org",
+ "timestamp": "20251205T140049",
+ "message": "Created note: ..."
+}
+```
+
+### Manual Creation
+
+If creating notes manually without org-manager:
+
+#### Generate Timestamp
```bash
# Get current timestamp for identifier
date +"%Y%m%dT%H%M%S"
```
-### Generate Full Date
+#### Generate Full Date
```bash
# Get org-mode formatted date
date +"[%Y-%m-%d %a %H:%M]"
@@ -270,11 +303,33 @@ CLOSED: [2025-12-03 Wed 14:23]
### Using the Org Skill
-This skill can integrate with the **Org skill** for programmatic org-mode operations on note files.
+This skill integrates with the **Org skill** for programmatic org-mode operations on note files.
**Tool location:** `~/.config/claude/skills/Org/tools/org-manager`
-**Search notes:**
+**Create notes (denote integration):**
+```bash
+# Create new note with proper denote formatting
+org-manager denote-create "My Note Title" "tag1,tag2" \
+ --category=category --directory=~/desktop/org/notes
+
+# Create automated note with signature
+org-manager denote-create "Session Log" "history,session" \
+ --signature=pkai --category=history
+
+# Read note metadata
+org-manager denote-metadata ~/desktop/org/notes/20251205T*.org
+
+# Update note frontmatter
+org-manager denote-update ~/desktop/org/notes/20251205T*.org \
+ --title="New Title" --tags="new,tags"
+
+# Append content to existing note
+echo "* New Section" > /tmp/content.org
+org-manager denote-append ~/desktop/org/notes/20251205T*.org /tmp/content.org
+```
+
+**Search and query notes:**
```bash
# Search for term across all notes
org-manager search ~/desktop/org/notes/*.org "wireguard"
dots/.config/claude/skills/Org/tools/denote-batch-functions.el
@@ -0,0 +1,245 @@
+;;; denote-batch-functions.el --- Batch operations for denote notes -*- lexical-binding: t; no-byte-compile: t; -*-
+
+;; Copyright (C) 2025 Vincent Demeester
+
+;; This file provides batch mode functions for creating and manipulating
+;; denote-formatted notes from the command line using the denote package.
+;;
+;; NOTE: This file requires the denote package to be installed in your Emacs
+;; configuration. It cannot be byte-compiled in isolation.
+
+;;; Commentary:
+
+;; These functions enable Claude Code and other tools to create denote notes
+;; programmatically using Emacs batch mode. They wrap the denote package's
+;; functions for non-interactive use.
+;;
+;; Usage:
+;; emacs --batch -l denote-batch-functions.el \
+;; --eval "(denote-batch-create-note \"Title\" '(tag1 tag2))"
+
+;;; Code:
+
+(require 'denote)
+(require 'json)
+
+;; Ensure denote-directory is set
+(unless (boundp 'denote-directory)
+ (setq denote-directory "~/desktop/org/notes/"))
+
+;; Helper to output JSON
+(defun denote-batch--output-json (data)
+ "Output DATA as JSON to stdout."
+ (princ (json-encode data))
+ (princ "\n"))
+
+;; Main function: Create denote note using denote package
+(defun denote-batch-create-note (title keywords &optional signature category directory)
+ "Create a denote note with TITLE and KEYWORDS using denote package.
+KEYWORDS can be a list of strings or symbols (will be converted to strings).
+Optional SIGNATURE for automated notes (e.g., \"pkai\").
+Optional CATEGORY is stored in frontmatter.
+Optional DIRECTORY (defaults to denote-directory).
+
+Returns JSON with created file path."
+ (condition-case err
+ (let* ((denote-directory (or directory denote-directory))
+ ;; Convert keywords to strings if they're symbols
+ (keywords-list (mapcar (lambda (k)
+ (if (symbolp k)
+ (symbol-name k)
+ k))
+ keywords))
+ ;; Use denote to create the note
+ (filepath (denote title keywords-list 'org denote-directory nil nil signature nil)))
+
+ ;; Add category to frontmatter if provided
+ (when (and filepath category)
+ (with-current-buffer (find-file-noselect filepath)
+ (goto-char (point-min))
+ ;; Find end of frontmatter
+ (when (re-search-forward "^#\\+identifier:" nil t)
+ (end-of-line)
+ (insert (format "\n#+category: %s" category)))
+ (save-buffer)
+ (kill-buffer)))
+
+ ;; Return JSON result
+ (denote-batch--output-json
+ (list :success t
+ :filepath filepath
+ :message (format "Created note: %s" (file-name-nondirectory filepath)))))
+ (error
+ (denote-batch--output-json
+ (list :success :json-false
+ :error (error-message-string err))))))
+
+;; Create note with content from file
+(defun denote-batch-create-note-from-file (title keywords content-file &optional signature category directory)
+ "Create denote note with TITLE and KEYWORDS, reading content from CONTENT-FILE.
+KEYWORDS can be a list of strings or symbols (will be converted to strings).
+Uses denote package for creation, then appends content from file.
+Optional SIGNATURE, CATEGORY, DIRECTORY same as denote-batch-create-note."
+ (condition-case err
+ (let* ((denote-directory (or directory denote-directory))
+ ;; Convert keywords to strings if they're symbols
+ (keywords-list (mapcar (lambda (k)
+ (if (symbolp k)
+ (symbol-name k)
+ k))
+ keywords))
+ ;; Create the note using denote
+ (filepath (denote title keywords-list 'org denote-directory nil nil signature nil)))
+
+ ;; Add category if provided
+ (when category
+ (with-current-buffer (find-file-noselect filepath)
+ (goto-char (point-min))
+ (when (re-search-forward "^#\\+identifier:" nil t)
+ (end-of-line)
+ (insert (format "\n#+category: %s" category)))
+ (save-buffer)
+ (kill-buffer)))
+
+ ;; Append content from file
+ (when (file-exists-p content-file)
+ (with-current-buffer (find-file-noselect filepath)
+ (goto-char (point-max))
+ (insert-file-contents content-file)
+ (save-buffer)
+ (kill-buffer)))
+
+ ;; Return JSON result
+ (denote-batch--output-json
+ (list :success t
+ :filepath filepath
+ :message (format "Created note: %s" (file-name-nondirectory filepath)))))
+ (error
+ (denote-batch--output-json
+ (list :success :json-false
+ :error (error-message-string err))))))
+
+;; Add content to existing denote note
+(defun denote-batch-append-content (filepath content)
+ "Append CONTENT to existing denote note at FILEPATH."
+ (condition-case err
+ (progn
+ (unless (file-exists-p filepath)
+ (error "File does not exist: %s" filepath))
+ (with-current-buffer (find-file-noselect filepath)
+ (goto-char (point-max))
+ ;; Ensure we're on a new line
+ (unless (bolp)
+ (insert "\n"))
+ (insert "\n" content "\n")
+ (save-buffer)
+ (kill-buffer))
+ (denote-batch--output-json
+ (list :success t
+ :filepath filepath
+ :message "Content appended")))
+ (error
+ (denote-batch--output-json
+ (list :success :json-false
+ :error (error-message-string err))))))
+
+;; Update denote note using denote-rename functions
+(defun denote-batch-update-frontmatter (filepath &optional new-title new-keywords new-category)
+ "Update frontmatter of denote note at FILEPATH.
+Optional NEW-TITLE to change title.
+Optional NEW-KEYWORDS (list of symbols) to change keywords.
+Optional NEW-CATEGORY to update category.
+
+Uses denote-rename-file-using-front-matter when possible."
+ (condition-case err
+ (progn
+ (unless (file-exists-p filepath)
+ (error "File does not exist: %s" filepath))
+
+ (with-current-buffer (find-file-noselect filepath)
+ ;; Update title in frontmatter
+ (when new-title
+ (goto-char (point-min))
+ (when (re-search-forward "^#\\+title:[ \t]*\\(.*\\)$" nil t)
+ (replace-match new-title nil nil nil 1)))
+
+ ;; Update keywords in frontmatter
+ (when new-keywords
+ (goto-char (point-min))
+ (when (re-search-forward "^#\\+filetags:[ \t]*\\(.*\\)$" nil t)
+ (let ((tags-string (concat ":" (mapconcat #'symbol-name new-keywords ":") ":")))
+ (replace-match tags-string nil nil nil 1))))
+
+ ;; Update category
+ (when new-category
+ (goto-char (point-min))
+ (if (re-search-forward "^#\\+category:[ \t]*\\(.*\\)$" nil t)
+ (replace-match new-category nil nil nil 1)
+ ;; Add category if it doesn't exist
+ (when (re-search-forward "^#\\+identifier:" nil t)
+ (end-of-line)
+ (insert (format "\n#+category: %s" new-category)))))
+
+ (save-buffer)
+
+ ;; Use denote-rename-file-using-front-matter to update filename
+ (when (or new-title new-keywords)
+ (denote-rename-file-using-front-matter filepath))
+
+ (kill-buffer))
+
+ (denote-batch--output-json
+ (list :success t
+ :filepath filepath
+ :message "Frontmatter updated")))
+ (error
+ (denote-batch--output-json
+ (list :success :json-false
+ :error (error-message-string err))))))
+
+;; Read denote note metadata using denote functions
+(defun denote-batch-read-metadata (filepath)
+ "Read metadata from denote note at FILEPATH using denote functions.
+Returns JSON with title, keywords, identifier, signature, date, and category."
+ (condition-case err
+ (progn
+ (unless (file-exists-p filepath)
+ (error "File does not exist: %s" filepath))
+
+ ;; Use denote's built-in metadata retrieval
+ (let* ((file-type (denote-filetype-heuristics filepath))
+ (title (denote-retrieve-title-value filepath file-type))
+ (keywords (denote-extract-keywords-from-path filepath))
+ (identifier (denote-retrieve-filename-identifier filepath))
+ (signature (denote-retrieve-filename-signature filepath))
+ (date-string nil)
+ (category nil))
+
+ ;; Get date and category from frontmatter
+ (with-temp-buffer
+ (insert-file-contents filepath)
+ (goto-char (point-min))
+ (when (re-search-forward "^#\\+date:[ \t]*\\(.*\\)$" nil t)
+ (setq date-string (match-string 1)))
+ (goto-char (point-min))
+ (when (re-search-forward "^#\\+category:[ \t]*\\(.*\\)$" nil t)
+ (setq category (match-string 1))))
+
+ ;; Return JSON
+ (denote-batch--output-json
+ (list :success t
+ :title title
+ :keywords keywords
+ :identifier identifier
+ :signature (or signature "")
+ :date date-string
+ :category (or category "")
+ :filepath filepath))))
+ (error
+ (denote-batch--output-json
+ (list :success :json-false
+ :error (error-message-string err))))))
+
+(provide 'denote-batch-functions)
+
+;;; denote-batch-functions.el ends here
dots/.config/claude/skills/Org/tools/org-manager
@@ -8,6 +8,7 @@ set -euo pipefail
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BATCH_FUNCTIONS="${BATCH_FUNCTIONS:-$SCRIPT_DIR/batch-functions.el}"
+DENOTE_FUNCTIONS="${DENOTE_FUNCTIONS:-$SCRIPT_DIR/denote-batch-functions.el}"
EMACS="${EMACS:-emacs}"
# Debug mode
@@ -45,6 +46,10 @@ check_deps() {
if [[ ! -f "$BATCH_FUNCTIONS" ]]; then
error "batch-functions.el not found at: $BATCH_FUNCTIONS"
fi
+
+ if [[ ! -f "$DENOTE_FUNCTIONS" ]]; then
+ error "denote-batch-functions.el not found at: $DENOTE_FUNCTIONS"
+ fi
}
# Run Emacs in batch mode
@@ -64,6 +69,23 @@ run_elisp() {
fi
}
+# Run Emacs in batch mode with denote functions
+run_denote_elisp() {
+ local elisp_code="$1"
+
+ debug "Running denote elisp: $elisp_code"
+
+ if [[ "$DEBUG" == "1" ]]; then
+ "$EMACS" --batch --no-init-file \
+ --load "$DENOTE_FUNCTIONS" \
+ --eval "$elisp_code" 2>&1
+ else
+ "$EMACS" --batch --no-init-file \
+ --load "$DENOTE_FUNCTIONS" \
+ --eval "$elisp_code" 2>/dev/null
+ fi
+}
+
# Usage information
usage() {
cat <<EOF
@@ -109,6 +131,20 @@ WRITE COMMANDS:
archive <file>
Archive all DONE and CANX items
+DENOTE COMMANDS:
+ denote-create <title> <tags> [--signature=SIG] [--category=CAT] [--directory=DIR] [--content=FILE]
+ Create a denote-formatted note with proper naming and frontmatter
+ Tags: comma-separated list (e.g., nixos,homelab,plan)
+
+ denote-append <filepath> <content-file>
+ Append content to existing denote note
+
+ denote-metadata <filepath>
+ Read metadata from denote note
+
+ denote-update <filepath> [--title=TITLE] [--tags=TAGS] [--category=CAT]
+ Update denote note frontmatter
+
OPTIONS:
--state=STATE Filter by TODO state
--priority=N Filter by priority (1-5) or list: 1,2
@@ -139,6 +175,17 @@ EXAMPLES:
# Count by state
org-manager count ~/desktop/org/todos.org
+ # Create denote note
+ org-manager denote-create "NixOS Refactoring Plan" "nixos,refactoring,plan" \\
+ --category=homelab --directory=~/desktop/org/notes
+
+ # Create automated note with signature
+ org-manager denote-create "Session Summary" "history,session" \\
+ --signature=pkai --category=history
+
+ # Read note metadata
+ org-manager denote-metadata ~/desktop/org/notes/20251205T*.org
+
OUTPUT:
All commands return JSON for easy parsing:
{"success": true, "data": [...]}
@@ -420,6 +467,151 @@ cmd_archive() {
run_elisp "$elisp"
}
+# Denote commands
+
+cmd_denote_create() {
+ local title="$1"
+ local tags="$2"; shift 2
+ local signature="" category="" directory="" content_file=""
+
+ [[ -n "$title" ]] || error "Title required"
+ [[ -n "$tags" ]] || error "Tags required (comma-separated)"
+
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --signature=*)
+ signature=$(parse_option "$1" "--signature=")
+ shift
+ ;;
+ --category=*)
+ category=$(parse_option "$1" "--category=")
+ shift
+ ;;
+ --directory=*)
+ directory=$(parse_option "$1" "--directory=")
+ shift
+ ;;
+ --content=*)
+ content_file=$(parse_option "$1" "--content=")
+ shift
+ ;;
+ *)
+ error "Unknown option: $1"
+ ;;
+ esac
+ done
+
+ # Convert comma-separated tags to elisp list
+ local tags_list="'(${tags//,/ })"
+
+ # Build elisp call
+ local elisp="(progn"
+
+ if [[ -n "$content_file" ]]; then
+ [[ -f "$content_file" ]] || error "Content file not found: $content_file"
+ elisp="$elisp
+ (denote-batch-create-note-from-file
+ \"$title\"
+ $tags_list
+ \"$content_file\""
+ else
+ elisp="$elisp
+ (denote-batch-create-note
+ \"$title\"
+ $tags_list"
+ fi
+
+ # Add optional parameters
+ [[ -n "$signature" ]] && elisp="$elisp
+ \"$signature\"" || elisp="$elisp
+ nil"
+ [[ -n "$category" ]] && elisp="$elisp
+ \"$category\"" || elisp="$elisp
+ nil"
+ [[ -n "$directory" ]] && elisp="$elisp
+ \"$directory\"" || elisp="$elisp
+ nil"
+
+ elisp="$elisp)
+ (kill-emacs 0))"
+
+ run_denote_elisp "$elisp"
+}
+
+cmd_denote_append() {
+ local filepath="$1"
+ local content_file="$2"
+
+ [[ -f "$filepath" ]] || error "File not found: $filepath"
+ [[ -f "$content_file" ]] || error "Content file not found: $content_file"
+
+ # Read content
+ local content
+ content=$(<"$content_file")
+
+ # Escape quotes for elisp
+ content="${content//\"/\\\"}"
+
+ local elisp="(progn
+ (denote-batch-append-content \"$filepath\" \"$content\")
+ (kill-emacs 0))"
+
+ run_denote_elisp "$elisp"
+}
+
+cmd_denote_metadata() {
+ local filepath="$1"
+
+ [[ -f "$filepath" ]] || error "File not found: $filepath"
+
+ local elisp="(progn
+ (denote-batch-read-metadata \"$filepath\")
+ (kill-emacs 0))"
+
+ run_denote_elisp "$elisp"
+}
+
+cmd_denote_update() {
+ local filepath="$1"; shift
+ local new_title="" new_tags="" new_category=""
+
+ [[ -f "$filepath" ]] || error "File not found: $filepath"
+
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --title=*)
+ new_title=$(parse_option "$1" "--title=")
+ shift
+ ;;
+ --tags=*)
+ new_tags=$(parse_option "$1" "--tags=")
+ shift
+ ;;
+ --category=*)
+ new_category=$(parse_option "$1" "--category=")
+ shift
+ ;;
+ *)
+ error "Unknown option: $1"
+ ;;
+ esac
+ done
+
+ # Convert tags if provided
+ local tags_list="nil"
+ [[ -n "$new_tags" ]] && tags_list="'(${new_tags//,/ })"
+
+ # shellcheck disable=SC2155
+ local elisp="(progn
+ (denote-batch-update-frontmatter \"$filepath\"
+ $([ -n "$new_title" ] && echo "\"$new_title\"" || echo "nil")
+ $tags_list
+ $([ -n "$new_category" ] && echo "\"$new_category\"" || echo "nil"))
+ (kill-emacs 0))"
+
+ run_denote_elisp "$elisp"
+}
+
# Main
main() {
check_deps
@@ -467,6 +659,18 @@ main() {
archive)
cmd_archive "$@"
;;
+ denote-create)
+ cmd_denote_create "$@"
+ ;;
+ denote-append)
+ cmd_denote_append "$@"
+ ;;
+ denote-metadata)
+ cmd_denote_metadata "$@"
+ ;;
+ denote-update)
+ cmd_denote_update "$@"
+ ;;
*)
error "Unknown command: $command. Use --help for usage."
;;
dots/.config/claude/skills/Org/README.md
@@ -8,9 +8,9 @@ This skill provides reliable access to org-mode files for TODO management, note
## Tool: org-manager
-CLI tool for org-mode operations via Emacs batch mode.
+CLI tool for org-mode and denote operations via Emacs batch mode.
-### Usage
+### TODO Operations
```bash
# List TODOs
@@ -30,6 +30,29 @@ CLI tool for org-mode operations via Emacs batch mode.
./tools/org-manager update-state ~/desktop/org/todos.org "Task name" DONE
```
+### Denote Operations
+
+```bash
+# Create denote-formatted note
+./tools/org-manager denote-create "Note Title" "tag1,tag2,tag3" \
+ --category=homelab --directory=~/desktop/org/notes
+
+# Create with signature (automated notes)
+./tools/org-manager denote-create "Session Log" "history,session" \
+ --signature=pkai --category=history
+
+# Read note metadata
+./tools/org-manager denote-metadata ~/desktop/org/notes/20251205T*.org
+
+# Update note frontmatter
+./tools/org-manager denote-update ~/desktop/org/notes/20251205T*.org \
+ --title="New Title" --tags="new,tags"
+
+# Append content to note
+echo "* New Section" > /tmp/content.org
+./tools/org-manager denote-append ~/desktop/org/notes/20251205T*.org /tmp/content.org
+```
+
### Output
All commands return JSON:
@@ -54,20 +77,19 @@ All commands return JSON:
### Files
-- `tools/batch-functions.el` - Core elisp operations (343 lines)
-- `tools/org-manager` - Bash CLI wrapper (450 lines)
+- `tools/batch-functions.el` - Core elisp TODO operations (343 lines)
+- `tools/denote-batch-functions.el` - Denote note creation and management (300 lines)
+- `tools/org-manager` - Bash CLI wrapper (680 lines)
### Functions
-**Read operations:**
+**TODO Operations (batch-functions.el):**
- `org-batch-list-todos` - Parse and filter TODOs
- `org-batch-scheduled-today` - Get scheduled items
- `org-batch-by-section` - Filter by section
- `org-batch-count-by-state` - Count statistics
- `org-batch-search` - Full-text search
- `org-batch-get-sections` - List sections
-
-**Write operations:**
- `org-batch-add-todo` - Add new TODO
- `org-batch-update-state` - Change states
- `org-batch-schedule-task` - Set SCHEDULED
@@ -75,6 +97,20 @@ All commands return JSON:
- `org-batch-set-priority` - Set priority
- `org-batch-archive-done` - Archive items
+**Denote Operations (denote-batch-functions.el):**
+- `denote-batch-create-note` - Create denote note with proper formatting
+- `denote-batch-create-note-from-file` - Create note with content from file
+- `denote-batch-append-content` - Append content to existing note
+- `denote-batch-update-frontmatter` - Update note metadata
+- `denote-batch-read-metadata` - Read note metadata as JSON
+
+**Features:**
+- Automatic timestamp generation (YYYYMMDDTHHMMSS)
+- Signature support for automated notes (`==pkai`)
+- Proper denote filename format: `TIMESTAMP==SIG--title__tags.org`
+- Org-mode frontmatter generation (#+title, #+date, #+filetags, etc.)
+- JSON output for all operations
+
## Performance
Tested on 354-item todos.org:
dots/.config/claude/skills/Org/SKILL.md
@@ -24,6 +24,7 @@ Provide reliable, programmatic access to org-mode files using Emacs batch mode a
### Usage
+#### TODO Operations
```bash
# List TODOs
./tools/org-manager list ~/desktop/org/todos.org --state=NEXT
@@ -45,6 +46,28 @@ Provide reliable, programmatic access to org-mode files using Emacs batch mode a
./tools/org-manager search ~/desktop/org/todos.org "term"
```
+#### Denote Operations
+```bash
+# Create denote-formatted note
+./tools/org-manager denote-create "My Note Title" "tag1,tag2,tag3" \
+ --category=homelab --directory=~/desktop/org/notes
+
+# Create with signature (for automated notes)
+./tools/org-manager denote-create "Session Log" "history,session" \
+ --signature=pkai --category=history
+
+# Read note metadata
+./tools/org-manager denote-metadata ~/desktop/org/notes/20251205T*.org
+
+# Update note frontmatter
+./tools/org-manager denote-update ~/desktop/org/notes/20251205T*.org \
+ --title="New Title" --tags="new,tags" --category="updated"
+
+# Append content to note
+echo "* New Section" > /tmp/content.org
+./tools/org-manager denote-append ~/desktop/org/notes/20251205T*.org /tmp/content.org
+```
+
### Output Format
All commands return JSON:
@@ -70,6 +93,7 @@ All commands return JSON:
### Core Functions (batch-functions.el)
+**TODO Operations:**
- `org-batch-list-todos` - Parse and filter TODOs
- `org-batch-scheduled-today` - Get scheduled items
- `org-batch-by-section` - Filter by section
@@ -82,6 +106,22 @@ All commands return JSON:
- `org-batch-set-priority` - Set priority
- `org-batch-archive-done` - Archive items
+### Denote Functions (denote-batch-functions.el)
+
+**Note Creation and Management:**
+- `denote-batch-create-note` - Create denote note with proper naming and frontmatter
+- `denote-batch-create-note-from-file` - Create note with content from file
+- `denote-batch-append-content` - Append content to existing note
+- `denote-batch-update-frontmatter` - Update note metadata (title, tags, category)
+- `denote-batch-read-metadata` - Read note metadata as JSON
+
+**Features:**
+- Automatic timestamp generation (YYYYMMDDTHHMMSS)
+- Signature support for automated notes (e.g., `==pkai`)
+- Proper denote filename format: `TIMESTAMP==SIG--title__tags.org`
+- Org-mode frontmatter generation (#+title, #+date, #+filetags, etc.)
+- JSON output for programmatic integration
+
### Configuration
TODO keywords and priorities are configured for your setup:
.gitignore
@@ -23,3 +23,4 @@ hardware-configuration.nix
.chatgpt-shell.el
.chatgpt-shell.el
/keyboards/firmwares/
+/dots/.config/claude/skills/Org/tools/batch-functions.elc