tasktree-manager 2.7.2__tar.gz → 2.9.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 (64) hide show
  1. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.github/workflows/release.yml +4 -2
  2. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/CHANGELOG.md +23 -0
  3. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/PKG-INFO +1 -1
  4. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/docs/configuration.md +67 -9
  5. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/docs/user-guide.md +6 -0
  6. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/pyproject.toml +1 -1
  7. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/_version.py +2 -2
  8. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/app.py +312 -242
  9. tasktree_manager-2.9.0/tasktree_manager/services/claude_hooks.py +178 -0
  10. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/services/config.py +22 -1
  11. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/services/task_manager.py +154 -78
  12. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/widgets/create_modal.py +27 -25
  13. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/widgets/task_list.py +7 -2
  14. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/widgets/worktree_list.py +6 -2
  15. tasktree_manager-2.9.0/tests/test_claude_hooks.py +274 -0
  16. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/test_config.py +10 -0
  17. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/test_task_manager.py +196 -17
  18. tasktree_manager-2.7.2/tasktree_manager/services/claude_hooks.py +0 -71
  19. tasktree_manager-2.7.2/tests/test_claude_hooks.py +0 -106
  20. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.claude/skills/textual-tui/SKILL.md +0 -0
  21. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.claude/skills/textual-tui/assets/README.md +0 -0
  22. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.claude/skills/textual-tui/assets/dashboard_app.py +0 -0
  23. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.claude/skills/textual-tui/assets/data_viewer.py +0 -0
  24. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.claude/skills/textual-tui/assets/todo_app.py +0 -0
  25. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.claude/skills/textual-tui/assets/worker_demo.py +0 -0
  26. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.claude/skills/textual-tui/references/layouts.md +0 -0
  27. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.claude/skills/textual-tui/references/official-guides-index.md +0 -0
  28. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.claude/skills/textual-tui/references/styling.md +0 -0
  29. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.claude/skills/textual-tui/references/widgets.md +0 -0
  30. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.github/templates/CHANGELOG.md.j2 +0 -0
  31. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.github/workflows/ci.yml +0 -0
  32. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/.gitignore +0 -0
  33. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/CONTRIBUTING.md +0 -0
  34. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/LICENSE +0 -0
  35. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/OPENSPEC.md +0 -0
  36. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/README.md +0 -0
  37. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/ROADMAP.md +0 -0
  38. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/docs/claude-code-integration-spec.md +0 -0
  39. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/docs/installation.md +0 -0
  40. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/docs/troubleshooting.md +0 -0
  41. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/mise.toml +0 -0
  42. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/scripts/bump_version.py +0 -0
  43. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/__init__.py +0 -0
  44. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/commands.py +0 -0
  45. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/services/__init__.py +0 -0
  46. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/services/git_ops.py +0 -0
  47. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/services/models.py +0 -0
  48. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/themes.py +0 -0
  49. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/widgets/__init__.py +0 -0
  50. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/widgets/app_header.py +0 -0
  51. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/widgets/messages_panel.py +0 -0
  52. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/widgets/setup_modal.py +0 -0
  53. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tasktree_manager/widgets/status_panel.py +0 -0
  54. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/__init__.py +0 -0
  55. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/conftest.py +0 -0
  56. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/test_app.py +0 -0
  57. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/test_commands.py +0 -0
  58. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/test_git_ops.py +0 -0
  59. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/test_integration_git.py +0 -0
  60. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/test_memory.py +0 -0
  61. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/test_messages_panel.py +0 -0
  62. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/test_modals.py +0 -0
  63. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/test_setup_modal.py +0 -0
  64. {tasktree_manager-2.7.2 → tasktree_manager-2.9.0}/tests/test_status_panel.py +0 -0
@@ -77,9 +77,11 @@ jobs:
77
77
  release_type="${{ inputs.release_type || 'auto' }}"
78
78
 
79
79
  if [ "$release_type" = "auto" ]; then
80
- # Use semantic-release to determine bump type from commit history
80
+ # Use semantic-release to determine bump type from commit history.
81
+ # When no qualifying commits exist, `version --print` prints the
82
+ # CURRENT version — require an actual bump before releasing.
81
83
  VERSION=$(semantic-release version --print 2>/dev/null || echo "")
82
- if [ -n "$VERSION" ]; then
84
+ if [ -n "$VERSION" ] && [ "$VERSION" != "$current_version" ]; then
83
85
  # Determine bump type by comparing versions
84
86
  IFS='.' read -r cur_major cur_minor cur_patch <<< "$current_version"
85
87
  IFS='.' read -r new_major new_minor new_patch <<< "$VERSION"
