@fifine/aim-studio 0.0.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 (289) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +159 -0
  3. package/bin/aim.js +3 -0
  4. package/dist/cli/index.d.ts +3 -0
  5. package/dist/cli/index.d.ts.map +1 -0
  6. package/dist/cli/index.js +89 -0
  7. package/dist/cli/index.js.map +1 -0
  8. package/dist/commands/init.d.ts +13 -0
  9. package/dist/commands/init.d.ts.map +1 -0
  10. package/dist/commands/init.js +513 -0
  11. package/dist/commands/init.js.map +1 -0
  12. package/dist/commands/update.d.ts +27 -0
  13. package/dist/commands/update.d.ts.map +1 -0
  14. package/dist/commands/update.js +1275 -0
  15. package/dist/commands/update.js.map +1 -0
  16. package/dist/configurators/claude.d.ts +32 -0
  17. package/dist/configurators/claude.d.ts.map +1 -0
  18. package/dist/configurators/claude.js +98 -0
  19. package/dist/configurators/claude.js.map +1 -0
  20. package/dist/configurators/index.d.ts +51 -0
  21. package/dist/configurators/index.d.ts.map +1 -0
  22. package/dist/configurators/index.js +113 -0
  23. package/dist/configurators/index.js.map +1 -0
  24. package/dist/configurators/shared.d.ts +12 -0
  25. package/dist/configurators/shared.d.ts.map +1 -0
  26. package/dist/configurators/shared.js +21 -0
  27. package/dist/configurators/shared.js.map +1 -0
  28. package/dist/configurators/workflow.d.ts +28 -0
  29. package/dist/configurators/workflow.d.ts.map +1 -0
  30. package/dist/configurators/workflow.js +147 -0
  31. package/dist/configurators/workflow.js.map +1 -0
  32. package/dist/constants/paths.d.ts +68 -0
  33. package/dist/constants/paths.d.ts.map +1 -0
  34. package/dist/constants/paths.js +77 -0
  35. package/dist/constants/paths.js.map +1 -0
  36. package/dist/constants/version.d.ts +9 -0
  37. package/dist/constants/version.d.ts.map +1 -0
  38. package/dist/constants/version.js +15 -0
  39. package/dist/constants/version.js.map +1 -0
  40. package/dist/index.d.ts +9 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +9 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/migrations/index.d.ts +54 -0
  45. package/dist/migrations/index.d.ts.map +1 -0
  46. package/dist/migrations/index.js +160 -0
  47. package/dist/migrations/index.js.map +1 -0
  48. package/dist/migrations/manifests/0.0.1.json +9 -0
  49. package/dist/migrations/manifests/0.1.9.json +30 -0
  50. package/dist/migrations/manifests/0.2.0.json +49 -0
  51. package/dist/migrations/manifests/0.2.12.json +9 -0
  52. package/dist/migrations/manifests/0.2.13.json +9 -0
  53. package/dist/migrations/manifests/0.2.14.json +175 -0
  54. package/dist/migrations/manifests/0.2.15.json +33 -0
  55. package/dist/migrations/manifests/0.3.0-beta.0.json +278 -0
  56. package/dist/migrations/manifests/0.3.0-beta.1.json +9 -0
  57. package/dist/migrations/manifests/0.3.0-beta.10.json +9 -0
  58. package/dist/migrations/manifests/0.3.0-beta.11.json +9 -0
  59. package/dist/migrations/manifests/0.3.0-beta.12.json +9 -0
  60. package/dist/migrations/manifests/0.3.0-beta.13.json +9 -0
  61. package/dist/migrations/manifests/0.3.0-beta.14.json +9 -0
  62. package/dist/migrations/manifests/0.3.0-beta.15.json +9 -0
  63. package/dist/migrations/manifests/0.3.0-beta.16.json +9 -0
  64. package/dist/migrations/manifests/0.3.0-beta.2.json +9 -0
  65. package/dist/migrations/manifests/0.3.0-beta.3.json +9 -0
  66. package/dist/migrations/manifests/0.3.0-beta.4.json +9 -0
  67. package/dist/migrations/manifests/0.3.0-beta.5.json +9 -0
  68. package/dist/migrations/manifests/0.3.0-beta.6.json +9 -0
  69. package/dist/migrations/manifests/0.3.0-beta.7.json +11 -0
  70. package/dist/migrations/manifests/0.3.0-beta.8.json +9 -0
  71. package/dist/migrations/manifests/0.3.0-beta.9.json +9 -0
  72. package/dist/migrations/manifests/0.3.0-rc.0.json +9 -0
  73. package/dist/migrations/manifests/0.3.0-rc.1.json +9 -0
  74. package/dist/migrations/manifests/0.3.0-rc.2.json +9 -0
  75. package/dist/templates/CLAUDE.md +71 -0
  76. package/dist/templates/aim/gitignore.txt +29 -0
  77. package/dist/templates/aim/index.d.ts +49 -0
  78. package/dist/templates/aim/index.d.ts.map +1 -0
  79. package/dist/templates/aim/index.js +92 -0
  80. package/dist/templates/aim/index.js.map +1 -0
  81. package/dist/templates/aim/scripts/__init__.py +5 -0
  82. package/dist/templates/aim/scripts/add_session.py +392 -0
  83. package/dist/templates/aim/scripts/common/__init__.py +80 -0
  84. package/dist/templates/aim/scripts/common/cli_adapter.py +435 -0
  85. package/dist/templates/aim/scripts/common/developer.py +190 -0
  86. package/dist/templates/aim/scripts/common/git_context.py +383 -0
  87. package/dist/templates/aim/scripts/common/paths.py +347 -0
  88. package/dist/templates/aim/scripts/common/phase.py +253 -0
  89. package/dist/templates/aim/scripts/common/registry.py +366 -0
  90. package/dist/templates/aim/scripts/common/task_queue.py +255 -0
  91. package/dist/templates/aim/scripts/common/task_utils.py +178 -0
  92. package/dist/templates/aim/scripts/common/worktree.py +219 -0
  93. package/dist/templates/aim/scripts/create_bootstrap.py +290 -0
  94. package/dist/templates/aim/scripts/get_context.py +16 -0
  95. package/dist/templates/aim/scripts/get_developer.py +26 -0
  96. package/dist/templates/aim/scripts/init_developer.py +51 -0
  97. package/dist/templates/aim/scripts/multi_agent/__init__.py +5 -0
  98. package/dist/templates/aim/scripts/multi_agent/cleanup.py +403 -0
  99. package/dist/templates/aim/scripts/multi_agent/create_pr.py +329 -0
  100. package/dist/templates/aim/scripts/multi_agent/plan.py +233 -0
  101. package/dist/templates/aim/scripts/multi_agent/start.py +461 -0
  102. package/dist/templates/aim/scripts/multi_agent/status.py +817 -0
  103. package/dist/templates/aim/scripts/task.py +1068 -0
  104. package/dist/templates/aim/scripts-shell-archive/add-session.sh +384 -0
  105. package/dist/templates/aim/scripts-shell-archive/common/developer.sh +129 -0
  106. package/dist/templates/aim/scripts-shell-archive/common/git-context.sh +263 -0
  107. package/dist/templates/aim/scripts-shell-archive/common/paths.sh +208 -0
  108. package/dist/templates/aim/scripts-shell-archive/common/phase.sh +150 -0
  109. package/dist/templates/aim/scripts-shell-archive/common/registry.sh +247 -0
  110. package/dist/templates/aim/scripts-shell-archive/common/task-queue.sh +142 -0
  111. package/dist/templates/aim/scripts-shell-archive/common/task-utils.sh +151 -0
  112. package/dist/templates/aim/scripts-shell-archive/common/worktree.sh +128 -0
  113. package/dist/templates/aim/scripts-shell-archive/create-bootstrap.sh +299 -0
  114. package/dist/templates/aim/scripts-shell-archive/get-context.sh +7 -0
  115. package/dist/templates/aim/scripts-shell-archive/get-developer.sh +15 -0
  116. package/dist/templates/aim/scripts-shell-archive/init-developer.sh +34 -0
  117. package/dist/templates/aim/scripts-shell-archive/multi-agent/cleanup.sh +396 -0
  118. package/dist/templates/aim/scripts-shell-archive/multi-agent/create-pr.sh +241 -0
  119. package/dist/templates/aim/scripts-shell-archive/multi-agent/plan.sh +207 -0
  120. package/dist/templates/aim/scripts-shell-archive/multi-agent/start.sh +317 -0
  121. package/dist/templates/aim/scripts-shell-archive/multi-agent/status.sh +828 -0
  122. package/dist/templates/aim/scripts-shell-archive/task.sh +1204 -0
  123. package/dist/templates/aim/tasks/.gitkeep +0 -0
  124. package/dist/templates/aim/workflow.md +258 -0
  125. package/dist/templates/aim/worktree.yaml +47 -0
  126. package/dist/templates/claude/agents/check.md +122 -0
  127. package/dist/templates/claude/agents/debug.md +106 -0
  128. package/dist/templates/claude/agents/dispatch.md +230 -0
  129. package/dist/templates/claude/agents/implement.md +96 -0
  130. package/dist/templates/claude/agents/plan.md +396 -0
  131. package/dist/templates/claude/agents/research.md +120 -0
  132. package/dist/templates/claude/agents/story.md +53 -0
  133. package/dist/templates/claude/commands/aim/before-backend-dev.md +13 -0
  134. package/dist/templates/claude/commands/aim/before-frontend-dev.md +13 -0
  135. package/dist/templates/claude/commands/aim/break-loop.md +153 -0
  136. package/dist/templates/claude/commands/aim/check-backend.md +13 -0
  137. package/dist/templates/claude/commands/aim/check-cross-layer.md +153 -0
  138. package/dist/templates/claude/commands/aim/check-frontend.md +13 -0
  139. package/dist/templates/claude/commands/aim/check-story.md +59 -0
  140. package/dist/templates/claude/commands/aim/create-command.md +154 -0
  141. package/dist/templates/claude/commands/aim/export.md +187 -0
  142. package/dist/templates/claude/commands/aim/finish-work.md +104 -0
  143. package/dist/templates/claude/commands/aim/integrate-skill.md +219 -0
  144. package/dist/templates/claude/commands/aim/onboard.md +358 -0
  145. package/dist/templates/claude/commands/aim/parallel.md +217 -0
  146. package/dist/templates/claude/commands/aim/portrait.md +170 -0
  147. package/dist/templates/claude/commands/aim/record-session.md +92 -0
  148. package/dist/templates/claude/commands/aim/start.md +112 -0
  149. package/dist/templates/claude/commands/aim/story.md +140 -0
  150. package/dist/templates/claude/commands/aim/update-spec.md +285 -0
  151. package/dist/templates/claude/commands/aim/visualize.md +182 -0
  152. package/dist/templates/claude/commands/trellis/before-backend-dev.md +13 -0
  153. package/dist/templates/claude/commands/trellis/before-frontend-dev.md +13 -0
  154. package/dist/templates/claude/commands/trellis/break-loop.md +125 -0
  155. package/dist/templates/claude/commands/trellis/check-backend.md +13 -0
  156. package/dist/templates/claude/commands/trellis/check-cross-layer.md +153 -0
  157. package/dist/templates/claude/commands/trellis/check-frontend.md +13 -0
  158. package/dist/templates/claude/commands/trellis/create-command.md +154 -0
  159. package/dist/templates/claude/commands/trellis/finish-work.md +129 -0
  160. package/dist/templates/claude/commands/trellis/integrate-skill.md +219 -0
  161. package/dist/templates/claude/commands/trellis/onboard.md +358 -0
  162. package/dist/templates/claude/commands/trellis/parallel.md +193 -0
  163. package/dist/templates/claude/commands/trellis/record-session.md +62 -0
  164. package/dist/templates/claude/commands/trellis/start.md +280 -0
  165. package/dist/templates/claude/commands/trellis/update-spec.md +285 -0
  166. package/dist/templates/claude/hooks/inject-subagent-context.py +772 -0
  167. package/dist/templates/claude/hooks/ralph-loop.py +388 -0
  168. package/dist/templates/claude/hooks/session-start.py +142 -0
  169. package/dist/templates/claude/index.d.ts +54 -0
  170. package/dist/templates/claude/index.d.ts.map +1 -0
  171. package/dist/templates/claude/index.js +85 -0
  172. package/dist/templates/claude/index.js.map +1 -0
  173. package/dist/templates/claude/settings.json +41 -0
  174. package/dist/templates/extract.d.ts +68 -0
  175. package/dist/templates/extract.d.ts.map +1 -0
  176. package/dist/templates/extract.js +128 -0
  177. package/dist/templates/extract.js.map +1 -0
  178. package/dist/templates/markdown/agents.md +25 -0
  179. package/dist/templates/markdown/gitignore.txt +12 -0
  180. package/dist/templates/markdown/index.d.ts +32 -0
  181. package/dist/templates/markdown/index.d.ts.map +1 -0
  182. package/dist/templates/markdown/index.js +58 -0
  183. package/dist/templates/markdown/index.js.map +1 -0
  184. package/dist/templates/markdown/spec/backend/database-guidelines.md.txt +51 -0
  185. package/dist/templates/markdown/spec/backend/directory-structure.md.txt +54 -0
  186. package/dist/templates/markdown/spec/backend/error-handling.md.txt +51 -0
  187. package/dist/templates/markdown/spec/backend/index.md +40 -0
  188. package/dist/templates/markdown/spec/backend/index.md.txt +38 -0
  189. package/dist/templates/markdown/spec/backend/logging-guidelines.md.txt +51 -0
  190. package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +51 -0
  191. package/dist/templates/markdown/spec/backend/script-conventions.md +467 -0
  192. package/dist/templates/markdown/spec/frontend/component-guidelines.md.txt +59 -0
  193. package/dist/templates/markdown/spec/frontend/directory-structure.md.txt +54 -0
  194. package/dist/templates/markdown/spec/frontend/hook-guidelines.md.txt +51 -0
  195. package/dist/templates/markdown/spec/frontend/index.md.txt +39 -0
  196. package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +51 -0
  197. package/dist/templates/markdown/spec/frontend/state-management.md.txt +51 -0
  198. package/dist/templates/markdown/spec/frontend/type-safety.md.txt +51 -0
  199. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md +118 -0
  200. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +92 -0
  201. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +94 -0
  202. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md +394 -0
  203. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +319 -0
  204. package/dist/templates/markdown/spec/guides/index.md.txt +89 -0
  205. package/dist/templates/markdown/spec/story/character.md.txt +95 -0
  206. package/dist/templates/markdown/spec/story/index.md.txt +31 -0
  207. package/dist/templates/markdown/spec/story/script.md.txt +313 -0
  208. package/dist/templates/markdown/spec/story/world.md.txt +92 -0
  209. package/dist/templates/markdown/workspace-index.md +123 -0
  210. package/dist/templates/markdown/worktree.yaml.txt +58 -0
  211. package/dist/templates/trellis/gitignore.txt +29 -0
  212. package/dist/templates/trellis/index.d.ts +49 -0
  213. package/dist/templates/trellis/index.d.ts.map +1 -0
  214. package/dist/templates/trellis/index.js +92 -0
  215. package/dist/templates/trellis/index.js.map +1 -0
  216. package/dist/templates/trellis/scripts/__init__.py +5 -0
  217. package/dist/templates/trellis/scripts/add_session.py +392 -0
  218. package/dist/templates/trellis/scripts/common/__init__.py +80 -0
  219. package/dist/templates/trellis/scripts/common/cli_adapter.py +435 -0
  220. package/dist/templates/trellis/scripts/common/developer.py +190 -0
  221. package/dist/templates/trellis/scripts/common/git_context.py +383 -0
  222. package/dist/templates/trellis/scripts/common/paths.py +347 -0
  223. package/dist/templates/trellis/scripts/common/phase.py +253 -0
  224. package/dist/templates/trellis/scripts/common/registry.py +366 -0
  225. package/dist/templates/trellis/scripts/common/task_queue.py +255 -0
  226. package/dist/templates/trellis/scripts/common/task_utils.py +178 -0
  227. package/dist/templates/trellis/scripts/common/worktree.py +219 -0
  228. package/dist/templates/trellis/scripts/create_bootstrap.py +290 -0
  229. package/dist/templates/trellis/scripts/get_context.py +16 -0
  230. package/dist/templates/trellis/scripts/get_developer.py +26 -0
  231. package/dist/templates/trellis/scripts/init_developer.py +51 -0
  232. package/dist/templates/trellis/scripts/multi_agent/__init__.py +5 -0
  233. package/dist/templates/trellis/scripts/multi_agent/cleanup.py +403 -0
  234. package/dist/templates/trellis/scripts/multi_agent/create_pr.py +329 -0
  235. package/dist/templates/trellis/scripts/multi_agent/plan.py +233 -0
  236. package/dist/templates/trellis/scripts/multi_agent/start.py +461 -0
  237. package/dist/templates/trellis/scripts/multi_agent/status.py +817 -0
  238. package/dist/templates/trellis/scripts/task.py +1056 -0
  239. package/dist/templates/trellis/scripts-shell-archive/add-session.sh +384 -0
  240. package/dist/templates/trellis/scripts-shell-archive/common/developer.sh +129 -0
  241. package/dist/templates/trellis/scripts-shell-archive/common/git-context.sh +263 -0
  242. package/dist/templates/trellis/scripts-shell-archive/common/paths.sh +208 -0
  243. package/dist/templates/trellis/scripts-shell-archive/common/phase.sh +150 -0
  244. package/dist/templates/trellis/scripts-shell-archive/common/registry.sh +247 -0
  245. package/dist/templates/trellis/scripts-shell-archive/common/task-queue.sh +142 -0
  246. package/dist/templates/trellis/scripts-shell-archive/common/task-utils.sh +151 -0
  247. package/dist/templates/trellis/scripts-shell-archive/common/worktree.sh +128 -0
  248. package/dist/templates/trellis/scripts-shell-archive/create-bootstrap.sh +299 -0
  249. package/dist/templates/trellis/scripts-shell-archive/get-context.sh +7 -0
  250. package/dist/templates/trellis/scripts-shell-archive/get-developer.sh +15 -0
  251. package/dist/templates/trellis/scripts-shell-archive/init-developer.sh +34 -0
  252. package/dist/templates/trellis/scripts-shell-archive/multi-agent/cleanup.sh +396 -0
  253. package/dist/templates/trellis/scripts-shell-archive/multi-agent/create-pr.sh +241 -0
  254. package/dist/templates/trellis/scripts-shell-archive/multi-agent/plan.sh +207 -0
  255. package/dist/templates/trellis/scripts-shell-archive/multi-agent/start.sh +317 -0
  256. package/dist/templates/trellis/scripts-shell-archive/multi-agent/status.sh +828 -0
  257. package/dist/templates/trellis/scripts-shell-archive/task.sh +1204 -0
  258. package/dist/templates/trellis/tasks/.gitkeep +0 -0
  259. package/dist/templates/trellis/workflow.md +416 -0
  260. package/dist/templates/trellis/worktree.yaml +47 -0
  261. package/dist/types/ai-tools.d.ts +48 -0
  262. package/dist/types/ai-tools.d.ts.map +1 -0
  263. package/dist/types/ai-tools.js +32 -0
  264. package/dist/types/ai-tools.js.map +1 -0
  265. package/dist/types/migration.d.ts +86 -0
  266. package/dist/types/migration.d.ts.map +1 -0
  267. package/dist/types/migration.js +8 -0
  268. package/dist/types/migration.js.map +1 -0
  269. package/dist/utils/compare-versions.d.ts +12 -0
  270. package/dist/utils/compare-versions.d.ts.map +1 -0
  271. package/dist/utils/compare-versions.js +76 -0
  272. package/dist/utils/compare-versions.js.map +1 -0
  273. package/dist/utils/file-writer.d.ts +23 -0
  274. package/dist/utils/file-writer.d.ts.map +1 -0
  275. package/dist/utils/file-writer.js +140 -0
  276. package/dist/utils/file-writer.js.map +1 -0
  277. package/dist/utils/project-detector.d.ts +16 -0
  278. package/dist/utils/project-detector.d.ts.map +1 -0
  279. package/dist/utils/project-detector.js +188 -0
  280. package/dist/utils/project-detector.js.map +1 -0
  281. package/dist/utils/template-fetcher.d.ts +51 -0
  282. package/dist/utils/template-fetcher.d.ts.map +1 -0
  283. package/dist/utils/template-fetcher.js +174 -0
  284. package/dist/utils/template-fetcher.js.map +1 -0
  285. package/dist/utils/template-hash.d.ts +78 -0
  286. package/dist/utils/template-hash.d.ts.map +1 -0
  287. package/dist/utils/template-hash.js +239 -0
  288. package/dist/utils/template-hash.js.map +1 -0
  289. package/package.json +87 -0
