diff --git a/plugins/taskman/README.md b/plugins/taskman/README.md new file mode 100644 index 000000000..b7f6524b7 --- /dev/null +++ b/plugins/taskman/README.md @@ -0,0 +1,286 @@ +# taskman + +A powerful terminal task manager plugin for Oh-My-Zsh. Manage your daily tasks without leaving the command line! + +![Task Manager Demo](https://via.placeholder.com/600x400/1e1e1e/00ff00?text=Terminal+Task+Manager+Demo) + +## Features + +- ๐Ÿ“ **Dual Interface**: Both interactive TUI and CLI operations +- โŒจ๏ธ **Vim-like Keybindings**: Navigate with `j`/`k`, `Space` to toggle +- ๐ŸŽฏ **Priority System**: High, normal, and low priority with color coding +- ๐Ÿ’พ **Persistent Storage**: Tasks saved in `~/.taskman/tasks.json` +- ๐ŸŽจ **Rich Colors**: Visual indicators for task status and priority +- โšก **Zero Config**: Works immediately after installation +- ๐Ÿ”ง **Shell Integration**: Aliases, completion, and sidebar workflow + +## Installation + +1. Add `taskman` to your plugins list in `~/.zshrc`: + + ```bash + plugins=(git taskman) + ``` + +2. Reload your shell: + + ```bash + source ~/.zshrc + ``` + +3. Start using! + + ```bash + tasks add "My first task" + ``` + +## Requirements + +- **Python 3.6+** (for interactive UI and CLI operations) +- **Terminal with color support** (most modern terminals) + +## Usage + +### Interactive UI + +Launch the full-screen task manager: + +```bash +tasks # Launch interactive UI +tasks ui # Same as above +task-sidebar # Launch with sidebar usage tips +``` + +#### Keyboard Shortcuts + +| Key | Action | +|-----|--------| +| `โ†‘`/`k` | Move up | +| `โ†“`/`j` | Move down | +| `n` | Create new task | +| `Space` | Toggle task completion | +| `d` | Delete selected task | +| `Tab` | Cycle priority when creating tasks | +| `h` | Toggle help panel | +| `q` | Quit | + +### Command Line Interface + +#### Adding Tasks + +```bash +tasks add "Fix login bug" # Normal priority +tasks add "Deploy to production" high # High priority +tasks add "Update documentation" low # Low priority +``` + +#### Listing Tasks + +```bash +tasks list # All tasks +tasks list pending # Only pending tasks +tasks list completed # Only completed tasks +tasks ls # Short alias +``` + +#### Managing Tasks + +```bash +tasks done 3 # Mark task ID 3 as completed +tasks delete 5 # Delete task ID 5 +tasks help # Show help +``` + +### Aliases + +The plugin provides convenient aliases: + +```bash +tm add "Buy groceries" # Same as 'tasks add' +task list # Same as 'tasks list' +todo done 1 # Same as 'tasks done 1' +``` + +## Priority Levels + +Tasks support three priority levels with color coding: + +- **High Priority** (`!`) - ๐Ÿ”ด Red, for urgent tasks +- **Normal Priority** (`-`) - ๐ŸŸก Yellow, default priority +- **Low Priority** (`ยท`) - ๐Ÿ”ต Cyan, for less urgent tasks + +## Task Display + +Tasks are displayed with visual indicators: + +``` + โœ“ [!] Completed high priority task (green) + โ—‹ [-] Pending normal priority task (yellow) + โ—‹ [ยท] Pending low priority task (cyan) +``` + +## Sidebar Workflow + +Perfect for split-terminal development workflow: + +1. **Split your terminal** horizontally or vertically +2. **Run `tasks ui`** in one pane for persistent task view +3. **Work in the other pane** while keeping tasks visible +4. **Quick updates** with keyboard shortcuts + +## Auto-completion + +The plugin provides intelligent tab completion: + +```bash +tasks # Shows: add, list, done, delete, etc. +tasks add "task" # Shows: high, normal, low +tasks done # Shows available task IDs +``` + +## Configuration + +### Optional Startup Summary + +To show task summary when opening terminal, uncomment this line in the plugin: + +```bash +# In ~/.oh-my-zsh/plugins/taskman/taskman.plugin.zsh +_taskman_startup_summary # Uncomment this line +``` + +This shows: +``` +๐Ÿ“‹ Task Summary: 3 pending, 2 completed + Type 'tasks' to manage your tasks +``` + +## Data Storage + +Tasks are stored in `~/.taskman/tasks.json`: + +```json +{ + "tasks": [ + { + "id": 1, + "text": "Fix login bug", + "completed": false, + "priority": "high", + "created_at": "2024-01-15T10:30:00" + } + ], + "next_id": 2 +} +``` + +## Examples + +### Daily Developer Workflow + +```bash +# Morning planning +tasks add "Review PR #123" high +tasks add "Fix login bug" high +tasks add "Update docs" low + +# Check current tasks +tasks list pending + +# Work in interactive mode (split terminal) +tasks ui + +# Quick CLI updates +tm done 1 +tm add "Deploy hotfix" high + +# End of day review +tasks list completed +``` + +### Project Management + +```bash +# Sprint planning +tasks add "Implement user auth" high +tasks add "Add unit tests" normal +tasks add "Update README" low + +# Track progress +tasks list + +# Mark completed +tasks done 1 +tasks done 2 + +# Cleanup +tasks delete 3 # Remove completed/outdated tasks +``` + +## Comparison with Alternatives + +| Feature | taskman | taskwarrior | todo.txt | Todoist | +|---------|---------|-------------|----------|----------| +| Interactive TUI | โœ… | โŒ | โŒ | โŒ | +| CLI Interface | โœ… | โœ… | โœ… | โœ… | +| Zero Setup | โœ… | โŒ | โœ… | โŒ | +| No External Deps | โœ… | โŒ | โœ… | โŒ | +| Rich Visual UI | โœ… | โŒ | โŒ | โŒ | +| Vim Keybindings | โœ… | โŒ | โŒ | โŒ | +| Local Data | โœ… | โœ… | โœ… | โŒ | + +## Troubleshooting + +### Python Not Found + +```bash +# Install Python 3 (macOS) +brew install python3 + +# Install Python 3 (Ubuntu/Debian) +sudo apt-get install python3 + +# Verify installation +python3 --version +``` + +### Plugin Not Loading + +1. Check that `taskman` is in your plugins list: + ```bash + echo $plugins + ``` + +2. Reload your shell: + ```bash + source ~/.zshrc + ``` + +3. Test plugin function: + ```bash + tasks help + ``` + +### File Permissions + +```bash +# Make Python files executable +chmod +x ~/.oh-my-zsh/plugins/taskman/bin/*.py +``` + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +## License + +MIT License - see the [Oh-My-Zsh license](https://github.com/ohmyzsh/ohmyzsh/blob/master/LICENSE.txt) for details. + +## Author + +Created by [@oiahoon](https://github.com/oiahoon) + +--- + +**Happy task managing! ๐Ÿš€** + diff --git a/plugins/taskman/_taskman b/plugins/taskman/_taskman new file mode 100644 index 000000000..8f9dbf964 --- /dev/null +++ b/plugins/taskman/_taskman @@ -0,0 +1,69 @@ +#compdef tasks tm task todo + +# Terminal Task Manager completion for Oh-My-Zsh +# Provides tab completion for all task management commands + +_taskman() { + local context state state_descr line + typeset -A opt_args + + _arguments -C \ + '1:command:->commands' \ + '*::arg:->args' && return 0 + + case $state in + commands) + local -a commands + commands=( + 'ui:Launch interactive UI' + 'show:Launch interactive UI' + 'add:Add new task' + 'new:Add new task' + 'create:Add new task' + 'list:List tasks' + 'ls:List tasks' + 'done:Mark task as completed' + 'complete:Mark task as completed' + 'delete:Delete a task' + 'del:Delete a task' + 'rm:Delete a task' + 'help:Show help' + ) + _describe 'taskman commands' commands + ;; + args) + case $line[1] in + add|new|create) + _arguments \ + '1:task description:' \ + '2:priority:(high normal low)' + ;; + list|ls) + _arguments \ + '1:filter:(all pending completed)' + ;; + done|complete|delete|del|rm) + # Complete with task IDs if possible + if command -v python3 >/dev/null 2>&1; then + local plugin_dir="${0:h}" + local -a task_ids + + # Try to get task IDs from the CLI + task_ids=($(python3 "$plugin_dir/bin/task_cli.py" list all 2>/dev/null | grep -o '(ID: [0-9]\+)' | grep -o '[0-9]\+' 2>/dev/null)) + + if [[ ${#task_ids[@]} -gt 0 ]]; then + _describe 'task IDs' task_ids + else + _message 'task ID' + fi + else + _message 'task ID' + fi + ;; + esac + ;; + esac +} + +_taskman + diff --git a/plugins/taskman/bin/task_cli.py b/plugins/taskman/bin/task_cli.py new file mode 100755 index 000000000..383fb3581 --- /dev/null +++ b/plugins/taskman/bin/task_cli.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +""" +Terminal Task Manager CLI - Command line interface for task operations +""" + +import sys +import os +import json +from datetime import datetime +from typing import List, Dict, Optional + +# Import the Task and TaskManager classes from task_manager.py +sys.path.append(os.path.dirname(os.path.abspath(__file__))) +from task_manager import Task, TaskManager + +class TaskCLI: + def __init__(self): + self.task_manager = TaskManager() + + def add_task(self, text: str, priority: str = "normal"): + """Add a new task via CLI""" + task = self.task_manager.add_task(text, priority) + return task + + def list_tasks(self, filter_type: str = "all"): + """List tasks with color coding""" + tasks = self.task_manager.tasks + + if filter_type == "pending": + tasks = [t for t in tasks if not t.completed] + elif filter_type == "completed": + tasks = [t for t in tasks if t.completed] + + if not tasks: + if filter_type == "all": + print("\033[33mNo tasks found. Add your first task with: tasks add 'task description'\033[0m") + else: + print(f"\033[33mNo {filter_type} tasks found.\033[0m") + return + + print(f"\033[1m{filter_type.title()} Tasks:\033[0m") + print() + + for task in tasks: + # Status icon + status_icon = "โœ“" if task.completed else "โ—‹" + + # Priority icon and color + priority_icons = {"high": "!", "normal": "-", "low": "ยท"} + priority_colors = {"high": "\033[31m", "normal": "\033[33m", "low": "\033[36m"} + + priority_icon = priority_icons.get(task.priority, "-") + priority_color = priority_colors.get(task.priority, "\033[33m") + + # Task color + if task.completed: + task_color = "\033[32m" # Green for completed + else: + task_color = priority_color + + # Format task line + task_line = f"{task_color} {status_icon} [{priority_icon}] (ID: {task.id}) {task.text}\033[0m" + print(task_line) + + print() + print(f"\033[2mTotal: {len(tasks)} tasks\033[0m") + + def complete_task(self, task_id: str): + """Mark a task as completed""" + try: + task_id_int = int(task_id) + except ValueError: + print(f"\033[31mError: Invalid task ID '{task_id}'. Must be a number.\033[0m") + return False + + task = next((t for t in self.task_manager.tasks if t.id == task_id_int), None) + if not task: + print(f"\033[31mError: Task with ID {task_id_int} not found.\033[0m") + return False + + if task.completed: + print(f"\033[33mTask '{task.text}' is already completed.\033[0m") + return True + + self.task_manager.toggle_task(task_id_int) + print(f"\033[32mโœ“ Completed task: {task.text}\033[0m") + return True + + def delete_task(self, task_id: str): + """Delete a task""" + try: + task_id_int = int(task_id) + except ValueError: + print(f"\033[31mError: Invalid task ID '{task_id}'. Must be a number.\033[0m") + return False + + task = next((t for t in self.task_manager.tasks if t.id == task_id_int), None) + if not task: + print(f"\033[31mError: Task with ID {task_id_int} not found.\033[0m") + return False + + task_text = task.text + self.task_manager.delete_task(task_id_int) + print(f"\033[31mร— Deleted task: {task_text}\033[0m") + return True + + def count_tasks(self, filter_type: str = "all"): + """Count tasks by type""" + tasks = self.task_manager.tasks + + if filter_type == "pending": + count = len([t for t in tasks if not t.completed]) + elif filter_type == "completed": + count = len([t for t in tasks if t.completed]) + else: + count = len(tasks) + + print(count) + return count + +def main(): + """Main CLI entry point""" + if len(sys.argv) < 2: + print("Usage: task_cli.py [args...]") + sys.exit(1) + + cli = TaskCLI() + command = sys.argv[1].lower() + + try: + if command == "add": + if len(sys.argv) < 3: + print("\033[31mError: Please provide task description\033[0m") + sys.exit(1) + + text = sys.argv[2] + priority = sys.argv[3] if len(sys.argv) > 3 else "normal" + + # Validate priority + if priority not in ["high", "normal", "low"]: + print(f"\033[33mWarning: Invalid priority '{priority}', using 'normal'\033[0m") + priority = "normal" + + cli.add_task(text, priority) + + elif command == "list": + filter_type = sys.argv[2] if len(sys.argv) > 2 else "all" + if filter_type not in ["all", "pending", "completed"]: + print(f"\033[33mWarning: Invalid filter '{filter_type}', using 'all'\033[0m") + filter_type = "all" + cli.list_tasks(filter_type) + + elif command == "complete": + if len(sys.argv) < 3: + print("\033[31mError: Please provide task ID\033[0m") + sys.exit(1) + cli.complete_task(sys.argv[2]) + + elif command == "delete": + if len(sys.argv) < 3: + print("\033[31mError: Please provide task ID\033[0m") + sys.exit(1) + cli.delete_task(sys.argv[2]) + + elif command == "count": + filter_type = sys.argv[2] if len(sys.argv) > 2 else "all" + cli.count_tasks(filter_type) + + else: + print(f"\033[31mError: Unknown command '{command}'\033[0m") + print("Available commands: add, list, complete, delete, count") + sys.exit(1) + + except Exception as e: + print(f"\033[31mError: {e}\033[0m") + sys.exit(1) + +if __name__ == "__main__": + main() + diff --git a/plugins/taskman/bin/task_manager.py b/plugins/taskman/bin/task_manager.py new file mode 100755 index 000000000..af7e5df93 --- /dev/null +++ b/plugins/taskman/bin/task_manager.py @@ -0,0 +1,342 @@ +#!/usr/bin/env python3 +""" +Terminal Task Manager - A sidebar-style task manager for the terminal + +Features: +- Sidebar display in terminal +- Keyboard shortcuts for task operations +- Persistent task storage +- Task prioritization and highlighting +""" + +import curses +import json +import os +from datetime import datetime +from typing import List, Dict, Optional + +class Task: + def __init__(self, id: int, text: str, completed: bool = False, priority: str = "normal", created_at: str = None): + self.id = id + self.text = text + self.completed = completed + self.priority = priority # "high", "normal", "low" + self.created_at = created_at or datetime.now().isoformat() + + def to_dict(self) -> Dict: + return { + "id": self.id, + "text": self.text, + "completed": self.completed, + "priority": self.priority, + "created_at": self.created_at + } + + @classmethod + def from_dict(cls, data: Dict) -> 'Task': + return cls( + id=data["id"], + text=data["text"], + completed=data["completed"], + priority=data.get("priority", "normal"), + created_at=data.get("created_at") + ) + +class TaskManager: + def __init__(self, data_file: str = None): + # Use environment variable or default path + self.data_file = data_file or os.environ.get('TASKMAN_DATA_FILE', os.path.expanduser("~/.taskman/tasks.json")) + self.tasks: List[Task] = [] + self.selected_index = 0 + self.next_id = 1 + self.load_tasks() + + def load_tasks(self): + """Load tasks from JSON file""" + if os.path.exists(self.data_file): + try: + with open(self.data_file, 'r') as f: + data = json.load(f) + self.tasks = [Task.from_dict(task_data) for task_data in data.get("tasks", [])] + self.next_id = data.get("next_id", 1) + except (json.JSONDecodeError, FileNotFoundError): + self.tasks = [] + self.next_id = 1 + + def save_tasks(self): + """Save tasks to JSON file""" + # Ensure directory exists + os.makedirs(os.path.dirname(self.data_file), exist_ok=True) + + data = { + "tasks": [task.to_dict() for task in self.tasks], + "next_id": self.next_id + } + with open(self.data_file, 'w') as f: + json.dump(data, f, indent=2) + + def add_task(self, text: str, priority: str = "normal") -> Task: + """Add a new task""" + task = Task(self.next_id, text, priority=priority) + self.tasks.append(task) + self.next_id += 1 + self.save_tasks() + return task + + def toggle_task(self, task_id: int): + """Toggle task completion status""" + for task in self.tasks: + if task.id == task_id: + task.completed = not task.completed + self.save_tasks() + break + + def delete_task(self, task_id: int): + """Delete a task""" + self.tasks = [task for task in self.tasks if task.id != task_id] + self.save_tasks() + if self.selected_index >= len(self.tasks) and self.tasks: + self.selected_index = len(self.tasks) - 1 + elif not self.tasks: + self.selected_index = 0 + + def get_selected_task(self) -> Optional[Task]: + """Get currently selected task""" + if 0 <= self.selected_index < len(self.tasks): + return self.tasks[self.selected_index] + return None + + def move_selection(self, direction: int): + """Move selection up or down""" + if self.tasks: + self.selected_index = max(0, min(len(self.tasks) - 1, self.selected_index + direction)) + +class TaskManagerUI: + def __init__(self): + self.task_manager = TaskManager() + self.input_mode = False + self.input_text = "" + self.input_priority = "normal" + self.show_help = False + + def run(self, stdscr): + """Main application loop""" + curses.curs_set(0) # Hide cursor + stdscr.nodelay(1) # Non-blocking input + stdscr.timeout(100) # Refresh every 100ms + + # Color pairs + curses.start_color() + curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE) # Selected + curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK) # Completed + curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) # High priority + curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) # Normal priority + curses.init_pair(5, curses.COLOR_CYAN, curses.COLOR_BLACK) # Low priority + curses.init_pair(6, curses.COLOR_WHITE, curses.COLOR_BLACK) # Default + curses.init_pair(7, curses.COLOR_BLACK, curses.COLOR_WHITE) # Input mode + + while True: + self.draw_ui(stdscr) + + try: + key = stdscr.getch() + except: + continue + + if key == -1: + continue + + if self.input_mode: + if self.handle_input_mode(key): + break + else: + if self.handle_normal_mode(key): + break + + def draw_ui(self, stdscr): + """Draw the user interface""" + stdscr.clear() + height, width = stdscr.getmaxyx() + + # Title + title = "Terminal Task Manager (osh plugin)" + stdscr.addstr(0, 2, title, curses.A_BOLD) + stdscr.addstr(0, width - 20, f"Tasks: {len(self.task_manager.tasks)}", curses.A_DIM) + + # Help line + if not self.show_help: + help_text = "Press 'h' for help, 'q' to quit" + stdscr.addstr(1, 2, help_text, curses.A_DIM) + + # Tasks list + start_row = 3 + for i, task in enumerate(self.task_manager.tasks): + if start_row + i >= height - 3: + break + + # Determine color based on task status and priority + color = curses.color_pair(6) # Default + if task.completed: + color = curses.color_pair(2) # Green for completed + elif task.priority == "high": + color = curses.color_pair(3) # Red for high priority + elif task.priority == "low": + color = curses.color_pair(5) # Cyan for low priority + else: + color = curses.color_pair(4) # Yellow for normal priority + + # Highlight selected task + if i == self.task_manager.selected_index: + color |= curses.A_REVERSE + + # Task status icon + status_icon = "โœ“" if task.completed else "โ—‹" + priority_icon = { + "high": "!", + "normal": "-", + "low": "ยท" + }.get(task.priority, "-") + + # Truncate task text if too long + max_text_width = width - 15 + task_text = task.text[:max_text_width] + "..." if len(task.text) > max_text_width else task.text + + task_line = f" {status_icon} [{priority_icon}] {task_text}" + stdscr.addstr(start_row + i, 2, task_line, color) + + # Input area + if self.input_mode: + input_y = height - 3 + stdscr.addstr(input_y, 2, "New task: ", curses.A_BOLD) + stdscr.addstr(input_y, 12, self.input_text, curses.color_pair(7)) + stdscr.addstr(input_y + 1, 2, f"Priority: {self.input_priority} (Tab to change)", curses.A_DIM) + curses.curs_set(1) # Show cursor in input mode + else: + curses.curs_set(0) # Hide cursor + + # Help panel + if self.show_help: + self.draw_help(stdscr, height, width) + + # Status line + status_y = height - 1 + if self.input_mode: + status_text = "Enter: Save task | Esc: Cancel | Tab: Change priority" + else: + status_text = "n: New | Space: Toggle | d: Delete | โ†‘โ†“: Navigate | h: Help | q: Quit" + + stdscr.addstr(status_y, 2, status_text[:width-4], curses.A_DIM) + + stdscr.refresh() + + def draw_help(self, stdscr, height, width): + """Draw help panel""" + help_lines = [ + "KEYBOARD SHORTCUTS:", + "", + "Navigation:", + " โ†‘/k - Move up", + " โ†“/j - Move down", + "", + "Task Operations:", + " n - New task", + " Space - Toggle completion", + " d - Delete task", + "", + "Priority Levels:", + " ! High priority (red)", + " - Normal priority (yellow)", + " ยท Low priority (cyan)", + "", + "Other:", + " h - Toggle this help", + " q - Quit application", + "", + "CLI Usage:", + " tasks add 'text' [priority]", + " tasks list [filter]", + " tasks done ", + " tasks delete ", + ] + + # Calculate help panel size + help_width = max(len(line) for line in help_lines) + 4 + help_height = len(help_lines) + 2 + + start_x = max(2, (width - help_width) // 2) + start_y = max(2, (height - help_height) // 2) + + # Draw help background + for i in range(help_height): + stdscr.addstr(start_y + i, start_x, " " * help_width, curses.color_pair(7)) + + # Draw help content + for i, line in enumerate(help_lines): + stdscr.addstr(start_y + 1 + i, start_x + 2, line, curses.color_pair(7)) + + def handle_normal_mode(self, key) -> bool: + """Handle key presses in normal mode""" + # Quit + if key == ord('q'): + return True + + # Navigation + elif key == curses.KEY_UP or key == ord('k'): + self.task_manager.move_selection(-1) + elif key == curses.KEY_DOWN or key == ord('j'): + self.task_manager.move_selection(1) + + # Task operations + elif key == ord('n'): + self.input_mode = True + self.input_text = "" + self.input_priority = "normal" + elif key == ord(' '): + selected_task = self.task_manager.get_selected_task() + if selected_task: + self.task_manager.toggle_task(selected_task.id) + elif key == ord('d'): + selected_task = self.task_manager.get_selected_task() + if selected_task: + self.task_manager.delete_task(selected_task.id) + + # Help + elif key == ord('h'): + self.show_help = not self.show_help + + return False + + def handle_input_mode(self, key) -> bool: + """Handle key presses in input mode""" + if key == 27: # Escape + self.input_mode = False + self.input_text = "" + elif key == ord('\n') or key == 10: # Enter + if self.input_text.strip(): + self.task_manager.add_task(self.input_text.strip(), self.input_priority) + self.input_mode = False + self.input_text = "" + elif key == ord('\t'): # Tab - cycle priority + priorities = ["normal", "high", "low"] + current_index = priorities.index(self.input_priority) + self.input_priority = priorities[(current_index + 1) % len(priorities)] + elif key == curses.KEY_BACKSPACE or key == 127: + self.input_text = self.input_text[:-1] + elif 32 <= key <= 126: # Printable characters + self.input_text += chr(key) + + return False + +def main(): + """Main entry point""" + ui = TaskManagerUI() + try: + curses.wrapper(ui.run) + except KeyboardInterrupt: + print("\nGoodbye!") + except Exception as e: + print(f"Error: {e}") + +if __name__ == "__main__": + main() + diff --git a/plugins/taskman/taskman.plugin.zsh b/plugins/taskman/taskman.plugin.zsh new file mode 100644 index 000000000..3cb772cc6 --- /dev/null +++ b/plugins/taskman/taskman.plugin.zsh @@ -0,0 +1,243 @@ +#!/usr/bin/env zsh +# +# Terminal Task Manager Plugin for Oh-My-Zsh +# A powerful sidebar-style task manager that runs entirely in your terminal +# +# Author: @oiahoon +# Version: 1.0.0 +# Repository: https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/taskman +# +# Features: +# - Interactive terminal UI with keyboard shortcuts +# - Command line interface for quick operations +# - Priority system with color coding +# - Persistent JSON storage +# - Vim-like keybindings +# - Zero external dependencies (Python 3 only) +# + +# Plugin directory detection (Oh-My-Zsh compatible) +TASKMAN_PLUGIN_DIR="${0:h}" + +# Data directory (store tasks in home directory) +TASKMAN_DATA_DIR="$HOME/.taskman" + +# Ensure data directory exists +[[ ! -d "$TASKMAN_DATA_DIR" ]] && mkdir -p "$TASKMAN_DATA_DIR" + +# Color definitions for output +TASKMAN_COLOR_RED="\033[31m" +TASKMAN_COLOR_GREEN="\033[32m" +TASKMAN_COLOR_YELLOW="\033[33m" +TASKMAN_COLOR_BLUE="\033[34m" +TASKMAN_COLOR_CYAN="\033[36m" +TASKMAN_COLOR_RESET="\033[0m" + +# Main task manager function +tasks() { + local action="$1" + shift + + case "$action" in + "" | "ui" | "show") + # Launch the full UI + _taskman_launch_ui + ;; + "add" | "new" | "create") + # Quick add task from command line + _taskman_add_task "$@" + ;; + "list" | "ls") + # List tasks in terminal + _taskman_list_tasks "$@" + ;; + "done" | "complete") + # Mark task as complete + _taskman_complete_task "$@" + ;; + "delete" | "del" | "rm") + # Delete a task + _taskman_delete_task "$@" + ;; + "help" | "-h" | "--help") + _taskman_show_help + ;; + *) + echo "${TASKMAN_COLOR_RED}Unknown action: $action${TASKMAN_COLOR_RESET}" + _taskman_show_help + return 1 + ;; + esac +} + +# Launch the full UI +_taskman_launch_ui() { + if ! command -v python3 >/dev/null 2>&1; then + echo "${TASKMAN_COLOR_RED}Error: Python 3 is required but not installed.${TASKMAN_COLOR_RESET}" + echo "${TASKMAN_COLOR_YELLOW}Please install Python 3 to use the interactive UI.${TASKMAN_COLOR_RESET}" + return 1 + fi + + # Set the data file path + export TASKMAN_DATA_FILE="$TASKMAN_DATA_DIR/tasks.json" + + # Run the Python task manager + python3 "$TASKMAN_PLUGIN_DIR/bin/task_manager.py" +} + +# Quick add task from command line +_taskman_add_task() { + if [[ $# -eq 0 ]]; then + echo "${TASKMAN_COLOR_RED}Error: Please provide task description${TASKMAN_COLOR_RESET}" + echo "Usage: tasks add [priority]" + return 1 + fi + + local task_text="$1" + local priority="${2:-normal}" + + # Validate priority + case "$priority" in + "high"|"normal"|"low") + ;; + *) + echo "${TASKMAN_COLOR_YELLOW}Warning: Invalid priority '$priority', using 'normal'${TASKMAN_COLOR_RESET}" + priority="normal" + ;; + esac + + if command -v python3 >/dev/null 2>&1; then + python3 "$TASKMAN_PLUGIN_DIR/bin/task_cli.py" add "$task_text" "$priority" + + # Show color-coded confirmation + local priority_color + case "$priority" in + "high") priority_color="$TASKMAN_COLOR_RED" ;; + "low") priority_color="$TASKMAN_COLOR_CYAN" ;; + *) priority_color="$TASKMAN_COLOR_YELLOW" ;; + esac + + echo "${TASKMAN_COLOR_GREEN}โœ“ Added task:${TASKMAN_COLOR_RESET} ${priority_color}[$priority]${TASKMAN_COLOR_RESET} $task_text" + else + echo "${TASKMAN_COLOR_RED}Error: Python 3 is required for task management.${TASKMAN_COLOR_RESET}" + return 1 + fi +} + +# List tasks in terminal +_taskman_list_tasks() { + local filter="$1" + + if command -v python3 >/dev/null 2>&1; then + python3 "$TASKMAN_PLUGIN_DIR/bin/task_cli.py" list "$filter" + else + echo "${TASKMAN_COLOR_RED}Error: Python 3 is required for task management.${TASKMAN_COLOR_RESET}" + return 1 + fi +} + +# Complete a task +_taskman_complete_task() { + if [[ $# -eq 0 ]]; then + echo "${TASKMAN_COLOR_RED}Error: Please provide task ID${TASKMAN_COLOR_RESET}" + echo "Usage: tasks done " + return 1 + fi + + local task_id="$1" + if command -v python3 >/dev/null 2>&1; then + python3 "$TASKMAN_PLUGIN_DIR/bin/task_cli.py" complete "$task_id" + else + echo "${TASKMAN_COLOR_RED}Error: Python 3 is required for task management.${TASKMAN_COLOR_RESET}" + return 1 + fi +} + +# Delete a task +_taskman_delete_task() { + if [[ $# -eq 0 ]]; then + echo "${TASKMAN_COLOR_RED}Error: Please provide task ID${TASKMAN_COLOR_RESET}" + echo "Usage: tasks delete " + return 1 + fi + + local task_id="$1" + if command -v python3 >/dev/null 2>&1; then + python3 "$TASKMAN_PLUGIN_DIR/bin/task_cli.py" delete "$task_id" + else + echo "${TASKMAN_COLOR_RED}Error: Python 3 is required for task management.${TASKMAN_COLOR_RESET}" + return 1 + fi +} + +# Show help +_taskman_show_help() { + cat << 'EOF' +Terminal Task Manager - Oh-My-Zsh Plugin + +Usage: + tasks [action] [arguments] + +Actions: + (no action) Launch full interactive UI + ui, show Launch full interactive UI + add [priority] Add new task (priority: high, normal, low) + list [filter] List tasks (filter: all, pending, completed) + done Mark task as completed + delete Delete a task + help Show this help + +Examples: + tasks # Launch interactive UI + tasks add "Fix bug in login" # Add normal priority task + tasks add "Deploy to prod" high # Add high priority task + tasks list # List all tasks + tasks list pending # List only pending tasks + tasks done 3 # Mark task ID 3 as completed + tasks delete 5 # Delete task ID 5 + +Interactive UI Keys: + โ†‘/k Move up n New task + โ†“/j Move down Space Toggle completion + h Help d Delete task + q Quit + +Aliases: + tm, task, todo - All point to 'tasks' command + +Data Storage: + Tasks are stored in: ~/.taskman/tasks.json + +Requirements: + Python 3.6+ (for interactive UI and CLI operations) +EOF +} + +# Convenient aliases +alias tm='tasks' +alias task='tasks' +alias todo='tasks' + +# Quick access function for sidebar workflow +task-sidebar() { + echo "${TASKMAN_COLOR_BLUE}Starting task manager in sidebar mode...${TASKMAN_COLOR_RESET}" + echo "${TASKMAN_COLOR_YELLOW}Tip: Use terminal split to run this alongside your work${TASKMAN_COLOR_RESET}" + tasks ui +} + +# Optional: Show task summary on shell startup +# Uncomment the next line to enable startup summary +# _taskman_startup_summary + +_taskman_startup_summary() { + if [[ -f "$TASKMAN_DATA_DIR/tasks.json" ]] && command -v python3 >/dev/null 2>&1; then + local pending_count=$(python3 "$TASKMAN_PLUGIN_DIR/bin/task_cli.py" count pending 2>/dev/null || echo "0") + local completed_count=$(python3 "$TASKMAN_PLUGIN_DIR/bin/task_cli.py" count completed 2>/dev/null || echo "0") + + if [[ "$pending_count" -gt 0 ]]; then + echo "${TASKMAN_COLOR_CYAN}๐Ÿ“‹ Task Summary: ${pending_count} pending, ${completed_count} completed${TASKMAN_COLOR_RESET}" + echo "${TASKMAN_COLOR_YELLOW} Type 'tasks' to manage your tasks${TASKMAN_COLOR_RESET}" + fi + fi +} +