@@ -15,6 +15,29 @@ 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.9.0] - 2026-07-14
19
+
20
+ ### Added
21
+ - **app**: Move blocking git work off the UI thread
22
+
23
+ ### Changed
24
+ - Format worktree settings call with ruff
25
+ - **app**: Extract Claude session preparation helper
26
+
27
+ ### Fixed
28
+ - Address review findings in async refresh and hook merging
29
+ - Harden task/branch validation, hook merging, markup rendering
30
+
31
+
32
+ ## [2.8.0] - 2026-07-14
33
+
34
+ ### Added
35
+ - **claude**: Per-repo Claude memory for worktree sessions
36
+
37
+ ### Changed
38
+ - Release only when semantic-release bumps the version
39
+
40
+
18
41
  ## [2.7.2] - 2026-07-13
19
42
 
20
43
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tasktree-manager
3
- Version: 2.7.2
3
+ Version: 2.9.0
4
4
  Summary: TUI application for managing worktree-based tasks
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.10
@@ -153,6 +153,18 @@ claude_path = "claude"
153
153
  # Default: "~/.claude/tasktree-memory"
154
154
  claude_memory_dir = "~/.claude/tasktree-memory"
155
155
 
156
+ # Per-repo Claude Code memory for worktree sessions.
157
+ # Sessions started inside a worktree would otherwise key their auto memory to
158
+ # the worktree path — memory that dies with the worktree. When enabled,
159
+ # tasktree-manager writes .claude/settings.local.json into each worktree
160
+ # pointing "autoMemoryDirectory" at the main repo's own memory directory
161
+ # (~/.claude/projects/<encoded-repo-path>/memory). What Claude learns in any
162
+ # worktree of a repo survives worktree deletion and is shared with future
163
+ # worktrees of that repo and with sessions in the main checkout. The settings
164
+ # file is added to the repo's .git/info/exclude so worktrees stay clean.
165
+ # Default: true
166
+ claude_repo_memory = true
167
+
156
168
  # Preferred shell
157
169
  # Leave empty to use $SHELL environment variable
158
170
  # Default: "" (uses $SHELL, falls back to /bin/bash)
@@ -210,7 +222,7 @@ cycle_theme = "t" # Cycle through the design-system themes
210
222
  # Symlinks
211
223
  # ============================================================================
212
224
  # When creating worktrees, gitignored files are symlinked from the source repo.
213
- # Blocklist specifies patterns to exclude (e.g., cache files).
225
+ # Blocklist specifies patterns to exclude (e.g., cache files, key material).
214
226
  [symlinks]
