@mindfoldhq/trellis 0.3.8 → 0.3.10-beta.0

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 (174) hide show
  1. package/dist/cli/index.js +2 -0
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/commands/init.d.ts +1 -0
  4. package/dist/commands/init.d.ts.map +1 -1
  5. package/dist/commands/init.js +203 -31
  6. package/dist/commands/init.js.map +1 -1
  7. package/dist/commands/update.d.ts.map +1 -1
  8. package/dist/commands/update.js +154 -6
  9. package/dist/commands/update.js.map +1 -1
  10. package/dist/configurators/workflow.d.ts +6 -2
  11. package/dist/configurators/workflow.d.ts.map +1 -1
  12. package/dist/configurators/workflow.js +88 -58
  13. package/dist/configurators/workflow.js.map +1 -1
  14. package/dist/migrations/index.d.ts +1 -0
  15. package/dist/migrations/index.d.ts.map +1 -1
  16. package/dist/migrations/index.js +2 -0
  17. package/dist/migrations/index.js.map +1 -1
  18. package/dist/migrations/manifests/0.3.9.json +9 -0
  19. package/dist/migrations/manifests/0.4.0-beta.1.json +228 -0
  20. package/dist/templates/claude/agents/dispatch.md +1 -2
  21. package/dist/templates/claude/agents/implement.md +2 -3
  22. package/dist/templates/claude/commands/trellis/before-dev.md +29 -0
  23. package/dist/templates/claude/commands/trellis/check.md +25 -0
  24. package/dist/templates/claude/commands/trellis/create-command.md +2 -2
  25. package/dist/templates/claude/commands/trellis/onboard.md +13 -13
  26. package/dist/templates/claude/commands/trellis/parallel.md +1 -2
  27. package/dist/templates/claude/commands/trellis/record-session.md +1 -1
  28. package/dist/templates/claude/commands/trellis/start.md +8 -4
  29. package/dist/templates/claude/hooks/inject-subagent-context.py +21 -13
  30. package/dist/templates/claude/hooks/session-start.py +170 -2
  31. package/dist/templates/codex/skills/before-dev/SKILL.md +34 -0
  32. package/dist/templates/codex/skills/check/SKILL.md +30 -0
  33. package/dist/templates/codex/skills/create-command/SKILL.md +2 -2
  34. package/dist/templates/codex/skills/onboard/SKILL.md +11 -11
  35. package/dist/templates/codex/skills/record-session/SKILL.md +1 -1
  36. package/dist/templates/codex/skills/start/SKILL.md +8 -3
  37. package/dist/templates/cursor/commands/trellis-before-dev.md +29 -0
  38. package/dist/templates/cursor/commands/trellis-check.md +25 -0
  39. package/dist/templates/cursor/commands/trellis-create-command.md +2 -2
  40. package/dist/templates/cursor/commands/trellis-onboard.md +13 -13
  41. package/dist/templates/cursor/commands/trellis-record-session.md +1 -1
  42. package/dist/templates/cursor/commands/trellis-start.md +7 -16
  43. package/dist/templates/gemini/commands/trellis/before-dev.toml +33 -0
  44. package/dist/templates/gemini/commands/trellis/check.toml +29 -0
  45. package/dist/templates/gemini/commands/trellis/create-command.toml +2 -2
  46. package/dist/templates/gemini/commands/trellis/onboard.toml +2 -2
  47. package/dist/templates/gemini/commands/trellis/record-session.toml +1 -1
  48. package/dist/templates/gemini/commands/trellis/start.toml +9 -4
  49. package/dist/templates/iflow/agents/dispatch.md +1 -2
  50. package/dist/templates/iflow/agents/implement.md +2 -3
  51. package/dist/templates/iflow/commands/trellis/before-dev.md +29 -0
  52. package/dist/templates/iflow/commands/trellis/check.md +25 -0
  53. package/dist/templates/iflow/commands/trellis/create-command.md +2 -2
  54. package/dist/templates/iflow/commands/trellis/onboard.md +13 -13
  55. package/dist/templates/iflow/commands/trellis/parallel.md +1 -2
  56. package/dist/templates/iflow/commands/trellis/record-session.md +1 -1
  57. package/dist/templates/iflow/commands/trellis/start.md +8 -4
  58. package/dist/templates/iflow/hooks/inject-subagent-context.py +21 -13
  59. package/dist/templates/iflow/hooks/session-start.py +156 -1
  60. package/dist/templates/iflow/settings.json +2 -2
  61. package/dist/templates/kilo/workflows/before-dev.md +29 -0
  62. package/dist/templates/kilo/workflows/check.md +25 -0
  63. package/dist/templates/kilo/workflows/create-command.md +2 -2
  64. package/dist/templates/kilo/workflows/onboard.md +13 -13
  65. package/dist/templates/kilo/workflows/parallel.md +1 -2
  66. package/dist/templates/kilo/workflows/record-session.md +1 -1
  67. package/dist/templates/kilo/workflows/start.md +8 -3
  68. package/dist/templates/kiro/skills/before-dev/SKILL.md +34 -0
  69. package/dist/templates/kiro/skills/check/SKILL.md +30 -0
  70. package/dist/templates/kiro/skills/create-command/SKILL.md +2 -2
  71. package/dist/templates/kiro/skills/onboard/SKILL.md +11 -11
  72. package/dist/templates/kiro/skills/record-session/SKILL.md +1 -1
  73. package/dist/templates/kiro/skills/start/SKILL.md +8 -3
  74. package/dist/templates/markdown/spec/backend/script-conventions.md +93 -0
  75. package/dist/templates/opencode/agents/dispatch.md +1 -2
  76. package/dist/templates/opencode/agents/implement.md +2 -2
  77. package/dist/templates/opencode/agents/research.md +1 -2
  78. package/dist/templates/opencode/commands/trellis/before-dev.md +29 -0
  79. package/dist/templates/opencode/commands/trellis/check.md +25 -0
  80. package/dist/templates/opencode/commands/trellis/create-command.md +2 -2
  81. package/dist/templates/opencode/commands/trellis/onboard.md +13 -13
  82. package/dist/templates/opencode/commands/trellis/parallel.md +1 -2
  83. package/dist/templates/opencode/commands/trellis/record-session.md +1 -1
  84. package/dist/templates/opencode/commands/trellis/start.md +8 -3
  85. package/dist/templates/opencode/plugin/inject-subagent-context.js +45 -18
  86. package/dist/templates/opencode/plugin/session-start.js +149 -1
  87. package/dist/templates/qoder/skills/before-dev/SKILL.md +34 -0
  88. package/dist/templates/qoder/skills/check/SKILL.md +30 -0
  89. package/dist/templates/qoder/skills/create-command/SKILL.md +2 -2
  90. package/dist/templates/qoder/skills/onboard/SKILL.md +13 -13
  91. package/dist/templates/qoder/skills/record-session/SKILL.md +1 -1
  92. package/dist/templates/qoder/skills/start/SKILL.md +8 -3
  93. package/dist/templates/trellis/config.yaml +20 -0
  94. package/dist/templates/trellis/index.d.ts +11 -0
  95. package/dist/templates/trellis/index.d.ts.map +1 -1
  96. package/dist/templates/trellis/index.js +22 -0
  97. package/dist/templates/trellis/index.js.map +1 -1
  98. package/dist/templates/trellis/scripts/add_session.py +52 -7
  99. package/dist/templates/trellis/scripts/common/cli_adapter.py +33 -45
  100. package/dist/templates/trellis/scripts/common/config.py +152 -0
  101. package/dist/templates/trellis/scripts/common/git.py +31 -0
  102. package/dist/templates/trellis/scripts/common/git_context.py +23 -586
  103. package/dist/templates/trellis/scripts/common/io.py +37 -0
  104. package/dist/templates/trellis/scripts/common/log.py +45 -0
  105. package/dist/templates/trellis/scripts/common/packages_context.py +233 -0
  106. package/dist/templates/trellis/scripts/common/paths.py +46 -0
  107. package/dist/templates/trellis/scripts/common/phase.py +50 -49
  108. package/dist/templates/trellis/scripts/common/registry.py +41 -72
  109. package/dist/templates/trellis/scripts/common/session_context.py +466 -0
  110. package/dist/templates/trellis/scripts/common/task_context.py +384 -0
  111. package/dist/templates/trellis/scripts/common/task_queue.py +27 -98
  112. package/dist/templates/trellis/scripts/common/task_store.py +534 -0
  113. package/dist/templates/trellis/scripts/common/task_utils.py +96 -6
  114. package/dist/templates/trellis/scripts/common/tasks.py +109 -0
  115. package/dist/templates/trellis/scripts/common/types.py +112 -0
  116. package/dist/templates/trellis/scripts/create_bootstrap.py +31 -26
  117. package/dist/templates/trellis/scripts/hooks/linear_sync.py +243 -0
  118. package/dist/templates/trellis/scripts/multi_agent/_bootstrap.py +17 -0
  119. package/dist/templates/trellis/scripts/multi_agent/cleanup.py +43 -48
  120. package/dist/templates/trellis/scripts/multi_agent/create_pr.py +336 -45
  121. package/dist/templates/trellis/scripts/multi_agent/plan.py +2 -26
  122. package/dist/templates/trellis/scripts/multi_agent/start.py +126 -57
  123. package/dist/templates/trellis/scripts/multi_agent/status.py +12 -753
  124. package/dist/templates/trellis/scripts/multi_agent/status_display.py +542 -0
  125. package/dist/templates/trellis/scripts/multi_agent/status_monitor.py +225 -0
  126. package/dist/templates/trellis/scripts/task.py +50 -975
  127. package/dist/templates/trellis/workflow.md +21 -34
  128. package/dist/types/migration.d.ts +3 -1
  129. package/dist/types/migration.d.ts.map +1 -1
  130. package/dist/utils/project-detector.d.ts +23 -0
  131. package/dist/utils/project-detector.d.ts.map +1 -1
  132. package/dist/utils/project-detector.js +364 -0
  133. package/dist/utils/project-detector.js.map +1 -1
  134. package/dist/utils/template-fetcher.d.ts +2 -2
  135. package/dist/utils/template-fetcher.d.ts.map +1 -1
  136. package/dist/utils/template-fetcher.js +5 -5
  137. package/dist/utils/template-fetcher.js.map +1 -1
  138. package/package.json +1 -1
  139. package/dist/templates/claude/commands/trellis/before-backend-dev.md +0 -13
  140. package/dist/templates/claude/commands/trellis/before-frontend-dev.md +0 -13
  141. package/dist/templates/claude/commands/trellis/check-backend.md +0 -13
  142. package/dist/templates/claude/commands/trellis/check-frontend.md +0 -13
  143. package/dist/templates/codex/skills/before-backend-dev/SKILL.md +0 -18
  144. package/dist/templates/codex/skills/before-frontend-dev/SKILL.md +0 -18
  145. package/dist/templates/codex/skills/check-backend/SKILL.md +0 -18
  146. package/dist/templates/codex/skills/check-frontend/SKILL.md +0 -18
  147. package/dist/templates/cursor/commands/trellis-before-backend-dev.md +0 -13
  148. package/dist/templates/cursor/commands/trellis-before-frontend-dev.md +0 -13
  149. package/dist/templates/cursor/commands/trellis-check-backend.md +0 -13
  150. package/dist/templates/cursor/commands/trellis-check-frontend.md +0 -13
  151. package/dist/templates/gemini/commands/trellis/before-backend-dev.toml +0 -17
  152. package/dist/templates/gemini/commands/trellis/before-frontend-dev.toml +0 -17
  153. package/dist/templates/gemini/commands/trellis/check-backend.toml +0 -17
  154. package/dist/templates/gemini/commands/trellis/check-frontend.toml +0 -17
  155. package/dist/templates/iflow/commands/trellis/before-backend-dev.md +0 -13
  156. package/dist/templates/iflow/commands/trellis/before-frontend-dev.md +0 -13
  157. package/dist/templates/iflow/commands/trellis/check-backend.md +0 -13
  158. package/dist/templates/iflow/commands/trellis/check-frontend.md +0 -13
  159. package/dist/templates/kilo/workflows/before-backend-dev.md +0 -13
  160. package/dist/templates/kilo/workflows/before-frontend-dev.md +0 -13
  161. package/dist/templates/kilo/workflows/check-backend.md +0 -13
  162. package/dist/templates/kilo/workflows/check-frontend.md +0 -13
  163. package/dist/templates/kiro/skills/before-backend-dev/SKILL.md +0 -18
  164. package/dist/templates/kiro/skills/before-frontend-dev/SKILL.md +0 -18
  165. package/dist/templates/kiro/skills/check-backend/SKILL.md +0 -18
  166. package/dist/templates/kiro/skills/check-frontend/SKILL.md +0 -18
  167. package/dist/templates/opencode/commands/trellis/before-backend-dev.md +0 -13
  168. package/dist/templates/opencode/commands/trellis/before-frontend-dev.md +0 -13
  169. package/dist/templates/opencode/commands/trellis/check-backend.md +0 -13
  170. package/dist/templates/opencode/commands/trellis/check-frontend.md +0 -13
  171. package/dist/templates/qoder/skills/before-backend-dev/SKILL.md +0 -18
  172. package/dist/templates/qoder/skills/before-frontend-dev/SKILL.md +0 -18
  173. package/dist/templates/qoder/skills/check-backend/SKILL.md +0 -18
  174. package/dist/templates/qoder/skills/check-frontend/SKILL.md +0 -18
