@jahanxu/trellis 0.4.2 → 0.5.1

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 (67) hide show
  1. package/dist/configurators/workflow.d.ts.map +1 -1
  2. package/dist/configurators/workflow.js +58 -1
  3. package/dist/configurators/workflow.js.map +1 -1
  4. package/dist/constants/paths.d.ts +17 -0
  5. package/dist/constants/paths.d.ts.map +1 -1
  6. package/dist/constants/paths.js +19 -0
  7. package/dist/constants/paths.js.map +1 -1
  8. package/dist/templates/claude/commands/trellis/handoff.md +90 -387
  9. package/dist/templates/claude/commands/trellis/pick-task.md +74 -444
  10. package/dist/templates/claude/commands/trellis/update-spec.md +19 -3
  11. package/dist/templates/claude/hooks/inject-subagent-context.py +17 -101
  12. package/dist/templates/claude/hooks/ralph-loop.py +1 -0
  13. package/dist/templates/claude/hooks/session-start.py +170 -54
  14. package/dist/templates/iflow/commands/trellis/handoff.md +148 -0
  15. package/dist/templates/iflow/commands/trellis/pick-task.md +145 -0
  16. package/dist/templates/iflow/commands/trellis/update-spec.md +19 -3
  17. package/dist/templates/iflow/hooks/inject-subagent-context.py +1 -0
  18. package/dist/templates/iflow/hooks/ralph-loop.py +1 -0
  19. package/dist/templates/iflow/hooks/session-start.py +171 -0
  20. package/dist/templates/markdown/index.d.ts +9 -0
  21. package/dist/templates/markdown/index.d.ts.map +1 -1
  22. package/dist/templates/markdown/index.js +10 -0
  23. package/dist/templates/markdown/index.js.map +1 -1
  24. package/dist/templates/markdown/spec/roles/designer/index.md.txt +57 -0
  25. package/dist/templates/markdown/spec/roles/designer/mock-data-standards.md.txt +63 -0
  26. package/dist/templates/markdown/spec/roles/designer/prototype-guidelines.md.txt +49 -0
  27. package/dist/templates/markdown/spec/roles/frontend-impl/api-integration.md.txt +63 -0
  28. package/dist/templates/markdown/spec/roles/frontend-impl/index.md.txt +57 -0
  29. package/dist/templates/markdown/spec/roles/frontend-impl/prototype-to-production.md.txt +57 -0
  30. package/dist/templates/markdown/spec/roles/pm/index.md.txt +45 -0
  31. package/dist/templates/markdown/spec/roles/pm/prd-template.md.txt +64 -0
  32. package/dist/templates/markdown/spec/roles/pm/requirement-checklist.md.txt +43 -0
  33. package/dist/templates/trellis/index.d.ts +1 -0
  34. package/dist/templates/trellis/index.d.ts.map +1 -1
  35. package/dist/templates/trellis/index.js +2 -0
  36. package/dist/templates/trellis/index.js.map +1 -1
  37. package/dist/templates/trellis/scripts/add_session.py +3 -2
  38. package/dist/templates/trellis/scripts/common/cli_adapter.py +4 -4
  39. package/dist/templates/trellis/scripts/common/developer.py +4 -4
  40. package/dist/templates/trellis/scripts/common/git_context.py +7 -7
  41. package/dist/templates/trellis/scripts/common/paths.py +64 -14
  42. package/dist/templates/trellis/scripts/common/phase.py +2 -2
  43. package/dist/templates/trellis/scripts/common/registry.py +16 -16
  44. package/dist/templates/trellis/scripts/common/task_queue.py +10 -10
  45. package/dist/templates/trellis/scripts/common/task_utils.py +5 -5
  46. package/dist/templates/trellis/scripts/common/worktree.py +8 -8
  47. package/dist/templates/trellis/scripts/pool.py +214 -266
  48. package/dist/templates/trellis/scripts/task.py +3 -116
  49. package/package.json +3 -3
  50. package/dist/templates/claude/commands/trellis/before-role-work.md +0 -364
  51. package/dist/templates/trellis/VERSION +0 -1
  52. package/dist/templates/trellis/deliverables/README.md +0 -51
  53. package/dist/templates/trellis/paths.README.md +0 -277
  54. package/dist/templates/trellis/paths.yaml +0 -41
  55. package/dist/templates/trellis/pool/implementations.json +0 -5
  56. package/dist/templates/trellis/pool/prototypes.json +0 -5
  57. package/dist/templates/trellis/pool/requirements.json +0 -5
  58. package/dist/templates/trellis/scripts/common/project_paths.py +0 -189
  59. package/dist/templates/trellis/scripts/handoff_generator.py +0 -380
  60. package/dist/templates/trellis/spec/roles/designer/index.md +0 -243
  61. package/dist/templates/trellis/spec/roles/designer/mock-data-standards.md +0 -481
  62. package/dist/templates/trellis/spec/roles/designer/prototype-guidelines.md +0 -429
  63. package/dist/templates/trellis/spec/roles/frontend-impl/api-integration.md +0 -565
  64. package/dist/templates/trellis/spec/roles/frontend-impl/index.md +0 -321
  65. package/dist/templates/trellis/spec/roles/frontend-impl/state-management.md +0 -599
  66. package/dist/templates/trellis/spec/roles/pm/index.md +0 -112
  67. package/dist/templates/trellis/spec/roles/pm/prd-template.md +0 -124
