-
Notifications
You must be signed in to change notification settings - Fork 0
feat: TUI state persistence, new commands, and non-interactive improvements #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: TUI state persistence, new commands, and non-interactive improvements #11
Conversation
…ements ## TUI State Persistence - Add configuration properties for remembering TUI state across sessions - remember_tui_state: master toggle (default: true) - tui_tree_view: persist tree/flat view preference - tui_last_view_item: persist selected repo/project/assignee - Restore view state on TUI startup - Save state on navigation (arrow keys, tree toggle) - Reset saved item when switching view modes (Tab key) - Fix bug where --repo flag always overwrote restored state - Update config display to show TUI persistence settings - Change tree toggle key from 'r' to 't' in help text ## New Commands - add-link: Add URLs to task links - Usage: tsk add-link <task_id> <url> - Validates HTTP/HTTPS URLs - append: Append text to task descriptions - Usage: tsk append <task_id> --text "content" - update: Batch update task fields - Usage: tsk update <task_ids> [--priority/--status/--project/etc] - Supports: priority, status, project, tags, assignees, due date, title ## Non-Interactive Terminal Detection - Add sys.stdin.isatty() checks across commands - delete: require --force flag in non-interactive mode - done: auto-confirm subtask marking in non-interactive mode - unarchive: auto-confirm subtask operations in non-interactive mode - sync: add --non-interactive flag to skip unexpected file prompts ## Archive Command Enhancement - Add --all-completed flag to archive all completed tasks at once - Usage: tsk archive --all-completed [--repo <name>] ## Sync Command Improvements - Add run_git_verbose() for interactive git operations - Implement SimpleSyncProgress for safer terminal output - Better handling of prompts and credential helpers - Improved display of unexpected files during sync 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Code Review - PR #11Thank you for this comprehensive PR! This adds several valuable features to TaskRepo. Below is my detailed review covering code quality, potential issues, performance, security, and testing. ✅ Strengths1. Well-Structured New CommandsThe three new commands ( 2. TUI State PersistenceThe implementation is clean with proper configuration management through properties and sensible defaults ( 3. Non-Interactive Mode ImprovementsGood safety-first approach requiring 4. Sync Command RefactoringThe switch from Rich's 🐛 Potential Bugs & IssuesHIGH PRIORITY1. Type Error in
|
- Add silent_errors parameter to list_tasks() and list_archived_tasks() - Detect and report git conflict markers specifically - Collect all failed files and show helpful summary with resolution steps - Improve error messages to show filename only (not full path) - Suggest running 'git status' when conflicts are detected This makes the TUI more resilient to YAML parsing errors caused by unresolved git merge conflicts, preventing crashes and providing better guidance to users.
- Fix all ruff linting errors (unused imports, whitespace, f-strings) - Move rich.console import to top of file_validation.py - Fix type error in update.py: use datetime instead of string for task.due - Format code with ruff formatter All must-fix issues from robot review addressed: - Type error in update.py:128 (datetime vs string) ✓ - detect_conflicts signature verified (skip_fetch parameter exists) ✓ - All linting errors fixed ✓
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces comprehensive TUI state persistence, three new CLI commands, and improved non-interactive terminal support for automation scenarios.
Changes:
- TUI state persistence system that remembers tree view preference, view mode, and last selected item across sessions with configurable toggle
- New commands:
add-linkfor adding URLs to tasks,appendfor appending text to descriptions, andupdatefor batch field updates - Non-interactive mode improvements with
--forcerequirements, auto-confirmation of subtask operations, and a new--non-interactivesync flag
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/taskrepo/core/config.py | Adds three new config properties for TUI state persistence (remember_tui_state, tui_tree_view, tui_last_view_item) |
| src/taskrepo/tui/task_tui.py | Implements state restoration on TUI initialization and state saving on navigation events; changes tree toggle key from 'r' to 't' |
| src/taskrepo/cli/commands/tui.py | Fixes --repo flag to only override state when explicitly provided, preventing unintended state overwrites |
| src/taskrepo/cli/commands/main.py | Registers three new commands (add-link, append, update) in the CLI |
| src/taskrepo/cli/commands/add_link.py | New command for adding URLs to task links with basic URL validation |
| src/taskrepo/cli/commands/append.py | New command for appending text to task descriptions |
| src/taskrepo/cli/commands/update.py | New command for batch updating task fields (status, priority, tags, assignees, etc.) |
| src/taskrepo/cli/commands/delete.py | Adds non-interactive terminal detection requiring --force flag for safety |
| src/taskrepo/cli/commands/done.py | Auto-confirms subtask operations in non-interactive terminals |
| src/taskrepo/cli/commands/unarchive.py | Auto-confirms subtask operations in non-interactive terminals |
| src/taskrepo/cli/commands/sync.py | Adds --non-interactive flag, SimpleSyncProgress class for better prompt handling, improved git operations with verbose output |
| src/taskrepo/cli/commands/archive.py | Adds --all-completed flag for bulk archiving completed tasks |
| src/taskrepo/cli/commands/config.py | Displays TUI state persistence settings in config output |
| src/taskrepo/core/repository.py | Adds silent_errors parameter to list_tasks and list_archived_tasks with improved conflict detection messages |
| src/taskrepo/utils/file_validation.py | Switches from click to Rich Console and prompt_toolkit for better interactive prompt handling |
Comments suppressed due to low confidence (1)
src/taskrepo/utils/file_validation.py:85
- The import statement for Console is placed in the middle of the file, after the detect_unexpected_files function. This violates Python best practices where all imports should be at the top of the file. Move this import to the top with the other imports.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Use --all-completed to archive all tasks with status 'completed' in one command. | ||
| """ | ||
| config = ctx.obj["config"] | ||
| manager = RepositoryManager(config.parent_dir) | ||
|
|
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic issue with --all-completed flag. If both --all-completed and task_ids are provided, the flag is ignored (line 34 condition: "if all_completed and not task_ids"). This could be confusing for users. Consider either: 1) rejecting when both are provided with an error message, or 2) documenting that --all-completed is ignored when explicit task IDs are provided.
| Use --all-completed to archive all tasks with status 'completed' in one command. | |
| """ | |
| config = ctx.obj["config"] | |
| manager = RepositoryManager(config.parent_dir) | |
| Use --all-completed to archive all tasks with status 'completed' in one command. | |
| Note: --all-completed cannot be used together with explicit TASK_IDS. | |
| """ | |
| config = ctx.obj["config"] | |
| manager = RepositoryManager(config.parent_dir) | |
| # Disallow using --all-completed together with explicit task IDs to avoid confusion | |
| if all_completed and task_ids: | |
| click.secho( | |
| "Error: --all-completed cannot be used together with TASK_IDS. " | |
| "Either specify task IDs, or use --all-completed without IDs.", | |
| fg="red", | |
| err=True, | |
| ) | |
| ctx.exit(1) |
| @@ -0,0 +1,48 @@ | |||
| """Append command for adding content to task descriptions.""" | |||
|
|
|||
| from typing import Optional | |||
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import. The sys module is imported but never used in this file. Remove this unused import.
| @@ -0,0 +1,58 @@ | |||
| """Add-link command for adding URLs to task links.""" | |||
|
|
|||
| from typing import Optional | |||
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import. The sys module is imported but never used in this file. Remove this unused import.
| @kb.add("t", filter=Condition(lambda: not self.filter_active)) | ||
| def _(event): | ||
| """Toggle tree view.""" | ||
| self.tree_view = not self.tree_view | ||
| # Save to config if remember_tui_state is enabled | ||
| if self.config.remember_tui_state: | ||
| self.config.tui_tree_view = self.tree_view |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Key binding conflict. Both the "extend task due date" functionality (line 768-771) and the "toggle tree view" functionality (line 790-796) are bound to the 't' key. This will cause the first binding to be overridden by the second, making the extend functionality unreachable. One of these keybindings needs to be changed to a different key.
| # The user explicitly requested a non-interactive progress bar (linear logs) | ||
| # to solve the hanging issues with the spinner. | ||
| progress_manager = SimpleSyncProgress(console=console) |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate variable initialization. The variables repo_timings and total_start_time are initialized twice in a row at lines 389-390 and 393-394. Remove the duplicate initialization.
| continue | ||
|
|
||
| # Fetch first to check for changes | ||
| # Use verbose fetch to avoid hanging silently on network/auth |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation - line 596 uses a space for indentation instead of the standard 4 spaces or tabs. This should be corrected to use proper indentation to match the surrounding code.
| if not run_git_verbose( | ||
| str(repository.path), ["push", "origin", current_branch], "Push failed" | ||
| ): | ||
| raise GitCommandError("git push", "Process failed") | ||
| elif not should_push and push and git_repo.remotes: | ||
| progress.console.print(" [dim]⊘ Pushing skipped (detached HEAD or error)[/dim]") | ||
| except GitCommandError: | ||
| # Fallback to recovery if we detect rejection | ||
| progress.console.print( | ||
| " [yellow]⚠[/yellow] Push failed. Attempting auto-recovery (pull --rebase)..." |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation - line 859 uses a space for indentation instead of the standard 4 spaces or tabs. This should be corrected to use proper indentation to match the surrounding code.
| @@ -0,0 +1,155 @@ | |||
| """Update command for modifying task fields.""" | |||
|
|
|||
| from typing import Optional, Tuple | |||
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'sys' is not used.
Pull Request Review - PR 11This PR adds TUI state persistence, three new commands, and non-interactive mode improvements. Overall well-structured code. HIGH PRIORITY ISSUES
MEDIUM PRIORITY
SECURITYGood: Proper subprocess usage, safe git operations, input validation SUMMARYHigh-quality PR but MUST FIX:
Target 80%+ test coverage per CONTRIBUTING.md Review by Claude Code |
Detailed Code ReviewCode Quality Strengths
New Features AnalysisTUI State Persistence
New Commands (add-link, append, update)
Non-Interactive Mode
Issue DetailsType Safety Bug (update.py:138) parsed_date = parse_date(due) # Returns Optional[datetime | date]
if parsed_date:
task.due = parsed_date # Expects Optional[datetime]Fix: Add isinstance check and convert date to datetime if needed SimpleSyncProgress Concern
URL Validation Performance Notes
Documentation Quality
Recommend documenting --non-interactive in README |
Suggested Code Fixes1. Fix Type Safety in update.pyAdd this import and fix: from datetime import date, datetime
# Around line 136-139
if due:
parsed_date = parse_date(due)
if parsed_date:
# Convert date to datetime if needed
if isinstance(parsed_date, date) and not isinstance(parsed_date, datetime):
parsed_date = datetime.combine(parsed_date, datetime.min.time())
task.due = parsed_date
changes.append(f"due → {due}")
else:
click.secho(f"Warning: Could not parse due date '{due}' for task {task_id}", fg="yellow")2. Improve URL Validation in add_link.pyfrom urllib.parse import urlparse
# Replace line 29-32 with:
def is_valid_url(url: str) -> bool:
try:
result = urlparse(url)
return all([result.scheme in ['http', 'https'], result.netloc])
except:
return False
if not is_valid_url(url):
click.secho("Error: Invalid URL format", fg="red", err=True)
ctx.exit(1)3. Remove Duplicates in sync.pyDelete lines 387-388: # DELETE THESE LINES:
repo_timings = {}
total_start_time = time.perf_counter()4. Example Test for add-link CommandCreate tests/unit/test_add_link.py: import pytest
from click.testing import CliRunner
from taskrepo.cli.commands.add_link import add_link
def test_add_link_valid_url(setup_repo):
runner = CliRunner()
result = runner.invoke(add_link, ['1', 'https://github.com/test'])
assert result.exit_code == 0
assert "Added link" in result.output
def test_add_link_invalid_url(setup_repo):
runner = CliRunner()
result = runner.invoke(add_link, ['1', 'not-a-url'])
assert result.exit_code == 1
assert "Error" in result.output |
Test Coverage RequirementsPer CONTRIBUTING.md: Aim for 80%+ coverage for new code Missing Tests:
Estimated 25-30 test cases needed Test areas:
|
Summary
This PR adds several major improvements to TaskRepo:
🎯 TUI State Persistence
The TUI now remembers your preferences across sessions:
Configuration:
remember_tui_state: Master toggle (default: true)tui_tree_view: Tree view preferencetui_last_view_item: Last selected view itemBug fixes:
--repoflag always overwriting restored state📝 New Commands
tsk add-link- Add URLs to taskstsk add-link 5 "https://github.com/org/repo/issues/123"tsk append- Append text to task descriptionstsk append 5 --text "Additional note from meeting"tsk update- Batch update task fields🤖 Non-Interactive Mode
Better support for automation and CI environments:
delete: Requires--forceflag in non-interactive terminalsdone/unarchive: Auto-confirms subtask operationssync: New--non-interactiveflag to skip prompts✨ Other Improvements
--all-completedflag to bulk-archive completed tasksTesting
🤖 Generated with Claude Code