215
227
  blocklist = [
216
228
  "*.pyc",
@@ -221,6 +233,9 @@ blocklist = [
221
233
  ".ruff_cache",
222
234
  ".coverage",
223
235
  "*.log",
236
+ "*.pem",
237
+ "*.key",
238
+ "id_rsa",
224
239
  ]
225
240
  ```
226
241
 
@@ -376,7 +391,9 @@ When creating worktrees, tasktree-manager automatically symlinks gitignored file
376
391
 
377
392
  ### Default Blocklist
378
393
 
379
- By default, these patterns are excluded from symlinking:
394
+ By default, these patterns are excluded from symlinking. Caches and build
395
+ artifacts are excluded as noise; key material (`*.pem`, `*.key`, SSH keys)
396
+ is excluded so private keys never spread beyond the main checkout:
380
397
 
381
398
  ```toml
382
399
  [symlinks]
@@ -397,14 +414,28 @@ blocklist = [
397
414
  ".nox",
398
415
  "*.so",
399
416
  "*.dylib",
417
+ "*.pem",
418
+ "*.key",
419
+ "*.p12",
420
+ "*.pfx",
421
+ "*.keystore",
422
+ "id_rsa",
423
+ "id_ecdsa",
424
+ "id_ed25519",
400
425
  ]
401
426
  ```
402
427
 
403
428
  ### How It Works
404
429
 
405
- 1. When a worktree is created, tasktree-manager reads the source repo's `.gitignore`
406
- 2. For each gitignored file pattern, it finds matching files
407
- 3. Files matching the blocklist are skipped
430
+ 1. When a worktree is created, tasktree-manager asks git for the repo's
431
+ ignored files (`git ls-files --others --ignored --exclude-standard`), so
432
+ full gitignore semantics apply: nested `.gitignore` files work, and a
433
+ root pattern like `*.log` matches in subdirectories
434
+ 2. Wholly-ignored directories (e.g. `node_modules/`) are skipped entirely —
435
+ their contents are never walked or symlinked
436
+ 3. Files matching the blocklist (by filename or repo-relative path, so
437
+ patterns like `secrets/*` work) and anything under `.git/` or `.claude/`
438
+ are skipped
408
439
  4. Remaining files are symlinked to the worktree
409
440
 
410
441
  ### Customizing the Blocklist
@@ -425,7 +456,9 @@ blocklist = [
425
456
  ]
426
457
  ```
427
458
 
428
- **Add to default blocklist (must include all patterns):**
459
+ **Add to default blocklist (must include ALL default patterns — an explicit
460
+ blocklist replaces the defaults entirely, including the key-material
461
+ protection):**
429
462
  ```toml
430
463
  [symlinks]
431
464
  blocklist = [
@@ -433,22 +466,47 @@ blocklist = [
433
466
  "*.pyc",
434
467
  "*.pyo",
435
468
  "__pycache__",
469
+ ".pytest_cache",
470
+ ".mypy_cache",
471
+ ".ruff_cache",
436
472
  ".coverage",
437
473
  "*.log",
474
+ "*.egg-info",
475
+ ".eggs",
476
+ "dist",
477
+ "build",
478
+ ".tox",
479
+ ".nox",
480
+ "*.so",
481
+ "*.dylib",
482
+ "*.pem",
483
+ "*.key",
484
+ "*.p12",
485
+ "*.pfx",
486
+ "*.keystore",
487
+ "id_rsa",
488
+ "id_ecdsa",
489
+ "id_ed25519",
438
490
  # Your additions
439
491
  "*.tmp",
440
492
  ".DS_Store",
441
493
  ]
442
494
  ```
443
495
 
496
+ > **Upgrading note:** if your existing `config.toml` already contains a
497
+ > `blocklist`, it keeps overriding the defaults — the key-material patterns
498
+ > added in newer versions do NOT apply until you add them (or delete the
499
+ > `blocklist` line to inherit the defaults).
500
+
444
501
  ### Common Use Cases
445
502
 
446
- **Symlink environment files, block caches:**
503
+ **Symlink environment files, block caches and keys:**
447
504
  ```toml
448
505
  [symlinks]
449
- blocklist = ["*.pyc", "__pycache__", ".coverage", "*.log"]
506
+ blocklist = ["*.pyc", "__pycache__", ".coverage", "*.log", "*.pem", "*.key", "id_rsa"]
450
507
  ```
451
- This allows `.env`, `.mise.toml` to be symlinked while blocking cache files.
508
+ This allows `.env`, `.mise.toml` to be symlinked while blocking cache files
509
+ and private keys.
452
510
 
453
511
  **Block everything (disable symlinks):**
454
512
  ```toml
@@ -309,6 +309,11 @@ what is displayed. Press `?` for the full reference at any time.
309
309
  5. Select repos with `Space` (can select multiple)
310
310
  6. Tab to "Create" button, press `Enter`
311
311
 
312
+ Task names may contain letters, numbers, `.`, `_`, `/` and `-`. Because the
313
+ name becomes a directory under your tasks dir (and a git branch), names with
314
+ `.`/`..` path segments, empty segments, or a leading `-` are rejected; base
315
+ branch names must not start with `-` either.
316
+
312
317
  **Clone task workflow:**
313
318
  Useful when you're spinning up several similar tasks that share the same set of repos.
314
319
 
@@ -367,6 +372,7 @@ Useful when you're spinning up several similar tasks that share the same set of
367
372
  - 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.
368
373
  - Session status (`⟳` running, `!` waiting, `✓` ended) appears next to the task name
369
374
  - Both variants point the session's Claude Code auto-memory at a shared pool (`claude_memory_dir`, default `~/.claude/tasktree-memory`). Task folders are not git repos, so without this each task would get its own memory directory that is orphaned when the task is deleted; with the shared pool, what Claude learns in one task carries over to future tasks.
375
+ - Each worktree additionally gets its own `.claude/settings.local.json` pointing auto-memory at the **main repo's** memory directory (`claude_repo_memory`, enabled by default). Sessions started manually inside a worktree (`cd` + `claude`) would otherwise key their memory to the worktree path and lose it when the worktree is deleted; with this, what Claude learns about a repo persists across worktrees and is shared with sessions in the main checkout. The file is git-excluded so worktrees stay clean, and these sessions also report status back to the task indicator.
370
376
 
371
377
  ### General
372
378
 
@@ -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.7.2"
73
+ version = "2.9.0"
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.7.2'
22
- __version_tuple__ = version_tuple = (2, 7, 2)
21
+ __version__ = version = '2.9.0'
22
+ __version_tuple__ = version_tuple = (2, 9, 0)
23
23
 
24
24
  __commit_id__ = commit_id = None