summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-22 21:55:17 +0200
committerPaul Buetow <paul@buetow.org>2026-02-22 21:55:17 +0200
commit4e251d553a84ad1958c3ce9d702ba93ad9a07726 (patch)
tree2ea23cf2243cb19d4abee9dfe46d541c68d3e79f
parent1cb57adccb3743a73365bb80c1e348136fe31ff6 (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.fish194
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