@@ -10,10 +10,11 @@ Provides:
10
10
  get_active_journal_file - Get current journal file
11
11
  """
12
12
 
13
+ from __future__ import annotations
14
+
13
15
  import re
14
16
  from datetime import datetime
15
17
  from pathlib import Path
16
- from typing import Optional
17
18
 
18
19
 
19
20
  # =============================================================================
@@ -27,19 +28,23 @@ DIR_TASKS = "tasks"
27
28
  DIR_ARCHIVE = "archive"
28
29
  DIR_SPEC = "spec"
29
30
  DIR_SCRIPTS = "scripts"
31
+ DIR_POOL = "pool"
32
+ DIR_DELIVERABLES = "deliverables"
30
33
 
31
34
  # File names
32
35
  FILE_DEVELOPER = ".developer"
33
36
  FILE_CURRENT_TASK = ".current-task"
34
37
  FILE_TASK_JSON = "task.json"
35
38
  FILE_JOURNAL_PREFIX = "journal-"
39
+ FILE_SOURCE_JSON = "source.json"
40
+ FILE_HANDOFF_MD = "HANDOFF.md"
36
41
 
37
42
 
38
43
  # =============================================================================
39
44
  # Repository Root
40
45
  # =============================================================================
41
46
 
42
- def get_repo_root(start_path: Optional[Path] = None) -> Path:
47
+ def get_repo_root(start_path: Path | None = None) -> Path:
43
48
  """Find the nearest directory containing .trellis/ folder.
44
49
 
45
50
  This handles nested git repos correctly (e.g., test project inside another repo).
@@ -65,7 +70,7 @@ def get_repo_root(start_path: Optional[Path] = None) -> Path:
65
70
  # Developer
66
71
  # =============================================================================
67
72
 
68
- def get_developer(repo_root: Optional[Path] = None) -> Optional[str]:
73
+ def get_developer(repo_root: Path | None = None) -> str | None:
69
74
  """Get developer name from .developer file.
70
75
 
71
76
  Args:
@@ -93,7 +98,7 @@ def get_developer(repo_root: Optional[Path] = None) -> Optional[str]:
93
98
  return None
94
99
 
95
100
 
96
- def check_developer(repo_root: Optional[Path] = None) -> bool:
101
+ def check_developer(repo_root: Path | None = None) -> bool:
97
102
  """Check if developer is initialized.
98
103
 
99
104
  Args:
@@ -109,7 +114,7 @@ def check_developer(repo_root: Optional[Path] = None) -> bool:
109
114
  # Tasks Directory
110
115
  # =============================================================================
111
116
 
