@jahanxu/trellis 0.4.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 (422) hide show
  1. package/LICENSE +235 -0
  2. package/README.md +212 -0
  3. package/bin/trellis.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 +97 -0
  7. package/dist/cli/index.js.map +1 -0
  8. package/dist/commands/init.d.ts +21 -0
  9. package/dist/commands/init.d.ts.map +1 -0
  10. package/dist/commands/init.js +527 -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 +1289 -0
  15. package/dist/commands/update.js.map +1 -0
  16. package/dist/configurators/antigravity.d.ts +8 -0
  17. package/dist/configurators/antigravity.d.ts.map +1 -0
  18. package/dist/configurators/antigravity.js +18 -0
  19. package/dist/configurators/antigravity.js.map +1 -0
  20. package/dist/configurators/claude.d.ts +32 -0
  21. package/dist/configurators/claude.d.ts.map +1 -0
  22. package/dist/configurators/claude.js +98 -0
  23. package/dist/configurators/claude.js.map +1 -0
  24. package/dist/configurators/codex.d.ts +8 -0
  25. package/dist/configurators/codex.d.ts.map +1 -0
  26. package/dist/configurators/codex.js +20 -0
  27. package/dist/configurators/codex.js.map +1 -0
  28. package/dist/configurators/cursor.d.ts +5 -0
  29. package/dist/configurators/cursor.d.ts.map +1 -0
  30. package/dist/configurators/cursor.js +52 -0
  31. package/dist/configurators/cursor.js.map +1 -0
  32. package/dist/configurators/gemini.d.ts +8 -0
  33. package/dist/configurators/gemini.d.ts.map +1 -0
  34. package/dist/configurators/gemini.js +52 -0
  35. package/dist/configurators/gemini.js.map +1 -0
  36. package/dist/configurators/iflow.d.ts +33 -0
  37. package/dist/configurators/iflow.d.ts.map +1 -0
  38. package/dist/configurators/iflow.js +99 -0
  39. package/dist/configurators/iflow.js.map +1 -0
  40. package/dist/configurators/index.d.ts +55 -0
  41. package/dist/configurators/index.d.ts.map +1 -0
  42. package/dist/configurators/index.js +220 -0
  43. package/dist/configurators/index.js.map +1 -0
  44. package/dist/configurators/kilo.d.ts +8 -0
  45. package/dist/configurators/kilo.d.ts.map +1 -0
  46. package/dist/configurators/kilo.js +51 -0
  47. package/dist/configurators/kilo.js.map +1 -0
  48. package/dist/configurators/kiro.d.ts +8 -0
  49. package/dist/configurators/kiro.d.ts.map +1 -0
  50. package/dist/configurators/kiro.js +20 -0
  51. package/dist/configurators/kiro.js.map +1 -0
  52. package/dist/configurators/opencode.d.ts +32 -0
  53. package/dist/configurators/opencode.d.ts.map +1 -0
  54. package/dist/configurators/opencode.js +92 -0
  55. package/dist/configurators/opencode.js.map +1 -0
  56. package/dist/configurators/shared.d.ts +12 -0
  57. package/dist/configurators/shared.d.ts.map +1 -0
  58. package/dist/configurators/shared.js +21 -0
  59. package/dist/configurators/shared.js.map +1 -0
  60. package/dist/configurators/workflow.d.ts +28 -0
  61. package/dist/configurators/workflow.d.ts.map +1 -0
  62. package/dist/configurators/workflow.js +134 -0
  63. package/dist/configurators/workflow.js.map +1 -0
  64. package/dist/constants/paths.d.ts +68 -0
  65. package/dist/constants/paths.d.ts.map +1 -0
  66. package/dist/constants/paths.js +77 -0
  67. package/dist/constants/paths.js.map +1 -0
  68. package/dist/constants/version.d.ts +9 -0
  69. package/dist/constants/version.d.ts.map +1 -0
  70. package/dist/constants/version.js +15 -0
  71. package/dist/constants/version.js.map +1 -0
  72. package/dist/index.d.ts +9 -0
  73. package/dist/index.d.ts.map +1 -0
  74. package/dist/index.js +9 -0
  75. package/dist/index.js.map +1 -0
  76. package/dist/migrations/index.d.ts +54 -0
  77. package/dist/migrations/index.d.ts.map +1 -0
  78. package/dist/migrations/index.js +160 -0
  79. package/dist/migrations/index.js.map +1 -0
  80. package/dist/migrations/manifests/0.1.9.json +30 -0
  81. package/dist/migrations/manifests/0.2.0.json +49 -0
  82. package/dist/migrations/manifests/0.2.12.json +9 -0
  83. package/dist/migrations/manifests/0.2.13.json +9 -0
  84. package/dist/migrations/manifests/0.2.14.json +175 -0
  85. package/dist/migrations/manifests/0.2.15.json +33 -0
  86. package/dist/migrations/manifests/0.3.0-beta.0.json +278 -0
  87. package/dist/migrations/manifests/0.3.0-beta.1.json +9 -0
  88. package/dist/migrations/manifests/0.3.0-beta.10.json +9 -0
  89. package/dist/migrations/manifests/0.3.0-beta.11.json +9 -0
  90. package/dist/migrations/manifests/0.3.0-beta.12.json +9 -0
  91. package/dist/migrations/manifests/0.3.0-beta.13.json +9 -0
  92. package/dist/migrations/manifests/0.3.0-beta.14.json +9 -0
  93. package/dist/migrations/manifests/0.3.0-beta.15.json +9 -0
  94. package/dist/migrations/manifests/0.3.0-beta.16.json +9 -0
  95. package/dist/migrations/manifests/0.3.0-beta.2.json +9 -0
  96. package/dist/migrations/manifests/0.3.0-beta.3.json +9 -0
  97. package/dist/migrations/manifests/0.3.0-beta.4.json +9 -0
  98. package/dist/migrations/manifests/0.3.0-beta.5.json +9 -0
  99. package/dist/migrations/manifests/0.3.0-beta.6.json +9 -0
  100. package/dist/migrations/manifests/0.3.0-beta.7.json +11 -0
  101. package/dist/migrations/manifests/0.3.0-beta.8.json +9 -0
  102. package/dist/migrations/manifests/0.3.0-beta.9.json +9 -0
  103. package/dist/migrations/manifests/0.3.0-rc.0.json +9 -0
  104. package/dist/migrations/manifests/0.3.0-rc.1.json +9 -0
  105. package/dist/migrations/manifests/0.3.0-rc.2.json +9 -0
  106. package/dist/migrations/manifests/0.3.0-rc.3.json +9 -0
  107. package/dist/migrations/manifests/0.3.0-rc.4.json +9 -0
  108. package/dist/migrations/manifests/0.3.0-rc.5.json +9 -0
  109. package/dist/migrations/manifests/0.3.0-rc.6.json +9 -0
  110. package/dist/migrations/manifests/0.3.0.json +11 -0
  111. package/dist/templates/antigravity/index.d.ts +12 -0
  112. package/dist/templates/antigravity/index.d.ts.map +1 -0
  113. package/dist/templates/antigravity/index.js +29 -0
  114. package/dist/templates/antigravity/index.js.map +1 -0
  115. package/dist/templates/claude/agents/check.md +122 -0
  116. package/dist/templates/claude/agents/debug.md +106 -0
  117. package/dist/templates/claude/agents/dispatch.md +214 -0
  118. package/dist/templates/claude/agents/implement.md +96 -0
  119. package/dist/templates/claude/agents/plan.md +396 -0
  120. package/dist/templates/claude/agents/research.md +120 -0
  121. package/dist/templates/claude/commands/trellis/before-backend-dev.md +13 -0
  122. package/dist/templates/claude/commands/trellis/before-frontend-dev.md +13 -0
  123. package/dist/templates/claude/commands/trellis/before-role-work.md +364 -0
  124. package/dist/templates/claude/commands/trellis/brainstorm.md +474 -0
  125. package/dist/templates/claude/commands/trellis/break-loop.md +125 -0
  126. package/dist/templates/claude/commands/trellis/check-backend.md +13 -0
  127. package/dist/templates/claude/commands/trellis/check-cross-layer.md +153 -0
  128. package/dist/templates/claude/commands/trellis/check-frontend.md +13 -0
  129. package/dist/templates/claude/commands/trellis/create-command.md +154 -0
  130. package/dist/templates/claude/commands/trellis/finish-work.md +153 -0
  131. package/dist/templates/claude/commands/trellis/handoff.md +445 -0
  132. package/dist/templates/claude/commands/trellis/integrate-skill.md +219 -0
  133. package/dist/templates/claude/commands/trellis/onboard.md +358 -0
  134. package/dist/templates/claude/commands/trellis/parallel.md +193 -0
  135. package/dist/templates/claude/commands/trellis/pick-task.md +515 -0
  136. package/dist/templates/claude/commands/trellis/record-session.md +62 -0
  137. package/dist/templates/claude/commands/trellis/start.md +373 -0
  138. package/dist/templates/claude/commands/trellis/update-spec.md +354 -0
  139. package/dist/templates/claude/hooks/inject-subagent-context.py +873 -0
  140. package/dist/templates/claude/hooks/ralph-loop.py +388 -0
  141. package/dist/templates/claude/hooks/session-start.py +200 -0
  142. package/dist/templates/claude/index.d.ts +54 -0
  143. package/dist/templates/claude/index.d.ts.map +1 -0
  144. package/dist/templates/claude/index.js +85 -0
  145. package/dist/templates/claude/index.js.map +1 -0
  146. package/dist/templates/claude/settings.json +41 -0
  147. package/dist/templates/codex/index.d.ts +18 -0
  148. package/dist/templates/codex/index.d.ts.map +1 -0
  149. package/dist/templates/codex/index.js +40 -0
  150. package/dist/templates/codex/index.js.map +1 -0
  151. package/dist/templates/codex/skills/before-backend-dev/SKILL.md +18 -0
  152. package/dist/templates/codex/skills/before-frontend-dev/SKILL.md +18 -0
  153. package/dist/templates/codex/skills/brainstorm/SKILL.md +479 -0
  154. package/dist/templates/codex/skills/break-loop/SKILL.md +130 -0
  155. package/dist/templates/codex/skills/check-backend/SKILL.md +18 -0
  156. package/dist/templates/codex/skills/check-cross-layer/SKILL.md +158 -0
  157. package/dist/templates/codex/skills/check-frontend/SKILL.md +18 -0
  158. package/dist/templates/codex/skills/create-command/SKILL.md +101 -0
  159. package/dist/templates/codex/skills/finish-work/SKILL.md +148 -0
  160. package/dist/templates/codex/skills/integrate-skill/SKILL.md +221 -0
  161. package/dist/templates/codex/skills/onboard/SKILL.md +363 -0
  162. package/dist/templates/codex/skills/record-session/SKILL.md +67 -0
  163. package/dist/templates/codex/skills/start/SKILL.md +330 -0
  164. package/dist/templates/codex/skills/update-spec/SKILL.md +335 -0
  165. package/dist/templates/cursor/commands/trellis-before-backend-dev.md +13 -0
  166. package/dist/templates/cursor/commands/trellis-before-frontend-dev.md +13 -0
  167. package/dist/templates/cursor/commands/trellis-brainstorm.md +474 -0
  168. package/dist/templates/cursor/commands/trellis-break-loop.md +107 -0
  169. package/dist/templates/cursor/commands/trellis-check-backend.md +13 -0
  170. package/dist/templates/cursor/commands/trellis-check-cross-layer.md +153 -0
  171. package/dist/templates/cursor/commands/trellis-check-frontend.md +13 -0
  172. package/dist/templates/cursor/commands/trellis-create-command.md +154 -0
  173. package/dist/templates/cursor/commands/trellis-finish-work.md +143 -0
  174. package/dist/templates/cursor/commands/trellis-integrate-skill.md +219 -0
  175. package/dist/templates/cursor/commands/trellis-onboard.md +358 -0
  176. package/dist/templates/cursor/commands/trellis-record-session.md +62 -0
  177. package/dist/templates/cursor/commands/trellis-start.md +366 -0
  178. package/dist/templates/cursor/commands/trellis-update-spec.md +354 -0
  179. package/dist/templates/cursor/index.d.ts +24 -0
  180. package/dist/templates/cursor/index.d.ts.map +1 -0
  181. package/dist/templates/cursor/index.js +44 -0
  182. package/dist/templates/cursor/index.js.map +1 -0
  183. package/dist/templates/extract.d.ts +166 -0
  184. package/dist/templates/extract.d.ts.map +1 -0
  185. package/dist/templates/extract.js +296 -0
  186. package/dist/templates/extract.js.map +1 -0
  187. package/dist/templates/gemini/commands/trellis/before-backend-dev.toml +17 -0
  188. package/dist/templates/gemini/commands/trellis/before-frontend-dev.toml +17 -0
  189. package/dist/templates/gemini/commands/trellis/brainstorm.toml +420 -0
  190. package/dist/templates/gemini/commands/trellis/break-loop.toml +129 -0
  191. package/dist/templates/gemini/commands/trellis/check-backend.toml +17 -0
  192. package/dist/templates/gemini/commands/trellis/check-cross-layer.toml +147 -0
  193. package/dist/templates/gemini/commands/trellis/check-frontend.toml +17 -0
  194. package/dist/templates/gemini/commands/trellis/create-command.toml +119 -0
  195. package/dist/templates/gemini/commands/trellis/finish-work.toml +133 -0
  196. package/dist/templates/gemini/commands/trellis/integrate-skill.toml +104 -0
  197. package/dist/templates/gemini/commands/trellis/onboard.toml +111 -0
  198. package/dist/templates/gemini/commands/trellis/record-session.toml +66 -0
  199. package/dist/templates/gemini/commands/trellis/start.toml +292 -0
  200. package/dist/templates/gemini/commands/trellis/update-spec.toml +132 -0
  201. package/dist/templates/gemini/index.d.ts +21 -0
  202. package/dist/templates/gemini/index.d.ts.map +1 -0
  203. package/dist/templates/gemini/index.js +44 -0
  204. package/dist/templates/gemini/index.js.map +1 -0
  205. package/dist/templates/iflow/agents/check.md +122 -0
  206. package/dist/templates/iflow/agents/debug.md +106 -0
  207. package/dist/templates/iflow/agents/dispatch.md +214 -0
  208. package/dist/templates/iflow/agents/implement.md +96 -0
  209. package/dist/templates/iflow/agents/plan.md +396 -0
  210. package/dist/templates/iflow/agents/research.md +120 -0
  211. package/dist/templates/iflow/commands/trellis/before-backend-dev.md +13 -0
  212. package/dist/templates/iflow/commands/trellis/before-frontend-dev.md +13 -0
  213. package/dist/templates/iflow/commands/trellis/brainstorm.md +474 -0
  214. package/dist/templates/iflow/commands/trellis/break-loop.md +125 -0
  215. package/dist/templates/iflow/commands/trellis/check-backend.md +13 -0
  216. package/dist/templates/iflow/commands/trellis/check-cross-layer.md +153 -0
  217. package/dist/templates/iflow/commands/trellis/check-frontend.md +13 -0
  218. package/dist/templates/iflow/commands/trellis/create-command.md +152 -0
  219. package/dist/templates/iflow/commands/trellis/finish-work.md +153 -0
  220. package/dist/templates/iflow/commands/trellis/integrate-skill.md +219 -0
  221. package/dist/templates/iflow/commands/trellis/onboard.md +358 -0
  222. package/dist/templates/iflow/commands/trellis/parallel.md +193 -0
  223. package/dist/templates/iflow/commands/trellis/record-session.md +62 -0
  224. package/dist/templates/iflow/commands/trellis/start.md +373 -0
  225. package/dist/templates/iflow/commands/trellis/update-spec.md +354 -0
  226. package/dist/templates/iflow/hooks/inject-subagent-context.py +788 -0
  227. package/dist/templates/iflow/hooks/ralph-loop.py +388 -0
  228. package/dist/templates/iflow/hooks/session-start.py +143 -0
  229. package/dist/templates/iflow/index.d.ts +54 -0
  230. package/dist/templates/iflow/index.d.ts.map +1 -0
  231. package/dist/templates/iflow/index.js +85 -0
  232. package/dist/templates/iflow/index.js.map +1 -0
  233. package/dist/templates/iflow/settings.json +40 -0
  234. package/dist/templates/kilo/commands/trellis/before-backend-dev.md +13 -0
  235. package/dist/templates/kilo/commands/trellis/before-frontend-dev.md +13 -0
  236. package/dist/templates/kilo/commands/trellis/brainstorm.md +474 -0
  237. package/dist/templates/kilo/commands/trellis/break-loop.md +125 -0
  238. package/dist/templates/kilo/commands/trellis/check-backend.md +13 -0
  239. package/dist/templates/kilo/commands/trellis/check-cross-layer.md +153 -0
  240. package/dist/templates/kilo/commands/trellis/check-frontend.md +13 -0
  241. package/dist/templates/kilo/commands/trellis/create-command.md +152 -0
  242. package/dist/templates/kilo/commands/trellis/finish-work.md +129 -0
  243. package/dist/templates/kilo/commands/trellis/integrate-skill.md +219 -0
  244. package/dist/templates/kilo/commands/trellis/onboard.md +358 -0
  245. package/dist/templates/kilo/commands/trellis/parallel.md +194 -0
  246. package/dist/templates/kilo/commands/trellis/record-session.md +62 -0
  247. package/dist/templates/kilo/commands/trellis/start.md +321 -0
  248. package/dist/templates/kilo/commands/trellis/update-spec.md +285 -0
  249. package/dist/templates/kilo/index.d.ts +16 -0
  250. package/dist/templates/kilo/index.d.ts.map +1 -0
  251. package/dist/templates/kilo/index.js +39 -0
  252. package/dist/templates/kilo/index.js.map +1 -0
  253. package/dist/templates/kiro/index.d.ts +18 -0
  254. package/dist/templates/kiro/index.d.ts.map +1 -0
  255. package/dist/templates/kiro/index.js +40 -0
  256. package/dist/templates/kiro/index.js.map +1 -0
  257. package/dist/templates/kiro/skills/before-backend-dev/SKILL.md +18 -0
  258. package/dist/templates/kiro/skills/before-frontend-dev/SKILL.md +18 -0
  259. package/dist/templates/kiro/skills/brainstorm/SKILL.md +479 -0
  260. package/dist/templates/kiro/skills/break-loop/SKILL.md +130 -0
  261. package/dist/templates/kiro/skills/check-backend/SKILL.md +18 -0
  262. package/dist/templates/kiro/skills/check-cross-layer/SKILL.md +158 -0
  263. package/dist/templates/kiro/skills/check-frontend/SKILL.md +18 -0
  264. package/dist/templates/kiro/skills/create-command/SKILL.md +101 -0
  265. package/dist/templates/kiro/skills/finish-work/SKILL.md +148 -0
  266. package/dist/templates/kiro/skills/integrate-skill/SKILL.md +221 -0
  267. package/dist/templates/kiro/skills/onboard/SKILL.md +363 -0
  268. package/dist/templates/kiro/skills/record-session/SKILL.md +67 -0
  269. package/dist/templates/kiro/skills/start/SKILL.md +330 -0
  270. package/dist/templates/kiro/skills/update-spec/SKILL.md +335 -0
  271. package/dist/templates/markdown/agents.md +18 -0
  272. package/dist/templates/markdown/gitignore.txt +12 -0
  273. package/dist/templates/markdown/index.d.ts +27 -0
  274. package/dist/templates/markdown/index.d.ts.map +1 -0
  275. package/dist/templates/markdown/index.js +52 -0
  276. package/dist/templates/markdown/index.js.map +1 -0
  277. package/dist/templates/markdown/spec/backend/database-guidelines.md.txt +51 -0
  278. package/dist/templates/markdown/spec/backend/directory-structure.md.txt +54 -0
  279. package/dist/templates/markdown/spec/backend/error-handling.md.txt +51 -0
  280. package/dist/templates/markdown/spec/backend/index.md +40 -0
  281. package/dist/templates/markdown/spec/backend/index.md.txt +38 -0
  282. package/dist/templates/markdown/spec/backend/logging-guidelines.md.txt +51 -0
  283. package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +51 -0
  284. package/dist/templates/markdown/spec/backend/script-conventions.md +467 -0
  285. package/dist/templates/markdown/spec/frontend/component-guidelines.md.txt +59 -0
  286. package/dist/templates/markdown/spec/frontend/directory-structure.md.txt +54 -0
  287. package/dist/templates/markdown/spec/frontend/hook-guidelines.md.txt +51 -0
  288. package/dist/templates/markdown/spec/frontend/index.md.txt +39 -0
  289. package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +51 -0
  290. package/dist/templates/markdown/spec/frontend/state-management.md.txt +51 -0
  291. package/dist/templates/markdown/spec/frontend/type-safety.md.txt +51 -0
  292. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md +118 -0
  293. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +92 -0
  294. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +94 -0
  295. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md +394 -0
  296. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +319 -0
  297. package/dist/templates/markdown/spec/guides/index.md.txt +79 -0
  298. package/dist/templates/markdown/workspace-index.md +123 -0
  299. package/dist/templates/markdown/worktree.yaml.txt +58 -0
  300. package/dist/templates/opencode/agents/check.md +146 -0
  301. package/dist/templates/opencode/agents/debug.md +129 -0
  302. package/dist/templates/opencode/agents/dispatch.md +223 -0
  303. package/dist/templates/opencode/agents/implement.md +120 -0
  304. package/dist/templates/opencode/agents/research.md +147 -0
  305. package/dist/templates/opencode/agents/trellis-plan.md +427 -0
  306. package/dist/templates/opencode/commands/trellis/before-backend-dev.md +13 -0
  307. package/dist/templates/opencode/commands/trellis/before-frontend-dev.md +13 -0
  308. package/dist/templates/opencode/commands/trellis/brainstorm.md +474 -0
  309. package/dist/templates/opencode/commands/trellis/break-loop.md +125 -0
  310. package/dist/templates/opencode/commands/trellis/check-backend.md +13 -0
  311. package/dist/templates/opencode/commands/trellis/check-cross-layer.md +153 -0
  312. package/dist/templates/opencode/commands/trellis/check-frontend.md +13 -0
  313. package/dist/templates/opencode/commands/trellis/create-command.md +154 -0
  314. package/dist/templates/opencode/commands/trellis/finish-work.md +144 -0
  315. package/dist/templates/opencode/commands/trellis/integrate-skill.md +219 -0
  316. package/dist/templates/opencode/commands/trellis/migrate-specs.md +0 -0
  317. package/dist/templates/opencode/commands/trellis/onboard.md +358 -0
  318. package/dist/templates/opencode/commands/trellis/parallel.md +194 -0
  319. package/dist/templates/opencode/commands/trellis/record-session.md +62 -0
  320. package/dist/templates/opencode/commands/trellis/start.md +338 -0
  321. package/dist/templates/opencode/commands/trellis/update-spec.md +354 -0
  322. package/dist/templates/opencode/lib/trellis-context.js +436 -0
  323. package/dist/templates/opencode/package.json +5 -0
  324. package/dist/templates/opencode/plugin/inject-subagent-context.js +538 -0
  325. package/dist/templates/opencode/plugin/session-start.js +192 -0
  326. package/dist/templates/trellis/VERSION +1 -0
  327. package/dist/templates/trellis/deliverables/README.md +51 -0
  328. package/dist/templates/trellis/gitignore.txt +29 -0
  329. package/dist/templates/trellis/index.d.ts +49 -0
  330. package/dist/templates/trellis/index.d.ts.map +1 -0
  331. package/dist/templates/trellis/index.js +92 -0
  332. package/dist/templates/trellis/index.js.map +1 -0
  333. package/dist/templates/trellis/paths.README.md +277 -0
  334. package/dist/templates/trellis/paths.yaml +41 -0
  335. package/dist/templates/trellis/pool/implementations.json +5 -0
  336. package/dist/templates/trellis/pool/prototypes.json +5 -0
  337. package/dist/templates/trellis/pool/requirements.json +5 -0
  338. package/dist/templates/trellis/scripts/__init__.py +5 -0
  339. package/dist/templates/trellis/scripts/add_session.py +391 -0
  340. package/dist/templates/trellis/scripts/common/__init__.py +80 -0
  341. package/dist/templates/trellis/scripts/common/cli_adapter.py +522 -0
  342. package/dist/templates/trellis/scripts/common/developer.py +189 -0
  343. package/dist/templates/trellis/scripts/common/git_context.py +383 -0
  344. package/dist/templates/trellis/scripts/common/paths.py +346 -0
  345. package/dist/templates/trellis/scripts/common/phase.py +253 -0
  346. package/dist/templates/trellis/scripts/common/project_paths.py +189 -0
  347. package/dist/templates/trellis/scripts/common/registry.py +365 -0
  348. package/dist/templates/trellis/scripts/common/task_queue.py +255 -0
  349. package/dist/templates/trellis/scripts/common/task_utils.py +177 -0
  350. package/dist/templates/trellis/scripts/common/worktree.py +218 -0
  351. package/dist/templates/trellis/scripts/create_bootstrap.py +290 -0
  352. package/dist/templates/trellis/scripts/get_context.py +16 -0
  353. package/dist/templates/trellis/scripts/get_developer.py +26 -0
  354. package/dist/templates/trellis/scripts/handoff_generator.py +380 -0
  355. package/dist/templates/trellis/scripts/init_developer.py +51 -0
  356. package/dist/templates/trellis/scripts/multi_agent/__init__.py +5 -0
  357. package/dist/templates/trellis/scripts/multi_agent/cleanup.py +403 -0
  358. package/dist/templates/trellis/scripts/multi_agent/create_pr.py +329 -0
  359. package/dist/templates/trellis/scripts/multi_agent/plan.py +233 -0
  360. package/dist/templates/trellis/scripts/multi_agent/start.py +461 -0
  361. package/dist/templates/trellis/scripts/multi_agent/status.py +817 -0
  362. package/dist/templates/trellis/scripts/pool.py +373 -0
  363. package/dist/templates/trellis/scripts/task.py +1162 -0
  364. package/dist/templates/trellis/scripts-shell-archive/add-session.sh +384 -0
  365. package/dist/templates/trellis/scripts-shell-archive/common/developer.sh +129 -0
  366. package/dist/templates/trellis/scripts-shell-archive/common/git-context.sh +263 -0
  367. package/dist/templates/trellis/scripts-shell-archive/common/paths.sh +208 -0
  368. package/dist/templates/trellis/scripts-shell-archive/common/phase.sh +150 -0
  369. package/dist/templates/trellis/scripts-shell-archive/common/registry.sh +247 -0
  370. package/dist/templates/trellis/scripts-shell-archive/common/task-queue.sh +142 -0
  371. package/dist/templates/trellis/scripts-shell-archive/common/task-utils.sh +151 -0
  372. package/dist/templates/trellis/scripts-shell-archive/common/worktree.sh +128 -0
  373. package/dist/templates/trellis/scripts-shell-archive/create-bootstrap.sh +299 -0
  374. package/dist/templates/trellis/scripts-shell-archive/get-context.sh +7 -0
  375. package/dist/templates/trellis/scripts-shell-archive/get-developer.sh +15 -0
  376. package/dist/templates/trellis/scripts-shell-archive/init-developer.sh +34 -0
  377. package/dist/templates/trellis/scripts-shell-archive/multi-agent/cleanup.sh +396 -0
  378. package/dist/templates/trellis/scripts-shell-archive/multi-agent/create-pr.sh +241 -0
  379. package/dist/templates/trellis/scripts-shell-archive/multi-agent/plan.sh +207 -0
  380. package/dist/templates/trellis/scripts-shell-archive/multi-agent/start.sh +317 -0
  381. package/dist/templates/trellis/scripts-shell-archive/multi-agent/status.sh +828 -0
  382. package/dist/templates/trellis/scripts-shell-archive/task.sh +1204 -0
  383. package/dist/templates/trellis/spec/roles/designer/index.md +243 -0
  384. package/dist/templates/trellis/spec/roles/designer/mock-data-standards.md +481 -0
  385. package/dist/templates/trellis/spec/roles/designer/prototype-guidelines.md +429 -0
  386. package/dist/templates/trellis/spec/roles/frontend-impl/api-integration.md +565 -0
  387. package/dist/templates/trellis/spec/roles/frontend-impl/index.md +321 -0
  388. package/dist/templates/trellis/spec/roles/frontend-impl/state-management.md +599 -0
  389. package/dist/templates/trellis/spec/roles/pm/index.md +112 -0
  390. package/dist/templates/trellis/spec/roles/pm/prd-template.md +124 -0
  391. package/dist/templates/trellis/tasks/.gitkeep +0 -0
  392. package/dist/templates/trellis/workflow.md +416 -0
  393. package/dist/templates/trellis/worktree.yaml +47 -0
  394. package/dist/types/ai-tools.d.ts +56 -0
  395. package/dist/types/ai-tools.d.ts.map +1 -0
  396. package/dist/types/ai-tools.js +103 -0
  397. package/dist/types/ai-tools.js.map +1 -0
  398. package/dist/types/migration.d.ts +86 -0
  399. package/dist/types/migration.d.ts.map +1 -0
  400. package/dist/types/migration.js +8 -0
  401. package/dist/types/migration.js.map +1 -0
  402. package/dist/utils/compare-versions.d.ts +12 -0
  403. package/dist/utils/compare-versions.d.ts.map +1 -0
  404. package/dist/utils/compare-versions.js +76 -0
  405. package/dist/utils/compare-versions.js.map +1 -0
  406. package/dist/utils/file-writer.d.ts +23 -0
  407. package/dist/utils/file-writer.d.ts.map +1 -0
  408. package/dist/utils/file-writer.js +140 -0
  409. package/dist/utils/file-writer.js.map +1 -0
  410. package/dist/utils/project-detector.d.ts +16 -0
  411. package/dist/utils/project-detector.d.ts.map +1 -0
  412. package/dist/utils/project-detector.js +186 -0
  413. package/dist/utils/project-detector.js.map +1 -0
  414. package/dist/utils/template-fetcher.d.ts +51 -0
  415. package/dist/utils/template-fetcher.d.ts.map +1 -0
  416. package/dist/utils/template-fetcher.js +174 -0
  417. package/dist/utils/template-fetcher.js.map +1 -0
  418. package/dist/utils/template-hash.d.ts +78 -0
  419. package/dist/utils/template-hash.d.ts.map +1 -0
  420. package/dist/utils/template-hash.js +233 -0
  421. package/dist/utils/template-hash.js.map +1 -0
  422. package/package.json +87 -0