@@ -0,0 +1,461 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Multi-Agent Pipeline: Start Worktree Agent.
4
+
5
+ Usage: python3 start.py <task-dir>
6
+ Example: python3 start.py .trellis/tasks/01-21-my-task
7
+
8
+ This script:
9
+ 1. Creates worktree (if not exists) with dependency install
10
+ 2. Copies environment files (from worktree.yaml config)
11
+ 3. Sets .current-task in worktree
12
+ 4. Starts claude agent in background
13
+ 5. Registers agent to registry.json
14
+
15
+ Prerequisites:
16
+ - task.json must exist with 'branch' field
17
+ - agents/dispatch.md must exist (in .claude/, .cursor/, .iflow/, or .opencode/)
18
+
19
+ Configuration: .trellis/worktree.yaml
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import json
25
+ import os
26
+ import shutil
27
+ import subprocess
28
+ import sys
29
+ import uuid
30
+ from pathlib import Path
31
+
32
+ # Add parent directory to path for imports
33
+ sys.path.insert(0, str(Path(__file__).parent.parent))
34
+
35
+ from common.cli_adapter import CLIAdapter, get_cli_adapter
36
+ from common.git_context import _run_git_command
37
+ from common.paths import (
38
+ DIR_WORKFLOW,
39
+ FILE_CURRENT_TASK,
40
+ FILE_TASK_JSON,
41
+ get_repo_root,
42
+ )
43
+ from common.registry import (
44
+ registry_add_agent,
45
+ registry_get_file,
46
+ )
47
+ from common.worktree import (
48
+ get_worktree_base_dir,
49
+ get_worktree_config,
50
+ get_worktree_copy_files,
51
+ get_worktree_post_create_hooks,
52
+ )
53
+
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}")
73
+
74
+
75
+ def log_warn(msg: str) -> None:
76
+ print(f"{Colors.YELLOW}[WARN]{Colors.NC} {msg}")
77
+
78
+
79
+ def log_error(msg: str) -> None:
80
+ print(f"{Colors.RED}[ERROR]{Colors.NC} {msg}")
81
+
82
+
83
+ # =============================================================================
84
+ # Helper Functions
85
+ # =============================================================================
86
+
87
+
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
+
95
+
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
105
+
106
+
107
+ # =============================================================================
108
+ # Constants
109
+ # =============================================================================
110
+
111
+ DEFAULT_PLATFORM = "claude"
112
+
113
+
114
+ # =============================================================================
115
+ # Main
116
+ # =============================================================================
117
+
118
+
119
+ def main() -> int:
120
+ """Main entry point."""
121
+ import argparse
122
+
123
+ parser = argparse.ArgumentParser(description="Multi-Agent Pipeline: Start Worktree Agent")
124
+ parser.add_argument("task_dir", help="Task directory path")
125
+ parser.add_argument(
126
+ "--platform", "-p",
127
+ choices=["claude", "cursor", "iflow", "opencode"],
128
+ default=DEFAULT_PLATFORM,
129
+ help="Platform to use (default: claude)"
130
+ )
131
+
132
+ args = parser.parse_args()
133
+ task_dir_arg = args.task_dir
134
+ platform = args.platform
135
+
136
+ # Initialize CLI adapter
137
+ adapter = get_cli_adapter(platform)
138
+
139
+ project_root = get_repo_root()
140
+
141
+ # Normalize paths
142
+ if task_dir_arg.startswith("/"):
143
+ task_dir_relative = task_dir_arg[len(str(project_root)) + 1 :]
144
+ task_dir_abs = Path(task_dir_arg)
145
+ else:
146
+ task_dir_relative = task_dir_arg
147
+ task_dir_abs = project_root / task_dir_arg
148
+
149
+ task_json_path = task_dir_abs / FILE_TASK_JSON
150
+
151
+ # =============================================================================
152
+ # Validation
153
+ # =============================================================================
154
+ if not task_json_path.is_file():
155
+ log_error(f"task.json not found at {task_json_path}")
156
+ return 1
157
+
158
+ dispatch_md = adapter.get_agent_path("dispatch", project_root)
159
+ if not dispatch_md.is_file():
160
+ log_error(f"dispatch.md not found at {dispatch_md}")
161
+ log_info(f"Platform: {platform}")
162
+ return 1
163
+
164
+ config_file = get_worktree_config(project_root)
165
+ if not config_file.is_file():
166
+ log_error(f"worktree.yaml not found at {config_file}")
167
+ return 1
168
+
169
+ # =============================================================================
170
+ # Read Task Config
171
+ # =============================================================================
172
+ print()
173
+ print(f"{Colors.BLUE}=== Multi-Agent Pipeline: Start ==={Colors.NC}")
174
+ log_info(f"Task: {task_dir_abs}")
175
+
176
+ task_data = _read_json_file(task_json_path)
177
+ if not task_data:
178
+ log_error("Failed to read task.json")
179
+ return 1
180
+
181
+ branch = task_data.get("branch")
182
+ task_name = task_data.get("name")
183
+ task_status = task_data.get("status")
184
+ worktree_path = task_data.get("worktree_path")
185
+
186
+ # Check if task was rejected
187
+ if task_status == "rejected":
188
+ log_error("Task was rejected by Plan Agent")
189
+ rejected_file = task_dir_abs / "REJECTED.md"
190
+ if rejected_file.is_file():
191
+ print()
192
+ print(f"{Colors.YELLOW}Rejection reason:{Colors.NC}")
193
+ print(rejected_file.read_text(encoding="utf-8"))
194
+ print()
195
+ log_info(
196
+ "To retry, delete this directory and run plan.py again with revised requirements"
197
+ )
198
+ return 1
199
+
200
+ # Check if prd.md exists (plan completed successfully)
201
+ prd_file = task_dir_abs / "prd.md"
202
+ if not prd_file.is_file():
203
+ log_error("prd.md not found - Plan Agent may not have completed")
204
+ log_info(f"Check plan log: {task_dir_abs}/.plan-log")
205
+ return 1
206
+
207
+ if not branch:
208
+ log_error("branch field not set in task.json")
209
+ log_info("Please set branch field first, e.g.:")
210
+ log_info(
211
+ " jq '.branch = \"task/my-task\"' task.json > tmp && mv tmp task.json"
212
+ )
213
+ return 1
214
+
215
+ log_info(f"Branch: {branch}")
216
+ log_info(f"Name: {task_name}")
217
+
218
+ # =============================================================================
219
+ # Step 1: Create Worktree (if not exists)
220
+ # =============================================================================
221
+ if not worktree_path or not Path(worktree_path).is_dir():
222
+ log_info("Step 1: Creating worktree...")
223
+
224
+ # Record current branch as base_branch (PR target)
225
+ _, base_branch_out, _ = _run_git_command(
226
+ ["branch", "--show-current"], cwd=project_root
227
+ )
228
+ base_branch = base_branch_out.strip()
229
+ log_info(f"Base branch (PR target): {base_branch}")
230
+
231
+ # Calculate worktree path
232
+ worktree_base = get_worktree_base_dir(project_root)
233
+ worktree_base.mkdir(parents=True, exist_ok=True)
234
+ worktree_base = worktree_base.resolve()
235
+ worktree_path_obj = worktree_base / branch
236
+ worktree_path = str(worktree_path_obj)
237
+
238
+ # Create parent directory
239
+ worktree_path_obj.parent.mkdir(parents=True, exist_ok=True)
240
+
241
+ # Create branch if not exists
242
+ ret, _, _ = _run_git_command(
243
+ ["show-ref", "--verify", "--quiet", f"refs/heads/{branch}"],
244
+ cwd=project_root,
245
+ )
246
+ if ret == 0:
247
+ log_info("Branch exists, checking out...")
248
+ ret, _, err = _run_git_command(
249
+ ["worktree", "add", worktree_path, branch], cwd=project_root
250
+ )
251
+ else:
252
+ log_info(f"Creating new branch: {branch}")
253
+ ret, _, err = _run_git_command(
254
+ ["worktree", "add", "-b", branch, worktree_path], cwd=project_root
255
+ )
256
+
257
+ if ret != 0:
258
+ log_error(f"Failed to create worktree: {err}")
259
+ return 1
260
+
261
+ log_success(f"Worktree created: {worktree_path}")
262
+
263
+ # Update task.json with worktree_path and base_branch
264
+ task_data["worktree_path"] = worktree_path
265
+ task_data["base_branch"] = base_branch
266
+ _write_json_file(task_json_path, task_data)
267
+
268
+ # ----- Copy environment files -----
269
+ log_info("Copying environment files...")
270
+ copy_list = get_worktree_copy_files(project_root)
271
+ copy_count = 0
272
+
273
+ for item in copy_list:
274
+ if not item:
275
+ continue
276
+
277
+ source = project_root / item
278
+ target = Path(worktree_path) / item
279
+
280
+ if source.is_file():
281
+ target.parent.mkdir(parents=True, exist_ok=True)
282
+ shutil.copy2(str(source), str(target))
283
+ copy_count += 1
284
+
285
+ if copy_count > 0:
286
+ log_success(f"Copied {copy_count} file(s)")
287
+
288
+ # ----- Copy task directory (may not be committed yet) -----
289
+ log_info("Copying task directory...")
290
+ task_target_dir = Path(worktree_path) / task_dir_relative
291
+ task_target_dir.parent.mkdir(parents=True, exist_ok=True)
292
+ if task_target_dir.exists():
293
+ shutil.rmtree(str(task_target_dir))
294
+ shutil.copytree(str(task_dir_abs), str(task_target_dir))
295
+ log_success("Task directory copied to worktree")
296
+
297
+ # ----- Run post_create hooks -----
298
+ log_info("Running post_create hooks...")
299
+ post_create = get_worktree_post_create_hooks(project_root)
300
+ hook_count = 0
301
+
302
+ for cmd in post_create:
303
+ if not cmd:
304
+ continue
305
+
306
+ log_info(f" Running: {cmd}")
307
+ ret = subprocess.run(cmd, shell=True, cwd=worktree_path)
308
+ if ret.returncode != 0:
309
+ log_error(f"Hook failed: {cmd}")
310
+ return 1
311
+ hook_count += 1
312
+
313
+ if hook_count > 0:
314
+ log_success(f"Ran {hook_count} hook(s)")
315
+ else:
316
+ log_info(f"Step 1: Using existing worktree: {worktree_path}")
317
+
318
+ # =============================================================================
319
+ # Step 2: Set .current-task in Worktree
320
+ # =============================================================================
321
+ log_info("Step 2: Setting current task in worktree...")
322
+
323
+ worktree_workflow_dir = Path(worktree_path) / DIR_WORKFLOW
324
+ worktree_workflow_dir.mkdir(parents=True, exist_ok=True)
325
+
326
+ current_task_file = worktree_workflow_dir / FILE_CURRENT_TASK
327
+ current_task_file.write_text(task_dir_relative, encoding="utf-8")
328
+ log_success(f"Current task set: {task_dir_relative}")
329
+
330
+ # =============================================================================
331
+ # Step 3: Prepare and Start Claude Agent
332
+ # =============================================================================
333
+ log_info(f"Step 3: Starting {adapter.cli_name} agent...")
334
+
335
+ # Update task status
336
+ task_data["status"] = "in_progress"
337
+ _write_json_file(task_json_path, task_data)
338
+
339
+ log_file = Path(worktree_path) / ".agent-log"
340
+ session_id_file = Path(worktree_path) / ".session-id"
341
+
342
+ log_file.touch()
343
+
344
+ # Generate session ID for resume support (Claude Code only)
345
+ # OpenCode generates its own session ID, we'll extract it from logs later
346
+ if adapter.supports_session_id_on_create:
347
+ session_id = str(uuid.uuid4()).lower()
348
+ session_id_file.write_text(session_id, encoding="utf-8")
349
+ log_info(f"Session ID: {session_id}")
350
+ else:
351
+ session_id = None # Will be extracted from logs after startup
352
+ log_info("Session ID will be extracted from logs after startup")
353
+
354
+ # Get proxy environment variables
355
+ https_proxy = os.environ.get("https_proxy", "")
356
+ http_proxy = os.environ.get("http_proxy", "")
357
+ all_proxy = os.environ.get("all_proxy", "")
358
+
359
+ # Start agent in background (cross-platform, no shell script needed)
360
+ env = os.environ.copy()
361
+ env["https_proxy"] = https_proxy
362
+ env["http_proxy"] = http_proxy
363
+ env["all_proxy"] = all_proxy
364
+
365
+ # Set non-interactive env var based on platform
366
+ env.update(adapter.get_non_interactive_env())
367
+
368
+ # Build CLI command using adapter
369
+ # Note: Use explicit prompt to avoid confusion with CI/CD pipelines
370
+ # Also remind the model to follow its agent definition for better cross-model compatibility
371
+ cli_cmd = adapter.build_run_command(
372
+ agent="dispatch",
373
+ prompt="Follow your agent instructions to execute the task workflow. Start by reading .trellis/.current-task to get the task directory, then execute each action in task.json next_action array in order.",
374
+ session_id=session_id if adapter.supports_session_id_on_create else None,
375
+ skip_permissions=True,
376
+ verbose=True,
377
+ json_output=True,
378
+ )
379
+
380
+ with log_file.open("w") as log_f:
381
+ # Use shell=False for cross-platform compatibility
382
+ # creationflags for Windows, start_new_session for Unix
383
+ popen_kwargs = {
384
+ "stdout": log_f,
385
+ "stderr": subprocess.STDOUT,
386
+ "cwd": worktree_path,
387
+ "env": env,
388
+ }
389
+ if sys.platform == "win32":
390
+ popen_kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP
391
+ else:
392
+ popen_kwargs["start_new_session"] = True
393
+
394
+ process = subprocess.Popen(cli_cmd, **popen_kwargs)
395
+ agent_pid = process.pid
396
+
397
+ log_success(f"Agent started with PID: {agent_pid}")
398
+
399
+ # For OpenCode: extract session ID from logs after startup
400
+ if not adapter.supports_session_id_on_create:
401
+ import time
402
+ log_info("Waiting for session ID from logs...")
403
+ # Wait a bit for the log to have session ID
404
+ for _ in range(10): # Try for up to 5 seconds
405
+ time.sleep(0.5)
406
+ try:
407
+ log_content = log_file.read_text(encoding="utf-8", errors="replace")
408
+ session_id = adapter.extract_session_id_from_log(log_content)
409
+ if session_id:
410
+ session_id_file.write_text(session_id, encoding="utf-8")
411
+ log_success(f"Session ID extracted: {session_id}")
412
+ break
413
+ except Exception:
414
+ pass
415
+ else:
416
+ log_warn("Could not extract session ID from logs")
417
+ session_id = "unknown"
418
+
419
+ # =============================================================================
420
+ # Step 4: Register to Registry (in main repo, not worktree)
421
+ # =============================================================================
422
+ log_info("Step 4: Registering agent to registry...")
423
+
424
+ # Generate agent ID
425
+ task_id = task_data.get("id")
426
+ if not task_id:
427
+ task_id = branch.replace("/", "-")
428
+
429
+ registry_add_agent(
430
+ task_id, worktree_path, agent_pid, task_dir_relative, project_root, platform
431
+ )
432
+
433
+ log_success(f"Agent registered: {task_id}")
434
+
435
+ # =============================================================================
436
+ # Summary
437
+ # =============================================================================
438
+ print()
439
+ print(f"{Colors.GREEN}=== Agent Started ==={Colors.NC}")
440
+ print()
441
+ print(f" ID: {task_id}")
442
+ print(f" PID: {agent_pid}")
443
+ print(f" Session: {session_id}")
444
+ print(f" Worktree: {worktree_path}")
445
+ print(f" Task: {task_dir_relative}")
446
+ print(f" Log: {log_file}")
447
+ print(f" Registry: {registry_get_file(project_root)}")
448
+ print()
449
+ print(f"{Colors.YELLOW}To monitor:{Colors.NC} tail -f {log_file}")
450
+ print(f"{Colors.YELLOW}To stop:{Colors.NC} kill {agent_pid}")
451
+ if session_id and session_id != "unknown":
452
+ resume_cmd = adapter.get_resume_command_str(session_id, cwd=worktree_path)
453
+ print(f"{Colors.YELLOW}To resume:{Colors.NC} {resume_cmd}")
454
+ else:
455
+ print(f"{Colors.YELLOW}To resume:{Colors.NC} (session ID not available)")
456
+
457
+ return 0
458
+
459
+
460
+ if __name__ == "__main__":
461
+ sys.exit(main())