112
- def get_tasks_dir(repo_root: Optional[Path] = None) -> Path:
117
+ def get_tasks_dir(repo_root: Path | None = None) -> Path:
113
118
  """Get tasks directory path.
114
119
 
115
120
  Args:
@@ -127,7 +132,7 @@ def get_tasks_dir(repo_root: Optional[Path] = None) -> Path:
127
132
  # Workspace Directory
128
133
  # =============================================================================
129
134
 
130
- def get_workspace_dir(repo_root: Optional[Path] = None) -> Optional[Path]:
135
+ def get_workspace_dir(repo_root: Path | None = None) -> Path | None:
131
136
  """Get developer workspace directory.
132
137
 
133
138
  Args:
@@ -149,7 +154,7 @@ def get_workspace_dir(repo_root: Optional[Path] = None) -> Optional[Path]:
149
154
  # Journal File
150
155
  # =============================================================================
151
156
 
152
- def get_active_journal_file(repo_root: Optional[Path] = None) -> Optional[Path]:
157
+ def get_active_journal_file(repo_root: Path | None = None) -> Path | None:
153
158
  """Get the current active journal file.
154
159
 
155
160
  Args:
@@ -165,7 +170,7 @@ def get_active_journal_file(repo_root: Optional[Path] = None) -> Optional[Path]:
165
170
  if workspace_dir is None or not workspace_dir.is_dir():
166
171
  return None
167
172
 
168
- latest: Optional[Path] = None
173
+ latest: Path | None = None
169
174
  highest = 0
170
175
 
171
176
  for f in workspace_dir.glob(f"{FILE_JOURNAL_PREFIX}*.md"):
@@ -206,7 +211,7 @@ def count_lines(file_path: Path) -> int:
206
211
  # Current Task Management
207
212
  # =============================================================================
208
213
 
209
- def _get_current_task_file(repo_root: Optional[Path] = None) -> Path:
214
+ def _get_current_task_file(repo_root: Path | None = None) -> Path:
210
215
  """Get .current-task file path.
211
216
 
212
217
  Args:
@@ -220,7 +225,7 @@ def _get_current_task_file(repo_root: Optional[Path] = None) -> Path:
220
225
  return repo_root / DIR_WORKFLOW / FILE_CURRENT_TASK
221
226
 
222
227
 
223
- def get_current_task(repo_root: Optional[Path] = None) -> Optional[str]:
228
+ def get_current_task(repo_root: Path | None = None) -> str | None:
224
229
  """Get current task directory path (relative to repo_root).
225
230
 
226
231
  Args:
@@ -240,7 +245,7 @@ def get_current_task(repo_root: Optional[Path] = None) -> Optional[str]:
240
245
  return None
241
246
 
242
247
 
243
- def get_current_task_abs(repo_root: Optional[Path] = None) -> Optional[Path]:
248
+ def get_current_task_abs(repo_root: Path | None = None) -> Path | None:
244
249
  """Get current task directory absolute path.
245
250
 
246
251
  Args:
@@ -258,7 +263,7 @@ def get_current_task_abs(repo_root: Optional[Path] = None) -> Optional[Path]:
258
263
  return None
259
264
 
260
265
 
261
- def set_current_task(task_path: str, repo_root: Optional[Path] = None) -> bool:
266
+ def set_current_task(task_path: str, repo_root: Path | None = None) -> bool:
262
267
  """Set current task.
263
268
 
264
269
  Args:
@@ -288,7 +293,7 @@ def set_current_task(task_path: str, repo_root: Optional[Path] = None) -> bool:
288
293
  return False
289
294
 
290
295
 
291
- def clear_current_task(repo_root: Optional[Path] = None) -> bool:
296
+ def clear_current_task(repo_root: Path | None = None) -> bool:
292
297
  """Clear current task.
293
298
 
294
299
  Args:
@@ -307,7 +312,7 @@ def clear_current_task(repo_root: Optional[Path] = None) -> bool:
307
312
  return False
308
313
 
309
314
 
