tasktree-manager 2.18.0__tar.gz → 2.19.0__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.
Files changed (85) hide show
  1. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/CHANGELOG.md +9 -0
  2. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/PKG-INFO +1 -1
  3. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/README.md +81 -58
  4. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/docs/configuration.md +11 -0
  5. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/docs/user-guide.md +89 -31
  6. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/pyproject.toml +1 -1
  7. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/_version.py +2 -2
  8. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/app.py +193 -36
  9. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/cli.py +24 -21
  10. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/services/agent_sessions.py +6 -3
  11. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/services/claude_sessions.py +26 -22
  12. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/services/config.py +22 -0
  13. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/services/epic_ask.py +24 -11
  14. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/services/git_ops.py +1 -1
  15. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/services/models.py +4 -0
  16. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/services/task_manager.py +37 -9
  17. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_app.py +1 -0
  18. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_app_review_fixes.py +45 -6
  19. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_epic_ask.py +5 -3
  20. tasktree_manager-2.19.0/tests/test_review_findings.py +208 -0
  21. tasktree_manager-2.19.0/tests/test_terminal_tabs.py +225 -0
  22. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.claude/skills/textual-tui/SKILL.md +0 -0
  23. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.claude/skills/textual-tui/assets/README.md +0 -0
  24. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.claude/skills/textual-tui/assets/dashboard_app.py +0 -0
  25. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.claude/skills/textual-tui/assets/data_viewer.py +0 -0
  26. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.claude/skills/textual-tui/assets/todo_app.py +0 -0
  27. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.claude/skills/textual-tui/assets/worker_demo.py +0 -0
  28. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.claude/skills/textual-tui/references/layouts.md +0 -0
  29. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.claude/skills/textual-tui/references/official-guides-index.md +0 -0
  30. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.claude/skills/textual-tui/references/styling.md +0 -0
  31. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.claude/skills/textual-tui/references/widgets.md +0 -0
  32. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.github/templates/CHANGELOG.md.j2 +0 -0
  33. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.github/workflows/ci.yml +0 -0
  34. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.github/workflows/release.yml +0 -0
  35. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/.gitignore +0 -0
  36. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/CONTRIBUTING.md +0 -0
  37. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/LICENSE +0 -0
  38. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/ROADMAP.md +0 -0
  39. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/docs/claude-code-integration-spec.md +0 -0
  40. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/docs/installation.md +0 -0
  41. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/docs/troubleshooting.md +0 -0
  42. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/mise.toml +0 -0
  43. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/scripts/bump_version.py +0 -0
  44. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/__init__.py +0 -0
  45. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/commands.py +0 -0
  46. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/services/__init__.py +0 -0
  47. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/services/claude_hooks.py +0 -0
  48. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/services/epic_log.py +0 -0
  49. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/services/forge.py +0 -0
  50. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/themes.py +0 -0
  51. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/widgets/__init__.py +0 -0
  52. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/widgets/app_header.py +0 -0
  53. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/widgets/create_modal.py +0 -0
  54. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/widgets/messages_panel.py +0 -0
  55. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/widgets/setup_modal.py +0 -0
  56. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/widgets/status_panel.py +0 -0
  57. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/widgets/task_list.py +0 -0
  58. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tasktree_manager/widgets/worktree_list.py +0 -0
  59. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/__init__.py +0 -0
  60. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/conftest.py +0 -0
  61. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_agent_sessions.py +0 -0
  62. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_agent_sessions_decode.py +0 -0
  63. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_bump_version.py +0 -0
  64. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_claude_hooks.py +0 -0
  65. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_claude_sessions.py +0 -0
  66. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_cli.py +0 -0
  67. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_commands.py +0 -0
  68. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_config.py +0 -0
  69. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_epic_claude_md.py +0 -0
  70. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_epic_log.py +0 -0
  71. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_epic_tui.py +0 -0
  72. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_epics.py +0 -0
  73. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_forge.py +0 -0
  74. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_git_ops.py +0 -0
  75. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_integration_git.py +0 -0
  76. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_list_selection.py +0 -0
  77. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_medium_fixes.py +0 -0
  78. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_memory.py +0 -0
  79. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_messages_panel.py +0 -0
  80. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_modals.py +0 -0
  81. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_safety_fixes.py +0 -0
  82. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_setup_modal.py +0 -0
  83. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_status_panel.py +0 -0
  84. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_task_manager.py +0 -0
  85. {tasktree_manager-2.18.0 → tasktree_manager-2.19.0}/tests/test_worktree_badges.py +0 -0
