tasktree-manager 2.11.0__tar.gz → 2.11.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/CHANGELOG.md +6 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/PKG-INFO +1 -1
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/README.md +2 -2
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/docs/configuration.md +1 -15
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/docs/troubleshooting.md +3 -3
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/docs/user-guide.md +12 -13
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/pyproject.toml +1 -1
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/_version.py +2 -2
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/app.py +8 -50
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/commands.py +5 -2
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/services/config.py +0 -16
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/widgets/create_modal.py +1 -2
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/widgets/worktree_list.py +2 -3
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_app.py +0 -36
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_config.py +0 -16
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/SKILL.md +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/assets/README.md +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/assets/dashboard_app.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/assets/data_viewer.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/assets/todo_app.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/assets/worker_demo.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/references/layouts.md +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/references/official-guides-index.md +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/references/styling.md +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/references/widgets.md +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.github/templates/CHANGELOG.md.j2 +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.github/workflows/ci.yml +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.github/workflows/release.yml +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.gitignore +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/CONTRIBUTING.md +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/LICENSE +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/OPENSPEC.md +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/ROADMAP.md +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/docs/claude-code-integration-spec.md +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/docs/installation.md +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/mise.toml +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/scripts/bump_version.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/__init__.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/cli.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/services/__init__.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/services/agent_sessions.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/services/claude_hooks.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/services/forge.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/services/git_ops.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/services/models.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/services/task_manager.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/themes.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/widgets/__init__.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/widgets/app_header.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/widgets/messages_panel.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/widgets/setup_modal.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/widgets/status_panel.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/widgets/task_list.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/__init__.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/conftest.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_agent_sessions.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_claude_hooks.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_cli.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_commands.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_forge.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_git_ops.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_integration_git.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_memory.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_messages_panel.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_modals.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_setup_modal.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_status_panel.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_task_manager.py +0 -0
- {tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tests/test_worktree_badges.py +0 -0
|
@@ -15,6 +15,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
15
15
|
- Git hooks support
|
|
16
16
|
- Custom task metadata
|
|
17
17
|
|
|
18
|
+
## [2.11.1] - 2026-07-31
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **tui**: Open editor in new terminal tab, drop Enter shell binding
|
|
22
|
+
|
|
23
|
+
|
|
18
24
|
## [2.11.0] - 2026-07-30
|
|
19
25
|
|
|
20
26
|
### Added
|
|
@@ -63,7 +63,7 @@ On first run, you'll configure:
|
|
|
63
63
|
### Basic Workflow
|
|
64
64
|
|
|
65
65
|
1. **Create a task** - Press `n`, enter task name, select repositories
|
|
66
|
-
2. **Work on code** - Press `e` for editor, `g` for lazygit, or `
|
|
66
|
+
2. **Work on code** - Press `e` for editor, `g` for lazygit, or `o` for a shell in a new terminal tab
|
|
67
67
|
3. **Review changes** - Press `h` to view the diff in [hunk](https://github.com/modem-dev/hunk) (all repos from the task panel, one repo from the worktree panel)
|
|
68
68
|
4. **Push changes** - Press `p` to push all worktrees
|
|
69
69
|
5. **Delete task** - Press `d` when finished
|
|
@@ -107,7 +107,7 @@ a Claude Code statusline or shell prompt.
|
|
|
107
107
|
| `a` | Add repo | `h` | Show diff (hunk) |
|
|
108
108
|
| `d` | Delete task | `e` | Open editor |
|
|
109
109
|
| `p` | Push all | `o` | Open folder |
|
|
110
|
-
| `P` | Pull all | `
|
|
110
|
+
| `P` | Pull all | `y` | Clone task |
|
|
111
111
|
| `c` | Claude (resume) | `C` | Claude (new) |
|
|
112
112
|
| `b` | Dispatch agent | `D` | Delete worktree |
|
|
113
113
|
| `r` | Refresh | `s` | Sort tasks |
|
|
@@ -180,11 +180,6 @@ claude_memory_dir = "~/.claude/tasktree-memory"
|
|
|
180
180
|
# Default: true
|
|
181
181
|
claude_repo_memory = true
|
|
182
182
|
|
|
183
|
-
# Preferred shell
|
|
184
|
-
# Leave empty to use $SHELL environment variable
|
|
185
|
-
# Default: "" (uses $SHELL, falls back to /bin/bash)
|
|
186
|
-
shell = ""
|
|
187
|
-
|
|
188
183
|
# Path to glab executable (GitLab CLI, used for MR/CI status)
|
|
189
184
|
# Default: "glab" (searches PATH)
|
|
190
185
|
glab_path = "glab"
|
|
@@ -235,7 +230,6 @@ open_lazygit = "g" # Open lazygit in selected worktree
|
|
|
235
230
|
show_diff = "h" # Open hunk diff (task panel = all repos, worktree panel = one repo)
|
|
236
231
|
open_editor = "e" # Open editor in selected task/worktree
|
|
237
232
|
open_folder = "o" # Open folder in new terminal tab
|
|
238
|
-
open_shell = "enter" # Open shell in selected worktree
|
|
239
233
|
push_all = "p" # Push all worktrees in current task
|
|
240
234
|
pull_all = "P" # Pull all worktrees in current task (shift+p)
|
|
241
235
|
refresh = "r" # Refresh git status
|
|
@@ -399,7 +393,6 @@ refresh_interval = 15 # Refresh every 15 seconds
|
|
|
399
393
|
| `editor` | string | `""` | `EDITOR` | Command to launch your editor |
|
|
400
394
|
| `lazygit_path` | string | `"lazygit"` | - | Path to lazygit executable |
|
|
401
395
|
| `hunk_path` | string | `"hunk"` | - | Path to hunk diff-viewer executable |
|
|
402
|
-
| `shell` | string | `""` | `SHELL` | Shell to use when opening terminals |
|
|
403
396
|
| `glab_path` | string | `"glab"` | - | Path to GitLab CLI (MR/CI status) |
|
|
404
397
|
| `gh_path` | string | `"gh"` | - | Path to GitHub CLI (PR/CI status) |
|
|
405
398
|
|
|
@@ -438,12 +431,6 @@ failures degrade silently — features fall back to plain git behavior.
|
|
|
438
431
|
- Install hunk: `brew install hunk` (macOS) or `npm i -g hunkdiff`
|
|
439
432
|
- Used when pressing `h`: from the task panel it opens a combined diff across all the task's repos; from the worktree panel it opens the selected repo's diff
|
|
440
433
|
|
|
441
|
-
**`shell`:**
|
|
442
|
-
- Leave empty (`""`) to use `$SHELL` environment variable
|
|
443
|
-
- Fallback: `/bin/bash`
|
|
444
|
-
- Examples: `"/bin/zsh"`, `"/bin/fish"`, `"/usr/local/bin/bash"`
|
|
445
|
-
- Used when pressing `Enter` to open shell in worktree
|
|
446
|
-
|
|
447
434
|
## Symlinks
|
|
448
435
|
|
|
449
436
|
When creating worktrees, tasktree-manager automatically symlinks gitignored files (like `.env`) from the source repository. This allows environment files to be shared without copying.
|
|
@@ -605,7 +592,6 @@ All 22 customizable keybindings with descriptions:
|
|
|
605
592
|
| `show_diff` | `h` | Open hunk diff (task = all repos, worktree = one) | `show_diff = "ctrl+h"` |
|
|
606
593
|
| `open_editor` | `e` | Open editor in task/worktree folder | `open_editor = "ctrl+e"` |
|
|
607
594
|
| `open_folder` | `o` | Open folder in new terminal tab | `open_folder = "ctrl+o"` |
|
|
608
|
-
| `open_shell` | `enter` | Open shell in selected worktree | `open_shell = "ctrl+t"` |
|
|
609
595
|
| `push_all` | `p` | Push all worktrees in current task | `push_all = "ctrl+p"` |
|
|
610
596
|
| `pull_all` | `P` | Pull all worktrees in current task | `pull_all = "ctrl+shift+p"` |
|
|
611
597
|
| `refresh` | `r` | Refresh git status for all worktrees | `refresh = "F5"` |
|
|
@@ -675,7 +661,7 @@ focus_previous = "h"
|
|
|
675
661
|
[keybindings]
|
|
676
662
|
new_task = "ctrl+n"
|
|
677
663
|
delete_task = "ctrl+d"
|
|
678
|
-
|
|
664
|
+
open_folder = "ctrl+t"
|
|
679
665
|
refresh = "F5"
|
|
680
666
|
quit = "ctrl+q"
|
|
681
667
|
```
|
|
@@ -732,8 +732,8 @@ HEAD detached at abc1234
|
|
|
732
732
|
|
|
733
733
|
**Solution:**
|
|
734
734
|
```bash
|
|
735
|
-
# Open
|
|
736
|
-
Press
|
|
735
|
+
# Open worktree in a new terminal tab
|
|
736
|
+
Press o
|
|
737
737
|
|
|
738
738
|
# Create/checkout branch
|
|
739
739
|
git checkout -b FEAT-123
|
|
@@ -1008,7 +1008,7 @@ Create
|
|
|
1008
1008
|
|
|
1009
1009
|
# Your work is in the remote branch
|
|
1010
1010
|
# Pull it down in the new worktree
|
|
1011
|
-
Press
|
|
1011
|
+
Press o (new terminal tab)
|
|
1012
1012
|
git pull origin FEAT-123
|
|
1013
1013
|
```
|
|
1014
1014
|
|
|
@@ -140,10 +140,10 @@ A **worktree** is a git working directory for a specific repository within a tas
|
|
|
140
140
|
- Exit hunk (press `q`)
|
|
141
141
|
|
|
142
142
|
4. **Or open a shell:**
|
|
143
|
-
- Press `
|
|
143
|
+
- Press `o` to open the worktree in a new terminal tab
|
|
144
144
|
- Make changes with your editor/IDE
|
|
145
145
|
- Run build/test commands
|
|
146
|
-
-
|
|
146
|
+
- Close the tab (or switch back) when done
|
|
147
147
|
|
|
148
148
|
5. **Push changes:**
|
|
149
149
|
- Press `p` to push all worktrees in the task
|
|
@@ -337,8 +337,7 @@ Useful when you're spinning up several similar tasks that share the same set of
|
|
|
337
337
|
|---------|--------------------|------------------------------------------------------|
|
|
338
338
|
| `g` | Open lazygit | Open lazygit in the selected worktree |
|
|
339
339
|
| `h` | Show diff | Open hunk: all repos from the task panel, the selected repo from the worktree panel |
|
|
340
|
-
| `e` | Open editor | Open editor in task/worktree folder (runs `$EDITOR
|
|
341
|
-
| `Enter` | Open shell | Open a shell in the selected worktree |
|
|
340
|
+
| `e` | Open editor | Open editor in a new terminal tab at the task/worktree folder (runs `$EDITOR .`; the TUI keeps running) |
|
|
342
341
|
| `p` | Push all | Push all worktrees in the current task (parallel) |
|
|
343
342
|
| `P` | Pull all | Pull all worktrees in the current task (parallel) |
|
|
344
343
|
| `r` | Refresh status | Refresh git status for all tasks and worktrees |
|
|
@@ -434,9 +433,9 @@ Create
|
|
|
434
433
|
# Work in backend
|
|
435
434
|
Tab (to worktree list)
|
|
436
435
|
j (to highlight backend)
|
|
437
|
-
|
|
436
|
+
o (open new terminal tab)
|
|
438
437
|
|
|
439
|
-
# In
|
|
438
|
+
# In the tab: make changes
|
|
440
439
|
cd src
|
|
441
440
|
vim auth.py
|
|
442
441
|
# ... make changes ...
|
|
@@ -516,7 +515,7 @@ Select: backend
|
|
|
516
515
|
Create
|
|
517
516
|
|
|
518
517
|
Tab
|
|
519
|
-
|
|
518
|
+
o (open new terminal tab)
|
|
520
519
|
git fetch origin feat-789:feat-789
|
|
521
520
|
git checkout feat-789
|
|
522
521
|
exit
|
|
@@ -553,7 +552,7 @@ q (quit tasktree-manager)
|
|
|
553
552
|
tasktree-manager
|
|
554
553
|
j (select FEAT-999)
|
|
555
554
|
Tab
|
|
556
|
-
|
|
555
|
+
o (new tab in first worktree)
|
|
557
556
|
|
|
558
557
|
# Merge main into feature branch
|
|
559
558
|
git fetch origin main:main
|
|
@@ -631,7 +630,7 @@ d (delete task)
|
|
|
631
630
|
**Working in worktree:**
|
|
632
631
|
- Press `e` to open editor directly in worktree
|
|
633
632
|
- Press `g` for lazygit (recommended for git operations)
|
|
634
|
-
- Press `
|
|
633
|
+
- Press `o` for a shell in a new terminal tab (building, testing, other commands)
|
|
635
634
|
|
|
636
635
|
**Git operations:**
|
|
637
636
|
- **Staging/committing**: Use lazygit (`g`)
|
|
@@ -764,9 +763,9 @@ editor = "nvim" # or "vim", "code", "emacs", etc.
|
|
|
764
763
|
|
|
765
764
|
Or set the `$EDITOR` environment variable.
|
|
766
765
|
|
|
767
|
-
**Alternative: Open
|
|
768
|
-
1. Press `
|
|
769
|
-
2. In
|
|
766
|
+
**Alternative: Open a terminal tab first:**
|
|
767
|
+
1. Press `o` (new terminal tab in the worktree)
|
|
768
|
+
2. In the tab:
|
|
770
769
|
```bash
|
|
771
770
|
vim . # Vim
|
|
772
771
|
nvim . # Neovim
|
|
@@ -807,7 +806,7 @@ tasktree-manager creates normal git branches, so CI/CD works seamlessly:
|
|
|
807
806
|
Integrate with GitHub CLI for PR creation:
|
|
808
807
|
|
|
809
808
|
```bash
|
|
810
|
-
# In worktree
|
|
809
|
+
# In a worktree tab (press o in tasktree-manager)
|
|
811
810
|
gh pr create --title "Add authentication" --body "Implements FEAT-123"
|
|
812
811
|
|
|
813
812
|
# Or after pushing all worktrees
|
|
@@ -70,7 +70,7 @@ version-file = "tasktree_manager/_version.py"
|
|
|
70
70
|
# Semantic Release Configuration
|
|
71
71
|
[tool.semantic_release]
|
|
72
72
|
version_toml = ["pyproject.toml:tool.semantic_release.version"]
|
|
73
|
-
version = "2.11.
|
|
73
|
+
version = "2.11.1"
|
|
74
74
|
tag_format = "v{version}"
|
|
75
75
|
branch = "main"
|
|
76
76
|
commit_message = "chore(release): {version} [skip ci]"
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.11.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 11,
|
|
21
|
+
__version__ = version = '2.11.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 11, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -208,7 +208,6 @@ class TaskTreeApp(App):
|
|
|
208
208
|
Binding("m", "toggle_messages", "Messages", show=False),
|
|
209
209
|
Binding("?", "help", "Help"),
|
|
210
210
|
Binding("q", "quit", "Quit"),
|
|
211
|
-
Binding("enter", "open_shell", "Shell", show=False),
|
|
212
211
|
Binding("C", "open_claude_gui_code", "Claude GUI", show=False),
|
|
213
212
|
Binding("P", "pull_all", "Pull All", show=False),
|
|
214
213
|
Binding("S", "toggle_grouping", "Group", show=False),
|
|
@@ -287,7 +286,6 @@ class TaskTreeApp(App):
|
|
|
287
286
|
Binding(kb.get("toggle_messages", "m"), "toggle_messages", "Messages", show=False),
|
|
288
287
|
Binding(kb.get("help", "?"), "help", "Help"),
|
|
289
288
|
Binding(kb.get("quit", "q"), "quit", "Quit"),
|
|
290
|
-
Binding(kb.get("open_shell", "enter"), "open_shell", "Shell", show=False),
|
|
291
289
|
Binding(
|
|
292
290
|
kb.get("open_claude_gui_code", "C"),
|
|
293
291
|
"open_claude_gui_code",
|
|
@@ -331,7 +329,6 @@ class TaskTreeApp(App):
|
|
|
331
329
|
(kb.get("show_diff", "h"), "show_diff", "Diff"),
|
|
332
330
|
(kb.get("open_editor", "e"), "open_editor", "Editor"),
|
|
333
331
|
(kb.get("open_folder", "o"), "open_folder", "Open"),
|
|
334
|
-
(kb.get("open_shell", "enter"), "open_shell", "Shell"),
|
|
335
332
|
(kb.get("dispatch_agent", "b"), "dispatch_agent", "Agent"),
|
|
336
333
|
(kb.get("push_all", "p"), "push_all", "Push"),
|
|
337
334
|
(kb.get("delete_worktree", "D"), "delete_worktree", "Del WT"),
|
|
@@ -1515,37 +1512,13 @@ class TaskTreeApp(App):
|
|
|
1515
1512
|
# Restore focus to task list
|
|
1516
1513
|
self.query_one("#task-list", TaskList).focus()
|
|
1517
1514
|
|
|
1518
|
-
def action_open_shell(self) -> None:
|
|
1519
|
-
"""Open a shell in the current worktree."""
|
|
1520
|
-
if not self.current_worktree:
|
|
1521
|
-
self.notify("No worktree selected", severity="warning")
|
|
1522
|
-
return
|
|
1523
|
-
|
|
1524
|
-
worktree_path = self.current_worktree.path
|
|
1525
|
-
if not worktree_path.exists():
|
|
1526
|
-
self.notify("Worktree directory not found", severity="error")
|
|
1527
|
-
return
|
|
1528
|
-
|
|
1529
|
-
# Save selection state BEFORE suspend (as local variables)
|
|
1530
|
-
saved_task_name = self.current_task.name if self.current_task else None
|
|
1531
|
-
saved_worktree_name = self.current_worktree.name
|
|
1532
|
-
|
|
1533
|
-
# Get shell from config
|
|
1534
|
-
shell = self.config.get_shell()
|
|
1535
|
-
|
|
1536
|
-
self.notify("Opening shell...")
|
|
1537
|
-
|
|
1538
|
-
# Suspend app and open shell
|
|
1539
|
-
with self.suspend():
|
|
1540
|
-
self._run_external_command([shell], cwd=worktree_path, name="shell")
|
|
1541
|
-
|
|
1542
|
-
# Refresh status after shell exits, restoring saved selection
|
|
1543
|
-
self._load_tasks_with_selection(saved_task_name, saved_worktree_name)
|
|
1544
|
-
# Restore focus to worktree list
|
|
1545
|
-
self.query_one("#worktree-list", WorktreeList).focus()
|
|
1546
|
-
|
|
1547
1515
|
def action_open_editor(self) -> None:
|
|
1548
|
-
"""Open editor in
|
|
1516
|
+
"""Open editor in a new terminal tab at the current folder.
|
|
1517
|
+
|
|
1518
|
+
Same mechanism as open_folder: the TUI keeps running instead of
|
|
1519
|
+
suspending, so the editor session and the dashboard live side by
|
|
1520
|
+
side (matches the `o`/`c` new-tab behavior).
|
|
1521
|
+
"""
|
|
1549
1522
|
focused = self.focused
|
|
1550
1523
|
|
|
1551
1524
|
# Determine folder based on focus (like open_folder does)
|
|
@@ -1562,24 +1535,9 @@ class TaskTreeApp(App):
|
|
|
1562
1535
|
self.notify("Folder not found", severity="error")
|
|
1563
1536
|
return
|
|
1564
1537
|
|
|
1565
|
-
# Save selection state BEFORE suspend
|
|
1566
|
-
saved_task_name = self.current_task.name if self.current_task else None
|
|
1567
|
-
saved_worktree_name = self.current_worktree.name if self.current_worktree else None
|
|
1568
|
-
|
|
1569
1538
|
editor = self.config.get_editor()
|
|
1570
|
-
self.
|
|
1571
|
-
|
|
1572
|
-
# Suspend app and run editor with "." to open directory
|
|
1573
|
-
with self.suspend():
|
|
1574
|
-
self._run_external_command([editor, "."], cwd=folder_path, name="editor")
|
|
1575
|
-
|
|
1576
|
-
# Refresh status after editor exits, restoring saved selection
|
|
1577
|
-
self._load_tasks_with_selection(saved_task_name, saved_worktree_name)
|
|
1578
|
-
# Restore focus to the panel that was focused
|
|
1579
|
-
if isinstance(focused, TaskList):
|
|
1580
|
-
self.query_one("#task-list", TaskList).focus()
|
|
1581
|
-
else:
|
|
1582
|
-
self.query_one("#worktree-list", WorktreeList).focus()
|
|
1539
|
+
self._open_ghostty_tab(folder_path, command=f"{editor} .")
|
|
1540
|
+
self.notify(escape(f"Opened {editor} in new tab"))
|
|
1583
1541
|
|
|
1584
1542
|
def _prepare_claude_session(self, task_path: Path) -> None:
|
|
1585
1543
|
"""Refresh CLAUDE.md files, worktree settings, and hooks before launching Claude."""
|
|
@@ -19,9 +19,12 @@ APP_COMMANDS: list[tuple[str, str, str]] = [
|
|
|
19
19
|
"Show Diff",
|
|
20
20
|
"Open hunk: all repos from the task panel, the selected repo from the worktree panel",
|
|
21
21
|
),
|
|
22
|
-
(
|
|
22
|
+
(
|
|
23
|
+
"open_editor",
|
|
24
|
+
"Open Editor",
|
|
25
|
+
"Open your editor in a new terminal tab at the focused task/worktree folder",
|
|
26
|
+
),
|
|
23
27
|
("open_folder", "Open Folder", "Open the focused folder in a new terminal tab"),
|
|
24
|
-
("open_shell", "Open Shell", "Open a shell in the selected worktree"),
|
|
25
28
|
("open_claude_resume", "Open Claude", "Open Claude Code for the task (resume if possible)"),
|
|
26
29
|
(
|
|
27
30
|
"dispatch_agent",
|
|
@@ -26,7 +26,6 @@ DEFAULT_KEYBINDINGS: dict[str, str] = {
|
|
|
26
26
|
"open_lazygit": "g",
|
|
27
27
|
"show_diff": "h",
|
|
28
28
|
"open_folder": "o",
|
|
29
|
-
"open_shell": "enter",
|
|
30
29
|
"open_editor": "e",
|
|
31
30
|
"open_claude_resume": "c",
|
|
32
31
|
"open_claude_gui_code": "C",
|
|
@@ -122,7 +121,6 @@ class Config:
|
|
|
122
121
|
claude_path: str = "claude"
|
|
123
122
|
claude_memory_dir: str = "~/.claude/tasktree-memory"
|
|
124
123
|
claude_repo_memory: bool = True
|
|
125
|
-
shell: str = ""
|
|
126
124
|
glab_path: str = "glab"
|
|
127
125
|
gh_path: str = "gh"
|
|
128
126
|
|
|
@@ -184,7 +182,6 @@ class Config:
|
|
|
184
182
|
claude_path = tools_config.get("claude_path", "claude")
|
|
185
183
|
claude_memory_dir = tools_config.get("claude_memory_dir", "~/.claude/tasktree-memory")
|
|
186
184
|
claude_repo_memory = bool(tools_config.get("claude_repo_memory", True))
|
|
187
|
-
shell = tools_config.get("shell", "")
|
|
188
185
|
glab_path = tools_config.get("glab_path", "glab")
|
|
189
186
|
gh_path = tools_config.get("gh_path", "gh")
|
|
190
187
|
|
|
@@ -221,8 +218,6 @@ class Config:
|
|
|
221
218
|
default_base_branch = os.environ["TASKTREE_DEFAULT_BRANCH"]
|
|
222
219
|
if "EDITOR" in os.environ and not editor:
|
|
223
220
|
editor = os.environ["EDITOR"]
|
|
224
|
-
if "SHELL" in os.environ and not shell:
|
|
225
|
-
shell = os.environ["SHELL"]
|
|
226
221
|
|
|
227
222
|
return cls(
|
|
228
223
|
repos_dir=repos_dir,
|
|
@@ -243,7 +238,6 @@ class Config:
|
|
|
243
238
|
claude_path=claude_path,
|
|
244
239
|
claude_memory_dir=claude_memory_dir,
|
|
245
240
|
claude_repo_memory=claude_repo_memory,
|
|
246
|
-
shell=shell,
|
|
247
241
|
glab_path=glab_path,
|
|
248
242
|
gh_path=gh_path,
|
|
249
243
|
forge_enabled=forge_enabled,
|
|
@@ -398,9 +392,6 @@ claude_memory_dir = "{self._toml_escape(self.claude_memory_dir)}"
|
|
|
398
392
|
# same repo and with the main checkout. Set to false to disable.
|
|
399
393
|
claude_repo_memory = {str(self.claude_repo_memory).lower()}
|
|
400
394
|
|
|
401
|
-
# Preferred shell (leave empty to use $SHELL)
|
|
402
|
-
shell = "{self._toml_escape(self.shell)}"
|
|
403
|
-
|
|
404
395
|
# Path to glab executable (GitLab CLI, used for MR/CI status)
|
|
405
396
|
glab_path = "{self._toml_escape(self.glab_path)}"
|
|
406
397
|
|
|
@@ -436,7 +427,6 @@ delete_task = "{self._toml_escape(self.keybindings.get("delete_task", "d"))}"
|
|
|
436
427
|
open_lazygit = "{self._toml_escape(self.keybindings.get("open_lazygit", "g"))}"
|
|
437
428
|
show_diff = "{self._toml_escape(self.keybindings.get("show_diff", "h"))}"
|
|
438
429
|
open_folder = "{self._toml_escape(self.keybindings.get("open_folder", "o"))}"
|
|
439
|
-
open_shell = "{self._toml_escape(self.keybindings.get("open_shell", "enter"))}"
|
|
440
430
|
open_editor = "{self._toml_escape(self.keybindings.get("open_editor", "e"))}"
|
|
441
431
|
open_claude_resume = "{self._toml_escape(self.keybindings.get("open_claude_resume", "c"))}"
|
|
442
432
|
open_claude_gui_code = "{self._toml_escape(self.keybindings.get("open_claude_gui_code", "C"))}"
|
|
@@ -513,12 +503,6 @@ blocklist = {self._toml_list(self.symlink_blocklist)}
|
|
|
513
503
|
|
|
514
504
|
return sorted(repos)
|
|
515
505
|
|
|
516
|
-
def get_shell(self) -> str:
|
|
517
|
-
"""Get the shell to use, with fallback to environment or /bin/bash."""
|
|
518
|
-
if self.shell:
|
|
519
|
-
return self.shell
|
|
520
|
-
return os.environ.get("SHELL", "/bin/bash")
|
|
521
|
-
|
|
522
506
|
def get_editor(self) -> str:
|
|
523
507
|
"""Get the editor to use, with fallback to environment or vi."""
|
|
524
508
|
if self.editor:
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/widgets/create_modal.py
RENAMED
|
@@ -684,9 +684,8 @@ class HelpModal(ThemedModalScreen[None]):
|
|
|
684
684
|
+ "\n"
|
|
685
685
|
)
|
|
686
686
|
tools_section += (
|
|
687
|
-
self._format_binding("
|
|
687
|
+
self._format_binding("open_editor", "e", "Open editor in new terminal tab") + "\n"
|
|
688
688
|
)
|
|
689
|
-
tools_section += self._format_binding("open_editor", "e", "Open editor in folder") + "\n"
|
|
690
689
|
tools_section += (
|
|
691
690
|
self._format_binding(
|
|
692
691
|
"open_claude_resume", "c", "Open Claude CLI (resume if session exists, else new)"
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/widgets/worktree_list.py
RENAMED
|
@@ -76,9 +76,8 @@ class WorktreeList(OptionList):
|
|
|
76
76
|
self._max_branch_len: int = 0
|
|
77
77
|
# Footer-visible (key, app action, description) bindings shown while
|
|
78
78
|
# this panel has focus; the keys also exist app-level (hidden) so
|
|
79
|
-
# they keep working regardless of focus.
|
|
80
|
-
#
|
|
81
|
-
# swallow the key before the app's open_shell binding could see it.
|
|
79
|
+
# they keep working regardless of focus. Keys are popped first so a
|
|
80
|
+
# context binding can override an OptionList default for that key.
|
|
82
81
|
for key, action, description in context_bindings or []:
|
|
83
82
|
self._bindings.key_to_bindings.pop(key, None)
|
|
84
83
|
self._bindings.bind(key, f"app.{action}", description, show=True)
|
|
@@ -391,21 +391,6 @@ class TestOpenExternalCommands:
|
|
|
391
391
|
# App should still be running
|
|
392
392
|
assert app.is_running
|
|
393
393
|
|
|
394
|
-
async def test_open_shell_no_worktree_warning(self, app, sample_repo, task_manager):
|
|
395
|
-
"""Test that shell with no worktree selected shows warning."""
|
|
396
|
-
async with app.run_test() as pilot:
|
|
397
|
-
await pilot.pause()
|
|
398
|
-
|
|
399
|
-
# Clear current worktree
|
|
400
|
-
app.current_worktree = None
|
|
401
|
-
|
|
402
|
-
# Press enter for shell
|
|
403
|
-
await pilot.press("enter")
|
|
404
|
-
await pilot.pause()
|
|
405
|
-
|
|
406
|
-
# App should still be running
|
|
407
|
-
assert app.is_running
|
|
408
|
-
|
|
409
394
|
|
|
410
395
|
class TestShowDiffAction:
|
|
411
396
|
"""Tests for the hunk diff action (h)."""
|
|
@@ -651,7 +636,6 @@ class TestContextSensitiveFooter:
|
|
|
651
636
|
assert "app.delete_task" in shown
|
|
652
637
|
# Worktree-context actions are hidden in this context
|
|
653
638
|
assert "app.open_lazygit" not in shown
|
|
654
|
-
assert "app.open_shell" not in shown
|
|
655
639
|
# Globals always visible
|
|
656
640
|
assert "cycle_theme" in shown
|
|
657
641
|
assert "refresh" in shown
|
|
@@ -675,7 +659,6 @@ class TestContextSensitiveFooter:
|
|
|
675
659
|
assert app.focused.id == "worktree-list"
|
|
676
660
|
shown = self._shown_actions(app)
|
|
677
661
|
assert "app.open_lazygit" in shown
|
|
678
|
-
assert "app.open_shell" in shown
|
|
679
662
|
assert "app.push_all" in shown
|
|
680
663
|
# Task-context actions are hidden in this context
|
|
681
664
|
assert "app.new_task" not in shown
|
|
@@ -699,22 +682,3 @@ class TestContextSensitiveFooter:
|
|
|
699
682
|
|
|
700
683
|
create_modals = [s for s in app.screen_stack if isinstance(s, CreateTaskModal)]
|
|
701
684
|
assert len(create_modals) == 1
|
|
702
|
-
|
|
703
|
-
async def test_enter_opens_shell_from_worktree_list(
|
|
704
|
-
self, app, sample_repo, task_manager, monkeypatch
|
|
705
|
-
):
|
|
706
|
-
"""Enter on the worktree list triggers open_shell (was swallowed by select)."""
|
|
707
|
-
repo_path, branch = sample_repo
|
|
708
|
-
task_manager.create_task("FOOTER-ENTER", ["sample-repo"], branch)
|
|
709
|
-
|
|
710
|
-
called = []
|
|
711
|
-
monkeypatch.setattr(app, "action_open_shell", lambda: called.append(True))
|
|
712
|
-
|
|
713
|
-
async with app.run_test() as pilot:
|
|
714
|
-
await pilot.pause()
|
|
715
|
-
await pilot.press("tab")
|
|
716
|
-
await pilot.pause()
|
|
717
|
-
await pilot.press("enter")
|
|
718
|
-
await pilot.pause()
|
|
719
|
-
|
|
720
|
-
assert called
|
|
@@ -131,7 +131,6 @@ class TestConfig:
|
|
|
131
131
|
assert config.editor == ""
|
|
132
132
|
assert config.lazygit_path == "lazygit"
|
|
133
133
|
assert config.hunk_path == "hunk"
|
|
134
|
-
assert config.shell == ""
|
|
135
134
|
assert config.archive_dir == ""
|
|
136
135
|
assert config.agent_poll_interval == 10
|
|
137
136
|
assert config.forge_poll_interval == 60
|
|
@@ -233,7 +232,6 @@ timeout = 60
|
|
|
233
232
|
editor = "nvim"
|
|
234
233
|
lazygit_path = "/usr/local/bin/lazygit"
|
|
235
234
|
hunk_path = "/usr/local/bin/hunk"
|
|
236
|
-
shell = "/bin/zsh"
|
|
237
235
|
'''
|
|
238
236
|
config_file.write_text(config_content)
|
|
239
237
|
|
|
@@ -254,24 +252,12 @@ shell = "/bin/zsh"
|
|
|
254
252
|
assert config.editor == "nvim"
|
|
255
253
|
assert config.lazygit_path == "/usr/local/bin/lazygit"
|
|
256
254
|
assert config.hunk_path == "/usr/local/bin/hunk"
|
|
257
|
-
assert config.shell == "/bin/zsh"
|
|
258
255
|
finally:
|
|
259
256
|
if old_xdg is not None:
|
|
260
257
|
os.environ["XDG_CONFIG_HOME"] = old_xdg
|
|
261
258
|
else:
|
|
262
259
|
os.environ.pop("XDG_CONFIG_HOME", None)
|
|
263
260
|
|
|
264
|
-
def test_get_shell_from_config(self):
|
|
265
|
-
"""Test get_shell returns config value when set."""
|
|
266
|
-
config = Config(shell="/bin/zsh")
|
|
267
|
-
assert config.get_shell() == "/bin/zsh"
|
|
268
|
-
|
|
269
|
-
def test_get_shell_fallback_to_env(self, monkeypatch):
|
|
270
|
-
"""Test get_shell falls back to environment."""
|
|
271
|
-
monkeypatch.setenv("SHELL", "/bin/fish")
|
|
272
|
-
config = Config(shell="")
|
|
273
|
-
assert config.get_shell() == "/bin/fish"
|
|
274
|
-
|
|
275
261
|
def test_get_editor_from_config(self):
|
|
276
262
|
"""Test get_editor returns config value when set."""
|
|
277
263
|
config = Config(editor="nvim")
|
|
@@ -304,7 +290,6 @@ shell = "/bin/zsh"
|
|
|
304
290
|
editor="vim",
|
|
305
291
|
lazygit_path="/opt/bin/lazygit",
|
|
306
292
|
hunk_path="/opt/bin/hunk",
|
|
307
|
-
shell="/bin/zsh",
|
|
308
293
|
glab_path="/opt/bin/glab",
|
|
309
294
|
gh_path="/opt/bin/gh",
|
|
310
295
|
forge_enabled=False,
|
|
@@ -329,7 +314,6 @@ shell = "/bin/zsh"
|
|
|
329
314
|
assert loaded.editor == "vim"
|
|
330
315
|
assert loaded.lazygit_path == "/opt/bin/lazygit"
|
|
331
316
|
assert loaded.hunk_path == "/opt/bin/hunk"
|
|
332
|
-
assert loaded.shell == "/bin/zsh"
|
|
333
317
|
assert loaded.archive_dir == "/tmp/archive"
|
|
334
318
|
assert loaded.agent_poll_interval == 15
|
|
335
319
|
assert loaded.forge_poll_interval == 90
|
|
File without changes
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/assets/README.md
RENAMED
|
File without changes
|
|
File without changes
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/assets/data_viewer.py
RENAMED
|
File without changes
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/assets/todo_app.py
RENAMED
|
File without changes
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/assets/worker_demo.py
RENAMED
|
File without changes
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/references/layouts.md
RENAMED
|
File without changes
|
|
File without changes
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/references/styling.md
RENAMED
|
File without changes
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/.claude/skills/textual-tui/references/widgets.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/services/agent_sessions.py
RENAMED
|
File without changes
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/services/claude_hooks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/services/task_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/widgets/messages_panel.py
RENAMED
|
File without changes
|
|
File without changes
|
{tasktree_manager-2.11.0 → tasktree_manager-2.11.1}/tasktree_manager/widgets/status_panel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|