diff options
Diffstat (limited to 'mac/.local/bin/task')
13 files changed, 599 insertions, 0 deletions
diff --git a/mac/.local/bin/task/taskwarrior-tui/annotate-with-new-note b/mac/.local/bin/task/taskwarrior-tui/annotate-with-new-note new file mode 100755 index 0000000..3c67d24 --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/annotate-with-new-note @@ -0,0 +1,41 @@ +#!/bin/sh + +# UUID of the task to annotate +uuid="$*" + +# Directory where notes are stored +notes_dir="$HOME/Private/repos/Obsidian/SI/Notes" +templates_dir="$HOME/Private/repos/Obsidian/SI/Resources/Templates" + +# Prompt for the new note name +printf "Enter the name for the new note: " +read -r new_note_name + +copy_note="$templates_dir/projects.md" +filepath="$notes_dir/$new_note_name.md" + +# Check if a file with this name already exists +if [ -f "$filepath" ]; then + echo "File with this name already exists. Annotating the task with the existing note." +else + nvim -n -c "ObsidianNew $new_note_name" --headless >/dev/null 2>&1 & + if [ -f "$copy_note" ]; then + cp "$copy_note" "$filepath" + echo "New note created and opened in Neovim." + fi +fi + +# Annotate the task with the filepath +task_output=$(task rc.bulk=0 rc.confirmation=off "$uuid" annotate "$filepath") + +# Check if annotation was successful +case "$task_output" in +*"Annotated"*) + echo "Successfully annotated the task with the note." + ;; +*) + echo "Failed to annotate the task." + ;; +esac + +${EDITOR:-nvim} "$filepath" diff --git a/mac/.local/bin/task/taskwarrior-tui/annotate-with-note b/mac/.local/bin/task/taskwarrior-tui/annotate-with-note new file mode 100755 index 0000000..c744314 --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/annotate-with-note @@ -0,0 +1,41 @@ +#!/bin/sh + +# UUID of the task to annotate +uuid="$*" + +# Base directory where notes are stored +notes_dir="$HOME/Private/repos/Obsidian/SI/Notes" + +# List of subdirectories to search in +subdirs="areas meetings projects resources reviews" + +# Construct the find command with the subdirectories +search_paths="" +for subdir in $subdirs; do + search_paths="$search_paths $notes_dir/$subdir" +done + +# Find files in the specified subdirectories and show fzf dialog to select an existing note +filepath=$(find "$search_paths" -type f -name '*.md' | fzf --preview "bat --color=always {}") + +# If fzf was cancelled, exit the script +if [ -z "$filepath" ]; then + echo "No file selected. Exiting." + exit 1 +fi + +# Annotate the task with the selected filepath +task_output=$(task rc.bulk=0 rc.confirmation=off "$uuid" annotate "$filepath") + +# Check if annotation was successful +case "$task_output" in +*"Annotated"*) + echo "Successfully annotated the task with the note." + ;; +*) + echo "Failed to annotate the task." + ;; +esac + +# Open the selected note in nvim +${EDITOR:-nvim} "$filepath" diff --git a/mac/.local/bin/task/taskwarrior-tui/cycle-priority b/mac/.local/bin/task/taskwarrior-tui/cycle-priority new file mode 100755 index 0000000..43bc91e --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/cycle-priority @@ -0,0 +1,35 @@ +#!/bin/sh + +uuid="$1" + +# Get the current priority of the task +current_priority=$(task _get $uuid.priority) + +# Cycle through the priorities: H -> M -> L -> (unset) -> H +case "$current_priority" in +H) + new_priority="M" + echo "Switching priority from 'H' to 'M'" + ;; +M) + new_priority="L" + echo "Switching priority from 'M' to 'L'" + ;; +L) + new_priority="" + echo "Switching priority from 'L' to (no priority)" + ;; +"") + new_priority="H" + echo "Switching priority from (no priority) to 'H'" + ;; +esac + +# Update the task with the new priority value, or remove priority if it's an empty string +if [ -n "$new_priority" ]; then + echo "Updating task $uuid with new priority: $new_priority" + task rc.bulk=0 rc.confirmation=off "$uuid" modify priority="$new_priority" +else + echo "Removing priority from task $uuid" + task rc.bulk=0 rc.confirmation=off "$uuid" modify priority: +fi diff --git a/mac/.local/bin/task/taskwarrior-tui/cycle-tmux-projects b/mac/.local/bin/task/taskwarrior-tui/cycle-tmux-projects new file mode 100755 index 0000000..360dc20 --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/cycle-tmux-projects @@ -0,0 +1,31 @@ +#!/bin/sh + +# Get the UUID of the selected task from the arguments +uuid="$*" + +# Fetch the project of the task using Taskwarrior and jq +project=$(task "$uuid" export | jq -r '.[0].project') + +# Check if the project is empty +if [ -z "$project" ] || [ "$project" = "null" ]; then + echo "No project found for the selected task." + # Optionally, reset the filter in taskwarrior-tui + tmux send-keys "/" Escape + tmux send-keys Escape + tmux send-keys "/" + tmux send-keys Enter + exit 0 +fi + +# Escape any special characters in the project name for use in tmux command +escaped_project=$(printf '%s' "$project" | sed 's/[][\.*^$(){}+?|]/\\&/g') + +# Send keys via tmux to apply the filter in taskwarrior-tui +tmux send-keys "/" +tmux send-keys Escape +tmux send-keys Escape +tmux send-keys "/" + +# Use exact match in the filter to avoid partial matches +tmux send-keys "project.is:$escaped_project" +tmux send-keys Enter diff --git a/mac/.local/bin/task/taskwarrior-tui/decrease-priority b/mac/.local/bin/task/taskwarrior-tui/decrease-priority new file mode 100755 index 0000000..b2b0508 --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/decrease-priority @@ -0,0 +1,15 @@ +#!/bin/sh + +uuid="$1" +current_priority=$(task _get "$uuid".manual_priority) + +# Check if manual_priority is set; if not, initialize to 0 +if [ -z "$current_priority" ]; then + current_priority=0 +fi + +# Decrement the priority using expr +new_priority=$((current_priority - 1)) + +# Update the task with the new manual_priority value +task rc.bulk=0 rc.confirmation=off "$uuid" modify manual_priority="$new_priority" diff --git a/mac/.local/bin/task/taskwarrior-tui/git-issue-sync b/mac/.local/bin/task/taskwarrior-tui/git-issue-sync new file mode 100755 index 0000000..da01bc4 --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/git-issue-sync @@ -0,0 +1,185 @@ +#!/bin/sh + +# github_issue_sync.sh +# Synchronizes GitHub and Linear issues with Taskwarrior + +# Set new line and tab for word splitting +IFS=' + ' + +# Logger with timestamp +log() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" +} + +# Function to trim leading and trailing whitespace using sed +trim_whitespace() { + input="$1" + echo "$input" | sed 's/^[ \t]*//;s/[ \t]*$//' +} + +# Validate necessary environment variables +validate_env_vars() { + oldIFS="$IFS" + IFS=' ' + required_vars="api userid" + for var in $required_vars; do + val=$(pass show "api/linear/$var") + if [ -z "$val" ]; then + echo "Error: Environment variable $var is not set." >&2 + exit 1 + fi + done + IFS="$oldIFS" +} + +# Retrieve and format GitHub issues +get_github_issues() { + issues=$(gh api -X GET /search/issues \ + -f q='is:issue is:open assignee:TheSiahxyz' \ + --jq '.items[] | {id: .number, description: .title, repository: .repository_url, html_url: .html_url}') || { + echo "Error: Unable to fetch GitHub issues" >&2 + return 1 + } + echo "$issues" +} + +# Retrieve and format Linear issues +get_linear_issues() { + issues=$(curl -s -X POST \ + -H "Content-Type: application/json" \ + -H "Authorization: $LINEAR_API_KEY" \ + --data '{"query": "query { user(id: \"'"$LINEAR_USER_ID"'\") { id name assignedIssues(filter: { state: { name: { nin: [\"Released\", \"Canceled\"] } } }) { nodes { id title url } } } }"}' \ + https://api.linear.app/graphql | jq -c '.data.user.assignedIssues.nodes[] | {id: .id, description: .title, repository: "linear", html_url: .url}') || { + echo "Error: Unable to fetch Linear issues" >&2 + return 1 + } + echo "$issues" +} + +# Synchronize a single issue with Taskwarrior +sync_to_taskwarrior() { + issue_line="$1" + issue_id=$(echo "$issue_line" | jq -r '.id') + issue_description=$(echo "$issue_line" | jq -r '.description') + issue_repo_name=$(echo "$issue_line" | jq -r '.repository' | awk -F/ '{print $NF}') + issue_url=$(echo "$issue_line" | jq -r '.html_url') + + log "Processing Issue ID: $issue_id, Description: $issue_description" + + task_uuid=$(get_task_id_by_description "$issue_description") + + if [ -z "$task_uuid" ]; then + log "Creating new task for issue: $issue_description" + task_uuid=$(create_task "$issue_description" "+$issue_repo_name" "project:$issue_repo_name") + + if [ -n "$task_uuid" ]; then + annotate_task "$task_uuid" "$issue_url" + log "Task created and annotated for: $issue_description" + else + log "Error: Failed to create task for: $issue_description" >&2 + fi + else + log "Task already exists for: $issue_description (UUID: $task_uuid)" + fi +} + +# Mark a GitHub issue as completed in Taskwarrior +sync_github_issue() { + task_description="$1" + task_uuid=$(get_task_id_by_description "$task_description") + + if [ -n "$task_uuid" ]; then + mark_task_completed "$task_uuid" + log "Task marked as completed: $task_description (UUID: $task_uuid)" + else + log "Task UUID not found for: $task_description" >&2 + fi +} + +# Compare existing Taskwarrior tasks with current issues and mark as completed if not present +compare_and_display_tasks_not_in_issues() { + existing_task_descriptions="$1" + issues_descriptions="$2" + + log "Starting comparison of Taskwarrior tasks and current issues." + + existing_task_descriptions_array=$(echo "$existing_task_descriptions" | tr '\n' ' ') + issues_descriptions_array=$(echo "$issues_descriptions" | tr '\n' ' ') + + for task_description in $existing_task_descriptions_array; do + trimmed_task_description=$(trim_whitespace "$task_description") + issue_exists=false + + for issue_description in $issues_descriptions_array; do + trimmed_issue_description=$(trim_whitespace "$issue_description") + if [ "$(echo "$trimmed_task_description" | tr '[:upper:]' '[:lower:]')" = "$(echo "$trimmed_issue_description" | tr '[:upper:]' '[:lower:]')" ]; then + issue_exists=true + break + fi + done + + if [ "$issue_exists" = false ]; then + log "No matching issue found for task: $trimmed_task_description. Marking as completed." + sync_github_issue "$trimmed_task_description" + fi + done + + log "Comparison of Taskwarrior tasks and issues completed." +} + +# Retrieve existing Taskwarrior task descriptions with +github or +linear tags and pending status +get_existing_task_descriptions() { + task +github or +linear status:pending export | jq -r '.[] | .description' +} + +# Log retrieved issues +log_issues() { + issue_type="$1" + issues="$2" + log "Retrieved $issue_type issues: $(echo "$issues" | jq '.')" +} + +# Synchronize all issues to Taskwarrior +sync_issues_to_taskwarrior() { + issues="$1" + echo "$issues" | jq -c '.' | while IFS= read -r line; do + sync_to_taskwarrior "$line" + done +} + +# Main function to orchestrate the synchronization +main() { + if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Not a git repository." + exit 1 + fi + validate_env_vars + + github_issues=$(get_github_issues) + linear_issues=$(get_linear_issues) + + if [ -z "$github_issues" ] && [ -z "$linear_issues" ]; then + log "No issues retrieved from GitHub or Linear. Exiting." + exit 0 + fi + + if [ -n "$github_issues" ]; then + log_issues "GitHub" "$github_issues" + sync_issues_to_taskwarrior "$github_issues" + fi + + if [ -n "$linear_issues" ]; then + log_issues "Linear" "$linear_issues" + sync_issues_to_taskwarrior "$linear_issues" + fi + + existing_task_descriptions=$(get_existing_task_descriptions) + + compare_and_display_tasks_not_in_issues "$existing_task_descriptions" "$( + echo "$github_issues" | jq -r '.description' + echo "$linear_issues" | jq -r '.description' + )" +} + +main diff --git a/mac/.local/bin/task/taskwarrior-tui/increase-priority b/mac/.local/bin/task/taskwarrior-tui/increase-priority new file mode 100755 index 0000000..26a3d53 --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/increase-priority @@ -0,0 +1,60 @@ +#!/bin/sh + +# The set -e option instructs the shell to immediately exit if any command has a non-zero exit status +# The set -u option treats unset variables (except for $* and $@) as an error +set -eu + +echo "Increase duration of task $*" + +# Fetch the duration value using taskwarrior and jq +dur=$(task "$@" export | jq -r '.[0].duration') + +# Check if the duration has hours +case "$dur" in +*H*) + # Extract both hour and minute parts + hour=$(echo "$dur" | awk -F 'T' '{split($2, a, "H"); print a[1]}') + minute=$(echo "$dur" | awk -F 'H' '{split($2, a, "M"); print a[1]}') + + # Add 30 to the minutes + new_minute=$((minute + 30)) + + # Check if minutes are 60 or more + if [ "$new_minute" -ge 60 ]; then + # Add 1 to the hour + new_hour=$((hour + 1)) + + # Calculate new minutes + new_minute=$((new_minute - 60)) + else + # Keep the original hour + new_hour=$hour + fi + + # Create the new duration string + new_dur="PT${new_hour}H${new_minute}M" + ;; +*) + # If duration is already 30M + if [ "$dur" = "PT30M" ]; then + # Make it 1H + new_dur="PT1H" + else + # Extract the minute part from the duration string + minute=$(echo "$dur" | awk -F 'T' '{split($2, a, "M"); print a[1]}') + + # Add 30 to the minutes + new_minute=$((minute + 30)) + + # Create the new duration string + new_dur="PT${new_minute}M" + fi + ;; +esac + +# Print the new duration +echo "Original duration: $dur" +echo "New duration: $new_dur" + +# Modify the task duration using taskwarrior +task rc.bulk=0 rc.confirmation=off rc.dependency.confirmation=off rc.recurrence.confirmation=off "$@" modify duration="$new_dur" diff --git a/mac/.local/bin/task/taskwarrior-tui/lib-task-interop b/mac/.local/bin/task/taskwarrior-tui/lib-task-interop new file mode 100755 index 0000000..04e60ac --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/lib-task-interop @@ -0,0 +1,82 @@ +#!/bin/sh + +# Interop functions for Taskwarrior + +# Create a new task in Taskwarrior with a given description and optional additional attributes. +# Properly handle special characters in the description and other arguments. +create_task() { + description="$1" + shift # Now $@ contains the rest of the arguments + + # Use a variable to hold arguments to prevent word splitting + task_args="" + for arg in "$@"; do + task_args="$task_args $arg" + done + + # Use -- to indicate the end of options, and pass the description safely + output=$(task add $task_args -- "$description") + + # Extract the UUID from the output using a reliable method + task_uuid=$(echo "$output" | grep -o 'Created task [a-z0-9\-]*' | awk '{print $3}') + + echo "$task_uuid" +} + +# Get task UUID from description with specific tags (+github or +linear) +get_task_id_by_description() { + description="$1" + # Use task export with tags +github or +linear and status:pending to find the task by description + task +github or +linear status:pending export | jq -r --arg desc "$description" '.[] | select(.description == $desc) | .uuid' +} + +# Annotate an existing task +annotate_task() { + task_uuid="$1" + annotation="$2" + task "$task_uuid" annotate -- "$annotation" +} + +# Mark a task as completed +mark_task_completed() { + task_uuid="$1" + echo "Attempting to mark task $task_uuid as completed..." + task "$task_uuid" done || { + echo "Failed to mark task $task_uuid as completed" >&2 + exit 1 + } +} + +# Mark a task as pending +mark_task_pending() { + task_uuid="$1" + task "$task_uuid" modify status:pending +} + +# Get task labels (tags) +get_task_labels() { + task_uuid="$1" + echo "Getting labels for task $task_uuid" + task _get "$task_uuid".tags +} + +# Add a label (tag) to a task +add_task_label() { + task_uuid="$1" + label="$2" + task "$task_uuid" modify +"$label" +} + +# Remove a label (tag) from a task +remove_task_label() { + task_uuid="$1" + label="$2" + task "$task_uuid" modify -"$label" +} + +# Set or change a task's project +change_task_project() { + task_uuid="$1" + project_name="$2" + task "$task_uuid" modify project:"$project_name" +} diff --git a/mac/.local/bin/task/taskwarrior-tui/task-switch-context b/mac/.local/bin/task/taskwarrior-tui/task-switch-context new file mode 100755 index 0000000..0b40141 --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/task-switch-context @@ -0,0 +1,23 @@ +#!/bin/sh + +switch_context() { + task context "$1" + echo "Switched to $1 context." +} + +# Get the current task context +current_context=$(task _get rc.context) + +# Check if the current context is "work" +if [ "$current_context" = "work" ]; then + switch_context home + # Set the marker file to indicate we should switch back to work next time +elif [ "$current_context" = "home" ]; then + # If we're in home and the marker file exists, it means we want to switch back to work + switch_context work + # Remove the marker file after switching back to avoid repeated switches +elif [ -z "$current_context" ]; then + switch_context home +else + echo "Current context is: $current_context. No action taken." +fi diff --git a/mac/.local/bin/task/taskwarrior-tui/taskopen-annotation b/mac/.local/bin/task/taskwarrior-tui/taskopen-annotation new file mode 100755 index 0000000..3b292e8 --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/taskopen-annotation @@ -0,0 +1,11 @@ +#!/bin/sh + +set -eu + +if [ -z "$2" ]; then + echo "Second argument is unset" >>/tmp/taskopen_debug.log +else + echo "Second argument is set to '$2'" >>/tmp/taskopen_debug.log +fi + +taskopen "$1" diff --git a/mac/.local/bin/task/taskwarrior-tui/taskopen-line b/mac/.local/bin/task/taskwarrior-tui/taskopen-line new file mode 100755 index 0000000..379a2af --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/taskopen-line @@ -0,0 +1,51 @@ +#!/bin/sh + +# Capture the current session name +current_session=$(tmux display-message -p '#S') + +# Sleep for a bit to allow tui to load +sleep 0.1 + +# Extract the line number and file path from the input string +line_number=$(echo "$1" | awk -F ':' '{print $2}') +file_path=$(echo "$1" | awk -F ':' '{print $3}') + +# Resolve the file path if it's a symlink +if [ -L "$file_path" ]; then + file_path=$(readlink -f "$file_path") +fi + +# Use all arguments beyond the first one as the task_description +shift +task_description="$*" + +# If a task description is provided, search for the line number containing that description +if [ -n "$task_description" ]; then + new_line_number=$(grep -n -F "$task_description" "$file_path" | awk -F ':' '{print $1}' | head -n 1) + if [ -n "$new_line_number" ]; then + line_number=$new_line_number + fi +fi + +# Capture the file name from the file path without the extension +file_name=$(basename "$file_path" | awk -F '.' '{print $1}') +dir_name=$(dirname "$file_path") + +# Check if directory exists +if [ ! -d "$dir_name" ]; then + exit 1 +fi + +# Create a new tmux session which opens the file with neovim at the specific line number +cd "$dir_name" && tmux new-session -d -s "$file_name" "nvim +$line_number $file_path" + +# Attach to the new session +tmux switch-client -t "$file_name" + +# Wait for the session to be closed, either by the user or some other way +while tmux has-session -t "$file_name" 2>/dev/null; do + sleep 1 +done + +# Switch back to the original session +tmux switch-client -t "$current_session" diff --git a/mac/.local/bin/task/taskwarrior-tui/tasks-sync b/mac/.local/bin/task/taskwarrior-tui/tasks-sync new file mode 100755 index 0000000..06334c4 --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/tasks-sync @@ -0,0 +1,11 @@ +#!/bin/sh + +if [ "$(task _get tw.syncneeded)" -eq 1 ]; then + if ! task sync 2>/dev/null; then + task config sync.encryption_secret "$(pass show server/task_secret)" + task config sync.gcp.bucket "task.thesiah.xyz" + task config sync.gcp.credential_path "${XDG_DATA_HOME:-${HOME}/.local/share}/task/task-sync-server.json" + + task sync + fi +fi diff --git a/mac/.local/bin/task/taskwarrior-tui/toggle-review-label b/mac/.local/bin/task/taskwarrior-tui/toggle-review-label new file mode 100755 index 0000000..9133c30 --- /dev/null +++ b/mac/.local/bin/task/taskwarrior-tui/toggle-review-label @@ -0,0 +1,13 @@ +#!/bin/sh + +uuid="$1" +# Use task _tags command to get current tags +current_tags=$(task _tags "$uuid") + +if echo "$current_tags" | grep -q "review"; then + # Remove review tag if present + task rc.bulk=0 rc.confirmation=off "$uuid" modify -review manual_priority: -next +else + # Add review tag if not present + task rc.bulk=0 rc.confirmation=off "$uuid" modify +review +fi |