@@ -15,6 +15,15 @@ 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.19.0] - 2026-10-29
19
+
20
+ ### Added
21
+ - Open zellij tabs that survive a terminal restart, and review fixes (#16)
22
+
23
+ ### Changed
24
+ - Split the headless CLI and epic sections into short parts (#15)
25
+
26
+
18
27
  ## [2.18.0] - 2026-10-20
19
28
 
20
29
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tasktree-manager
3
- Version: 2.18.0
3
+ Version: 2.19.0
4
4
  Summary: TUI application for managing worktree-based tasks
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.10
@@ -23,6 +23,7 @@
23
23
  - ✻ **Session Recap** - The Info panel shows each task's latest Claude Code session: title, last turn duration, and Claude's own idle recap of what was done and what comes next — read straight from the transcript, no CLI call
24
24
  - 🔀 **Forge-Aware Safety** - MR/PR and CI state via `glab`/`gh`; squash- and rebase-merged branches count as merged instead of blocking deletion forever
25
25
  - 🗂️ **Epics** - Group related tasks under an epic: a foldable row with rolled-up dirty and Claude state, `E` to attach tasks, and an epic log and `epic ask` so the Claude sessions of one epic share what they know
26
+ - 🧷 **zellij Tabs** - Run the TUI inside zellij (or set `zellij_autostart`) and `o`/`e`/`c` open zellij tabs that keep running across a terminal restart; pressing the key again jumps to the open tab ([details](docs/user-guide.md#zellij))
26
27
  - 📦 **Finish Flow with Archives** - Ending a task saves its combined diff (committed-but-unmerged + uncommitted) and the task-folder files outside the worktrees (plans, ADRs, notes) to an archive before deleting
27
28
 
28
29
  ## 📊 Status Indicators
@@ -76,72 +77,93 @@ For detailed workflows and examples, see the [User Guide](docs/user-guide.md).
76
77
 
77
78
  ### Headless CLI
78
79
 
79
- Running `tasktree-manager` with arguments skips the TUI — useful for scripts and AI-agent
80
- workflows (e.g. a skill that creates a Jira ticket and then the matching task):
80
+ Running `tasktree-manager` with arguments skips the TUI. Use it from scripts and AI agents,
81
+ for example a skill that creates a Jira ticket and then the matching task.
82
+
83
+ #### Tasks
81
84
 
82
85
  ```bash
83
- tasktree-manager create DIC-1901-argocd-tls --repos backend,frontend # --base overrides the default base branch
84
- tasktree-manager list --json # tasks with repos and state: clean / dirty / error (plain text without --json)
85
- tasktree-manager repos # available repos in REPOS_DIR
86
+ tasktree-manager create DIC-1901-argocd-tls --repos backend,frontend # --base sets the base branch
86
87
  tasktree-manager add-repo DIC-1901-argocd-tls infra
87
- tasktree-manager status # current task inferred from $PWD; also --json / --oneline / --forge
88
- tasktree-manager finish DIC-1901-argocd-tls # safety sweep -> archive diff + task files -> delete; --push / --no-archive / --force
89
- tasktree-manager delete DIC-1901-argocd-tls # archive diff + task files -> delete; refuses on unfinished work; --force overrides
88
+ tasktree-manager list --json # tasks, repos and state: clean / dirty / error
89
+ tasktree-manager repos # repos available in REPOS_DIR
90
+ tasktree-manager status # task of the current folder; --json, --oneline, --forge
91
+ tasktree-manager finish DIC-1901-argocd-tls # safety check, archive, delete; --push, --no-archive
92
+ tasktree-manager delete DIC-1901-argocd-tls # archive, delete; refuses unfinished work
90
93
  ```
91
94
 
92
- Epics group related tasks, for example a Jira epic and its child tasks. An epic is a task
93
- folder with a `.tasktree_kind` marker. It can hold its own notes, Claude session and
94
- worktrees. A child task names its epic in `.tasktree_epic`. Folders never move:
95
+ `status --oneline` prints a compact `TASK repoA✓ repoB●2↑1` line for a Claude Code
96
+ statusline or a shell prompt.
97
+
98
+ #### Epics
99
+
100
+ An epic groups related tasks, for example a Jira epic and its child tasks. It is a task
101
+ folder too, so it can hold notes, a Claude session and worktrees. Tasks stay in their own
102
+ folders; a marker file names their epic. Epics don't nest.
95
103
 
96
104
  ```bash
97
- tasktree-manager epic create DIC-2466-pcaps-epic # --repos is optional for an epic
105
+ tasktree-manager epic create DIC-2466-pcaps-epic # repos are optional for an epic
98
106
  tasktree-manager create DIC-2861-pcaps-logs --repos infra --epic DIC-2466-pcaps-epic
99
- tasktree-manager epic add DIC-2466-pcaps-epic DIC-2862-pcaps-s3 # attach or move existing tasks
100
- tasktree-manager epic remove DIC-2862-pcaps-s3 # detach
101
- tasktree-manager epic mark DIC-2466-pcaps-epic # turn an existing task into an epic; unmark reverts
102
- tasktree-manager epic list # epics and their tasks; also --json
103
- tasktree-manager epic note "chose layout A for the S3 keys" # add a note to this task's file in the epic log
104
- tasktree-manager epic log # notes and latest session recap of every task in the epic
105
- tasktree-manager epic log DIC-2862-pcaps-s3 --json # one task (or a whole epic by its name), as JSON
106
- tasktree-manager epic ask DIC-2862-pcaps-s3 "which bucket?" # a read-only copy of that task's latest session answers
107
- tasktree-manager epic sync # refresh the CLAUDE.md epic block after an upgrade
107
+ tasktree-manager epic add DIC-2466-pcaps-epic DIC-2862-pcaps-s3 # attach or move tasks
108
+ tasktree-manager epic remove DIC-2862-pcaps-s3 # detach
109
+ tasktree-manager epic mark DIC-2466-pcaps-epic # make an existing task an epic; unmark reverts
110
+ tasktree-manager epic list # epics and their tasks; --json
108
111
  ```
109
112
 
110
- Each task's `CLAUDE.md` gets a managed block naming its epic and how to find sibling
111
- tasks; it follows every epic change and leaves the rest of the file alone. The block
112
- also tells the session about the epic log: one notes file per task in `epic-log/` in the
113
- epic folder, written with `epic note` and read with `epic log`, which adds the recap of each
114
- task's latest Claude session (in the task folder or one of its worktrees). Inside a task
115
- folder, `epic note` writes only that task's notes and `epic log` reads only its epic;
116
- `epic note -` reads the text from stdin. Notes of a task that left the epic stay readable.
117
- `epic ask TASK "question"` resumes a copy of that task's latest Claude session to answer:
118
- forked and not saved, hooks off, only the Read/Grep/Glob tools, no MCP servers, so the
119
- task's own session and status stay untouched; credential files (`~/.ssh`, `~/.aws`, `.env`
120
- and similar) are denied to it. It exits 3 without asking when the task's session is
121
- running; the calling session then messages it with SendMessage (`--fork` asks a copy
122
- anyway). Each turn of the copy is a model call on that session's context, at most
123
- `--max-turns` (default 8); `--model sonnet` makes it cheaper.
124
- Epics don't
125
- nest. `list --json` and `status --json` carry `kind` (`task` or `epic`) and
126
- `epic` (the parent epic or `null`). `delete` and `finish` refuse an epic that still has
127
- tasks, even with `--force`.
128
-
129
- Exit code is 0 on success, 1 on failure with the reason on stderr, and 3 from `epic ask`
130
- when the task's session is running (its name goes to stdout). `delete` and `finish` run
131
- the same safety check as the TUI (uncommitted, unpushed, or unmerged work blocks deletion);
132
- with `glab`/`gh` installed, squash/rebase-merged branches are recognized as merged. If the
133
- diff cannot be archived for any repo, the CLI stops before deleting and prints the path of
134
- the partial archive; the TUI warns and deletes anyway. Files in the task folder outside the
135
- worktrees are copied to `<task>-<timestamp>.files/` next to the patch:
136
-
137
- - Skipped silently: hidden files, symlinks, `node_modules`/`vendor`/`.terraform` folders and
138
- an unedited generated `CLAUDE.md`.
139
- - Left out with a warning: key material (`*.pem`, `*.key`, `id_rsa`, kubeconfigs,
140
- `*.tfstate`, `credentials*.json`), files over 10 MB and files past 100 MB in total.
141
- - A file that cannot be read or copied stops `delete`/`finish` before anything is removed,
142
- unless `--force`; the TUI warns and deletes anyway.
143
- `status --oneline` emits a compact `TASK repoA✓ repoB●2↑1` summary that slots straight into
144
- a Claude Code statusline or shell prompt.
113
+ `list --json` and `status --json` show each task's `kind` (`task` or `epic`) and its `epic`.
114
+ `delete` and `finish` refuse an epic that still has tasks, even with `--force`.
115
+
116
+ #### Sharing context inside an epic
117
+
118
+ The Claude sessions of one epic can learn from each other:
119
+
120
+ | Command | What it does |
121
+ | --- | --- |
122
+ | `epic note "text"` | Adds a note to this task's file in the epic log (`-` reads stdin) |
123
+ | `epic log [TASK]` | Shows each task's notes and the recap of its latest Claude session; `--json` |
124
+ | `epic ask TASK "question"` | A read-only copy of that task's latest session answers |
125
+ | `epic sync` | Refreshes the epic block in existing tasks' `CLAUDE.md` after an upgrade |
126
+
127
+ Each task's `CLAUDE.md` gets a managed block that explains these commands to its session.
128
+
129
+ - **Scope:** inside a task folder, the commands reach only that task's epic.
130
+ - **Running sessions:** `epic ask` exits with code 3 when the task's session is running and
131
+ prints its name; the asking session then sends it a message with SendMessage.
132
+ - **Read-only:** the copy that answers can read files but not change them, can't read
133
+ credential files, and leaves the asked task's session untouched.
134
+ - **Cost:** each turn of the copy is a model call on that session's context, 8 turns at
135
+ most (`--max-turns`); `--model sonnet` makes it cheaper.
136
+
137
+ The [User Guide](docs/user-guide.md#epics) has the details.
138
+
139
+ #### Exit codes
140
+
141
+ | Code | Meaning |
142
+ | --- | --- |
143
+ | 0 | Success |
144
+ | 1 | Failure; the reason is on stderr |
145
+ | 3 | `epic ask` only: the task's session is running; its name is on stdout |
146
+
147
+ #### Safe delete and archive
148
+
149
+ `delete` and `finish` run the same safety check as the TUI: uncommitted, unpushed or
150
+ unmerged work blocks deletion unless `--force`. With `glab` or `gh` installed, squash and
151
+ rebase merges count as merged.
152
+
153
+ Before deleting, the CLI archives the task's remaining diff as a patch and copies the files
154
+ in the task folder outside the worktrees to `<task>-<timestamp>.files/` next to it
155
+ (`finish --no-archive` skips both):
156
+
157
+ - **Skipped:** hidden files, symlinks, `node_modules`, `vendor` and `.terraform` folders,
158
+ and an unedited generated `CLAUDE.md`.
159
+ - **Left out with a warning:** key material (`*.pem`, `*.key`, `id_rsa`, kubeconfigs,
160
+ `*.tfstate`, `credentials*.json`), files over 10 MB, and files past 100 MB in total.
161
+ - **Always stops the delete:** a diff that cannot be archived; the CLI prints the partial
162
+ archive's path.
163
+ - **Stops the delete unless `--force`:** a task file that cannot be read or copied.
164
+
165
+ In both cases the TUI stops too, names what was left out in the messages panel, and asks
166
+ again before it deletes.
145
167
 
146
168
  ## 📖 Documentation
147
169
 
@@ -227,8 +249,9 @@ Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
227
249
  - Git 2.0+
228
250
  - 256-color terminal
229
251
  - Unix-like OS (macOS, Linux, WSL2). Opening a folder, editor or Claude session in a
230
- new terminal tab (`o`, `e`, `c`) drives Ghostty via AppleScript and is macOS-only;
231
- elsewhere those keys show a notice
252
+ new terminal tab (`o`, `e`, `c`) opens a zellij tab when the TUI runs inside zellij,
253
+ on any OS. Otherwise it drives Ghostty via AppleScript and is macOS-only; elsewhere
254
+ those keys show a notice
232
255
 
233
256
  See [Installation Guide](docs/installation.md) for detailed requirements and setup.
234
257
 
@@ -210,6 +210,14 @@ glab_path = "glab"
210
210
  # Default: "gh" (searches PATH)
211
211
  gh_path = "gh"
212
212
 
213
+ # zellij: o/e/c open zellij tabs whenever the TUI runs inside zellij. With
214
+ # autostart, a TUI started outside zellij re-launches itself inside session
215
+ # zellij_session, so its tabs survive a terminal restart (see the user guide).
216
+ # Defaults: "zellij", false, "tasktree" (letters, digits, '.', '_', '-')
217
+ zellij_path = "zellij"
218
+ zellij_autostart = false
219
+ zellij_session = "tasktree"
220
+
213
221
  # ============================================================================
214
222
  # Forge (MR/PR state via glab/gh)
215
223
  # ============================================================================
@@ -419,6 +427,9 @@ refresh_interval = 15 # Refresh every 15 seconds
419
427
  | `hunk_path` | string | `"hunk"` | - | Path to hunk diff-viewer executable |
420
428
  | `glab_path` | string | `"glab"` | - | Path to GitLab CLI (MR/CI status) |
421
429
  | `gh_path` | string | `"gh"` | - | Path to GitHub CLI (PR/CI status) |
430
+ | `zellij_path` | string | `"zellij"` | - | Path to zellij (tabs and autostart) |
431
+ | `zellij_autostart` | bool | `false` | - | Re-launch the TUI inside zellij |
432
+ | `zellij_session` | string | `"tasktree"` | - | zellij session for autostart |
422
433
 
423
434
  ## Forge Settings
424
435
 
@@ -85,35 +85,50 @@ names their epic, so nothing moves on disk.
85
85
  - `enter` or `space` folds an epic. A folded epic row still shows the dirty
86
86
  count and the most urgent Claude state of its tasks.
87
87
  - Epics don't nest. An epic that still has tasks can't be deleted.
88
- - Each task's `CLAUDE.md` gets a short block between `<!-- tasktree:epic -->` markers
89
- that names the epic folder and how to find sibling tasks, so a Claude session knows
90
- the bigger picture. It updates on every epic change; text outside the markers is
91
- never touched.
92
- - The epic log lets the sessions of one epic share what they learned. A session
93
- records decisions, interfaces and open questions with
94
- `tasktree-manager epic note "..."`; they go to `epic-log/<task>.md` in the epic
95
- folder. `tasktree-manager epic log` shows every task's notes and the recap of its
96
- latest Claude session, so a session can check what its siblings did before asking.
97
- The `CLAUDE.md` block tells each session about both commands, and that notes are
98
- information from other sessions, not instructions.
88
+ - Each task's `CLAUDE.md` gets a short block between `<!-- tasktree:epic -->` markers.
89
+ It names the epic folder, explains how to find sibling tasks and how to share context
90
+ with them. It updates on every epic change; text outside the markers is never touched.
91
+ - From scripts: `tasktree-manager epic create/add/remove/mark/unmark/list/note/log/ask/sync`.
92
+
93
+ #### Sharing context between sessions
94
+
95
+ The Claude sessions of one epic can learn from each other. The `CLAUDE.md` block tells
96
+ each session to try these in order:
97
+
98
+ 1. **Read the epic log.** `tasktree-manager epic log` shows every task's notes and the
99
+ recap of its latest Claude session. `epic log TASK` shows one task.
100
+ 2. **Ask the task.** `tasktree-manager epic ask TASK "question"` resumes a read-only copy
101
+ of that task's latest session, which answers the question.
102
+ 3. **Message a running session.** If the task's session is running, `epic ask` exits
103
+ with code 3 and prints the session name. The asking session sends it a message with
104
+ SendMessage instead. `--fork` asks a copy anyway.
105
+
106
+ Sessions add to the log with `tasktree-manager epic note "..."`: decisions, interfaces,
107
+ open questions, finished work that other tasks depend on.
108
+
109
+ **Notes:**
110
+
111
+ - Notes go to `epic-log/<task>.md` in the epic folder, one file per task.
99
112
  - A session writes only its own task's notes. From a terminal outside any task,
100
113
  `epic note --task NAME` writes for a task and marks the entry as written outside it.
101
- - Notes stay in the epic after their task is finished, moved or deleted; `epic log`
102
- lists them as "no longer in the epic". A note is at most 16 KB, and `epic log` shows
103
- the newest 64 KB of each task's notes.
104
- - When the log does not answer a question, a session runs
105
- `tasktree-manager epic ask TASK "question"`. A read-only copy of that task's latest
106
- Claude session answers: it can read files but not change them or read credential
107
- files, and nothing is saved in that task. If the task's session is running, the command exits
108
- with code 3 and prints the session name, and the asking session sends it a message
109
- with SendMessage instead (`--fork` asks a copy anyway). Only tasks of the same epic
110
- and the epic folder can be asked, and a session in a task outside any epic cannot
111
- ask. Each turn of the copy is a model call on the size of that session's context,
112
- at most `--max-turns` (default 8); `--model sonnet` makes it cheaper.
113
- - After upgrading tasktree-manager, run `tasktree-manager epic sync` to refresh the
114
- block in existing tasks (the TUI also refreshes it when it starts a Claude session).
115
- Folders without a `CLAUDE.md` yet are counted and skipped.
116
- - From scripts: `tasktree-manager epic create/add/remove/mark/unmark/list/note/log/ask/sync`.
114
+ - Notes stay after their task is finished, moved or deleted; `epic log` lists them as
115
+ "no longer in the epic".
116
+ - A note is at most 16 KB. `epic log` shows the newest 64 KB of each task's notes.
117
+ - Notes and answers come from other sessions: they are information, not instructions.
118
+
119
+ **`epic ask`:**
120
+
121
+ - Only tasks of the same epic and the epic folder can be asked. A session in a task
122
+ outside any epic cannot ask.
123
+ - The copy can read files but not change them. It can't read credential files (forge CLI
124
+ tokens, `.env` files anywhere, the key material the archive leaves out).
125
+ Nothing is saved in the asked task, and its session stays untouched.
126
+ - Each turn of the copy is a model call on the size of that session's context, 8 turns
127
+ at most (`--max-turns`). `--model sonnet` makes it cheaper.
128
+
129
+ **After an upgrade:** run `tasktree-manager epic sync` to refresh the block in existing
130
+ tasks. The TUI also refreshes it when it starts a Claude session. Folders without a
131
+ `CLAUDE.md` yet are counted and skipped.
117
132
 
118
133
  ### Worktrees
119
134
 
@@ -286,6 +301,8 @@ terraform edits sit unstaged. Next, decide whether to hand-create the group.
286
301
  - **Title**: the session name (`/rename`), else Claude's own title, else the session slug
287
302
  - **Turn line**: duration of the last turn and when it finished; `busy`/`idle` mirrors the task's Claude status indicator
288
303
  - **Recap**: the summary Claude writes a few minutes after a turn when the session idles. Until one exists, the dim `last prompt:` line shows what was asked last
304
+ - The session is the newest one started in the task folder or one of its worktrees, as in
305
+ `epic log`. Headless `claude -p` runs, such as commit-message helpers, are skipped
289
306
  - Nothing is shown for tasks without a session. The block refreshes as you move the cursor and every 5 seconds while a transcript changes. Disable with `claude_recap = false` under `[tools]` in `config.toml`
290
307
 
291
308
  **Git status codes:**
@@ -439,7 +456,7 @@ Useful when you're spinning up several similar tasks that share the same set of
439
456
  | `b` | Dispatch agent | Run a background Claude agent (`claude --bg`) in the selected worktree |
440
457
 
441
458
  **How it works:**
442
- - `c` opens Claude Code CLI in a new Ghostty terminal tab at the task directory.
459
+ - `c` opens Claude Code CLI in a new terminal tab at the task directory (a zellij tab inside zellij, see [zellij](#zellij)).
443
460
  - If `~/.claude/projects/<task-folder>/` already has session transcripts, runs `claude -r` to pick from them; otherwise runs `claude` to start fresh.
444
461
  - `C` opens the Claude desktop app via the `claude://code/new?folder=<task-folder>` URL scheme.
445
462
  - The CLI variant (`c`) generates a task-level `CLAUDE.md` with task/worktree context, copies each repo's own `CLAUDE.md` into worktrees whose branch predates it (never generating stub content), and installs hooks that report session status back to tasktree-manager.
@@ -840,9 +857,9 @@ npm i -g hunkdiff
840
857
 
841
858
  **Direct editor integration:**
842
859
  1. Highlight task or worktree
843
- 2. Press `e` to open your configured editor in a new Ghostty tab (`$EDITOR .`)
844
- 3. tasktree-manager keeps running alongside the editor (macOS + Ghostty only, see
845
- Limitations)
860
+ 2. Press `e` to open your configured editor in a new terminal tab (`$EDITOR .`)
861
+ 3. tasktree-manager keeps running alongside the editor (a zellij tab inside zellij,
862
+ otherwise a Ghostty tab on macOS; see [zellij](#zellij))
846
863
 
847
864
  **Configure your editor** in `~/.config/tasktree-manager/config.toml`:
848
865
  ```toml
@@ -873,6 +890,47 @@ code ~/tasks/FEAT-123 # Open entire task as workspace
873
890
  - Use "Open Folder" or equivalent
874
891
  - Git integration works normally
875
892
 
893
+ ### zellij
894
+
895
+ `o`, `e` and `c` open a new terminal tab. Where the tab lives depends on where
896
+ tasktree-manager runs:
897
+
898
+ - Inside a zellij session (`ZELLIJ_SESSION_NAME` is set), each key opens a zellij tab
899
+ named after the folder and what runs in it: `TASK · shell`, `TASK/repo · editor`,
900
+ `TASK · claude`. The tab runs in the zellij server, so it keeps running when you close
901
+ or restart the terminal. Pressing the key again while that tab is open switches to it
902
+ instead of opening a second one.
903
+ - Anywhere else, each key opens a Ghostty tab via AppleScript (macOS only).
904
+
905
+ To run the TUI in zellij every time, set this in `config.toml`:
906
+
907
+ ```toml
908
+ [tools]
909
+ zellij_autostart = true
910
+ zellij_session = "tasktree" # the default
911
+ ```
912
+
913
+ Plain `tasktree-manager` then re-launches itself as
914
+ `zellij attach -c tasktree -- python -m tasktree_manager.app`. Without the setting, start
915
+ it that way yourself: `zellij attach -c tasktree -- tasktree-manager`.
916
+
917
+ After a terminal restart, run `tasktree-manager` (with autostart) or
918
+ `zellij attach tasktree`. Either brings back the TUI and every tab it opened, Claude
919
+ sessions included. The command after `--` runs only when the session is created, so a
920
+ second `tasktree-manager` in another terminal tab shows the same session.
921
+
922
+ - A tab that runs a command (`e`, `c`) closes when the command exits.
923
+ - The zellij tab runs the command with `sh -c`, so `editor` and `claude_path` must name
924
+ programs on `PATH`. A shell alias or fish function works only in the Ghostty tab.
925
+ - A zellij session does not survive a reboot. Attaching afterwards (or autostart) makes
926
+ zellij resurrect the saved layout, and each tab waits for ENTER before its command runs
927
+ again; tasktree-manager does not test that path.
928
+ - Autostart applies only when the TUI starts in a terminal outside zellij, never to a
929
+ piped run or to the headless CLI.
930
+ - zellij's default keybindings take Ctrl+P, Ctrl+T, Ctrl+O and other keys before the TUI
931
+ or Claude Code sees them. Pick zellij's "Unlock First (non-colliding)" preset or Alt-based
932
+ bindings, so Ctrl+P still opens the command palette.
933
+
876
934
  ### CI/CD
877
935
 
878
936
  tasktree-manager creates normal git branches, so CI/CD works seamlessly:
@@ -71,7 +71,7 @@ version-file = "tasktree_manager/_version.py"
71
71
  # Semantic Release Configuration
72
72
  [tool.semantic_release]
73
73
  version_toml = ["pyproject.toml:tool.semantic_release.version"]
74
- version = "2.18.0"
74
+ version = "2.19.0"
75
75
  tag_format = "v{version}"
76
76
  branch = "main"
77
77
  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.18.0'
22
- __version_tuple__ = version_tuple = (2, 18, 0)
21
+ __version__ = version = '2.19.0'
22
+ __version_tuple__ = version_tuple = (2, 19, 0)
23
23
 
24
24
  __commit_id__ = commit_id = None