310
- def has_current_task(repo_root: Optional[Path] = None) -> bool:
315
+ def has_current_task(repo_root: Path | None = None) -> bool:
311
316
  """Check if has current task.
312
317
 
313
318
  Args:
@@ -319,6 +324,51 @@ def has_current_task(repo_root: Optional[Path] = None) -> bool:
319
324
  return get_current_task(repo_root) is not None
320
325
 
321
326
 
327
+ # =============================================================================
328
+ # Pool Directory
329
+ # =============================================================================
330
+
331
+ def get_pool_dir(repo_root: Path | None = None) -> Path:
332
+ """Get pool directory path.
333
+
334
+ Args:
335
+ repo_root: Repository root path. Defaults to auto-detected.
336
+
337
+ Returns:
338
+ Path to pool directory.
339
+ """
340
+ if repo_root is None:
341
+ repo_root = get_repo_root()
342
+ return repo_root / DIR_WORKFLOW / DIR_POOL
343
+
344
+
345
+ def get_pool_file(pool_name: str, repo_root: Path | None = None) -> Path:
346
+ """Get pool JSON file path.
347
+
348
+ Args:
349
+ pool_name: Pool name (e.g., 'requirements').
350
+ repo_root: Repository root path. Defaults to auto-detected.
351
+
352
+ Returns:
353
+ Path to pool JSON file.
354
+ """
355
+ return get_pool_dir(repo_root) / f"{pool_name}.json"
356
+
357
+
358
+ def get_deliverables_dir(repo_root: Path | None = None) -> Path:
359
+ """Get deliverables directory path (at project root).
360
+
361
+ Args:
362
+ repo_root: Repository root path. Defaults to auto-detected.
363
+
364
+ Returns:
365
+ Path to deliverables directory.
366
+ """
367
+ if repo_root is None:
368
+ repo_root = get_repo_root()
369
+ return repo_root / DIR_DELIVERABLES
370
+
371
+
322
372
  # =============================================================================
323
373
  # Task ID Generation
324
374
  # =============================================================================
@@ -17,13 +17,13 @@ Provides:
17
17
  is_current_action - Check if at specific action
18
18
  """
19
19
 
20
+ from __future__ import annotations
20
21
 
21
22
  import json
22
23
  from pathlib import Path
23
- from typing import Optional
24
24
 
25
25
 
26
- def _read_json_file(path: Path) -> Optional[dict]:
26
+ def _read_json_file(path: Path) -> dict | None:
27
27
  """Read and parse a JSON file."""
28
28
  try:
29
29
  return json.loads(path.read_text(encoding="utf-8"))
@@ -14,17 +14,17 @@ Provides:
14
14
  registry_list_agents - List all agents
15
15
  """
16
16
 
17
+ from __future__ import annotations
17
18
 
18
19
  import json
19
20
  from datetime import datetime
20
21
  from pathlib import Path
21
- from typing import Optional
22
22
 
23
23
  from .paths import get_repo_root
24
24
  from .worktree import get_agents_dir
25
25
 
26
26
 
27
- def _read_json_file(path: Path) -> Optional[dict]:
27
+ def _read_json_file(path: Path) -> dict | None:
28
28
  """Read and parse a JSON file."""
29
29
  try:
30
30
  return json.loads(path.read_text(encoding="utf-8"))
@@ -45,7 +45,7 @@ def _write_json_file(path: Path, data: dict) -> bool:
45
45
  # Registry File Access
46
46
  # =============================================================================
47
47
 
48
- def registry_get_file(repo_root: Optional[Path] = None) -> Optional[Path]:
48
+ def registry_get_file(repo_root: Path | None = None) -> Path | None:
49
49
  """Get registry file path.
50
50
 
51
51
  Args:
@@ -63,7 +63,7 @@ def registry_get_file(repo_root: Optional[Path] = None) -> Optional[Path]:
63
63
  return None
64
64
 
65
65
 
