diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-22 21:55:17 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-22 21:55:17 +0200 |
| commit | 4e251d553a84ad1958c3ce9d702ba93ad9a07726 (patch) | |
| tree | 2ea23cf2243cb19d4abee9dfe46d541c68d3e79f | |
| parent | 1cb57adccb3743a73365bb80c1e348136fe31ff6 (diff) | |
Implement taskwarrior quicklogger and random_quote in fish
- Add _taskwarrior::add_task helper: parses --due, --project, --tag
flags and calls `task add`, printing the command verbosely before
executing it
- Implement taskwarrior::quicklogger: scans ql-* files in known notes
dirs, parses each line ([NUMBER] TAG[,...] description), creates tasks,
then chmod 600 + moves processed files to /tmp
- Implement taskwarrior::random_quote: fills +random task slots from
random *.md bullet entries; tags each task with +random and the source
filename; 10% chance to also add +work
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| -rw-r--r-- | fish/conf.d/taskwarrior.fish | 194 |
1 files changed, 160 insertions, 34 deletions
diff --git a/fish/conf.d/taskwarrior.fish b/fish/conf.d/taskwarrior.fish index 881a56a..1449dbe 100644 --- a/fish/conf.d/taskwarrior.fish +++ b/fish/conf.d/taskwarrior.fish @@ -52,39 +52,6 @@ function taskwarrior::normalize_tags printf '%s\n' $normalized end -function taskwarrior::unscheduled - echo "Scheduling unscheduled tasks (not yet implemented)" - # taskwarrior::ids "+track due:" eow - # for id in (task status:pending -unsched -nosched -meeting -track due: rc.verbose:nothing export | jq -r '.[] | (.id // 0) | select(. > 0)') - # timeout 5s task modify "$id" due:(builtin random 0 $TASKWARRIOR_PERSONAL_TIMESPAN_D)d - # end -end - -function taskwarrior::quicklogger - set -l notes_dirs "$HOME/Notes,$HOME/Notes/Quicklogger,$WORKTIME_DIR" - echo taskwarrior::quicklogger not yet implemented -end - -function taskwarrior::random_quote - set -l random_dir "$HOME/Notes/random" - echo taskwarrior::random_quote not yet implemented - - # set -l count (taskwarrior::random_slots_left) - # if test -d "$random_dir" - # for md_file in (find "$random_dir" -name '*.md' | sort -R) - # if test $count -le 0 - # break - # end - # if test (builtin random 0 1) -eq 0 - # continue - # end - - # taskwarrior::random_quote "$md_file" taskwarrior::task_add - # set count (math "$count - 1") - # end - # end -end - function taskwarrior::export::bd if test -d ~/Notes/Bulgarian # Export bulgarian dumi @@ -150,13 +117,172 @@ function taskwarrior::cleanup yes | task +agent status:completed delete end +function taskwarrior::unscheduled + for id in (task status:pending -unsched -nosched -meeting -track due: _ids) + echo imeout 5s task modify "$id" due:(builtin random 0 30)d + end +end + +# Adds a single taskwarrior task. Can be reused anywhere task creation is needed. +# Description is the required positional argument. +# Optional flags: +# --due N due in N days (passed as due:Nd to task) +# --project NAME assign a project +# --tag TAG add a tag; repeat for multiple tags +function _taskwarrior::add_task + argparse 'due=' 'project=' 'tag=+' -- $argv + or return 1 + + # Remaining positional arguments form the description (required) + set -l description (string join ' ' -- $argv) + test -n "$description"; or return 1 + + # Build argument list for `task add`, only including flags that were provided + set -l cmd_args + test -n "$_flag_due"; and set -a cmd_args "due:$_flag_due"d + test -n "$_flag_project"; and set -a cmd_args "project:$_flag_project" + for tag in $_flag_tag + set -a cmd_args "+$tag" + end + + # Print the full command before executing it for transparency; + # description is escaped so the output is unambiguous even with quotes or special chars + echo "task add $cmd_args "(string escape -- $description) + task add $cmd_args $description +end + +# Scans all known notes directories for quick-log files (ql-*) and parses each +# line into its constituent task fields. Each line follows the format: +# [NUMBER] TAG[,TAG,...] description +# where NUMBER is an optional due offset in days, TAG is either a +# comma-separated list of lowercase tags or a capitalized project name, and +# everything after TAG is the task description. +function taskwarrior::quicklogger + # Directories to scan for quick-log files (ql-*) + set -l notes_dirs "$HOME/Notes" "$HOME/Notes/Quicklogger" "$WORKTIME_DIR" + + for dir in $notes_dirs + # Skip directories that don't exist on this machine + if not test -d "$dir" + continue + end + + # -L follows symlinks (~/Notes is a symlink to Syncthing vault) + # -maxdepth 1 keeps the search non-recursive + for ql_file in (find -L "$dir" -maxdepth 1 -name 'ql-*' -type f) + while read -l line + # Skip blank lines + test -n "$line"; or continue + + # Tokenise by spaces; idx tracks the current parse position + set -l tokens (string split ' ' -- "$line") + set -l idx 1 + + # Optional first token: a plain integer means due in N days + set -l due "" + if string match -qr '^\d+$' -- "$tokens[1]" + set due "$tokens[1]" + set idx 2 + end + + # Next token is the tag/project field; advance idx past it + set -l tag_field "$tokens[$idx]" + set idx (math "$idx + 1") + + # Split the tag field on commas first, then inspect the first element. + # A capital first letter on the first element signals a project name; + # any remaining comma-separated elements become plain tags. + # e.g. "Foo,bar,baz" → project=foo, tags=(bar baz) + # e.g. "bar,baz" → project="", tags=(bar baz) + set -l tag_parts (string split ',' -- "$tag_field") + set -l project "" + set -l tags + if string match -qr '^[A-Z]' -- "$tag_parts[1]" + set project (string lower -- "$tag_parts[1]") + # Remaining parts (if any) are plain tags, lowercased for consistency + test (count $tag_parts) -gt 1; and set tags (string lower -- $tag_parts[2..-1]) + else + set tags (string lower -- $tag_parts) + end + + # Everything from idx onward is the free-text description + set -l description "" + if test $idx -le (count $tokens) + set description (string join ' ' -- $tokens[$idx..-1]) + end + + # Build flag args for the helper, omitting empty optional fields + set -l add_args + test -n "$due"; and set -a add_args --due $due + test -n "$project"; and set -a add_args --project $project + for tag in $tags + set -a add_args --tag $tag + end + _taskwarrior::add_task $add_args $description + end <$ql_file + # Restrict permissions before moving so the file is not world-readable in /tmp + chmod 600 $ql_file + mv $ql_file /tmp/ + end + end +end + +# Fills available +random task slots by picking random bullet-point entries from +# random .md files in the notes/random directory. Each slot gets one entry chosen +# by selecting a random file and then a random "* "-prefixed line within it. +function taskwarrior::random_quote + set -l random_dir "$HOME/Notes/random" + + # Nothing to do if the random notes directory doesn't exist on this machine + if not test -d "$random_dir" + return + end + + # Check how many pending +random task slots are still open + set -l slots (taskwarrior::random_slots_left) + if test $slots -le 0 + return + end + + # Collect .md files, skipping Syncthing conflict copies which are not canonical + set -l md_files (find "$random_dir" -name '*.md' -not -name '*.sync-conflict*') + if test (count $md_files) -eq 0 + return + end + + # Fill each open slot with one randomly selected task + for i in (seq $slots) + # Pick a random .md file from the pool + set -l file $md_files[(builtin random 1 (count $md_files))] + + # Derive a tag from the filename: strip path and extension, lowercase + # e.g. /home/paul/Notes/random/Focus.md → focus + set -l file_tag (string lower -- (string replace -r '\.md$' '' (basename $file))) + + # Extract all bullet entries (lines starting with "* ") and strip the marker + set -l entries (grep '^\* ' $file | string replace -r '^\* ' '') + if test (count $entries) -eq 0 + continue + end + + # Pick one entry at random and tag it with both +random and the source file tag + set -l entry $entries[(builtin random 1 (count $entries))] + + # 10% chance to also tag the task as +work (1-in-10 roll) + set -l extra_tags + test (builtin random 1 10) -eq 1; and set extra_tags --tag work + + _taskwarrior::add_task --tag random --tag $file_tag $extra_tags $entry + end +end + function taskwarrior::invoke taskwarrior::export taskwarrior::import taskwarrior::cleanup - taskwarrior::quicklogger taskwarrior::random_quote taskwarrior::unscheduled + taskwarrior::quicklogger end abbr -a t task |