@@ -0,0 +1,1162 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Task Management Script for Multi-Agent Pipeline.
5
+
6
+ Usage:
7
+ python3 task.py create "<title>" [--slug <name>] [--assignee <dev>] [--priority P0|P1|P2|P3]
8
+ python3 task.py init-context <dir> <type> # Initialize jsonl files
9
+ python3 task.py add-context <dir> <file> <path> [reason] # Add jsonl entry
10
+ python3 task.py validate <dir> # Validate jsonl files
11
+ python3 task.py list-context <dir> # List jsonl entries
12
+ python3 task.py start <dir> # Set as current task
13
+ python3 task.py finish # Clear current task
14
+ python3 task.py set-branch <dir> <branch> # Set git branch
15
+ python3 task.py set-base-branch <dir> <branch> # Set PR target branch
16
+ python3 task.py set-scope <dir> <scope> # Set scope for PR title
17
+ python3 task.py create-pr [dir] [--dry-run] # Create PR from task
18
+ python3 task.py archive <task-name> # Archive completed task
19
+ python3 task.py list # List active tasks
20
+ python3 task.py list-archive [month] # List archived tasks
21
+ """
22
+
23
+ import sys
24
+ from typing import Optional
25
+
26
+ # IMPORTANT: Force stdout to use UTF-8 on Windows
27
+ # This fixes UnicodeEncodeError when outputting non-ASCII characters
28
+ if sys.platform == "win32":
29
+ import io as _io
30
+ if hasattr(sys.stdout, "reconfigure"):
31
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
32
+ elif hasattr(sys.stdout, "detach"):
33
+ sys.stdout = _io.TextIOWrapper(sys.stdout.detach(), encoding="utf-8", errors="replace") # type: ignore[union-attr]
34
+
35
+ import argparse
36
+ import json
37
+ import re
38
+ import sys
39
+ from datetime import datetime
40
+ from pathlib import Path
41
+
42
+ from common.cli_adapter import get_cli_adapter_auto
43
+ from common.git_context import _run_git_command
44
+ from common.paths import (
45
+ DIR_WORKFLOW,
46
+ DIR_TASKS,
47
+ DIR_SPEC,
48
+ DIR_ARCHIVE,
49
+ FILE_TASK_JSON,
50
+ get_repo_root,
51
+ get_developer,
52
+ get_tasks_dir,
53
+ get_current_task,
54
+ set_current_task,
55
+ clear_current_task,
56
+ generate_task_date_prefix,
57
+ )
58
+ from common.task_utils import (
59
+ find_task_by_name,
60
+ archive_task_complete,
61
+ )
62
+
63
+
64
+ # =============================================================================
65
+ # Colors
66
+ # =============================================================================
67
+
68
+ class Colors:
69
+ RED = "\033[0;31m"
70
+ GREEN = "\033[0;32m"
71
+ YELLOW = "\033[1;33m"
72
+ BLUE = "\033[0;34m"
73
+ CYAN = "\033[0;36m"
74
+ NC = "\033[0m"
75
+
76
+
77
+ def colored(text: str, color: str) -> str:
78
+ """Apply color to text."""
79
+ return f"{color}{text}{Colors.NC}"
80
+
81
+
82
+ # =============================================================================
83
+ # Helper Functions
84
+ # =============================================================================
85
+
86
+ def _read_json_file(path: Path) -> Optional[dict]:
87
+ """Read and parse a JSON file."""
88
+ try:
89
+ return json.loads(path.read_text(encoding="utf-8"))
90
+ except (FileNotFoundError, json.JSONDecodeError, OSError):
91
+ return None
92
+
93
+
94
+ def _write_json_file(path: Path, data: dict) -> bool:
95
+ """Write dict to JSON file."""
96
+ try:
97
+ path.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
98
+ return True
99
+ except (OSError, IOError):
100
+ return False
101
+
102
+
103
+ def _slugify(title: str) -> str:
104
+ """Convert title to slug (only works with ASCII)."""
105
+ result = title.lower()
106
+ result = re.sub(r"[^a-z0-9]", "-", result)
107
+ result = re.sub(r"-+", "-", result)
108
+ result = result.strip("-")
109
+ return result
110
+
111
+
112
+ def _resolve_task_dir(target_dir: str, repo_root: Path) -> Path:
113
+ """Resolve task directory to absolute path.
114
+
115
+ Supports:
116
+ - Absolute path: /path/to/task
117
+ - Relative path: .trellis/tasks/01-31-my-task
118
+ - Task name: my-task (uses find_task_by_name for lookup)
119
+ """
120
+ if not target_dir:
121
+ return Path()
122
+
123
+ # Absolute path
124
+ if target_dir.startswith("/"):
125
+ return Path(target_dir)
126
+
127
+ # Relative path (contains path separator or starts with .trellis)
128
+ if "/" in target_dir or target_dir.startswith(".trellis"):
129
+ return repo_root / target_dir
130
+
131
+ # Task name - try to find in tasks directory
132
+ tasks_dir = get_tasks_dir(repo_root)
133
+ found = find_task_by_name(target_dir, tasks_dir)
134
+ if found:
135
+ return found
136
+
137
+ # Fallback to treating as relative path
138
+ return repo_root / target_dir
139
+
140
+
141
+ # =============================================================================
142
+ # JSONL Default Content Generators
143
+ # =============================================================================
144
+
145
+ def get_implement_base() -> list[dict]:
146
+ """Get base implement context entries."""
147
+ return [
148
+ {"file": f"{DIR_WORKFLOW}/workflow.md", "reason": "Project workflow and conventions"},
149
+ ]
150
+
151
+
152
+ def get_implement_backend() -> list[dict]:
153
+ """Get backend implement context entries."""
154
+ return [
155
+ {"file": f"{DIR_WORKFLOW}/{DIR_SPEC}/backend/index.md", "reason": "Backend development guide"},
156
+ ]
157
+
158
+
159
+ def get_implement_frontend() -> list[dict]:
160
+ """Get frontend implement context entries."""
161
+ return [
162
+ {"file": f"{DIR_WORKFLOW}/{DIR_SPEC}/frontend/index.md", "reason": "Frontend development guide"},
163
+ ]
164
+
165
+
166
+ def get_check_context(dev_type: str, repo_root: Path) -> list[dict]:
167
+ """Get check context entries."""
168
+ adapter = get_cli_adapter_auto(repo_root)
169
+
170
+ entries = [
171
+ {"file": adapter.get_trellis_command_path("finish-work"), "reason": "Finish work checklist"},
172
+ ]
173
+
174
+ if dev_type in ("backend", "fullstack"):
175
+ entries.append({"file": adapter.get_trellis_command_path("check-backend"), "reason": "Backend check spec"})
176
+ if dev_type in ("frontend", "fullstack"):
177
+ entries.append({"file": adapter.get_trellis_command_path("check-frontend"), "reason": "Frontend check spec"})
178
+
179
+ return entries
180
+
181
+
182
+ def get_debug_context(dev_type: str, repo_root: Path) -> list[dict]:
183
+ """Get debug context entries."""
184
+ adapter = get_cli_adapter_auto(repo_root)
185
+
186
+ entries: list[dict] = []
187
+
188
+ if dev_type in ("backend", "fullstack"):
189
+ entries.append({"file": adapter.get_trellis_command_path("check-backend"), "reason": "Backend check spec"})
190
+ if dev_type in ("frontend", "fullstack"):
191
+ entries.append({"file": adapter.get_trellis_command_path("check-frontend"), "reason": "Frontend check spec"})
192
+
193
+ return entries
194
+
195
+
196
+ def _write_jsonl(path: Path, entries: list[dict]) -> None:
197
+ """Write entries to JSONL file."""
198
+ lines = [json.dumps(entry, ensure_ascii=False) for entry in entries]
199
+ path.write_text("\n".join(lines) + "\n", encoding="utf-8")
200
+
201
+
202
+ # =============================================================================
203
+ # Task Operations
204
+ # =============================================================================
205
+
206
+ def ensure_tasks_dir(repo_root: Path) -> Path:
207
+ """Ensure tasks directory exists."""
208
+ tasks_dir = get_tasks_dir(repo_root)
209
+ archive_dir = tasks_dir / "archive"
210
+
211
+ if not tasks_dir.exists():
212
+ tasks_dir.mkdir(parents=True)
213
+ print(colored(f"Created tasks directory: {tasks_dir}", Colors.GREEN), file=sys.stderr)
214
+
215
+ if not archive_dir.exists():
216
+ archive_dir.mkdir(parents=True)
217
+
218
+ return tasks_dir
219
+
220
+
221
+ # =============================================================================
222
+ # Command: create
223
+ # =============================================================================
224
+
225
+ def cmd_create(args: argparse.Namespace) -> int:
226
+ """Create a new task."""
227
+ repo_root = get_repo_root()
228
+
229
+ if not args.title:
230
+ print(colored("Error: title is required", Colors.RED), file=sys.stderr)
231
+ return 1
232
+
233
+ # Default assignee to current developer
234
+ assignee = args.assignee
235
+ if not assignee:
236
+ assignee = get_developer(repo_root)
237
+ if not assignee:
238
+ print(colored("Error: No developer set. Run init_developer.py first or use --assignee", Colors.RED), file=sys.stderr)
239
+ return 1
240
+
241
+ ensure_tasks_dir(repo_root)
242
+
243
+ # Get current developer as creator
244
+ creator = get_developer(repo_root) or assignee
245
+
246
+ # Generate slug if not provided
247
+ slug = args.slug or _slugify(args.title)
248
+ if not slug:
249
+ print(colored("Error: could not generate slug from title", Colors.RED), file=sys.stderr)
250
+ return 1
251
+
252
+ # Create task directory with MM-DD-slug format
253
+ tasks_dir = get_tasks_dir(repo_root)
254
+ date_prefix = generate_task_date_prefix()
255
+ dir_name = f"{date_prefix}-{slug}"
256
+ task_dir = tasks_dir / dir_name
257
+ task_json_path = task_dir / FILE_TASK_JSON
258
+
259
+ if task_dir.exists():
260
+ print(colored(f"Warning: Task directory already exists: {dir_name}", Colors.YELLOW), file=sys.stderr)
261
+ else:
262
+ task_dir.mkdir(parents=True)
263
+
264
+ today = datetime.now().strftime("%Y-%m-%d")
265
+
266
+ # Record current branch as base_branch (PR target)
267
+ _, branch_out, _ = _run_git_command(["branch", "--show-current"], cwd=repo_root)
268
+ current_branch = branch_out.strip() or "main"
269
+
270
+ task_data = {
271
+ "id": slug,
272
+ "name": slug,
273
+ "title": args.title,
274
+ "description": args.description or "",
275
+ "status": "planning",
276
+ "dev_type": None,
277
+ "scope": None,
278
+ "priority": args.priority,
279
+ "creator": creator,
280
+ "assignee": assignee,
281
+ "createdAt": today,
282
+ "completedAt": None,
283
+ "branch": None,
284
+ "base_branch": current_branch,
285
+ "worktree_path": None,
286
+ "current_phase": 0,
287
+ "next_action": [
288
+ {"phase": 1, "action": "implement"},
289
+ {"phase": 2, "action": "check"},
290
+ {"phase": 3, "action": "finish"},
291
+ {"phase": 4, "action": "create-pr"},
292
+ ],
293
+ "commit": None,
294
+ "pr_url": None,
295
+ "subtasks": [],
296
+ "relatedFiles": [],
297
+ "notes": "",
298
+ }
299
+
300
+ _write_json_file(task_json_path, task_data)
301
+
302
+ print(colored(f"Created task: {dir_name}", Colors.GREEN), file=sys.stderr)
303
+ print("", file=sys.stderr)
304
+ print(colored("Next steps:", Colors.BLUE), file=sys.stderr)
305
+ print(" 1. Create prd.md with requirements", file=sys.stderr)
306
+ print(" 2. Run: python3 task.py init-context <dir> <dev_type>", file=sys.stderr)
307
+ print(" 3. Run: python3 task.py start <dir>", file=sys.stderr)
308
+ print("", file=sys.stderr)
309
+
310
+ # Output relative path for script chaining
311
+ print(f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}")
312
+ return 0
313
+
314
+
315
+ # =============================================================================
316
+ # Command: init-context
317
+ # =============================================================================
318
+
319
+ def cmd_init_context(args: argparse.Namespace) -> int:
320
+ """Initialize JSONL context files for a task."""
321
+ repo_root = get_repo_root()
322
+ target_dir = _resolve_task_dir(args.dir, repo_root)
323
+ dev_type = args.type
324
+
325
+ if not dev_type:
326
+ print(colored("Error: Missing arguments", Colors.RED))
327
+ print("Usage: python3 task.py init-context <task-dir> <dev_type>")
328
+ print(" dev_type: backend | frontend | fullstack | test | docs")
329
+ return 1
330
+
331
+ if not target_dir.is_dir():
332
+ print(colored(f"Error: Directory not found: {target_dir}", Colors.RED))
333
+ return 1
334
+
335
+ print(colored("=== Initializing Agent Context Files ===", Colors.BLUE))
336
+ print(f"Target dir: {target_dir}")
337
+ print(f"Dev type: {dev_type}")
338
+ print()
339
+
340
+ # implement.jsonl
341
+ print(colored("Creating implement.jsonl...", Colors.CYAN))
342
+ implement_entries = get_implement_base()
343
+ if dev_type in ("backend", "test"):
344
+ implement_entries.extend(get_implement_backend())
345
+ elif dev_type == "frontend":
346
+ implement_entries.extend(get_implement_frontend())
347
+ elif dev_type == "fullstack":
348
+ implement_entries.extend(get_implement_backend())
349
+ implement_entries.extend(get_implement_frontend())
350
+
351
+ implement_file = target_dir / "implement.jsonl"
352
+ _write_jsonl(implement_file, implement_entries)
353
+ print(f" {colored('✓', Colors.GREEN)} {len(implement_entries)} entries")
354
+
355
+ # check.jsonl
356
+ print(colored("Creating check.jsonl...", Colors.CYAN))
357
+ check_entries = get_check_context(dev_type, repo_root)
358
+ check_file = target_dir / "check.jsonl"
359
+ _write_jsonl(check_file, check_entries)
360
+ print(f" {colored('✓', Colors.GREEN)} {len(check_entries)} entries")
361
+
362
+ # debug.jsonl
363
+ print(colored("Creating debug.jsonl...", Colors.CYAN))
364
+ debug_entries = get_debug_context(dev_type, repo_root)
365
+ debug_file = target_dir / "debug.jsonl"
366
+ _write_jsonl(debug_file, debug_entries)
367
+ print(f" {colored('✓', Colors.GREEN)} {len(debug_entries)} entries")
368
+
369
+ print()
370
+ print(colored("✓ All context files created", Colors.GREEN))
371
+ print()
372
+ print(colored("Next steps:", Colors.BLUE))
373
+ print(" 1. Add task-specific specs: python3 task.py add-context <dir> <jsonl> <path>")
374
+ print(" 2. Set as current: python3 task.py start <dir>")
375
+
376
+ return 0
377
+
378
+
379
+ # =============================================================================
380
+ # Command: add-context
381
+ # =============================================================================
382
+
383
+ def cmd_add_context(args: argparse.Namespace) -> int:
384
+ """Add entry to JSONL context file."""
385
+ repo_root = get_repo_root()
386
+ target_dir = _resolve_task_dir(args.dir, repo_root)
387
+
388
+ jsonl_name = args.file
389
+ path = args.path
390
+ reason = args.reason or "Added manually"
391
+
392
+ if not target_dir.is_dir():
393
+ print(colored(f"Error: Directory not found: {target_dir}", Colors.RED))
394
+ return 1
395
+
396
+ # Support shorthand
397
+ if not jsonl_name.endswith(".jsonl"):
398
+ jsonl_name = f"{jsonl_name}.jsonl"
399
+
400
+ jsonl_file = target_dir / jsonl_name
401
+ full_path = repo_root / path
402
+
403
+ entry_type = "file"
404
+ if full_path.is_dir():
405
+ entry_type = "directory"
406
+ if not path.endswith("/"):
407
+ path = f"{path}/"
408
+ elif not full_path.is_file():
409
+ print(colored(f"Error: Path not found: {path}", Colors.RED))
410
+ return 1
411
+
412
+ # Check if already exists
413
+ if jsonl_file.is_file():
414
+ content = jsonl_file.read_text(encoding="utf-8")
415
+ if f'"{path}"' in content:
416
+ print(colored(f"Warning: Entry already exists for {path}", Colors.YELLOW))
417
+ return 0
418
+
419
+ # Add entry
420
+ entry: dict
421
+ if entry_type == "directory":
422
+ entry = {"file": path, "type": "directory", "reason": reason}
423
+ else:
424
+ entry = {"file": path, "reason": reason}
425
+
426
+ with jsonl_file.open("a", encoding="utf-8") as f:
427
+ f.write(json.dumps(entry, ensure_ascii=False) + "\n")
428
+
429
+ print(colored(f"Added {entry_type}: {path}", Colors.GREEN))
430
+ return 0
431
+
432
+
433
+ # =============================================================================
434
+ # Command: validate
435
+ # =============================================================================
436
+
437
+ def cmd_validate(args: argparse.Namespace) -> int:
438
+ """Validate JSONL context files."""
439
+ repo_root = get_repo_root()
440
+ target_dir = _resolve_task_dir(args.dir, repo_root)
441
+
442
+ if not target_dir.is_dir():
443
+ print(colored("Error: task directory required", Colors.RED))
444
+ return 1
445
+
446
+ print(colored("=== Validating Context Files ===", Colors.BLUE))
447
+ print(f"Target dir: {target_dir}")
448
+ print()
449
+
450
+ total_errors = 0
451
+ for jsonl_name in ["implement.jsonl", "check.jsonl", "debug.jsonl"]:
452
+ jsonl_file = target_dir / jsonl_name
453
+ errors = _validate_jsonl(jsonl_file, repo_root)
454
+ total_errors += errors
455
+
456
+ print()
457
+ if total_errors == 0:
458
+ print(colored("✓ All validations passed", Colors.GREEN))
459
+ return 0
460
+ else:
461
+ print(colored(f"✗ Validation failed ({total_errors} errors)", Colors.RED))
462
+ return 1
463
+
464
+
465
+ def _validate_jsonl(jsonl_file: Path, repo_root: Path) -> int:
466
+ """Validate a single JSONL file."""
467
+ file_name = jsonl_file.name
468
+ errors = 0
469
+
470
+ if not jsonl_file.is_file():
471
+ print(f" {colored(f'{file_name}: not found (skipped)', Colors.YELLOW)}")
472
+ return 0
473
+
474
+ line_num = 0
475
+ for line in jsonl_file.read_text(encoding="utf-8").splitlines():
476
+ line_num += 1
477
+ if not line.strip():
478
+ continue
479
+
480
+ try:
481
+ data = json.loads(line)
482
+ except json.JSONDecodeError:
483
+ print(f" {colored(f'{file_name}:{line_num}: Invalid JSON', Colors.RED)}")
484
+ errors += 1
485
+ continue
486
+
487
+ file_path = data.get("file")
488
+ entry_type = data.get("type", "file")
489
+
490
+ if not file_path:
491
+ print(f" {colored(f'{file_name}:{line_num}: Missing file field', Colors.RED)}")
492
+ errors += 1
493
+ continue
494
+
495
+ full_path = repo_root / file_path
496
+ if entry_type == "directory":
497
+ if not full_path.is_dir():
498
+ print(f" {colored(f'{file_name}:{line_num}: Directory not found: {file_path}', Colors.RED)}")
499
+ errors += 1
500
+ else:
501
+ if not full_path.is_file():
502
+ print(f" {colored(f'{file_name}:{line_num}: File not found: {file_path}', Colors.RED)}")
503
+ errors += 1
504
+
505
+ if errors == 0:
506
+ print(f" {colored(f'{file_name}: ✓ ({line_num} entries)', Colors.GREEN)}")
507
+ else:
508
+ print(f" {colored(f'{file_name}: ✗ ({errors} errors)', Colors.RED)}")
509
+
510
+ return errors
511
+
512
+
513
+ # =============================================================================
514
+ # Command: list-context
515
+ # =============================================================================
516
+
517
+ def cmd_list_context(args: argparse.Namespace) -> int:
518
+ """List JSONL context entries."""
519
+ repo_root = get_repo_root()
520
+ target_dir = _resolve_task_dir(args.dir, repo_root)
521
+
522
+ if not target_dir.is_dir():
523
+ print(colored("Error: task directory required", Colors.RED))
524
+ return 1
525
+
526
+ print(colored("=== Context Files ===", Colors.BLUE))
527
+ print()
528
+
529
+ for jsonl_name in ["implement.jsonl", "check.jsonl", "debug.jsonl"]:
530
+ jsonl_file = target_dir / jsonl_name
531
+ if not jsonl_file.is_file():
532
+ continue
533
+
534
+ print(colored(f"[{jsonl_name}]", Colors.CYAN))
535
+
536
+ count = 0
537
+ for line in jsonl_file.read_text(encoding="utf-8").splitlines():
538
+ if not line.strip():
539
+ continue
540
+
541
+ try:
542
+ data = json.loads(line)
543
+ except json.JSONDecodeError:
544
+ continue
545
+
546
+ count += 1
547
+ file_path = data.get("file", "?")
548
+ entry_type = data.get("type", "file")
549
+ reason = data.get("reason", "-")
550
+
551
+ if entry_type == "directory":
552
+ print(f" {colored(f'{count}.', Colors.GREEN)} [DIR] {file_path}")
553
+ else:
554
+ print(f" {colored(f'{count}.', Colors.GREEN)} {file_path}")
555
+ print(f" {colored('→', Colors.YELLOW)} {reason}")
556
+
557
+ print()
558
+
559
+ return 0
560
+
561
+
562
+ # =============================================================================
563
+ # Command: start / finish
564
+ # =============================================================================
565
+
566
+ def cmd_start(args: argparse.Namespace) -> int:
567
+ """Set current task."""
568
+ repo_root = get_repo_root()
569
+ task_input = args.dir
570
+
571
+ if not task_input:
572
+ print(colored("Error: task directory or name required", Colors.RED))
573
+ return 1
574
+
575
+ # Resolve task directory (supports task name, relative path, or absolute path)
576
+ full_path = _resolve_task_dir(task_input, repo_root)
577
+
578
+ if not full_path.is_dir():
579
+ print(colored(f"Error: Task not found: {task_input}", Colors.RED))
580
+ print("Hint: Use task name (e.g., 'my-task') or full path (e.g., '.trellis/tasks/01-31-my-task')")
581
+ return 1
582
+
583
+ # Convert to relative path for storage
584
+ try:
585
+ task_dir = str(full_path.relative_to(repo_root))
586
+ except ValueError:
587
+ task_dir = str(full_path)
588
+
589
+ if set_current_task(task_dir, repo_root):
590
+ print(colored(f"✓ Current task set to: {task_dir}", Colors.GREEN))
591
+ print()
592
+ print(colored("The hook will now inject context from this task's jsonl files.", Colors.BLUE))
593
+ return 0
594
+ else:
595
+ print(colored("Error: Failed to set current task", Colors.RED))
596
+ return 1
597
+
598
+
599
+ def cmd_finish(args: argparse.Namespace) -> int:
600
+ """Clear current task."""
601
+ repo_root = get_repo_root()
602
+ current = get_current_task(repo_root)
603
+
604
+ if not current:
605
+ print(colored("No current task set", Colors.YELLOW))
606
+ return 0
607
+
608
+ clear_current_task(repo_root)
609
+ print(colored(f"✓ Cleared current task (was: {current})", Colors.GREEN))
610
+ return 0
611
+
612
+
613
+ # =============================================================================
614
+ # Command: archive
615
+ # =============================================================================
616
+
617
+ def cmd_archive(args: argparse.Namespace) -> int:
618
+ """Archive completed task."""
619
+ repo_root = get_repo_root()
620
+ task_name = args.name
621
+
622
+ if not task_name:
623
+ print(colored("Error: Task name is required", Colors.RED), file=sys.stderr)
624
+ return 1
625
+
626
+ tasks_dir = get_tasks_dir(repo_root)
627
+
628
+ # Find task directory
629
+ task_dir = find_task_by_name(task_name, tasks_dir)
630
+
631
+ if not task_dir or not task_dir.is_dir():
632
+ print(colored(f"Error: Task not found: {task_name}", Colors.RED), file=sys.stderr)
633
+ print("Active tasks:", file=sys.stderr)
634
+ cmd_list(argparse.Namespace(mine=False, status=None))
635
+ return 1
636
+
637
+ dir_name = task_dir.name
638
+ task_json_path = task_dir / FILE_TASK_JSON
639
+
640
+ # Update status before archiving
641
+ today = datetime.now().strftime("%Y-%m-%d")
642
+ if task_json_path.is_file():
643
+ data = _read_json_file(task_json_path)
644
+ if data:
645
+ data["status"] = "completed"
646
+ data["completedAt"] = today
647
+ _write_json_file(task_json_path, data)
648
+
649
+ # Clear if current task
650
+ current = get_current_task(repo_root)
651
+ if current and dir_name in current:
652
+ clear_current_task(repo_root)
653
+
654
+ # Archive
655
+ result = archive_task_complete(task_dir, repo_root)
656
+ if "archived_to" in result:
657
+ archive_dest = Path(result["archived_to"])
658
+ year_month = archive_dest.parent.name
659
+ print(colored(f"Archived: {dir_name} -> archive/{year_month}/", Colors.GREEN), file=sys.stderr)
660
+
661
+ # Return the archive path
662
+ print(f"{DIR_WORKFLOW}/{DIR_TASKS}/{DIR_ARCHIVE}/{year_month}/{dir_name}")
663
+ return 0
664
+
665
+ return 1
666
+
667
+
668
+ # =============================================================================
669
+ # Command: list
670
+ # =============================================================================
671
+
672
+ def cmd_list(args: argparse.Namespace) -> int:
673
+ """List active tasks."""
674
+ repo_root = get_repo_root()
675
+ tasks_dir = get_tasks_dir(repo_root)
676
+ current_task = get_current_task(repo_root)
677
+ developer = get_developer(repo_root)
678
+ filter_mine = args.mine
679
+ filter_status = args.status
680
+
681
+ if filter_mine:
682
+ if not developer:
683
+ print(colored("Error: No developer set. Run init_developer.py first", Colors.RED), file=sys.stderr)
684
+ return 1
685
+ print(colored(f"My tasks (assignee: {developer}):", Colors.BLUE))
686
+ else:
687
+ print(colored("All active tasks:", Colors.BLUE))
688
+ print()
689
+
690
+ count = 0
691
+ if tasks_dir.is_dir():
692
+ for d in sorted(tasks_dir.iterdir()):
693
+ if not d.is_dir() or d.name == "archive":
694
+ continue
695
+
696
+ dir_name = d.name
697
+ task_json = d / FILE_TASK_JSON
698
+ status = "unknown"
699
+ assignee = "-"
700
+ relative_path = f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}"
701
+
702
+ if task_json.is_file():
703
+ data = _read_json_file(task_json)
704
+ if data:
705
+ status = data.get("status", "unknown")
706
+ assignee = data.get("assignee", "-")
707
+
708
+ # Apply --mine filter
709
+ if filter_mine and assignee != developer:
710
+ continue
711
+
712
+ # Apply --status filter
713
+ if filter_status and status != filter_status:
714
+ continue
715
+
716
+ marker = ""
717
+ if relative_path == current_task:
718
+ marker = f" {colored('<- current', Colors.GREEN)}"
719
+
720
+ if filter_mine:
721
+ print(f" - {dir_name}/ ({status}){marker}")
722
+ else:
723
+ print(f" - {dir_name}/ ({status}) [{colored(assignee, Colors.CYAN)}]{marker}")
724
+ count += 1
725
+
726
+ if count == 0:
727
+ if filter_mine:
728
+ print(" (no tasks assigned to you)")
729
+ else:
730
+ print(" (no active tasks)")
731
+
732
+ print()
733
+ print(f"Total: {count} task(s)")
734
+ return 0
735
+
736
+
737
+ # =============================================================================
738
+ # Command: list-archive
739
+ # =============================================================================
740
+
741
+ def cmd_list_archive(args: argparse.Namespace) -> int:
742
+ """List archived tasks."""
743
+ repo_root = get_repo_root()
744
+ tasks_dir = get_tasks_dir(repo_root)
745
+ archive_dir = tasks_dir / "archive"
746
+ month = args.month
747
+
748
+ print(colored("Archived tasks:", Colors.BLUE))
749
+ print()
750
+
751
+ if month:
752
+ month_dir = archive_dir / month
753
+ if month_dir.is_dir():
754
+ print(f"[{month}]")
755
+ for d in sorted(month_dir.iterdir()):
756
+ if d.is_dir():
757
+ print(f" - {d.name}/")
758
+ else:
759
+ print(f" No archives for {month}")
760
+ else:
761
+ if archive_dir.is_dir():
762
+ for month_dir in sorted(archive_dir.iterdir()):
763
+ if month_dir.is_dir():
764
+ month_name = month_dir.name
765
+ count = sum(1 for d in month_dir.iterdir() if d.is_dir())
766
+ print(f"[{month_name}] - {count} task(s)")
767
+
768
+ return 0
769
+
770
+
771
+ # =============================================================================
772
+ # Command: set-branch
773
+ # =============================================================================
774
+
775
+ def cmd_set_branch(args: argparse.Namespace) -> int:
776
+ """Set git branch for task."""
777
+ repo_root = get_repo_root()
778
+ target_dir = _resolve_task_dir(args.dir, repo_root)
779
+ branch = args.branch
780
+
781
+ if not branch:
782
+ print(colored("Error: Missing arguments", Colors.RED))
783
+ print("Usage: python3 task.py set-branch <task-dir> <branch-name>")
784
+ return 1
785
+
786
+ task_json = target_dir / FILE_TASK_JSON
787
+ if not task_json.is_file():
788
+ print(colored(f"Error: task.json not found at {target_dir}", Colors.RED))
789
+ return 1
790
+
791
+ data = _read_json_file(task_json)
792
+ if not data:
793
+ return 1
794
+
795
+ data["branch"] = branch
796
+ _write_json_file(task_json, data)
797
+
798
+ print(colored(f"✓ Branch set to: {branch}", Colors.GREEN))
799
+ print()
800
+ print(colored("Now you can start the multi-agent pipeline:", Colors.BLUE))
801
+ print(f" python3 ./.trellis/scripts/multi_agent/start.py {args.dir}")
802
+ return 0
803
+
804
+
805
+ # =============================================================================
806
+ # Command: set-base-branch
807
+ # =============================================================================
808
+
809
+ def cmd_set_base_branch(args: argparse.Namespace) -> int:
810
+ """Set the base branch (PR target) for task."""
811
+ repo_root = get_repo_root()
812
+ target_dir = _resolve_task_dir(args.dir, repo_root)
813
+ base_branch = args.base_branch
814
+
815
+ if not base_branch:
816
+ print(colored("Error: Missing arguments", Colors.RED))
817
+ print("Usage: python3 task.py set-base-branch <task-dir> <base-branch>")
818
+ print("Example: python3 task.py set-base-branch <dir> develop")
819
+ print()
820
+ print("This sets the target branch for PR (the branch your feature will merge into).")
821
+ return 1
822
+
823
+ task_json = target_dir / FILE_TASK_JSON
824
+ if not task_json.is_file():
825
+ print(colored(f"Error: task.json not found at {target_dir}", Colors.RED))
826
+ return 1
827
+
828
+ data = _read_json_file(task_json)
829
+ if not data:
830
+ return 1
831
+
832
+ data["base_branch"] = base_branch
833
+ _write_json_file(task_json, data)
834
+
835
+ print(colored(f"✓ Base branch set to: {base_branch}", Colors.GREEN))
836
+ print(f" PR will target: {base_branch}")
837
+ return 0
838
+
839
+
840
+ # =============================================================================
841
+ # Command: set-scope
842
+ # =============================================================================
843
+
844
+ def cmd_set_scope(args: argparse.Namespace) -> int:
845
+ """Set scope for PR title."""
846
+ repo_root = get_repo_root()
847
+ target_dir = _resolve_task_dir(args.dir, repo_root)
848
+ scope = args.scope
849
+
850
+ if not scope:
851
+ print(colored("Error: Missing arguments", Colors.RED))
852
+ print("Usage: python3 task.py set-scope <task-dir> <scope>")
853
+ return 1
854
+
855
+ task_json = target_dir / FILE_TASK_JSON
856
+ if not task_json.is_file():
857
+ print(colored(f"Error: task.json not found at {target_dir}", Colors.RED))
858
+ return 1
859
+
860
+ data = _read_json_file(task_json)
861
+ if not data:
862
+ return 1
863
+
864
+ data["scope"] = scope
865
+ _write_json_file(task_json, data)
866
+
867
+ print(colored(f"✓ Scope set to: {scope}", Colors.GREEN))
868
+ return 0
869
+
870
+
871
+ # =============================================================================
872
+ # Command: set-role
873
+ # =============================================================================
874
+
875
+ def cmd_set_role(args: argparse.Namespace) -> int:
876
+ """Set role for three-role collaboration."""
877
+ repo_root = get_repo_root()
878
+ target_dir = _resolve_task_dir(args.dir, repo_root)
879
+ role = args.role
880
+
881
+ if not role:
882
+ print(colored("Error: Missing arguments", Colors.RED))
883
+ print("Usage: python3 task.py set-role <task-dir> <pm|designer|frontend>")
884
+ return 1
885
+
886
+ task_json = target_dir / FILE_TASK_JSON
887
+ if not task_json.is_file():
888
+ print(colored(f"Error: task.json not found at {target_dir}", Colors.RED))
889
+ return 1
890
+
891
+ data = _read_json_file(task_json)
892
+ if not data:
893
+ return 1
894
+
895
+ data["role"] = role
896
+ _write_json_file(task_json, data)
897
+
898
+ print(colored(f"✓ Role set to: {role}", Colors.GREEN))
899
+ print()
900
+ print(colored("Next steps:", Colors.BLUE))
901
+ print(f" 1. Read role spec: cat .trellis/spec/roles/{role}/index.md")
902
+ print(f" 2. Set as current: python3 task.py start {args.dir}")
903
+ return 0
904
+
905
+
906
+ # =============================================================================
907
+ # Command: set-source
908
+ # =============================================================================
909
+
910
+ def cmd_set_source(args: argparse.Namespace) -> int:
911
+ """Set upstream source reference for three-role collaboration."""
912
+ repo_root = get_repo_root()
913
+ target_dir = _resolve_task_dir(args.dir, repo_root)
914
+ pool_type = args.pool_type
915
+ task_id = args.task_id
916
+
917
+ if not pool_type or not task_id:
918
+ print(colored("Error: Missing arguments", Colors.RED))
919
+ print("Usage: python3 task.py set-source <task-dir> <requirements|prototypes> <task-id>")
920
+ return 1
921
+
922
+ task_json = target_dir / FILE_TASK_JSON
923
+ if not task_json.is_file():
924
+ print(colored(f"Error: task.json not found at {target_dir}", Colors.RED))
925
+ return 1
926
+
927
+ # Read pool to find upstream task
928
+ import sys
929
+ sys.path.insert(0, str(Path(__file__).parent))
930
+ from pool import get_pool_file, read_pool, find_task_in_pool
931
+
932
+ pool_file = get_pool_file(pool_type, repo_root)
933
+ pool_data = read_pool(pool_file)
934
+
935
+ status, upstream_task = find_task_in_pool(pool_data, task_id)
936
+
937
+ if not status:
938
+ print(colored(f"Error: Task '{task_id}' not found in {pool_type} pool", Colors.RED))
939
+ return 1
940
+
941
+ if status == "consumed":
942
+ print(colored(f"Warning: Task '{task_id}' is already consumed", Colors.YELLOW))
943
+ confirm = input("Continue anyway? [y/N] ")
944
+ if confirm.lower() != 'y':
945
+ return 0
946
+
947
+ # Create source.json
948
+ source_data = {
949
+ "based_on": {
950
+ "type": pool_type.rstrip('s'), # requirements -> requirement
951
+ "id": task_id,
952
+ "path": upstream_task["path"],
953
+ "handoff_doc": upstream_task["handoff_doc"]
954
+ }
955
+ }
956
+
957
+ source_file = target_dir / "source.json"
958
+ source_file.write_text(json.dumps(source_data, indent=2, ensure_ascii=False), encoding="utf-8")
959
+
960
+ print(colored(f"✓ Source reference set", Colors.GREEN))
961
+ print(f" Type: {pool_type}")
962
+ print(f" ID: {task_id}")
963
+ print(f" Path: {upstream_task['path']}")
964
+ print()
965
+ print(colored("Upstream context will be auto-injected when you start this task", Colors.BLUE))
966
+
967
+ return 0
968
+
969
+
970
+ # =============================================================================
971
+ # Command: create-pr (delegates to multi-agent script)
972
+ # =============================================================================
973
+
974
+ def cmd_create_pr(args: argparse.Namespace) -> int:
975
+ """Create PR from task - delegates to multi_agent/create_pr.py."""
976
+ import subprocess
977
+ script_dir = Path(__file__).parent
978
+ create_pr_script = script_dir / "multi_agent" / "create_pr.py"
979
+
980
+ cmd = [sys.executable, str(create_pr_script)]
981
+ if args.dir:
982
+ cmd.append(args.dir)
983
+ if args.dry_run:
984
+ cmd.append("--dry-run")
985
+
986
+ result = subprocess.run(cmd)
987
+ return result.returncode
988
+
989
+
990
+ # =============================================================================
991
+ # Help
992
+ # =============================================================================
993
+
994
+ def show_usage() -> None:
995
+ """Show usage help."""
996
+ print("""Task Management Script for Multi-Agent Pipeline
997
+
998
+ Usage:
999
+ python3 task.py create <title> Create new task directory
1000
+ python3 task.py init-context <dir> <dev_type> Initialize jsonl files
1001
+ python3 task.py add-context <dir> <jsonl> <path> [reason] Add entry to jsonl
1002
+ python3 task.py validate <dir> Validate jsonl files
1003
+ python3 task.py list-context <dir> List jsonl entries
1004
+ python3 task.py start <dir> Set as current task
1005
+ python3 task.py finish Clear current task
1006
+ python3 task.py set-branch <dir> <branch> Set git branch for multi-agent
1007
+ python3 task.py set-scope <dir> <scope> Set scope for PR title
1008
+ python3 task.py set-role <dir> <role> Set role (pm|designer|frontend)
1009
+ python3 task.py set-source <dir> <pool> <task-id> Set upstream source reference
1010
+ python3 task.py create-pr [dir] [--dry-run] Create PR from task
1011
+ python3 task.py archive <task-name> Archive completed task
1012
+ python3 task.py list [--mine] [--status <status>] List tasks
1013
+ python3 task.py list-archive [YYYY-MM] List archived tasks
1014
+
1015
+ Arguments:
1016
+ dev_type: backend | frontend | fullstack | test | docs
1017
+
1018
+ List options:
1019
+ --mine, -m Show only tasks assigned to current developer
1020
+ --status, -s <s> Filter by status (planning, in_progress, review, completed)
1021
+
1022
+ Examples:
1023
+ python3 task.py create "Add login feature" --slug add-login
1024
+ python3 task.py init-context .trellis/tasks/01-21-add-login backend
1025
+ python3 task.py add-context <dir> implement .trellis/spec/backend/auth.md "Auth guidelines"
1026
+ python3 task.py set-branch <dir> task/add-login
1027
+ python3 task.py start .trellis/tasks/01-21-add-login
1028
+ python3 task.py create-pr # Uses current task
1029
+ python3 task.py create-pr <dir> --dry-run # Preview without changes
1030
+ python3 task.py finish
1031
+ python3 task.py archive add-login
1032
+ python3 task.py list # List all active tasks
1033
+ python3 task.py list --mine # List my tasks only
1034
+ python3 task.py list --mine --status in_progress # List my in-progress tasks
1035
+ """)
1036
+
1037
+
1038
+ # =============================================================================
1039
+ # Main Entry
1040
+ # =============================================================================
1041
+
1042
+ def main() -> int:
1043
+ """CLI entry point."""
1044
+ parser = argparse.ArgumentParser(
1045
+ description="Task Management Script for Multi-Agent Pipeline",
1046
+ formatter_class=argparse.RawDescriptionHelpFormatter,
1047
+ )
1048
+ subparsers = parser.add_subparsers(dest="command", help="Commands")
1049
+
1050
+ # create
1051
+ p_create = subparsers.add_parser("create", help="Create new task")
1052
+ p_create.add_argument("title", help="Task title")
1053
+ p_create.add_argument("--slug", "-s", help="Task slug")
1054
+ p_create.add_argument("--assignee", "-a", help="Assignee developer")
1055
+ p_create.add_argument("--priority", "-p", default="P2", help="Priority (P0-P3)")
1056
+ p_create.add_argument("--description", "-d", help="Task description")
1057
+
1058
+ # init-context
1059
+ p_init = subparsers.add_parser("init-context", help="Initialize context files")
1060
+ p_init.add_argument("dir", help="Task directory")
1061
+ p_init.add_argument("type", help="Dev type: backend|frontend|fullstack|test|docs")
1062
+
1063
+ # add-context
1064
+ p_add = subparsers.add_parser("add-context", help="Add context entry")
1065
+ p_add.add_argument("dir", help="Task directory")
1066
+ p_add.add_argument("file", help="JSONL file (implement|check|debug)")
1067
+ p_add.add_argument("path", help="File path to add")
1068
+ p_add.add_argument("reason", nargs="?", help="Reason for adding")
1069
+
1070
+ # validate
1071
+ p_validate = subparsers.add_parser("validate", help="Validate context files")
1072
+ p_validate.add_argument("dir", help="Task directory")
1073
+
1074
+ # list-context
1075
+ p_listctx = subparsers.add_parser("list-context", help="List context entries")
1076
+ p_listctx.add_argument("dir", help="Task directory")
1077
+
1078
+ # start
1079
+ p_start = subparsers.add_parser("start", help="Set current task")
1080
+ p_start.add_argument("dir", help="Task directory")
1081
+
1082
+ # finish
1083
+ subparsers.add_parser("finish", help="Clear current task")
1084
+
1085
+ # set-branch
1086
+ p_branch = subparsers.add_parser("set-branch", help="Set git branch")
1087
+ p_branch.add_argument("dir", help="Task directory")
1088
+ p_branch.add_argument("branch", help="Branch name")
1089
+
1090
+ # set-base-branch
1091
+ p_base = subparsers.add_parser("set-base-branch", help="Set PR target branch")
1092
+ p_base.add_argument("dir", help="Task directory")
1093
+ p_base.add_argument("base_branch", help="Base branch name (PR target)")
1094
+
1095
+ # set-scope
1096
+ p_scope = subparsers.add_parser("set-scope", help="Set scope")
1097
+ p_scope.add_argument("dir", help="Task directory")
1098
+ p_scope.add_argument("scope", help="Scope name")
1099
+
1100
+ # set-role
1101
+ p_role = subparsers.add_parser("set-role", help="Set role (pm|designer|frontend)")
1102
+ p_role.add_argument("dir", help="Task directory")
1103
+ p_role.add_argument("role", choices=["pm", "designer", "frontend"], help="Role type")
1104
+
1105
+ # set-source
1106
+ p_source = subparsers.add_parser("set-source", help="Set upstream source")
1107
+ p_source.add_argument("dir", help="Task directory")
1108
+ p_source.add_argument("pool_type", choices=["requirements", "prototypes"], help="Upstream pool type")
1109
+ p_source.add_argument("task_id", help="Upstream task ID")
1110
+
1111
+ # create-pr
1112
+ p_pr = subparsers.add_parser("create-pr", help="Create PR")
1113
+ p_pr.add_argument("dir", nargs="?", help="Task directory")
1114
+ p_pr.add_argument("--dry-run", action="store_true", help="Dry run mode")
1115
+
1116
+ # archive
1117
+ p_archive = subparsers.add_parser("archive", help="Archive task")
1118
+ p_archive.add_argument("name", help="Task name")
1119
+
1120
+ # list
1121
+ p_list = subparsers.add_parser("list", help="List tasks")
1122
+ p_list.add_argument("--mine", "-m", action="store_true", help="My tasks only")
1123
+ p_list.add_argument("--status", "-s", help="Filter by status")
1124
+
1125
+ # list-archive
1126
+ p_listarch = subparsers.add_parser("list-archive", help="List archived tasks")
1127
+ p_listarch.add_argument("month", nargs="?", help="Month (YYYY-MM)")
1128
+
1129
+ args = parser.parse_args()
1130
+
1131
+ if not args.command:
1132
+ show_usage()
1133
+ return 1
1134
+
1135
+ commands = {
1136
+ "create": cmd_create,
1137
+ "init-context": cmd_init_context,
1138
+ "add-context": cmd_add_context,
1139
+ "validate": cmd_validate,
1140
+ "list-context": cmd_list_context,
1141
+ "start": cmd_start,
1142
+ "finish": cmd_finish,
1143
+ "set-branch": cmd_set_branch,
1144
+ "set-base-branch": cmd_set_base_branch,
1145
+ "set-scope": cmd_set_scope,
1146
+ "set-role": cmd_set_role,
1147
+ "set-source": cmd_set_source,
1148
+ "create-pr": cmd_create_pr,
1149
+ "archive": cmd_archive,
1150
+ "list": cmd_list,
1151
+ "list-archive": cmd_list_archive,
1152
+ }
1153
+
1154
+ if args.command in commands:
1155
+ return commands[args.command](args)
1156
+ else:
1157
+ show_usage()
1158
+ return 1
1159
+
1160
+
1161
+ if __name__ == "__main__":
1162
+ sys.exit(main())