flake-update-20260201
  1#!/usr/bin/env bash
  2# journelly-manager - CLI tool for Journelly journal file manipulation via Emacs batch mode
  3# Copyright (C) 2026 Vincent Demeester
  4# Loads elisp from site-lisp for consistency with interactive Emacs
  5
  6set -euo pipefail
  7
  8# Configuration
  9EMACS="${EMACS:-emacs}"
 10EMACS_DIR="${EMACS_DIR:-$HOME/.config/emacs}"
 11SITE_LISP="$EMACS_DIR/site-lisp"
 12
 13# Debug mode
 14DEBUG="${DEBUG:-0}"
 15
 16# Colors for output (if not outputting JSON)
 17if [[ -t 1 ]] && [[ "${JSON_OUTPUT:-1}" != "1" ]]; then
 18    RED='\033[0;31m'
 19    GREEN='\033[0;32m'
 20    YELLOW='\033[1;33m'
 21    BLUE='\033[0;34m'
 22    NC='\033[0m' # No Color
 23else
 24    RED=''
 25    GREEN=''
 26    YELLOW=''
 27    BLUE=''
 28    NC=''
 29fi
 30
 31# Error handling
 32error() {
 33    echo -e "${RED}Error: $*${NC}" >&2
 34    exit 1
 35}
 36
 37debug() {
 38    if [[ "$DEBUG" == "1" ]]; then
 39        echo -e "${YELLOW}Debug: $*${NC}" >&2
 40    fi
 41}
 42
 43info() {
 44    if [[ "${JSON_OUTPUT:-1}" != "1" ]]; then
 45        echo -e "${BLUE}$*${NC}" >&2
 46    fi
 47}
 48
 49success() {
 50    if [[ "${JSON_OUTPUT:-1}" != "1" ]]; then
 51        echo -e "${GREEN}$*${NC}" >&2
 52    fi
 53}
 54
 55# Check dependencies
 56check_deps() {
 57    if ! command -v "$EMACS" &> /dev/null; then
 58        error "Emacs not found. Set EMACS environment variable or install emacs."
 59    fi
 60
 61    if [[ ! -d "$SITE_LISP" ]]; then
 62        error "Emacs site-lisp directory not found at: $SITE_LISP"
 63    fi
 64
 65    if [[ ! -f "$SITE_LISP/journelly-batch-functions.el" ]]; then
 66        error "journelly-batch-functions.el not found in site-lisp"
 67    fi
 68}
 69
 70# Run Emacs batch command
 71run_batch() {
 72    local function_call="$1"
 73    debug "Running: $EMACS --batch --directory \"$SITE_LISP\" --load journelly-batch-functions.el --eval \"$function_call\""
 74
 75    "$EMACS" --batch \
 76        --directory "$SITE_LISP" \
 77        --load journelly-batch-functions.el \
 78        --eval "$function_call" 2>&1
 79}
 80
 81# Usage information
 82usage() {
 83    cat <<EOF
 84journelly-manager - CLI tool for managing Journelly journal files
 85
 86USAGE:
 87    journelly-manager <command> [arguments]
 88
 89COMMANDS:
 90    create FILE LOCATION CONTENT [options]
 91        Create new journal entry
 92
 93        Options:
 94            --latitude=LAT         GPS latitude
 95            --longitude=LON        GPS longitude
 96            --temperature=TEMP     Temperature (e.g., "15,2°C")
 97            --condition=COND       Weather condition (e.g., "Cloudy")
 98            --symbol=SYM           Weather symbol (e.g., "cloud")
 99
100        Examples:
101            journelly-manager create ~/desktop/org/Journelly.org "Home" "Today was great"
102
103            journelly-manager create ~/desktop/org/Journelly.org "Kyushu" \\
104                "Work session notes" \\
105                --latitude=48.8534 --longitude=2.3488 \\
106                --temperature="15°C" --condition="Cloudy" --symbol="cloud"
107
108    append FILE DATE CONTENT
109        Append to existing journal entry by date (YYYY-MM-DD)
110
111        Examples:
112            journelly-manager append ~/desktop/org/Journelly.org \\
113                "2026-01-16" "Additional thoughts"
114
115    list FILE [--limit=N]
116        List recent journal entries
117
118        Options:
119            --limit=N    Number of entries to show (default: 10)
120
121        Examples:
122            journelly-manager list ~/desktop/org/Journelly.org
123            journelly-manager list ~/desktop/org/Journelly.org --limit=20
124
125    search FILE QUERY
126        Search journal entries for keyword
127
128        Examples:
129            journelly-manager search ~/desktop/org/Journelly.org "wireguard"
130
131    get FILE DATE
132        Get specific entry by date (YYYY-MM-DD)
133
134        Examples:
135            journelly-manager get ~/desktop/org/Journelly.org "2026-01-16"
136
137ENVIRONMENT:
138    EMACS           Emacs executable (default: emacs)
139    EMACS_DIR       Emacs config directory (default: ~/.config/emacs)
140    DEBUG           Set to 1 for debug output
141    JSON_OUTPUT     Set to 1 for JSON output (no colors)
142
143EXAMPLES:
144    # Create entry with auto location/weather (use get-location/get-weather)
145    LOC=\$(get-location --json)
146    WEATHER=\$(get-weather --json)
147    journelly-manager create ~/desktop/org/Journelly.org \\
148        "\$(echo \$LOC | jq -r .city)" "Entry content" \\
149        --latitude="\$(echo \$LOC | jq -r .lat)" \\
150        --longitude="\$(echo \$LOC | jq -r .lon)" \\
151        --temperature="\$(echo \$WEATHER | jq -r .temperature)" \\
152        --condition="\$(echo \$WEATHER | jq -r .condition)" \\
153        --symbol="\$(echo \$WEATHER | jq -r .symbol)"
154
155    # Append to today's entry
156    journelly-manager append ~/desktop/org/Journelly.org \\
157        "\$(date +%Y-%m-%d)" "More thoughts"
158
159    # Search entries
160    journelly-manager search ~/desktop/org/Journelly.org "claude"
161
162EOF
163    exit 0
164}
165
166# Parse create command
167cmd_create() {
168    local file="$1"
169    local location="$2"
170    local content="$3"
171    shift 3
172
173    local latitude="" longitude="" temperature="" condition="" symbol=""
174
175    # Parse options
176    while [[ $# -gt 0 ]]; do
177        case "$1" in
178            --latitude=*)
179                latitude="${1#*=}"
180                shift
181                ;;
182            --longitude=*)
183                longitude="${1#*=}"
184                shift
185                ;;
186            --temperature=*)
187                temperature="${1#*=}"
188                shift
189                ;;
190            --condition=*)
191                condition="${1#*=}"
192                shift
193                ;;
194            --symbol=*)
195                symbol="${1#*=}"
196                shift
197                ;;
198            *)
199                error "Unknown option: $1"
200                ;;
201        esac
202    done
203
204    # Build Emacs Lisp call
205    local elisp_call="(journelly-batch-create-entry \"$file\" \"$location\" \"$content\""
206
207    if [[ -n "$latitude" ]]; then elisp_call="$elisp_call :latitude \"$latitude\""; fi
208    if [[ -n "$longitude" ]]; then elisp_call="$elisp_call :longitude \"$longitude\""; fi
209    if [[ -n "$temperature" ]]; then elisp_call="$elisp_call :temperature \"$temperature\""; fi
210    if [[ -n "$condition" ]]; then elisp_call="$elisp_call :condition \"$condition\""; fi
211    if [[ -n "$symbol" ]]; then elisp_call="$elisp_call :symbol \"$symbol\""; fi
212
213    elisp_call="$elisp_call)"
214
215    run_batch "$elisp_call"
216    success "Journal entry created"
217}
218
219# Parse append command
220cmd_append() {
221    local file="$1"
222    local date="$2"
223    local content="$3"
224
225    local elisp_call="(journelly-batch-append-to-date \"$file\" \"$date\" \"$content\")"
226    run_batch "$elisp_call"
227    success "Content appended to entry"
228}
229
230# Parse list command
231cmd_list() {
232    local file="$1"
233    shift
234
235    local limit="10"
236
237    # Parse options
238    while [[ $# -gt 0 ]]; do
239        case "$1" in
240            --limit=*)
241                limit="${1#*=}"
242                shift
243                ;;
244            *)
245                error "Unknown option: $1"
246                ;;
247        esac
248    done
249
250    local elisp_call="(journelly-batch-list-entries \"$file\" $limit)"
251    run_batch "$elisp_call"
252}
253
254# Parse search command
255cmd_search() {
256    local file="$1"
257    local query="$2"
258
259    local elisp_call="(journelly-batch-search \"$file\" \"$query\")"
260    run_batch "$elisp_call"
261}
262
263# Parse get command
264cmd_get() {
265    local file="$1"
266    local date="$2"
267
268    local elisp_call="(journelly-batch-get-entry \"$file\" \"$date\")"
269    run_batch "$elisp_call"
270}
271
272# Main command dispatcher
273main() {
274    if [[ $# -eq 0 ]]; then
275        usage
276    fi
277
278    local command="$1"
279    shift
280
281    case "$command" in
282        -h|--help|help)
283            usage
284            ;;
285        create)
286            check_deps
287            if [[ $# -lt 3 ]]; then
288                error "create requires: FILE LOCATION CONTENT"
289            fi
290            cmd_create "$@"
291            ;;
292        append)
293            check_deps
294            if [[ $# -lt 3 ]]; then
295                error "append requires: FILE DATE CONTENT"
296            fi
297            cmd_append "$@"
298            ;;
299        list)
300            check_deps
301            if [[ $# -lt 1 ]]; then
302                error "list requires: FILE"
303            fi
304            cmd_list "$@"
305            ;;
306        search)
307            check_deps
308            if [[ $# -lt 2 ]]; then
309                error "search requires: FILE QUERY"
310            fi
311            cmd_search "$@"
312            ;;
313        get)
314            check_deps
315            if [[ $# -lt 2 ]]; then
316                error "get requires: FILE DATE"
317            fi
318            cmd_get "$@"
319            ;;
320        *)
321            error "Unknown command: $command (try --help)"
322            ;;
323    esac
324}
325
326main "$@"