auto-update-daily-20260202
  1#!/usr/bin/env bash
  2# journelly-manager - CLI tool for Journelly journal file manipulation via Emacs batch mode
  3# Copyright (C) 2025 Vincent Demeester
  4# Part of Claude Code Journal skill
  5
  6set -euo pipefail
  7
  8# Configuration
  9SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 10BATCH_FUNCTIONS="${BATCH_FUNCTIONS:-$SCRIPT_DIR/journelly-batch-functions.el}"
 11EMACS="${EMACS:-emacs}"
 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 [[ ! -f "$BATCH_FUNCTIONS" ]]; then
 62        error "journelly-batch-functions.el not found at: $BATCH_FUNCTIONS"
 63    fi
 64}
 65
 66# Run Emacs batch command
 67run_batch() {
 68    local function_call="$1"
 69    debug "Running: $EMACS --batch --load \"$BATCH_FUNCTIONS\" --eval \"$function_call\""
 70
 71    "$EMACS" --batch \
 72        --load "$BATCH_FUNCTIONS" \
 73        --eval "$function_call" 2>&1
 74}
 75
 76# Usage information
 77usage() {
 78    cat <<EOF
 79journelly-manager - CLI tool for managing Journelly journal files
 80
 81USAGE:
 82    journelly-manager <command> [arguments]
 83
 84COMMANDS:
 85    create FILE LOCATION CONTENT [options]
 86        Create new journal entry
 87
 88        Options:
 89            --latitude=LAT         GPS latitude
 90            --longitude=LON        GPS longitude
 91            --temperature=TEMP     Temperature (e.g., "15,2°C")
 92            --condition=COND       Weather condition (e.g., "Cloudy")
 93            --symbol=SYM           Weather symbol (e.g., "cloud")
 94            --content-file=PATH    Read content from file instead
 95
 96        Examples:
 97            journelly-manager create ~/desktop/org/Journelly.org "Home" "Today was great"
 98
 99            journelly-manager create ~/desktop/org/Journelly.org "Kyushu" \\
100                "Work session notes" \\
101                --latitude=48.8672 --longitude=2.1851 \\
102                --temperature="15,2°C" --condition="Partly Cloudy" --symbol="cloud.sun"
103
104    append FILE CONTENT [--content-file=PATH]
105        Append to today's journal entry
106
107        Examples:
108            journelly-manager append ~/desktop/org/Journelly.org "Additional thoughts"
109            journelly-manager append ~/desktop/org/Journelly.org --content-file=/tmp/notes.txt
110
111    list FILE [--limit=N]
112        List recent journal entries (default: 10)
113
114        Examples:
115            journelly-manager list ~/desktop/org/Journelly.org
116            journelly-manager list ~/desktop/org/Journelly.org --limit=20
117
118    search FILE QUERY
119        Search journal entries for text
120
121        Examples:
122            journelly-manager search ~/desktop/org/Journelly.org "claude"
123            journelly-manager search ~/desktop/org/Journelly.org "homelab"
124
125    get FILE DATE [TIME]
126        Get specific entry by date and optional time
127
128        Examples:
129            journelly-manager get ~/desktop/org/Journelly.org "2025-12-08"
130            journelly-manager get ~/desktop/org/Journelly.org "2025-12-08" "15:30"
131
132GLOBAL OPTIONS:
133    --help, -h             Show this help message
134    --version              Show version information
135    --debug                Enable debug output
136
137ENVIRONMENT VARIABLES:
138    EMACS                  Path to Emacs binary (default: emacs)
139    BATCH_FUNCTIONS        Path to journelly-batch-functions.el
140    DEBUG                  Enable debug mode (1 or 0)
141    JSON_OUTPUT            Output JSON only (1 or 0)
142
143EXAMPLES:
144    # Create simple entry
145    journelly-manager create ~/desktop/org/Journelly.org "Home" \\
146        "Productive day working on Claude skills."
147
148    # Create entry with weather data
149    journelly-manager create ~/desktop/org/Journelly.org "Rue Jean Bourguignon" \\
150        "Evening reflection" \\
151        --latitude=48.86721 --longitude=2.18509 \\
152        --temperature="8,5°C" --condition="Clear" --symbol="moon.stars"
153
154    # Create entry with content from file
155    echo "My journal thoughts..." > /tmp/entry.txt
156    journelly-manager create ~/desktop/org/Journelly.org "Kyushu" \\
157        --content-file=/tmp/entry.txt
158
159    # Append to today's entry
160    journelly-manager append ~/desktop/org/Journelly.org \\
161        "Update: Made more progress on the project!"
162
163    # List recent entries
164    journelly-manager list ~/desktop/org/Journelly.org --limit=5
165
166    # Search for entries about work
167    journelly-manager search ~/desktop/org/Journelly.org "work"
168
169    # Get specific entry
170    journelly-manager get ~/desktop/org/Journelly.org "2025-12-08" "15:30"
171
172VERSION:
173    1.0.0
174
175AUTHOR:
176    Vincent Demeester <vincent@demeester.fr>
177
178SEE ALSO:
179    Journelly iOS App: https://journelly.com
180    Org-mode: https://orgmode.org
181EOF
182}
183
184# Parse command line arguments
185parse_args() {
186    local cmd="${1:-}"
187    shift || true
188
189    case "$cmd" in
190        create)
191            cmd_create "$@"
192            ;;
193        append)
194            cmd_append "$@"
195            ;;
196        list)
197            cmd_list "$@"
198            ;;
199        search)
200            cmd_search "$@"
201            ;;
202        get)
203            cmd_get "$@"
204            ;;
205        --help|-h|help)
206            usage
207            exit 0
208            ;;
209        --version)
210            echo "journelly-manager 1.0.0"
211            exit 0
212            ;;
213        "")
214            error "No command specified. Use --help for usage."
215            ;;
216        *)
217            error "Unknown command: $cmd. Use --help for usage."
218            ;;
219    esac
220}
221
222# Command: create
223cmd_create() {
224    local file="${1:-}"
225    local location="${2:-}"
226    local content="${3:-}"
227    shift 3 || error "create requires FILE LOCATION CONTENT arguments"
228
229    [[ -z "$file" ]] && error "FILE argument required"
230    [[ -z "$location" ]] && error "LOCATION argument required"
231    [[ ! -f "$file" ]] && error "File not found: $file"
232
233    # Parse optional arguments
234    local latitude=""
235    local longitude=""
236    local temperature=""
237    local condition=""
238    local symbol=""
239    local content_file=""
240
241    while [[ $# -gt 0 ]]; do
242        case "$1" in
243            --latitude=*)
244                latitude="${1#*=}"
245                ;;
246            --longitude=*)
247                longitude="${1#*=}"
248                ;;
249            --temperature=*)
250                temperature="${1#*=}"
251                ;;
252            --condition=*)
253                condition="${1#*=}"
254                ;;
255            --symbol=*)
256                symbol="${1#*=}"
257                ;;
258            --content-file=*)
259                content_file="${1#*=}"
260                [[ ! -f "$content_file" ]] && error "Content file not found: $content_file"
261                ;;
262            *)
263                error "Unknown option: $1"
264                ;;
265        esac
266        shift
267    done
268
269    # Build Emacs Lisp call
270    local elisp_call="(journelly-batch-create-entry \"$file\" \"$location\" \"$content\""
271
272    [[ -n "$latitude" ]] && elisp_call="$elisp_call \"$latitude\"" || elisp_call="$elisp_call nil"
273    [[ -n "$longitude" ]] && elisp_call="$elisp_call \"$longitude\"" || elisp_call="$elisp_call nil"
274    [[ -n "$temperature" ]] && elisp_call="$elisp_call \"$temperature\"" || elisp_call="$elisp_call nil"
275    [[ -n "$condition" ]] && elisp_call="$elisp_call \"$condition\"" || elisp_call="$elisp_call nil"
276    [[ -n "$symbol" ]] && elisp_call="$elisp_call \"$symbol\"" || elisp_call="$elisp_call nil"
277    [[ -n "$content_file" ]] && elisp_call="$elisp_call \"$content_file\"" || elisp_call="$elisp_call nil"
278
279    elisp_call="$elisp_call)"
280
281    info "Creating journal entry..."
282    local result
283    result=$(run_batch "$elisp_call")
284    echo "$result"
285
286    if echo "$result" | grep -q '"success":true'; then
287        success "Journal entry created successfully"
288    fi
289}
290
291# Command: append
292cmd_append() {
293    local file="${1:-}"
294    local content="${2:-}"
295    shift 2 || error "append requires FILE CONTENT arguments"
296
297    [[ -z "$file" ]] && error "FILE argument required"
298    [[ ! -f "$file" ]] && error "File not found: $file"
299
300    local content_file=""
301
302    while [[ $# -gt 0 ]]; do
303        case "$1" in
304            --content-file=*)
305                content_file="${1#*=}"
306                [[ ! -f "$content_file" ]] && error "Content file not found: $content_file"
307                ;;
308            *)
309                error "Unknown option: $1"
310                ;;
311        esac
312        shift
313    done
314
315    local elisp_call="(journelly-batch-append-to-today \"$file\" \"$content\""
316    [[ -n "$content_file" ]] && elisp_call="$elisp_call \"$content_file\"" || elisp_call="$elisp_call nil"
317    elisp_call="$elisp_call)"
318
319    info "Appending to today's entry..."
320    local result
321    result=$(run_batch "$elisp_call")
322    echo "$result"
323
324    if echo "$result" | grep -q '"success":true'; then
325        success "Content appended successfully"
326    fi
327}
328
329# Command: list
330cmd_list() {
331    local file="${1:-}"
332    shift || error "list requires FILE argument"
333
334    [[ -z "$file" ]] && error "FILE argument required"
335    [[ ! -f "$file" ]] && error "File not found: $file"
336
337    local limit=""
338
339    while [[ $# -gt 0 ]]; do
340        case "$1" in
341            --limit=*)
342                limit="${1#*=}"
343                ;;
344            *)
345                error "Unknown option: $1"
346                ;;
347        esac
348        shift
349    done
350
351    local elisp_call="(journelly-batch-list-entries \"$file\""
352    [[ -n "$limit" ]] && elisp_call="$elisp_call \"$limit\"" || elisp_call="$elisp_call nil"
353    elisp_call="$elisp_call)"
354
355    info "Listing recent entries..."
356    run_batch "$elisp_call"
357}
358
359# Command: search
360cmd_search() {
361    local file="${1:-}"
362    local query="${2:-}"
363    shift 2 || error "search requires FILE QUERY arguments"
364
365    [[ -z "$file" ]] && error "FILE argument required"
366    [[ -z "$query" ]] && error "QUERY argument required"
367    [[ ! -f "$file" ]] && error "File not found: $file"
368
369    local elisp_call="(journelly-batch-search \"$file\" \"$query\")"
370
371    info "Searching for: $query"
372    run_batch "$elisp_call"
373}
374
375# Command: get
376cmd_get() {
377    local file="${1:-}"
378    local date="${2:-}"
379    local time="${3:-}"
380    shift 2 || error "get requires FILE DATE arguments"
381
382    [[ -z "$file" ]] && error "FILE argument required"
383    [[ -z "$date" ]] && error "DATE argument required"
384    [[ ! -f "$file" ]] && error "File not found: $file"
385
386    local elisp_call="(journelly-batch-get-entry \"$file\" \"$date\""
387    [[ -n "$time" ]] && elisp_call="$elisp_call \"$time\"" || elisp_call="$elisp_call nil"
388    elisp_call="$elisp_call)"
389
390    info "Getting entry for: $date${time:+ at $time}"
391    run_batch "$elisp_call"
392}
393
394# Main
395main() {
396    check_deps
397    parse_args "$@"
398}
399
400main "$@"