summaryrefslogtreecommitdiff
path: root/mac/.local/bin/task
diff options
context:
space:
mode:
Diffstat (limited to 'mac/.local/bin/task')
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/annotate-with-new-note41
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/annotate-with-note41
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/cycle-priority35
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/cycle-tmux-projects31
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/decrease-priority15
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/git-issue-sync185
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/increase-priority60
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/lib-task-interop82
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/task-switch-context23
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/taskopen-annotation11
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/taskopen-line51
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/tasks-sync11
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/toggle-review-label13
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