66
- def _ensure_registry(repo_root: Optional[Path] = None) -> Optional[Path]:
66
+ def _ensure_registry(repo_root: Path | None = None) -> Path | None:
67
67
  """Ensure registry file exists with valid structure.
68
68
 
69
69
  Args:
@@ -98,8 +98,8 @@ def _ensure_registry(repo_root: Optional[Path] = None) -> Optional[Path]:
98
98
 
99
99
  def registry_get_agent_by_id(
100
100
  agent_id: str,
101
- repo_root: Optional[Path] = None
102
- ) -> Optional[dict]:
101
+ repo_root: Path | None = None
102
+ ) -> dict | None:
103
103
  """Get agent by ID.
104
104
 
105
105
  Args:
@@ -129,8 +129,8 @@ def registry_get_agent_by_id(
129
129
 
130
130
  def registry_get_agent_by_worktree(
131
131
  worktree_path: str,
132
- repo_root: Optional[Path] = None
133
- ) -> Optional[dict]:
132
+ repo_root: Path | None = None
133
+ ) -> dict | None:
134
134
  """Get agent by worktree path.
135
135
 
136
136
  Args:
@@ -160,8 +160,8 @@ def registry_get_agent_by_worktree(
160
160
 
161
161
  def registry_search_agent(
162
162
  search: str,
163
- repo_root: Optional[Path] = None
164
- ) -> Optional[dict]:
163
+ repo_root: Path | None = None
164
+ ) -> dict | None:
165
165
  """Search agent by ID or task_dir containing search term.
166
166
 
167
167
  Args:
@@ -196,8 +196,8 @@ def registry_search_agent(
196
196
 
197
197
  def registry_get_task_dir(
198
198
  worktree_path: str,
199
- repo_root: Optional[Path] = None
200
- ) -> Optional[str]:
199
+ repo_root: Path | None = None
200
+ ) -> str | None:
201
201
  """Get task directory for a worktree.
202
202
 
203
203
  Args:
@@ -217,7 +217,7 @@ def registry_get_task_dir(
217
217
  # Agent Modification
218
218
  # =============================================================================
219
219
 
220
- def registry_remove_by_id(agent_id: str, repo_root: Optional[Path] = None) -> bool:
220
+ def registry_remove_by_id(agent_id: str, repo_root: Path | None = None) -> bool:
221
221
  """Remove agent by ID.
222
222
 
223
223
  Args:
@@ -246,7 +246,7 @@ def registry_remove_by_id(agent_id: str, repo_root: Optional[Path] = None) -> bo
246
246
 
247
247
  def registry_remove_by_worktree(
248
248
  worktree_path: str,
249
- repo_root: Optional[Path] = None
249
+ repo_root: Path | None = None
250
250
  ) -> bool:
251
251
  """Remove agent by worktree path.
252
252
 
@@ -279,7 +279,7 @@ def registry_add_agent(
279
279
  worktree_path: str,
280
280
  pid: int,
281
281
  task_dir: str,
282
- repo_root: Optional[Path] = None,
282
+ repo_root: Path | None = None,
283
283
  platform: str = "claude",
284
284
  ) -> bool:
285
285
  """Add agent to registry (replaces if same ID exists).