@@ -21,7 +21,6 @@ Configuration: .trellis/worktree.yaml
21
21
 
22
22
  from __future__ import annotations
23
23
 
24
- import json
25
24
  import os
26
25
  import shutil
27
26
  import subprocess
@@ -29,11 +28,12 @@ import sys
29
28
  import uuid
30
29
  from pathlib import Path
31
30
 
32
- # Add parent directory to path for imports
33
- sys.path.insert(0, str(Path(__file__).parent.parent))
31
+ import _bootstrap # noqa: F401 — adds parent scripts/ dir to sys.path
34
32
 
35
- from common.cli_adapter import CLIAdapter, get_cli_adapter
36
- from common.git_context import _run_git_command
33
+ from common.cli_adapter import get_cli_adapter
34
+ from common.git import run_git
35
+ from common.io import read_json, write_json
36
+ from common.log import Colors, log_info, log_success, log_warn, log_error
37
37
  from common.paths import (
38
38
  DIR_WORKFLOW,
39
39
  FILE_CURRENT_TASK,
@@ -44,6 +44,12 @@ from common.registry import (
44
44
  registry_add_agent,
45
45
  registry_get_file,
46
46
  )
47
+ from common.config import (
48
+ get_default_package,
49
+ get_packages,
50
+ get_submodule_packages,
51
+ validate_package,
52
+ )
47
53
  from common.worktree import (
48
54
  get_worktree_base_dir,
49
55
  get_worktree_config,
@@ -51,64 +57,121 @@ from common.worktree import (
51
57
  get_worktree_post_create_hooks,
52
58
  )
53
59
 
54
- # =============================================================================
55
- # Colors
56
- # =============================================================================
57
-
58
-
59
- class Colors:
60
- RED = "\033[0;31m"
61
- GREEN = "\033[0;32m"
62
- YELLOW = "\033[1;33m"
63
- BLUE = "\033[0;34m"
64
- NC = "\033[0m"
65
-
66
-
67
- def log_info(msg: str) -> None:
68
- print(f"{Colors.BLUE}[INFO]{Colors.NC} {msg}")
69
-
70
-
71
- def log_success(msg: str) -> None:
72
- print(f"{Colors.GREEN}[SUCCESS]{Colors.NC} {msg}")
60
+ # Colors, log_info, log_success, log_warn, log_error, read_json, write_json
61
+ # are now imported from common.log and common.io above.
73
62
 
74
63
 
75
- def log_warn(msg: str) -> None:
76
- print(f"{Colors.YELLOW}[WARN]{Colors.NC} {msg}")
77
-
64
+ # =============================================================================
65
+ # Constants
66
+ # =============================================================================
78
67
 
79
- def log_error(msg: str) -> None:
80
- print(f"{Colors.RED}[ERROR]{Colors.NC} {msg}")
68
+ DEFAULT_PLATFORM = "claude"
81
69
 
82
70
 
83
71
  # =============================================================================
84
- # Helper Functions
72
+ # Submodule Init
85
73
  # =============================================================================
86
74
 
87
75
 
88
- def _read_json_file(path: Path) -> dict | None:
89
- """Read and parse a JSON file."""
90
- try:
91
- return json.loads(path.read_text(encoding="utf-8"))
92
- except (FileNotFoundError, json.JSONDecodeError, OSError):
93
- return None
94
-
76
+ def _init_submodules_for_task(
77
+ task_data: dict, worktree_path: str, project_root: Path
78
+ ) -> None:
79
+ """Initialize submodules in worktree based on task's target package.
95
80
 
96
- def _write_json_file(path: Path, data: dict) -> bool:
97
- """Write dict to JSON file."""
98
- try:
99
- path.write_text(
100
- json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8"
101
- )
102
- return True
103
- except (OSError, IOError):
104
- return False
81
+ Resolves the target package from task_data.package -> default_package -> None.
82
+ Only initializes submodule-type packages. Idempotent: skips already-initialized
83
+ submodules to avoid detaching HEAD on in-progress work.
84
+ """
85
+ # Skip if not a monorepo (no packages configured)
86
+ if get_packages(project_root) is None:
87
+ return
105
88
 
89
+ # Resolve package: task.package -> default_package -> None
90
+ task_package = task_data.get("package")
91
+ package = None
106
92
 
107
- # =============================================================================
108
- # Constants
109
- # =============================================================================
93
+ if task_package and isinstance(task_package, str):
94
+ if validate_package(task_package, project_root):
95
+ package = task_package
96
+ else:
97
+ log_warn(
98
+ f"package '{task_package}' not found in config.yaml, "
99
+ "skipping submodule init"
100
+ )
101
+ return
102
+ else:
103
+ # Fallback to default_package
104
+ default_pkg = get_default_package(project_root)
105
+ if default_pkg:
106
+ if validate_package(default_pkg, project_root):
107
+ package = default_pkg
108
+ else:
109
+ log_warn(
110
+ f"package '{default_pkg}' not found in config.yaml, "
111
+ "skipping submodule init"
112
+ )
113
+ return
114
+
115
+ if not package:
116
+ log_warn("no package specified, skipping submodule init")
117
+ return
118
+
119
+ # Check if this package is a submodule
120
+ submodule_packages = get_submodule_packages(project_root)
121
+ if package not in submodule_packages:
122
+ log_info(f"Package '{package}' is not a submodule, skipping submodule init")
123
+ return
124
+
125
+ submodule_path = submodule_packages[package]
126
+ log_info(f"Checking submodule status for '{package}' ({submodule_path})...")
127
+
128
+ # Run git submodule status in worktree directory
129
+ ret, status_out, status_err = run_git(
130
+ ["submodule", "status", submodule_path], cwd=Path(worktree_path)
131
+ )
110
132
 
111
- DEFAULT_PLATFORM = "claude"
133
+ if ret != 0:
134
+ log_warn(
135
+ f"git submodule status failed for '{submodule_path}': {status_err.strip()}, "
136
+ "skipping submodule init"
137
+ )
138
+ return
139
+
140
+ # Parse the prefix character from submodule status output
141
+ # Format: "<prefix><sha1> <path> (<describe>)"
142
+ # Prefix: '-' (uninitialized), ' ' (normal), '+' (commit mismatch), 'U' (conflict)
143
+ status_line = status_out.rstrip("\n\r")
144
+ if not status_line:
145
+ log_warn(f"Empty submodule status for '{submodule_path}', skipping")
146
+ return
147
+
148
+ prefix = status_line[0]
149
+
150
+ if prefix == "-":
151
+ # Uninitialized: run git submodule update --init
152
+ log_info(f"Initializing submodule '{submodule_path}'...")
153
+ ret, _, err = run_git(
154
+ ["submodule", "update", "--init", submodule_path],
155
+ cwd=Path(worktree_path),
156
+ )
157
+ if ret != 0:
158
+ log_warn(f"Failed to initialize submodule '{submodule_path}': {err.strip()}")
159
+ else:
160
+ log_success(f"Submodule '{submodule_path}' initialized")
161
+ elif prefix == " ":
162
+ log_info(f"Submodule '{submodule_path}' already initialized, skipping")
163
+ elif prefix == "+":
164
+ log_warn(
165
+ f"submodule {submodule_path} has local changes, skipping update"
166
+ )
167
+ elif prefix == "U":
168
+ log_warn(
169
+ f"submodule {submodule_path} has conflicts, skipping"
170
+ )
171
+ else:
172
+ log_warn(
173
+ f"Unknown submodule status prefix '{prefix}' for '{submodule_path}', skipping"
174
+ )
112
175
 
113
176
 
114
177
  # =============================================================================
@@ -173,7 +236,7 @@ def main() -> int:
173
236
  print(f"{Colors.BLUE}=== Multi-Agent Pipeline: Start ==={Colors.NC}")
174
237
  log_info(f"Task: {task_dir_abs}")
175
238
 
176
- task_data = _read_json_file(task_json_path)
239
+ task_data = read_json(task_json_path)
177
240
  if not task_data:
178
241
  log_error("Failed to read task.json")
179
242
  return 1
@@ -222,7 +285,7 @@ def main() -> int:
222
285
  log_info("Step 1: Creating worktree...")
223
286
 
224
287
  # Record current branch as base_branch (PR target)
225
- _, base_branch_out, _ = _run_git_command(
288
+ _, base_branch_out, _ = run_git(
226
289
  ["branch", "--show-current"], cwd=project_root
227
290
  )
228
291
  base_branch = base_branch_out.strip()
@@ -239,18 +302,18 @@ def main() -> int:
239
302
  worktree_path_obj.parent.mkdir(parents=True, exist_ok=True)
240
303
 
241
304
  # Create branch if not exists
242
- ret, _, _ = _run_git_command(
305
+ ret, _, _ = run_git(
243
306
  ["show-ref", "--verify", "--quiet", f"refs/heads/{branch}"],
244
307
  cwd=project_root,
245
308
  )
246
309
  if ret == 0:
247
310
  log_info("Branch exists, checking out...")
248
- ret, _, err = _run_git_command(
311
+ ret, _, err = run_git(
249
312
  ["worktree", "add", worktree_path, branch], cwd=project_root
250
313
  )
251
314
  else:
252
315
  log_info(f"Creating new branch: {branch}")
253
- ret, _, err = _run_git_command(
316
+ ret, _, err = run_git(
254
317
  ["worktree", "add", "-b", branch, worktree_path], cwd=project_root
255
318
  )
256
319
 
@@ -263,7 +326,7 @@ def main() -> int:
263
326
  # Update task.json with worktree_path and base_branch
264
327
  task_data["worktree_path"] = worktree_path
265
328
  task_data["base_branch"] = base_branch
266
- _write_json_file(task_json_path, task_data)
329
+ write_json(task_json_path, task_data)
267
330
 
268
331
  # ----- Copy environment files -----
269
332
  log_info("Copying environment files...")
@@ -294,6 +357,9 @@ def main() -> int:
294
357
  shutil.copytree(str(task_dir_abs), str(task_target_dir))
295
358
  log_success("Task directory copied to worktree")
296
359
 
360
+ # ----- Initialize submodules (before hooks, so hooks can use submodule content) -----
361
+ _init_submodules_for_task(task_data, worktree_path, project_root)
362
+
297
363
  # ----- Run post_create hooks -----
298
364
  log_info("Running post_create hooks...")
299
365
  post_create = get_worktree_post_create_hooks(project_root)
@@ -315,6 +381,9 @@ def main() -> int:
315
381
  else:
316
382
  log_info(f"Step 1: Using existing worktree: {worktree_path}")
317
383
 
384
+ # ----- Initialize submodules (idempotent, for reused worktrees) -----
385
+ _init_submodules_for_task(task_data, worktree_path, project_root)
386
+
318
387
  # =============================================================================
319
388
  # Step 2: Set .current-task in Worktree
320
389
  # =============================================================================
@@ -334,7 +403,7 @@ def main() -> int:
334
403
 
335
404
  # Update task status
336
405
  task_data["status"] = "in_progress"
337
- _write_json_file(task_json_path, task_data)
406
+ write_json(task_json_path, task_data)
338
407
 
339
408
  log_file = Path(worktree_path) / ".agent-log"
340
409
  session_id_file = Path(worktree_path) / ".session-id"