@@ -327,7 +327,7 @@ def registry_add_agent(
327
327
  return _write_json_file(registry_file, data)
328
328
 
329
329
 
330
- def registry_list_agents(repo_root: Optional[Path] = None) -> list[dict]:
330
+ def registry_list_agents(repo_root: Path | None = None) -> list[dict]:
331
331
  """List all agents.
332
332
 
333
333
  Args:
@@ -10,10 +10,10 @@ Provides:
10
10
  get_task_stats - Get P0/P1/P2/P3 counts
11
11
  """
12
12
 
13
+ from __future__ import annotations
13
14
 
14
15
  import json
15
16
  from pathlib import Path
16
- from typing import Optional, List
17
17
 
18
18
  from .paths import (
19
19
  FILE_TASK_JSON,
@@ -23,7 +23,7 @@ from .paths import (
23
23
  )
24
24
 
25
25
 
26
- def _read_json_file(path: Path) -> Optional[dict]:
26
+ def _read_json_file(path: Path) -> dict | None:
27
27
  """Read and parse a JSON file."""
28
28
  try:
29
29
  return json.loads(path.read_text(encoding="utf-8"))
@@ -36,8 +36,8 @@ def _read_json_file(path: Path) -> Optional[dict]:
36
36
  # =============================================================================
37
37
 
38
38
  def list_tasks_by_status(
39
- filter_status: Optional[str] = None,
40
- repo_root: Optional[Path] = None
39
+ filter_status: str | None = None,
40
+ repo_root: Path | None = None
41
41
  ) -> list[dict]:
42
42
  """List tasks by status.
43
43
 
@@ -91,7 +91,7 @@ def list_tasks_by_status(
91
91
  return results
92
92
 
93
93
 
94
- def list_pending_tasks(repo_root: Optional[Path] = None) -> list[dict]:
94
+ def list_pending_tasks(repo_root: Path | None = None) -> list[dict]:
95
95
  """List pending tasks.
96
96
 
97
97
  Args:
@@ -105,8 +105,8 @@ def list_pending_tasks(repo_root: Optional[Path] = None) -> list[dict]:
105
105
 
106
106
  def list_tasks_by_assignee(
107
107
  assignee: str,
108
- filter_status: Optional[str] = None,
109
- repo_root: Optional[Path] = None
108
+ filter_status: str | None = None,
109
+ repo_root: Path | None = None
110
110
  ) -> list[dict]:
111
111
  """List tasks assigned to a specific developer.
112
112
 
@@ -167,8 +167,8 @@ def list_tasks_by_assignee(
167
167
 
168
168
 
169
169
  def list_my_tasks(
170
- filter_status: Optional[str] = None,
171
- repo_root: Optional[Path] = None
170
+ filter_status: str | None = None,
171
+ repo_root: Path | None = None
172
172
  ) -> list[dict]:
173
173
  """List tasks assigned to current developer.
174
174
 
@@ -192,7 +192,7 @@ def list_my_tasks(
192
192
  return list_tasks_by_assignee(developer, filter_status, repo_root)
193
193
 
194
194
 
195
- def get_task_stats(repo_root: Optional[Path] = None) -> dict[str, int]:
195
+ def get_task_stats(repo_root: Path | None = None) -> dict[str, int]:
196
196
  """Get task statistics.
197
197
 
198
198
  Args:
@@ -8,12 +8,12 @@ Provides:
8
8
  archive_task_dir - Archive task to monthly directory
9
9
  """
10
10
 
11
+ from __future__ import annotations
11
12
 
12
13
  import shutil
13
14
  import sys
14
15
  from datetime import datetime
15
16
  from pathlib import Path
16
- from typing import Optional
17
17
 
18
18
  from .paths import get_repo_root
19
19
 
@@ -22,7 +22,7 @@ from .paths import get_repo_root
22
22
  # Path Safety
23
23
  # =============================================================================
24
24
 
25
- def is_safe_task_path(task_path: str, repo_root: Optional[Path] = None) -> bool:
25
+ def is_safe_task_path(task_path: str, repo_root: Path | None = None) -> bool:
26
26
  """Check if a relative task path is safe to operate on.
27
27
 
28
28
  Args:
@@ -69,7 +69,7 @@ def is_safe_task_path(task_path: str, repo_root: Optional[Path] = None) -> bool:
69
69
  # Task Lookup
70
70
  # =============================================================================
71
71
 
72
- def find_task_by_name(task_name: str, tasks_dir: Path) -> Optional[Path]:
72
+ def find_task_by_name(task_name: str, tasks_dir: Path) -> Path | None:
73
73
  """Find task directory by name (exact or suffix match).
74
74
 
75
75
  Args:
@@ -99,7 +99,7 @@ def find_task_by_name(task_name: str, tasks_dir: Path) -> Optional[Path]:
99
99
  # Archive Operations
100
100
  # =============================================================================
101
101
 
102
- def archive_task_dir(task_dir_abs: Path, repo_root: Optional[Path] = None) -> Optional[Path]:
102
+ def archive_task_dir(task_dir_abs: Path, repo_root: Path | None = None) -> Path | None:
103
103
  """Archive a task directory to archive/{YYYY-MM}/.
104
104
 
105
105
  Args:
@@ -141,7 +141,7 @@ def archive_task_dir(task_dir_abs: Path, repo_root: Optional[Path] = None) -> Op
141
141
 
142
142
  def archive_task_complete(
143
143
  task_dir_abs: Path,
144
- repo_root: Optional[Path] = None
144
+ repo_root: Path | None = None
145
145
  ) -> dict[str, str]:
146
146
  """Complete archive workflow: archive directory.
147
147
 
@@ -10,9 +10,9 @@ Provides:
10
10
  get_agents_dir - Get agents registry directory
11
11
  """
12
12
 
13
+ from __future__ import annotations
13
14
 
14
15
  from pathlib import Path
15
- from typing import Optional
16
16
 
17
17
  from .paths import (
18
18
  DIR_WORKFLOW,
@@ -35,7 +35,7 @@ def parse_simple_yaml(content: str) -> dict:
35
35
  Parsed dict.
36
36
  """
37
37
  result: dict = {}
38
- current_list: Optional[list] = None
38
+ current_list: list | None = None
39
39
 
40
40
  for line in content.splitlines():
41
41
  stripped = line.strip()
@@ -61,7 +61,7 @@ def parse_simple_yaml(content: str) -> dict:
61
61
  return result
62
62
 
63
63
 
64
- def _yaml_get_value(config_file: Path, key: str) -> Optional[str]:
64
+ def _yaml_get_value(config_file: Path, key: str) -> str | None:
65
65
  """Read simple value from worktree.yaml.
66
66
 
67
67
  Args:
@@ -111,7 +111,7 @@ def _yaml_get_list(config_file: Path, section: str) -> list[str]:
111
111
  WORKTREE_CONFIG_PATH = f"{DIR_WORKFLOW}/worktree.yaml"
112
112
 
113
113
 
114
- def get_worktree_config(repo_root: Optional[Path] = None) -> Path:
114
+ def get_worktree_config(repo_root: Path | None = None) -> Path:
115
115
  """Get worktree.yaml config file path.
116
116
 
117
117
  Args:
@@ -125,7 +125,7 @@ def get_worktree_config(repo_root: Optional[Path] = None) -> Path:
125
125
  return repo_root / WORKTREE_CONFIG_PATH
126
126
 
127
127
 
128
- def get_worktree_base_dir(repo_root: Optional[Path] = None) -> Path:
128
+ def get_worktree_base_dir(repo_root: Path | None = None) -> Path:
129
129
  """Get worktree base directory.
130
130
 
131
131
  Args:
@@ -153,7 +153,7 @@ def get_worktree_base_dir(repo_root: Optional[Path] = None) -> Path:
153
153
  return Path(worktree_dir)
154
154
 
155
155
 
156
- def get_worktree_copy_files(repo_root: Optional[Path] = None) -> list[str]:
156
+ def get_worktree_copy_files(repo_root: Path | None = None) -> list[str]:
157
157
  """Get files to copy list.
158
158
 
159
159
  Args:
@@ -168,7 +168,7 @@ def get_worktree_copy_files(repo_root: Optional[Path] = None) -> list[str]:
168
168
  return _yaml_get_list(config, "copy")
169
169
 
170
170
 
171
- def get_worktree_post_create_hooks(repo_root: Optional[Path] = None) -> list[str]:
171
+ def get_worktree_post_create_hooks(repo_root: Path | None = None) -> list[str]:
172
172
  """Get post_create hooks.
173
173
 
174
174
  Args:
@@ -187,7 +187,7 @@ def get_worktree_post_create_hooks(repo_root: Optional[Path] = None) -> list[str
187
187
  # Agents Registry
188
188
  # =============================================================================
189
189
 
190
- def get_agents_dir(repo_root: Optional[Path] = None) -> Optional[Path]:
190
+ def get_agents_dir(repo_root: Path | None = None) -> Path | None:
191
191
  """Get agents directory for current developer.
192
192
 
193
193
  Args: