trellis-he 0.6.0-beta.17

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 (657) hide show
  1. package/LICENSE +235 -0
  2. package/README.md +149 -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 +207 -0
  7. package/dist/cli/index.js.map +1 -0
  8. package/dist/commands/channel/adapters/claude.d.ts +38 -0
  9. package/dist/commands/channel/adapters/claude.d.ts.map +1 -0
  10. package/dist/commands/channel/adapters/claude.js +209 -0
  11. package/dist/commands/channel/adapters/claude.js.map +1 -0
  12. package/dist/commands/channel/adapters/codex.d.ts +81 -0
  13. package/dist/commands/channel/adapters/codex.d.ts.map +1 -0
  14. package/dist/commands/channel/adapters/codex.js +512 -0
  15. package/dist/commands/channel/adapters/codex.js.map +1 -0
  16. package/dist/commands/channel/adapters/index.d.ts +79 -0
  17. package/dist/commands/channel/adapters/index.d.ts.map +1 -0
  18. package/dist/commands/channel/adapters/index.js +109 -0
  19. package/dist/commands/channel/adapters/index.js.map +1 -0
  20. package/dist/commands/channel/adapters/types.d.ts +33 -0
  21. package/dist/commands/channel/adapters/types.d.ts.map +1 -0
  22. package/dist/commands/channel/adapters/types.js +2 -0
  23. package/dist/commands/channel/adapters/types.js.map +1 -0
  24. package/dist/commands/channel/agent-loader.d.ts +32 -0
  25. package/dist/commands/channel/agent-loader.d.ts.map +1 -0
  26. package/dist/commands/channel/agent-loader.js +154 -0
  27. package/dist/commands/channel/agent-loader.js.map +1 -0
  28. package/dist/commands/channel/context-loader.d.ts +26 -0
  29. package/dist/commands/channel/context-loader.d.ts.map +1 -0
  30. package/dist/commands/channel/context-loader.js +290 -0
  31. package/dist/commands/channel/context-loader.js.map +1 -0
  32. package/dist/commands/channel/context.d.ts +16 -0
  33. package/dist/commands/channel/context.d.ts.map +1 -0
  34. package/dist/commands/channel/context.js +83 -0
  35. package/dist/commands/channel/context.js.map +1 -0
  36. package/dist/commands/channel/create.d.ts +27 -0
  37. package/dist/commands/channel/create.d.ts.map +1 -0
  38. package/dist/commands/channel/create.js +39 -0
  39. package/dist/commands/channel/create.js.map +1 -0
  40. package/dist/commands/channel/dev-parse-trace.d.ts +14 -0
  41. package/dist/commands/channel/dev-parse-trace.d.ts.map +1 -0
  42. package/dist/commands/channel/dev-parse-trace.js +70 -0
  43. package/dist/commands/channel/dev-parse-trace.js.map +1 -0
  44. package/dist/commands/channel/index.d.ts +3 -0
  45. package/dist/commands/channel/index.d.ts.map +1 -0
  46. package/dist/commands/channel/index.js +503 -0
  47. package/dist/commands/channel/index.js.map +1 -0
  48. package/dist/commands/channel/kill.d.ts +7 -0
  49. package/dist/commands/channel/kill.d.ts.map +1 -0
  50. package/dist/commands/channel/kill.js +121 -0
  51. package/dist/commands/channel/kill.js.map +1 -0
  52. package/dist/commands/channel/list.d.ts +17 -0
  53. package/dist/commands/channel/list.d.ts.map +1 -0
  54. package/dist/commands/channel/list.js +233 -0
  55. package/dist/commands/channel/list.js.map +1 -0
  56. package/dist/commands/channel/messages.d.ts +16 -0
  57. package/dist/commands/channel/messages.d.ts.map +1 -0
  58. package/dist/commands/channel/messages.js +249 -0
  59. package/dist/commands/channel/messages.js.map +1 -0
  60. package/dist/commands/channel/rm.d.ts +27 -0
  61. package/dist/commands/channel/rm.d.ts.map +1 -0
  62. package/dist/commands/channel/rm.js +216 -0
  63. package/dist/commands/channel/rm.js.map +1 -0
  64. package/dist/commands/channel/run.d.ts +31 -0
  65. package/dist/commands/channel/run.d.ts.map +1 -0
  66. package/dist/commands/channel/run.js +137 -0
  67. package/dist/commands/channel/run.js.map +1 -0
  68. package/dist/commands/channel/send.d.ts +13 -0
  69. package/dist/commands/channel/send.d.ts.map +1 -0
  70. package/dist/commands/channel/send.js +26 -0
  71. package/dist/commands/channel/send.js.map +1 -0
  72. package/dist/commands/channel/spawn.d.ts +30 -0
  73. package/dist/commands/channel/spawn.d.ts.map +1 -0
  74. package/dist/commands/channel/spawn.js +194 -0
  75. package/dist/commands/channel/spawn.js.map +1 -0
  76. package/dist/commands/channel/store/events.d.ts +39 -0
  77. package/dist/commands/channel/store/events.d.ts.map +1 -0
  78. package/dist/commands/channel/store/events.js +87 -0
  79. package/dist/commands/channel/store/events.js.map +1 -0
  80. package/dist/commands/channel/store/filter.d.ts +3 -0
  81. package/dist/commands/channel/store/filter.d.ts.map +1 -0
  82. package/dist/commands/channel/store/filter.js +2 -0
  83. package/dist/commands/channel/store/filter.js.map +1 -0
  84. package/dist/commands/channel/store/lock.d.ts +23 -0
  85. package/dist/commands/channel/store/lock.d.ts.map +1 -0
  86. package/dist/commands/channel/store/lock.js +99 -0
  87. package/dist/commands/channel/store/lock.js.map +1 -0
  88. package/dist/commands/channel/store/paths.d.ts +63 -0
  89. package/dist/commands/channel/store/paths.d.ts.map +1 -0
  90. package/dist/commands/channel/store/paths.js +246 -0
  91. package/dist/commands/channel/store/paths.js.map +1 -0
  92. package/dist/commands/channel/store/schema.d.ts +27 -0
  93. package/dist/commands/channel/store/schema.d.ts.map +1 -0
  94. package/dist/commands/channel/store/schema.js +34 -0
  95. package/dist/commands/channel/store/schema.js.map +1 -0
  96. package/dist/commands/channel/store/thread-state.d.ts +5 -0
  97. package/dist/commands/channel/store/thread-state.d.ts.map +1 -0
  98. package/dist/commands/channel/store/thread-state.js +16 -0
  99. package/dist/commands/channel/store/thread-state.js.map +1 -0
  100. package/dist/commands/channel/store/watch.d.ts +19 -0
  101. package/dist/commands/channel/store/watch.d.ts.map +1 -0
  102. package/dist/commands/channel/store/watch.js +146 -0
  103. package/dist/commands/channel/store/watch.js.map +1 -0
  104. package/dist/commands/channel/supervisor/inbox.d.ts +30 -0
  105. package/dist/commands/channel/supervisor/inbox.d.ts.map +1 -0
  106. package/dist/commands/channel/supervisor/inbox.js +160 -0
  107. package/dist/commands/channel/supervisor/inbox.js.map +1 -0
  108. package/dist/commands/channel/supervisor/shutdown.d.ts +66 -0
  109. package/dist/commands/channel/supervisor/shutdown.d.ts.map +1 -0
  110. package/dist/commands/channel/supervisor/shutdown.js +143 -0
  111. package/dist/commands/channel/supervisor/shutdown.js.map +1 -0
  112. package/dist/commands/channel/supervisor/stdout.d.ts +51 -0
  113. package/dist/commands/channel/supervisor/stdout.d.ts.map +1 -0
  114. package/dist/commands/channel/supervisor/stdout.js +121 -0
  115. package/dist/commands/channel/supervisor/stdout.js.map +1 -0
  116. package/dist/commands/channel/supervisor/turns.d.ts +20 -0
  117. package/dist/commands/channel/supervisor/turns.d.ts.map +1 -0
  118. package/dist/commands/channel/supervisor/turns.js +28 -0
  119. package/dist/commands/channel/supervisor/turns.js.map +1 -0
  120. package/dist/commands/channel/supervisor/warning.d.ts +48 -0
  121. package/dist/commands/channel/supervisor/warning.d.ts.map +1 -0
  122. package/dist/commands/channel/supervisor/warning.js +77 -0
  123. package/dist/commands/channel/supervisor/warning.js.map +1 -0
  124. package/dist/commands/channel/supervisor.d.ts +53 -0
  125. package/dist/commands/channel/supervisor.d.ts.map +1 -0
  126. package/dist/commands/channel/supervisor.js +304 -0
  127. package/dist/commands/channel/supervisor.js.map +1 -0
  128. package/dist/commands/channel/text-body.d.ts +13 -0
  129. package/dist/commands/channel/text-body.d.ts.map +1 -0
  130. package/dist/commands/channel/text-body.js +47 -0
  131. package/dist/commands/channel/text-body.js.map +1 -0
  132. package/dist/commands/channel/threads.d.ts +39 -0
  133. package/dist/commands/channel/threads.d.ts.map +1 -0
  134. package/dist/commands/channel/threads.js +106 -0
  135. package/dist/commands/channel/threads.js.map +1 -0
  136. package/dist/commands/channel/title.d.ts +12 -0
  137. package/dist/commands/channel/title.d.ts.map +1 -0
  138. package/dist/commands/channel/title.js +24 -0
  139. package/dist/commands/channel/title.js.map +1 -0
  140. package/dist/commands/channel/wait.d.ts +18 -0
  141. package/dist/commands/channel/wait.d.ts.map +1 -0
  142. package/dist/commands/channel/wait.js +76 -0
  143. package/dist/commands/channel/wait.js.map +1 -0
  144. package/dist/commands/init.d.ts +59 -0
  145. package/dist/commands/init.d.ts.map +1 -0
  146. package/dist/commands/init.js +1459 -0
  147. package/dist/commands/init.js.map +1 -0
  148. package/dist/commands/mem.d.ts +30 -0
  149. package/dist/commands/mem.d.ts.map +1 -0
  150. package/dist/commands/mem.js +424 -0
  151. package/dist/commands/mem.js.map +1 -0
  152. package/dist/commands/uninstall.d.ts +27 -0
  153. package/dist/commands/uninstall.d.ts.map +1 -0
  154. package/dist/commands/uninstall.js +339 -0
  155. package/dist/commands/uninstall.js.map +1 -0
  156. package/dist/commands/update.d.ts +72 -0
  157. package/dist/commands/update.d.ts.map +1 -0
  158. package/dist/commands/update.js +1926 -0
  159. package/dist/commands/update.js.map +1 -0
  160. package/dist/commands/upgrade.d.ts +28 -0
  161. package/dist/commands/upgrade.d.ts.map +1 -0
  162. package/dist/commands/upgrade.js +84 -0
  163. package/dist/commands/upgrade.js.map +1 -0
  164. package/dist/commands/workflow.d.ts +35 -0
  165. package/dist/commands/workflow.d.ts.map +1 -0
  166. package/dist/commands/workflow.js +219 -0
  167. package/dist/commands/workflow.js.map +1 -0
  168. package/dist/configurators/antigravity.d.ts +7 -0
  169. package/dist/configurators/antigravity.d.ts.map +1 -0
  170. package/dist/configurators/antigravity.js +19 -0
  171. package/dist/configurators/antigravity.js.map +1 -0
  172. package/dist/configurators/claude.d.ts +9 -0
  173. package/dist/configurators/claude.d.ts.map +1 -0
  174. package/dist/configurators/claude.js +72 -0
  175. package/dist/configurators/claude.js.map +1 -0
  176. package/dist/configurators/codebuddy.d.ts +10 -0
  177. package/dist/configurators/codebuddy.d.ts.map +1 -0
  178. package/dist/configurators/codebuddy.js +30 -0
  179. package/dist/configurators/codebuddy.js.map +1 -0
  180. package/dist/configurators/codex.d.ts +8 -0
  181. package/dist/configurators/codex.d.ts.map +1 -0
  182. package/dist/configurators/codex.js +87 -0
  183. package/dist/configurators/codex.js.map +1 -0
  184. package/dist/configurators/copilot.d.ts +10 -0
  185. package/dist/configurators/copilot.d.ts.map +1 -0
  186. package/dist/configurators/copilot.js +51 -0
  187. package/dist/configurators/copilot.js.map +1 -0
  188. package/dist/configurators/cursor.d.ts +10 -0
  189. package/dist/configurators/cursor.d.ts.map +1 -0
  190. package/dist/configurators/cursor.js +29 -0
  191. package/dist/configurators/cursor.js.map +1 -0
  192. package/dist/configurators/droid.d.ts +10 -0
  193. package/dist/configurators/droid.d.ts.map +1 -0
  194. package/dist/configurators/droid.js +30 -0
  195. package/dist/configurators/droid.js.map +1 -0
  196. package/dist/configurators/gemini.d.ts +16 -0
  197. package/dist/configurators/gemini.d.ts.map +1 -0
  198. package/dist/configurators/gemini.js +38 -0
  199. package/dist/configurators/gemini.js.map +1 -0
  200. package/dist/configurators/index.d.ts +65 -0
  201. package/dist/configurators/index.d.ts.map +1 -0
  202. package/dist/configurators/index.js +367 -0
  203. package/dist/configurators/index.js.map +1 -0
  204. package/dist/configurators/kilo.d.ts +7 -0
  205. package/dist/configurators/kilo.d.ts.map +1 -0
  206. package/dist/configurators/kilo.js +19 -0
  207. package/dist/configurators/kilo.js.map +1 -0
  208. package/dist/configurators/kiro.d.ts +8 -0
  209. package/dist/configurators/kiro.d.ts.map +1 -0
  210. package/dist/configurators/kiro.js +24 -0
  211. package/dist/configurators/kiro.js.map +1 -0
  212. package/dist/configurators/opencode.d.ts +14 -0
  213. package/dist/configurators/opencode.d.ts.map +1 -0
  214. package/dist/configurators/opencode.js +96 -0
  215. package/dist/configurators/opencode.js.map +1 -0
  216. package/dist/configurators/pi.d.ts +3 -0
  217. package/dist/configurators/pi.d.ts.map +1 -0
  218. package/dist/configurators/pi.js +45 -0
  219. package/dist/configurators/pi.js.map +1 -0
  220. package/dist/configurators/qoder.d.ts +11 -0
  221. package/dist/configurators/qoder.d.ts.map +1 -0
  222. package/dist/configurators/qoder.js +31 -0
  223. package/dist/configurators/qoder.js.map +1 -0
  224. package/dist/configurators/shared.d.ts +178 -0
  225. package/dist/configurators/shared.d.ts.map +1 -0
  226. package/dist/configurators/shared.js +541 -0
  227. package/dist/configurators/shared.js.map +1 -0
  228. package/dist/configurators/windsurf.d.ts +7 -0
  229. package/dist/configurators/windsurf.d.ts.map +1 -0
  230. package/dist/configurators/windsurf.js +19 -0
  231. package/dist/configurators/windsurf.js.map +1 -0
  232. package/dist/configurators/workflow.d.ts +37 -0
  233. package/dist/configurators/workflow.d.ts.map +1 -0
  234. package/dist/configurators/workflow.js +164 -0
  235. package/dist/configurators/workflow.js.map +1 -0
  236. package/dist/constants/paths.d.ts +70 -0
  237. package/dist/constants/paths.d.ts.map +1 -0
  238. package/dist/constants/paths.js +79 -0
  239. package/dist/constants/paths.js.map +1 -0
  240. package/dist/constants/version.d.ts +9 -0
  241. package/dist/constants/version.d.ts.map +1 -0
  242. package/dist/constants/version.js +15 -0
  243. package/dist/constants/version.js.map +1 -0
  244. package/dist/index.d.ts +9 -0
  245. package/dist/index.d.ts.map +1 -0
  246. package/dist/index.js +9 -0
  247. package/dist/index.js.map +1 -0
  248. package/dist/migrations/index.d.ts +62 -0
  249. package/dist/migrations/index.d.ts.map +1 -0
  250. package/dist/migrations/index.js +187 -0
  251. package/dist/migrations/index.js.map +1 -0
  252. package/dist/migrations/manifests/0.1.9.json +30 -0
  253. package/dist/migrations/manifests/0.2.0.json +49 -0
  254. package/dist/migrations/manifests/0.2.12.json +9 -0
  255. package/dist/migrations/manifests/0.2.13.json +9 -0
  256. package/dist/migrations/manifests/0.2.14.json +175 -0
  257. package/dist/migrations/manifests/0.2.15.json +33 -0
  258. package/dist/migrations/manifests/0.3.0-beta.0.json +297 -0
  259. package/dist/migrations/manifests/0.3.0-beta.1.json +9 -0
  260. package/dist/migrations/manifests/0.3.0-beta.10.json +9 -0
  261. package/dist/migrations/manifests/0.3.0-beta.11.json +9 -0
  262. package/dist/migrations/manifests/0.3.0-beta.12.json +9 -0
  263. package/dist/migrations/manifests/0.3.0-beta.13.json +9 -0
  264. package/dist/migrations/manifests/0.3.0-beta.14.json +9 -0
  265. package/dist/migrations/manifests/0.3.0-beta.15.json +9 -0
  266. package/dist/migrations/manifests/0.3.0-beta.16.json +9 -0
  267. package/dist/migrations/manifests/0.3.0-beta.2.json +9 -0
  268. package/dist/migrations/manifests/0.3.0-beta.3.json +9 -0
  269. package/dist/migrations/manifests/0.3.0-beta.4.json +9 -0
  270. package/dist/migrations/manifests/0.3.0-beta.5.json +9 -0
  271. package/dist/migrations/manifests/0.3.0-beta.6.json +9 -0
  272. package/dist/migrations/manifests/0.3.0-beta.7.json +11 -0
  273. package/dist/migrations/manifests/0.3.0-beta.8.json +9 -0
  274. package/dist/migrations/manifests/0.3.0-beta.9.json +9 -0
  275. package/dist/migrations/manifests/0.3.0-rc.0.json +9 -0
  276. package/dist/migrations/manifests/0.3.0-rc.1.json +9 -0
  277. package/dist/migrations/manifests/0.3.0-rc.2.json +9 -0
  278. package/dist/migrations/manifests/0.3.0-rc.3.json +9 -0
  279. package/dist/migrations/manifests/0.3.0-rc.4.json +9 -0
  280. package/dist/migrations/manifests/0.3.0-rc.5.json +9 -0
  281. package/dist/migrations/manifests/0.3.0-rc.6.json +9 -0
  282. package/dist/migrations/manifests/0.3.0.json +11 -0
  283. package/dist/migrations/manifests/0.3.1.json +9 -0
  284. package/dist/migrations/manifests/0.3.10.json +9 -0
  285. package/dist/migrations/manifests/0.3.2.json +9 -0
  286. package/dist/migrations/manifests/0.3.3.json +9 -0
  287. package/dist/migrations/manifests/0.3.4.json +21 -0
  288. package/dist/migrations/manifests/0.3.5.json +9 -0
  289. package/dist/migrations/manifests/0.3.6.json +9 -0
  290. package/dist/migrations/manifests/0.3.7.json +9 -0
  291. package/dist/migrations/manifests/0.3.8.json +9 -0
  292. package/dist/migrations/manifests/0.3.9.json +9 -0
  293. package/dist/migrations/manifests/0.4.0-beta.1.json +228 -0
  294. package/dist/migrations/manifests/0.4.0-beta.10.json +9 -0
  295. package/dist/migrations/manifests/0.4.0-beta.2.json +9 -0
  296. package/dist/migrations/manifests/0.4.0-beta.3.json +9 -0
  297. package/dist/migrations/manifests/0.4.0-beta.4.json +9 -0
  298. package/dist/migrations/manifests/0.4.0-beta.5.json +9 -0
  299. package/dist/migrations/manifests/0.4.0-beta.6.json +9 -0
  300. package/dist/migrations/manifests/0.4.0-beta.7.json +9 -0
  301. package/dist/migrations/manifests/0.4.0-beta.8.json +34 -0
  302. package/dist/migrations/manifests/0.4.0-beta.9.json +9 -0
  303. package/dist/migrations/manifests/0.4.0-rc.0.json +9 -0
  304. package/dist/migrations/manifests/0.4.0-rc.1.json +9 -0
  305. package/dist/migrations/manifests/0.4.0.json +9 -0
  306. package/dist/migrations/manifests/0.5.0-beta.0.json +1646 -0
  307. package/dist/migrations/manifests/0.5.0-beta.1.json +9 -0
  308. package/dist/migrations/manifests/0.5.0-beta.10.json +9 -0
  309. package/dist/migrations/manifests/0.5.0-beta.11.json +9 -0
  310. package/dist/migrations/manifests/0.5.0-beta.12.json +9 -0
  311. package/dist/migrations/manifests/0.5.0-beta.13.json +9 -0
  312. package/dist/migrations/manifests/0.5.0-beta.14.json +9 -0
  313. package/dist/migrations/manifests/0.5.0-beta.15.json +116 -0
  314. package/dist/migrations/manifests/0.5.0-beta.16.json +9 -0
  315. package/dist/migrations/manifests/0.5.0-beta.17.json +9 -0
  316. package/dist/migrations/manifests/0.5.0-beta.18.json +9 -0
  317. package/dist/migrations/manifests/0.5.0-beta.19.json +9 -0
  318. package/dist/migrations/manifests/0.5.0-beta.2.json +9 -0
  319. package/dist/migrations/manifests/0.5.0-beta.3.json +9 -0
  320. package/dist/migrations/manifests/0.5.0-beta.4.json +9 -0
  321. package/dist/migrations/manifests/0.5.0-beta.5.json +222 -0
  322. package/dist/migrations/manifests/0.5.0-beta.6.json +9 -0
  323. package/dist/migrations/manifests/0.5.0-beta.7.json +9 -0
  324. package/dist/migrations/manifests/0.5.0-beta.8.json +9 -0
  325. package/dist/migrations/manifests/0.5.0-beta.9.json +48 -0
  326. package/dist/migrations/manifests/0.5.0-rc.0.json +9 -0
  327. package/dist/migrations/manifests/0.5.0-rc.1.json +9 -0
  328. package/dist/migrations/manifests/0.5.0-rc.2.json +9 -0
  329. package/dist/migrations/manifests/0.5.0-rc.3.json +9 -0
  330. package/dist/migrations/manifests/0.5.0-rc.4.json +9 -0
  331. package/dist/migrations/manifests/0.5.0-rc.5.json +9 -0
  332. package/dist/migrations/manifests/0.5.0-rc.6.json +9 -0
  333. package/dist/migrations/manifests/0.5.0-rc.7.json +9 -0
  334. package/dist/migrations/manifests/0.5.0.json +9 -0
  335. package/dist/migrations/manifests/0.5.1.json +9 -0
  336. package/dist/migrations/manifests/0.5.10.json +9 -0
  337. package/dist/migrations/manifests/0.5.11.json +16 -0
  338. package/dist/migrations/manifests/0.5.12.json +9 -0
  339. package/dist/migrations/manifests/0.5.13.json +9 -0
  340. package/dist/migrations/manifests/0.5.14.json +9 -0
  341. package/dist/migrations/manifests/0.5.15.json +9 -0
  342. package/dist/migrations/manifests/0.5.2.json +9 -0
  343. package/dist/migrations/manifests/0.5.3.json +9 -0
  344. package/dist/migrations/manifests/0.5.4.json +9 -0
  345. package/dist/migrations/manifests/0.5.5.json +9 -0
  346. package/dist/migrations/manifests/0.5.6.json +9 -0
  347. package/dist/migrations/manifests/0.5.7.json +16 -0
  348. package/dist/migrations/manifests/0.5.8.json +9 -0
  349. package/dist/migrations/manifests/0.5.9.json +9 -0
  350. package/dist/migrations/manifests/0.6.0-beta.0.json +16 -0
  351. package/dist/migrations/manifests/0.6.0-beta.1.json +9 -0
  352. package/dist/migrations/manifests/0.6.0-beta.10.json +9 -0
  353. package/dist/migrations/manifests/0.6.0-beta.11.json +9 -0
  354. package/dist/migrations/manifests/0.6.0-beta.12.json +9 -0
  355. package/dist/migrations/manifests/0.6.0-beta.13.json +9 -0
  356. package/dist/migrations/manifests/0.6.0-beta.14.json +9 -0
  357. package/dist/migrations/manifests/0.6.0-beta.15.json +9 -0
  358. package/dist/migrations/manifests/0.6.0-beta.16.json +9 -0
  359. package/dist/migrations/manifests/0.6.0-beta.17.json +9 -0
  360. package/dist/migrations/manifests/0.6.0-beta.2.json +9 -0
  361. package/dist/migrations/manifests/0.6.0-beta.3.json +9 -0
  362. package/dist/migrations/manifests/0.6.0-beta.4.json +9 -0
  363. package/dist/migrations/manifests/0.6.0-beta.5.json +9 -0
  364. package/dist/migrations/manifests/0.6.0-beta.6.json +16 -0
  365. package/dist/migrations/manifests/0.6.0-beta.7.json +9 -0
  366. package/dist/migrations/manifests/0.6.0-beta.8.json +9 -0
  367. package/dist/migrations/manifests/0.6.0-beta.9.json +9 -0
  368. package/dist/templates/claude/agents/trellis-check.md +123 -0
  369. package/dist/templates/claude/agents/trellis-code-architecture-review.md +71 -0
  370. package/dist/templates/claude/agents/trellis-code-review.md +71 -0
  371. package/dist/templates/claude/agents/trellis-implement.md +118 -0
  372. package/dist/templates/claude/agents/trellis-merge-review.md +72 -0
  373. package/dist/templates/claude/agents/trellis-research.md +137 -0
  374. package/dist/templates/claude/agents/trellis-spec-review.md +71 -0
  375. package/dist/templates/claude/index.d.ts +22 -0
  376. package/dist/templates/claude/index.d.ts.map +1 -0
  377. package/dist/templates/claude/index.js +46 -0
  378. package/dist/templates/claude/index.js.map +1 -0
  379. package/dist/templates/claude/settings.json +73 -0
  380. package/dist/templates/codebuddy/agents/trellis-check.md +115 -0
  381. package/dist/templates/codebuddy/agents/trellis-implement.md +110 -0
  382. package/dist/templates/codebuddy/agents/trellis-research.md +137 -0
  383. package/dist/templates/codebuddy/index.d.ts +15 -0
  384. package/dist/templates/codebuddy/index.d.ts.map +1 -0
  385. package/dist/templates/codebuddy/index.js +15 -0
  386. package/dist/templates/codebuddy/index.js.map +1 -0
  387. package/dist/templates/codebuddy/settings.json +59 -0
  388. package/dist/templates/codex/agents/trellis-check.toml +84 -0
  389. package/dist/templates/codex/agents/trellis-implement.toml +65 -0
  390. package/dist/templates/codex/agents/trellis-research.toml +73 -0
  391. package/dist/templates/codex/config.toml +35 -0
  392. package/dist/templates/codex/hooks/session-start.py +545 -0
  393. package/dist/templates/codex/hooks.json +15 -0
  394. package/dist/templates/codex/index.d.ts +39 -0
  395. package/dist/templates/codex/index.d.ts.map +1 -0
  396. package/dist/templates/codex/index.js +85 -0
  397. package/dist/templates/codex/index.js.map +1 -0
  398. package/dist/templates/codex/skills/before-dev/SKILL.md +40 -0
  399. package/dist/templates/codex/skills/brainstorm/SKILL.md +112 -0
  400. package/dist/templates/codex/skills/break-loop/SKILL.md +130 -0
  401. package/dist/templates/codex/skills/check/SKILL.md +98 -0
  402. package/dist/templates/codex/skills/check-cross-layer/SKILL.md +158 -0
  403. package/dist/templates/codex/skills/create-command/SKILL.md +101 -0
  404. package/dist/templates/codex/skills/finish-work/SKILL.md +90 -0
  405. package/dist/templates/codex/skills/improve-ut/SKILL.md +69 -0
  406. package/dist/templates/codex/skills/integrate-skill/SKILL.md +221 -0
  407. package/dist/templates/codex/skills/onboard/SKILL.md +363 -0
  408. package/dist/templates/codex/skills/record-session/SKILL.md +67 -0
  409. package/dist/templates/codex/skills/start/SKILL.md +64 -0
  410. package/dist/templates/codex/skills/update-spec/SKILL.md +335 -0
  411. package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +73 -0
  412. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/add-project-local-conventions.md +83 -0
  413. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +54 -0
  414. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +84 -0
  415. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-hooks.md +57 -0
  416. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +78 -0
  417. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-spec-structure.md +83 -0
  418. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +90 -0
  419. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-workflow.md +65 -0
  420. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +55 -0
  421. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/context-injection.md +68 -0
  422. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +80 -0
  423. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/overview.md +51 -0
  424. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/spec-system.md +102 -0
  425. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +130 -0
  426. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workflow.md +75 -0
  427. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workspace-memory.md +71 -0
  428. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +80 -0
  429. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +69 -0
  430. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/overview.md +59 -0
  431. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +74 -0
  432. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +83 -0
  433. package/dist/templates/common/commands/continue.md +56 -0
  434. package/dist/templates/common/commands/finish-work.md +66 -0
  435. package/dist/templates/common/commands/start.md +59 -0
  436. package/dist/templates/common/index.d.ts +48 -0
  437. package/dist/templates/common/index.d.ts.map +1 -0
  438. package/dist/templates/common/index.js +104 -0
  439. package/dist/templates/common/index.js.map +1 -0
  440. package/dist/templates/common/skills/before-dev.md +41 -0
  441. package/dist/templates/common/skills/brainstorm.md +112 -0
  442. package/dist/templates/common/skills/break-loop.md +125 -0
  443. package/dist/templates/common/skills/check.md +93 -0
  444. package/dist/templates/common/skills/grill-me.md +49 -0
  445. package/dist/templates/common/skills/improve-codebase-architecture.md +38 -0
  446. package/dist/templates/common/skills/tdd.md +34 -0
  447. package/dist/templates/common/skills/update-spec.md +351 -0
  448. package/dist/templates/copilot/hooks/session-start.py +547 -0
  449. package/dist/templates/copilot/hooks.json +19 -0
  450. package/dist/templates/copilot/index.d.ts +23 -0
  451. package/dist/templates/copilot/index.d.ts.map +1 -0
  452. package/dist/templates/copilot/index.js +54 -0
  453. package/dist/templates/copilot/index.js.map +1 -0
  454. package/dist/templates/copilot/prompts/before-dev.prompt.md +39 -0
  455. package/dist/templates/copilot/prompts/brainstorm.prompt.md +111 -0
  456. package/dist/templates/copilot/prompts/break-loop.prompt.md +129 -0
  457. package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +157 -0
  458. package/dist/templates/copilot/prompts/check.prompt.md +97 -0
  459. package/dist/templates/copilot/prompts/create-command.prompt.md +116 -0
  460. package/dist/templates/copilot/prompts/finish-work.prompt.md +99 -0
  461. package/dist/templates/copilot/prompts/integrate-skill.prompt.md +223 -0
  462. package/dist/templates/copilot/prompts/onboard.prompt.md +362 -0
  463. package/dist/templates/copilot/prompts/parallel.prompt.md +204 -0
  464. package/dist/templates/copilot/prompts/record-session.prompt.md +66 -0
  465. package/dist/templates/copilot/prompts/start.prompt.md +63 -0
  466. package/dist/templates/copilot/prompts/update-spec.prompt.md +358 -0
  467. package/dist/templates/cursor/agents/trellis-check.md +114 -0
  468. package/dist/templates/cursor/agents/trellis-implement.md +109 -0
  469. package/dist/templates/cursor/agents/trellis-research.md +136 -0
  470. package/dist/templates/cursor/hooks.json +24 -0
  471. package/dist/templates/cursor/index.d.ts +13 -0
  472. package/dist/templates/cursor/index.d.ts.map +1 -0
  473. package/dist/templates/cursor/index.js +13 -0
  474. package/dist/templates/cursor/index.js.map +1 -0
  475. package/dist/templates/droid/droids/trellis-check.md +107 -0
  476. package/dist/templates/droid/droids/trellis-implement.md +102 -0
  477. package/dist/templates/droid/droids/trellis-research.md +137 -0
  478. package/dist/templates/droid/index.d.ts +15 -0
  479. package/dist/templates/droid/index.d.ts.map +1 -0
  480. package/dist/templates/droid/index.js +15 -0
  481. package/dist/templates/droid/index.js.map +1 -0
  482. package/dist/templates/droid/settings.json +59 -0
  483. package/dist/templates/extract.d.ts +40 -0
  484. package/dist/templates/extract.d.ts.map +1 -0
  485. package/dist/templates/extract.js +106 -0
  486. package/dist/templates/extract.js.map +1 -0
  487. package/dist/templates/gemini/agents/trellis-check.md +107 -0
  488. package/dist/templates/gemini/agents/trellis-implement.md +102 -0
  489. package/dist/templates/gemini/agents/trellis-research.md +136 -0
  490. package/dist/templates/gemini/index.d.ts +13 -0
  491. package/dist/templates/gemini/index.d.ts.map +1 -0
  492. package/dist/templates/gemini/index.js +13 -0
  493. package/dist/templates/gemini/index.js.map +1 -0
  494. package/dist/templates/gemini/settings.json +28 -0
  495. package/dist/templates/kiro/agents/trellis-check.json +26 -0
  496. package/dist/templates/kiro/agents/trellis-implement.json +26 -0
  497. package/dist/templates/kiro/agents/trellis-research.json +30 -0
  498. package/dist/templates/kiro/index.d.ts +18 -0
  499. package/dist/templates/kiro/index.d.ts.map +1 -0
  500. package/dist/templates/kiro/index.js +18 -0
  501. package/dist/templates/kiro/index.js.map +1 -0
  502. package/dist/templates/markdown/agents.md +21 -0
  503. package/dist/templates/markdown/gitignore.txt +15 -0
  504. package/dist/templates/markdown/index.d.ts +27 -0
  505. package/dist/templates/markdown/index.d.ts.map +1 -0
  506. package/dist/templates/markdown/index.js +52 -0
  507. package/dist/templates/markdown/index.js.map +1 -0
  508. package/dist/templates/markdown/spec/backend/database-guidelines.md.txt +51 -0
  509. package/dist/templates/markdown/spec/backend/directory-structure.md.txt +54 -0
  510. package/dist/templates/markdown/spec/backend/error-handling.md.txt +51 -0
  511. package/dist/templates/markdown/spec/backend/index.md.txt +38 -0
  512. package/dist/templates/markdown/spec/backend/logging-guidelines.md.txt +51 -0
  513. package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +51 -0
  514. package/dist/templates/markdown/spec/frontend/component-guidelines.md.txt +59 -0
  515. package/dist/templates/markdown/spec/frontend/directory-structure.md.txt +54 -0
  516. package/dist/templates/markdown/spec/frontend/hook-guidelines.md.txt +51 -0
  517. package/dist/templates/markdown/spec/frontend/index.md.txt +39 -0
  518. package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +51 -0
  519. package/dist/templates/markdown/spec/frontend/state-management.md.txt +51 -0
  520. package/dist/templates/markdown/spec/frontend/type-safety.md.txt +51 -0
  521. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +223 -0
  522. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +259 -0
  523. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +595 -0
  524. package/dist/templates/markdown/spec/guides/index.md.txt +97 -0
  525. package/dist/templates/markdown/workspace-index.md +125 -0
  526. package/dist/templates/markdown/worktree.yaml.txt +58 -0
  527. package/dist/templates/opencode/agents/trellis-check.md +122 -0
  528. package/dist/templates/opencode/agents/trellis-implement.md +118 -0
  529. package/dist/templates/opencode/agents/trellis-research.md +145 -0
  530. package/dist/templates/opencode/lib/session-utils.js +521 -0
  531. package/dist/templates/opencode/lib/trellis-context.js +381 -0
  532. package/dist/templates/opencode/package.json +5 -0
  533. package/dist/templates/opencode/plugins/inject-subagent-context.js +513 -0
  534. package/dist/templates/opencode/plugins/inject-workflow-state.js +159 -0
  535. package/dist/templates/opencode/plugins/session-start.js +101 -0
  536. package/dist/templates/pi/agents/trellis-check.md +37 -0
  537. package/dist/templates/pi/agents/trellis-implement.md +42 -0
  538. package/dist/templates/pi/agents/trellis-research.md +25 -0
  539. package/dist/templates/pi/extensions/trellis/index.ts.txt +1174 -0
  540. package/dist/templates/pi/index.d.ts +5 -0
  541. package/dist/templates/pi/index.d.ts.map +1 -0
  542. package/dist/templates/pi/index.js +12 -0
  543. package/dist/templates/pi/index.js.map +1 -0
  544. package/dist/templates/pi/settings.json +21 -0
  545. package/dist/templates/qoder/agents/trellis-check.md +108 -0
  546. package/dist/templates/qoder/agents/trellis-implement.md +103 -0
  547. package/dist/templates/qoder/agents/trellis-research.md +137 -0
  548. package/dist/templates/qoder/index.d.ts +15 -0
  549. package/dist/templates/qoder/index.d.ts.map +1 -0
  550. package/dist/templates/qoder/index.js +15 -0
  551. package/dist/templates/qoder/index.js.map +1 -0
  552. package/dist/templates/qoder/settings.json +47 -0
  553. package/dist/templates/shared-hooks/index.d.ts +50 -0
  554. package/dist/templates/shared-hooks/index.d.ts.map +1 -0
  555. package/dist/templates/shared-hooks/index.js +88 -0
  556. package/dist/templates/shared-hooks/index.js.map +1 -0
  557. package/dist/templates/shared-hooks/inject-shell-session-context.py +183 -0
  558. package/dist/templates/shared-hooks/inject-subagent-context.py +771 -0
  559. package/dist/templates/shared-hooks/inject-workflow-state.py +363 -0
  560. package/dist/templates/shared-hooks/session-start.py +832 -0
  561. package/dist/templates/template-utils.d.ts +26 -0
  562. package/dist/templates/template-utils.d.ts.map +1 -0
  563. package/dist/templates/template-utils.js +60 -0
  564. package/dist/templates/template-utils.js.map +1 -0
  565. package/dist/templates/trellis/config.yaml +90 -0
  566. package/dist/templates/trellis/gitignore.txt +32 -0
  567. package/dist/templates/trellis/index.d.ts +52 -0
  568. package/dist/templates/trellis/index.d.ts.map +1 -0
  569. package/dist/templates/trellis/index.js +97 -0
  570. package/dist/templates/trellis/index.js.map +1 -0
  571. package/dist/templates/trellis/scripts/__init__.py +5 -0
  572. package/dist/templates/trellis/scripts/add_session.py +547 -0
  573. package/dist/templates/trellis/scripts/common/__init__.py +92 -0
  574. package/dist/templates/trellis/scripts/common/active_task.py +626 -0
  575. package/dist/templates/trellis/scripts/common/cli_adapter.py +811 -0
  576. package/dist/templates/trellis/scripts/common/config.py +445 -0
  577. package/dist/templates/trellis/scripts/common/developer.py +190 -0
  578. package/dist/templates/trellis/scripts/common/git.py +31 -0
  579. package/dist/templates/trellis/scripts/common/git_context.py +106 -0
  580. package/dist/templates/trellis/scripts/common/io.py +37 -0
  581. package/dist/templates/trellis/scripts/common/log.py +45 -0
  582. package/dist/templates/trellis/scripts/common/packages_context.py +238 -0
  583. package/dist/templates/trellis/scripts/common/paths.py +447 -0
  584. package/dist/templates/trellis/scripts/common/safe_commit.py +285 -0
  585. package/dist/templates/trellis/scripts/common/session_context.py +821 -0
  586. package/dist/templates/trellis/scripts/common/task_context.py +223 -0
  587. package/dist/templates/trellis/scripts/common/task_queue.py +188 -0
  588. package/dist/templates/trellis/scripts/common/task_store.py +698 -0
  589. package/dist/templates/trellis/scripts/common/task_utils.py +274 -0
  590. package/dist/templates/trellis/scripts/common/tasks.py +112 -0
  591. package/dist/templates/trellis/scripts/common/trellis_config.py +131 -0
  592. package/dist/templates/trellis/scripts/common/types.py +110 -0
  593. package/dist/templates/trellis/scripts/common/workflow_phase.py +212 -0
  594. package/dist/templates/trellis/scripts/get_context.py +16 -0
  595. package/dist/templates/trellis/scripts/get_developer.py +26 -0
  596. package/dist/templates/trellis/scripts/hooks/linear_sync.py +243 -0
  597. package/dist/templates/trellis/scripts/init_developer.py +51 -0
  598. package/dist/templates/trellis/scripts/task.py +500 -0
  599. package/dist/templates/trellis/tasks/.gitkeep +0 -0
  600. package/dist/templates/trellis/workflow.md +750 -0
  601. package/dist/types/ai-tools.d.ts +95 -0
  602. package/dist/types/ai-tools.d.ts.map +1 -0
  603. package/dist/types/ai-tools.js +280 -0
  604. package/dist/types/ai-tools.js.map +1 -0
  605. package/dist/types/migration.d.ts +125 -0
  606. package/dist/types/migration.d.ts.map +1 -0
  607. package/dist/types/migration.js +8 -0
  608. package/dist/types/migration.js.map +1 -0
  609. package/dist/utils/compare-versions.d.ts +12 -0
  610. package/dist/utils/compare-versions.d.ts.map +1 -0
  611. package/dist/utils/compare-versions.js +86 -0
  612. package/dist/utils/compare-versions.js.map +1 -0
  613. package/dist/utils/cwd-guard.d.ts +38 -0
  614. package/dist/utils/cwd-guard.d.ts.map +1 -0
  615. package/dist/utils/cwd-guard.js +62 -0
  616. package/dist/utils/cwd-guard.js.map +1 -0
  617. package/dist/utils/file-writer.d.ts +36 -0
  618. package/dist/utils/file-writer.d.ts.map +1 -0
  619. package/dist/utils/file-writer.js +203 -0
  620. package/dist/utils/file-writer.js.map +1 -0
  621. package/dist/utils/manifest-prune.d.ts +61 -0
  622. package/dist/utils/manifest-prune.d.ts.map +1 -0
  623. package/dist/utils/manifest-prune.js +136 -0
  624. package/dist/utils/manifest-prune.js.map +1 -0
  625. package/dist/utils/posix.d.ts +13 -0
  626. package/dist/utils/posix.d.ts.map +1 -0
  627. package/dist/utils/posix.js +15 -0
  628. package/dist/utils/posix.js.map +1 -0
  629. package/dist/utils/project-detector.d.ts +46 -0
  630. package/dist/utils/project-detector.d.ts.map +1 -0
  631. package/dist/utils/project-detector.js +666 -0
  632. package/dist/utils/project-detector.js.map +1 -0
  633. package/dist/utils/proxy.d.ts +25 -0
  634. package/dist/utils/proxy.d.ts.map +1 -0
  635. package/dist/utils/proxy.js +60 -0
  636. package/dist/utils/proxy.js.map +1 -0
  637. package/dist/utils/task-json.d.ts +13 -0
  638. package/dist/utils/task-json.d.ts.map +1 -0
  639. package/dist/utils/task-json.js +12 -0
  640. package/dist/utils/task-json.js.map +1 -0
  641. package/dist/utils/template-fetcher.d.ts +150 -0
  642. package/dist/utils/template-fetcher.d.ts.map +1 -0
  643. package/dist/utils/template-fetcher.js +907 -0
  644. package/dist/utils/template-fetcher.js.map +1 -0
  645. package/dist/utils/template-hash.d.ts +123 -0
  646. package/dist/utils/template-hash.d.ts.map +1 -0
  647. package/dist/utils/template-hash.js +334 -0
  648. package/dist/utils/template-hash.js.map +1 -0
  649. package/dist/utils/uninstall-scrubbers.d.ts +66 -0
  650. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -0
  651. package/dist/utils/uninstall-scrubbers.js +342 -0
  652. package/dist/utils/uninstall-scrubbers.js.map +1 -0
  653. package/dist/utils/workflow-resolver.d.ts +86 -0
  654. package/dist/utils/workflow-resolver.d.ts.map +1 -0
  655. package/dist/utils/workflow-resolver.js +265 -0
  656. package/dist/utils/workflow-resolver.js.map +1 -0
  657. package/package.json +90 -0
@@ -0,0 +1,1459 @@
1
+ import { execSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import readline from "node:readline";
5
+ import chalk from "chalk";
6
+ import figlet from "figlet";
7
+ import inquirer from "inquirer";
8
+ import { createWorkflowStructure } from "../configurators/workflow.js";
9
+ import { getInitToolChoices, resolveCliFlag, configurePlatform, getConfiguredPlatforms, getPlatformsWithPythonHooks, } from "../configurators/index.js";
10
+ import { getPythonCommandForPlatform, setResolvedPythonCommand, } from "../configurators/shared.js";
11
+ import { AI_TOOLS } from "../types/ai-tools.js";
12
+ import { DIR_NAMES, FILE_NAMES, PATHS } from "../constants/paths.js";
13
+ import { VERSION } from "../constants/version.js";
14
+ import { agentsMdContent } from "../templates/markdown/index.js";
15
+ import { setWriteMode, startRecordingWrites, stopRecordingWrites, writeFile, } from "../utils/file-writer.js";
16
+ import { emptyTaskJson } from "../utils/task-json.js";
17
+ import { detectProjectType, detectMonorepo, sanitizePkgName, } from "../utils/project-detector.js";
18
+ import { initializeHashes, removeHash } from "../utils/template-hash.js";
19
+ import { NATIVE_WORKFLOW_ID, resolveWorkflowTemplate, } from "../utils/workflow-resolver.js";
20
+ import { isCwdHomedir, homedirGuardMessage, homedirBypassEnabled, } from "../utils/cwd-guard.js";
21
+ import { fetchTemplateIndex, probeRegistryIndex, downloadTemplateById, downloadRegistryDirect, parseRegistrySource, TIMEOUTS, TEMPLATE_INDEX_URL, } from "../utils/template-fetcher.js";
22
+ import { setupProxy, maskProxyUrl } from "../utils/proxy.js";
23
+ const MIN_PYTHON_MAJOR = 3;
24
+ const MIN_PYTHON_MINOR = 9;
25
+ const PYTHON_VERSION_RE = /Python (\d+)\.(\d+)/;
26
+ export function isSupportedPythonVersion(versionOutput) {
27
+ const match = versionOutput.match(PYTHON_VERSION_RE);
28
+ if (!match)
29
+ return false;
30
+ const major = Number(match[1]);
31
+ const minor = Number(match[2]);
32
+ return (major > MIN_PYTHON_MAJOR ||
33
+ (major === MIN_PYTHON_MAJOR && minor >= MIN_PYTHON_MINOR));
34
+ }
35
+ function detectPythonVersion(command) {
36
+ try {
37
+ return execSync(`${command} --version`, {
38
+ encoding: "utf-8",
39
+ stdio: "pipe",
40
+ }).trim();
41
+ }
42
+ catch (err) {
43
+ const code = err?.code;
44
+ if (code === "EPERM" || code === "EACCES") {
45
+ return "sandbox-restricted";
46
+ }
47
+ return null;
48
+ }
49
+ }
50
+ export function requireSupportedPython(command) {
51
+ // Final escape hatch — set when the user knows python3 is on PATH but
52
+ // the probe keeps failing for environment-specific reasons.
53
+ if (process.env.TRELLIS_SKIP_PYTHON_CHECK === "1") {
54
+ return `version check skipped (TRELLIS_SKIP_PYTHON_CHECK=1)`;
55
+ }
56
+ const versionOutput = detectPythonVersion(command);
57
+ if (versionOutput === "sandbox-restricted") {
58
+ console.warn(chalk.yellow(`⚠ Python version check skipped — sandboxed environment blocked ` +
59
+ `child_process spawn (EPERM/EACCES). Assuming "${command}" is on ` +
60
+ `PATH. If init fails later, re-run on the host or set ` +
61
+ `TRELLIS_SKIP_PYTHON_CHECK=1.`));
62
+ return `version unknown (sandbox-restricted)`;
63
+ }
64
+ if (!versionOutput) {
65
+ throw new Error(`Python command "${command}" not found. Trellis init requires Python ≥ 3.9.`);
66
+ }
67
+ if (!isSupportedPythonVersion(versionOutput)) {
68
+ throw new Error(`${versionOutput} detected via "${command}", but Trellis init requires Python ≥ 3.9.`);
69
+ }
70
+ return versionOutput;
71
+ }
72
+ /**
73
+ * Candidate Python command list per platform.
74
+ *
75
+ * Windows: `python` is the usual python.org installer choice, but Microsoft
76
+ * Store ships `python3`, and the `py` launcher is `py -3`. We try all three
77
+ * before giving up — fixes #236 where users with only `python3` (not
78
+ * `python`) had `trellis init` fail outright.
79
+ *
80
+ * Non-Windows: `python3` is canonical; `python` is a fallback for systems
81
+ * where Python 3 is the only Python and is named `python` (some Arch
82
+ * configs, conda envs).
83
+ */
84
+ const PYTHON_CANDIDATES = {
85
+ win32: ["python", "python3", "py -3"],
86
+ other: ["python3", "python"],
87
+ };
88
+ /**
89
+ * Detect a working Python ≥ 3.9 command on the host platform.
90
+ *
91
+ * Honors `TRELLIS_PYTHON_CMD` (explicit override, no probe) and
92
+ * `TRELLIS_SKIP_PYTHON_CHECK=1` (skip probe, trust platform default).
93
+ *
94
+ * Otherwise tries each candidate in `PYTHON_CANDIDATES` in order and returns
95
+ * the first whose `--version` matches `Python ≥ 3.9`. Caches the result via
96
+ * `setResolvedPythonCommand` so all downstream template / configurator
97
+ * writes pick up the resolved value.
98
+ *
99
+ * Throws a helpful, Windows-aware error if no candidate works.
100
+ */
101
+ export function resolveSupportedPython() {
102
+ // Explicit override — user knows their environment.
103
+ const override = process.env.TRELLIS_PYTHON_CMD?.trim();
104
+ if (override) {
105
+ setResolvedPythonCommand(override);
106
+ return { command: override, version: "set via TRELLIS_PYTHON_CMD" };
107
+ }
108
+ // Skip probe entirely.
109
+ if (process.env.TRELLIS_SKIP_PYTHON_CHECK === "1") {
110
+ const fallback = getPythonCommandForPlatform();
111
+ setResolvedPythonCommand(fallback);
112
+ return {
113
+ command: fallback,
114
+ version: "version check skipped (TRELLIS_SKIP_PYTHON_CHECK=1)",
115
+ };
116
+ }
117
+ const candidates = process.platform === "win32"
118
+ ? PYTHON_CANDIDATES.win32
119
+ : PYTHON_CANDIDATES.other;
120
+ const probeFailures = [];
121
+ for (const candidate of candidates) {
122
+ const probe = detectPythonVersion(candidate);
123
+ if (probe === "sandbox-restricted") {
124
+ console.warn(chalk.yellow(`⚠ Python version check skipped — sandboxed environment blocked ` +
125
+ `child_process spawn (EPERM/EACCES). Assuming "${candidate}" is ` +
126
+ `on PATH. If init fails later, re-run on the host or set ` +
127
+ `TRELLIS_SKIP_PYTHON_CHECK=1.`));
128
+ setResolvedPythonCommand(candidate);
129
+ return {
130
+ command: candidate,
131
+ version: "version unknown (sandbox-restricted)",
132
+ };
133
+ }
134
+ if (!probe) {
135
+ probeFailures.push(`${candidate}: not found`);
136
+ continue;
137
+ }
138
+ if (!isSupportedPythonVersion(probe)) {
139
+ probeFailures.push(`${candidate}: ${probe} (< 3.9)`);
140
+ continue;
141
+ }
142
+ setResolvedPythonCommand(candidate);
143
+ return { command: candidate, version: probe };
144
+ }
145
+ const isWindows = process.platform === "win32";
146
+ const installHint = isWindows
147
+ ? `Install Python ≥ 3.9 from https://www.python.org/downloads/windows/ — make sure ` +
148
+ `"Add Python to PATH" is checked in the installer. Or, if Python is ` +
149
+ `installed under a different name, set TRELLIS_PYTHON_CMD=<your-cmd> ` +
150
+ `before re-running init (e.g. \`set TRELLIS_PYTHON_CMD=py -3\`).`
151
+ : `Install Python ≥ 3.9 from https://www.python.org/downloads/ or via your ` +
152
+ `package manager. Or set TRELLIS_PYTHON_CMD=<your-cmd> before re-running.`;
153
+ throw new Error(`No supported Python command found. Tried: ${candidates.join(", ")}.\n` +
154
+ `Probe results:\n ${probeFailures.join("\n ")}\n\n` +
155
+ `Trellis init requires Python ≥ 3.9. ${installHint}\n` +
156
+ `Last-resort escape hatch: set TRELLIS_SKIP_PYTHON_CHECK=1 to skip the probe entirely.`);
157
+ }
158
+ function getOsDisplayName(platform = process.platform) {
159
+ switch (platform) {
160
+ case "win32":
161
+ return "Windows";
162
+ case "darwin":
163
+ return "macOS";
164
+ case "linux":
165
+ return "Linux";
166
+ default:
167
+ return platform;
168
+ }
169
+ }
170
+ function logPythonAdaptationNotice(command) {
171
+ const osName = getOsDisplayName();
172
+ console.log(chalk.blue(`📌 ${osName} detected: Trellis rendered Python commands as "${command}" in generated hooks, settings, and help text`));
173
+ }
174
+ // =============================================================================
175
+ // Bootstrap Task Creation
176
+ // =============================================================================
177
+ const BOOTSTRAP_TASK_NAME = "00-bootstrap-guidelines";
178
+ /**
179
+ * Slugify a developer name for safe use in task directory names.
180
+ *
181
+ * Unlike `sanitizePkgName` (which only strips npm @scope/ prefixes), this
182
+ * handles arbitrary developer input: spaces, Unicode letters, punctuation,
183
+ * path separators. Returns "user" fallback when input slugifies to empty.
184
+ *
185
+ * Exported for unit testing; not part of the public API.
186
+ */
187
+ export function slugifyDeveloperName(name) {
188
+ const slug = name
189
+ .toLowerCase()
190
+ .normalize("NFKD")
191
+ .replace(/[^\p{Letter}\p{Number}]+/gu, "-")
192
+ .replace(/^-+|-+$/g, "");
193
+ return slug || "user";
194
+ }
195
+ /**
196
+ * Write a task skeleton (task.json + prd.md).
197
+ *
198
+ * Idempotent: if the task dir already exists, returns true without touching
199
+ * anything. Shared by both creator bootstrap and joiner onboarding flows.
200
+ */
201
+ function writeTaskSkeleton(cwd, taskName, taskJson, prdContent) {
202
+ const taskDir = path.join(cwd, PATHS.TASKS, taskName);
203
+ if (fs.existsSync(taskDir))
204
+ return true; // idempotent
205
+ try {
206
+ fs.mkdirSync(taskDir, { recursive: true });
207
+ fs.writeFileSync(path.join(taskDir, FILE_NAMES.TASK_JSON), JSON.stringify(taskJson, null, 2), "utf-8");
208
+ fs.writeFileSync(path.join(taskDir, FILE_NAMES.PRD), prdContent, "utf-8");
209
+ return true;
210
+ }
211
+ catch {
212
+ return false;
213
+ }
214
+ }
215
+ /**
216
+ * Compute the bootstrap checklist items (previously stored as structured
217
+ * `subtasks: [{name, status}]` in task.json). Per task 04-21-task-schema-unify
218
+ * (D1), these live as markdown `- [ ]` items in prd.md instead, so task.json
219
+ * stays canonical with `subtasks: string[]` (child task dir names, same as
220
+ * task_store.py).
221
+ */
222
+ function getBootstrapChecklistItems(projectType, packages) {
223
+ if (packages && packages.length > 0) {
224
+ const items = packages.map((pkg) => `补充 ${pkg.name} 的开发规范`);
225
+ items.push("补充代码示例");
226
+ return items;
227
+ }
228
+ if (projectType === "frontend") {
229
+ return ["补充前端开发规范", "补充代码示例"];
230
+ }
231
+ if (projectType === "backend") {
232
+ return ["补充后端开发规范", "补充代码示例"];
233
+ }
234
+ return ["补充后端开发规范", "补充前端开发规范", "补充代码示例"];
235
+ }
236
+ function getBootstrapRelatedFiles(projectType, packages) {
237
+ if (packages && packages.length > 0) {
238
+ return packages.map((pkg) => `.trellis/spec/${sanitizePkgName(pkg.name)}/`);
239
+ }
240
+ if (projectType === "frontend") {
241
+ return [".trellis/spec/frontend/"];
242
+ }
243
+ if (projectType === "backend") {
244
+ return [".trellis/spec/backend/"];
245
+ }
246
+ return [".trellis/spec/backend/", ".trellis/spec/frontend/"];
247
+ }
248
+ function getBootstrapPrdContent(projectType, pythonCmd, packages) {
249
+ const checklistItems = getBootstrapChecklistItems(projectType, packages);
250
+ const checklistMarkdown = checklistItems
251
+ .map((item) => `- [ ] ${item}`)
252
+ .join("\n");
253
+ const header = `# 启动任务:补全项目开发规范
254
+
255
+ **你(AI)正在执行这个任务,开发者不会直接阅读这个文件。**
256
+
257
+ 开发者刚刚第一次在这个项目里运行了 \`trellis init\`。
258
+ 现在 \`.trellis/\` 已创建,里面包含待补全的 spec 脚手架,这个启动任务也已经
259
+ 出现在 \`.trellis/tasks/\` 下。当他们准备处理它时,应当在提供 Trellis 会话身份
260
+ 的会话里启动这个任务。
261
+
262
+ **你的目标**:帮助他们把团队真实的编码规范补充进 \`.trellis/spec/\`。
263
+ 未来这个项目里的每次 AI 会话——包括 \`trellis-implement\` 和 \`trellis-check\`
264
+ 子代理——都会自动加载每个任务 jsonl 清单里列出的 spec 文件。spec 为空,
265
+ 子代理就会写出泛化代码;spec 真实完整,子代理才会贴近团队现有风格。
266
+
267
+ 不要一上来倾倒说明。先用一句简短欢迎语开场,确认仓库里是否已有约定文档
268
+ (如 CLAUDE.md、.cursorrules 等),再以对话方式推进。
269
+
270
+ ---
271
+
272
+ ## 当前状态(完成后更新下面复选框)
273
+
274
+ ${checklistMarkdown}
275
+
276
+ ---
277
+
278
+ ## 需要补充的 Spec 文件
279
+ `;
280
+ const backendSection = `
281
+
282
+ ### 后端规范
283
+
284
+ | 文件 | 需要记录的内容 |
285
+ |------|----------------|
286
+ | \`.trellis/spec/backend/directory-structure.md\` | 各类文件的放置位置(routes、services、utils 等) |
287
+ | \`.trellis/spec/backend/database-guidelines.md\` | ORM、迁移、查询模式、命名约定 |
288
+ | \`.trellis/spec/backend/error-handling.md\` | 错误如何捕获、记录和返回 |
289
+ | \`.trellis/spec/backend/logging-guidelines.md\` | 日志级别、格式、记录范围 |
290
+ | \`.trellis/spec/backend/quality-guidelines.md\` | 代码评审标准、测试要求 |
291
+ `;
292
+ const frontendSection = `
293
+
294
+ ### 前端规范
295
+
296
+ | 文件 | 需要记录的内容 |
297
+ |------|----------------|
298
+ | \`.trellis/spec/frontend/directory-structure.md\` | 组件、页面、hook 的组织方式 |
299
+ | \`.trellis/spec/frontend/component-guidelines.md\` | 组件模式、props 约定 |
300
+ | \`.trellis/spec/frontend/hook-guidelines.md\` | 自定义 hook 命名与模式 |
301
+ | \`.trellis/spec/frontend/state-management.md\` | 本地状态、全局状态、服务端状态的使用方式 |
302
+ | \`.trellis/spec/frontend/type-safety.md\` | TypeScript 约定、类型组织方式 |
303
+ | \`.trellis/spec/frontend/quality-guidelines.md\` | 代码规范、禁用模式 |
304
+ `;
305
+ const footer = `
306
+
307
+ ### 思考指南(已预填)
308
+
309
+ \`.trellis/spec/guides/\` 中已预置通用思考指南。
310
+ 只有当其中内容明显不适合当前项目时,才需要调整。
311
+
312
+ ---
313
+
314
+ ## 如何补充 Spec
315
+
316
+ ### 第一步:优先导入已有约定文档(推荐)
317
+
318
+ 先在仓库中搜索已有的约定文档。如果存在,先读这些文件,再把相关规则整理到对应
319
+ 的 \`.trellis/spec/\` 文件里——这通常比从零开始快得多。
320
+
321
+ | 文件 / 目录 | 工具 |
322
+ |------|------|
323
+ | \`CLAUDE.md\` / \`CLAUDE.local.md\` | Claude Code |
324
+ | \`AGENTS.md\` | Codex / Claude Code / 兼容 agent 的工具 |
325
+ | \`.cursorrules\` | Cursor |
326
+ | \`.cursor/rules/*.mdc\` | Cursor(规则目录) |
327
+ | \`.windsurfrules\` | Windsurf |
328
+ | \`.clinerules\` | Cline |
329
+ | \`.roomodes\` | Roo Code |
330
+ | \`.github/copilot-instructions.md\` | GitHub Copilot |
331
+ | \`.vscode/settings.json\` → \`github.copilot.chat.codeGeneration.instructions\` | VS Code Copilot |
332
+ | \`CONVENTIONS.md\` / \`.aider.conf.yml\` | aider |
333
+ | \`CONTRIBUTING.md\` | 通用项目约定 |
334
+ | \`.editorconfig\` | 编辑器格式规则 |
335
+
336
+ ### 第二步:分析代码库中未被文档覆盖的部分
337
+
338
+ 从真实代码里归纳模式。写每个 spec 文件前:
339
+ - 先找到 2-3 个真实示例。
340
+ - 记录真实文件路径,不要写假想路径。
341
+ - 记下团队明确避免的反模式。
342
+
343
+ ### 第三步:记录现实,而不是理想状态
344
+
345
+ **关键**:写代码库现在**实际上怎样做**,而不是“应该怎样做”。
346
+ 子代理会按 spec 来实现;如果 spec 写的是不存在的理想模式,后续生成的代码就会和
347
+ 仓库现状脱节。
348
+
349
+ 如果团队存在已知技术债,请记录当前现状——如何改进是后续话题,不属于这次启动任务。
350
+
351
+ ---
352
+
353
+ ## 运行时机制速览(当开发者问“为什么需要 spec”时再解释)
354
+
355
+ - 每个 AI 编码任务都会派生两个子代理:\`trellis-implement\`(负责写代码)和
356
+ \`trellis-check\`(负责验证质量)。
357
+ - 每个任务都有 \`implement.jsonl\` / \`check.jsonl\` 清单,用于列出需要加载的 spec 文件。
358
+ - 平台 hook 会自动把这些 spec 文件以及任务的 \`prd.md\` 注入到每个子代理的 prompt 中,
359
+ 这样它们能按团队约定编码或评审,而不需要人工反复粘贴。
360
+ - 唯一事实来源是 \`.trellis/spec/\`。这也是为什么现在把它补好,会长期持续收益。
361
+
362
+ ---
363
+
364
+ ## 完成方式
365
+
366
+ 当开发者确认上面的清单都已结合真实示例补完后,引导他们执行:
367
+
368
+ \`\`\`bash
369
+ ${pythonCmd} ./.trellis/scripts/task.py finish
370
+ ${pythonCmd} ./.trellis/scripts/task.py archive 00-bootstrap-guidelines
371
+ \`\`\`
372
+
373
+ 归档后,后续每位新加入这个项目的开发者拿到的将不再是这个启动任务,而是
374
+ \`00-join-<slug>\` 入项引导任务。
375
+
376
+ ---
377
+
378
+ ## 建议开场白
379
+
380
+ “欢迎使用 Trellis!刚才的初始化已经让我可以帮助你补齐项目 spec。这是一次性设置,
381
+ 做好之后,后续每次 AI 会话都会按团队规范工作,而不是产出泛化代码。开始前你手头有
382
+ 现成的约定文档(如 CLAUDE.md、.cursorrules、CONTRIBUTING.md)可以先让我读取吗?
383
+ 如果没有,我就从代码库里开始归纳。”
384
+ `;
385
+ let content = header;
386
+ if (packages && packages.length > 0) {
387
+ // Monorepo: generate per-package sections
388
+ for (const pkg of packages) {
389
+ const pkgType = pkg.type === "unknown" ? "fullstack" : pkg.type;
390
+ const specName = sanitizePkgName(pkg.name);
391
+ content += `\n### Package: ${pkg.name} (\`spec/${specName}/\`)\n`;
392
+ if (pkgType !== "frontend") {
393
+ content += `\n- Backend guidelines: \`.trellis/spec/${specName}/backend/\`\n`;
394
+ }
395
+ if (pkgType !== "backend") {
396
+ content += `\n- Frontend guidelines: \`.trellis/spec/${specName}/frontend/\`\n`;
397
+ }
398
+ }
399
+ }
400
+ else if (projectType === "frontend") {
401
+ content += frontendSection;
402
+ }
403
+ else if (projectType === "backend") {
404
+ content += backendSection;
405
+ }
406
+ else {
407
+ // fullstack
408
+ content += backendSection;
409
+ content += frontendSection;
410
+ }
411
+ content += footer;
412
+ return content;
413
+ }
414
+ function getBootstrapTaskJson(developer, projectType, packages) {
415
+ const today = new Date().toISOString().split("T")[0];
416
+ const relatedFiles = getBootstrapRelatedFiles(projectType, packages);
417
+ // Canonical 24-field shape via emptyTaskJson factory.
418
+ // Checklist items (previously stored as structured `subtasks`) are now
419
+ // rendered as `- [ ]` items in prd.md; task.json.subtasks is always
420
+ // string[] (child task dir names) per the canonical schema.
421
+ return emptyTaskJson({
422
+ id: BOOTSTRAP_TASK_NAME,
423
+ name: BOOTSTRAP_TASK_NAME,
424
+ title: "Bootstrap Guidelines",
425
+ description: "Fill in project development guidelines for AI agents",
426
+ status: "in_progress",
427
+ dev_type: "docs",
428
+ priority: "P1",
429
+ creator: developer,
430
+ assignee: developer,
431
+ createdAt: today,
432
+ relatedFiles,
433
+ notes: `First-time setup task created by trellis init (${projectType} project)`,
434
+ });
435
+ }
436
+ /**
437
+ * Create bootstrap task for first-time setup
438
+ */
439
+ function createBootstrapTask(cwd, developer, pythonCmd, projectType, packages) {
440
+ const taskJson = getBootstrapTaskJson(developer, projectType, packages);
441
+ const prdContent = getBootstrapPrdContent(projectType, pythonCmd, packages);
442
+ return writeTaskSkeleton(cwd, BOOTSTRAP_TASK_NAME, taskJson, prdContent);
443
+ }
444
+ // =============================================================================
445
+ // Joiner Onboarding Task Creation
446
+ // =============================================================================
447
+ /**
448
+ * task.json factory for joiner onboarding. Mirrors the bootstrap factory but
449
+ * uses dev_type "docs", higher priority "P1", and the developer-specific task
450
+ * name (so multiple joiners in the same checkout don't collide).
451
+ */
452
+ function getJoinerTaskJson(developer, taskName) {
453
+ const today = new Date().toISOString().split("T")[0];
454
+ return emptyTaskJson({
455
+ id: taskName,
456
+ name: taskName,
457
+ title: `Joining: Onboard to this Trellis project (${developer})`,
458
+ description: "Onboard a new developer to an existing Trellis project: learn the workflow, conventions, and find assigned work",
459
+ status: "in_progress",
460
+ dev_type: "docs",
461
+ priority: "P1",
462
+ creator: developer,
463
+ assignee: developer,
464
+ createdAt: today,
465
+ notes: "Generated by trellis init for a new developer joining an existing Trellis project",
466
+ });
467
+ }
468
+ /**
469
+ * PRD content for joiner onboarding. Kept concise (~80 lines) — deeper
470
+ * guidance lives in skills and docs.
471
+ */
472
+ function getJoinerPrdContent(developer, pythonCmd) {
473
+ const slug = slugifyDeveloperName(developer);
474
+ return `# 入项引导任务
475
+
476
+ **你(AI)正在执行这个任务,开发者不会直接阅读这个文件。**
477
+
478
+ \`${developer}\` 刚在一个全新 clone 上运行了 \`trellis init\`,看到了 “Developer
479
+ initialized” 提示,接下来会开始在聊天里向你提问。这个入项任务已经位于
480
+ \`.trellis/tasks/\` 下;当他们准备处理它时,应当在提供 Trellis 会话身份的会话中启动。
481
+
482
+ 你的职责是帮助他们理解 Trellis。不要一次性把所有内容倾倒给他们——先用一句简短问候开场,
483
+ 问他们想从哪里开始,再随着对话逐步补充后续内容。
484
+
485
+ ---
486
+
487
+ ## 需要覆盖的主题(顺序可按他们的问题调整)
488
+
489
+ ### 1. Trellis 是什么 + 工作流是什么
490
+
491
+ Trellis 是叠加在 Claude Code / Cursor 等工具之上的一层工作流,用来让 AI agent
492
+ 在每次会话里都遵循项目自己的规范,而不是反复产出泛化代码。
493
+
494
+ - **三阶段流程**:Plan(brainstorm → \`prd.md\`)→ Execute(编码 + 检查)→
495
+ Finish(沉淀 + 收尾)。完整参考见 \`.trellis/workflow.md\`。
496
+ - **任务生命周期**:planning → in_progress → done → archive,目录位于
497
+ \`.trellis/tasks/\`。
498
+ - **核心 slash commands**:
499
+ - \`/trellis:continue\` —— 恢复当前会话的活动任务
500
+ - \`/trellis:finish-work\` —— 收尾已完成任务
501
+ - \`/trellis:start\` —— 从零启动一次会话(这里通常不需要;SessionStart hook 会自动完成启动)
502
+
503
+ ### 2. 运行时机制(当他们问“它怎么知道该做什么”时解释)
504
+
505
+ - **SessionStart hook** 会运行 \`get_context.py\`,并在每次新会话开始时把身份、git 状态、
506
+ 当前会话活动任务、活动任务列表、工作流阶段注入到 AI 对话中。
507
+ - **\`<workflow-state>\` 标签** 会在每次用户消息时自动注入,携带当前任务和阶段提示。
508
+ - **\`/trellis:continue\`** 会加载 Phase Index,读取 \`prd.md\` 与近期活动,然后把当前任务
509
+ 路由到正确技能(规划走 \`trellis-brainstorm\`,编码走 \`trellis-implement\`,验证走 \`trellis-check\`)。
510
+ - **\`trellis-implement\` 子代理** 会在需要写代码时启动。平台 hook 会读取
511
+ \`{TASK_DIR}/implement.jsonl\`,并把这些 spec 文件与 \`prd.md\` 一起注入子代理 prompt,
512
+ 让它按项目规范编码。
513
+ - **\`trellis-check\` 子代理** 也采用同样机制读取 \`check.jsonl\` —— 按规范审查改动、自动修复问题、运行 lint/typecheck。
514
+
515
+ 文件布局(当他们问“东西都放哪”时提到):
516
+ - \`.trellis/.runtime/sessions/<session>.json\` —— 会话级活动任务状态,已 gitignore
517
+ - \`.trellis/tasks/<task>/{implement,check}.jsonl\` —— 每个任务的上下文清单
518
+ - \`.trellis/spec/\` —— 项目级规范(唯一事实来源)
519
+ - \`.trellis/workspace/${developer}/journal-*.md\` —— 该开发者的会话日志,约 2000 行轮转一次
520
+
521
+ ### 3. 这个项目自己的约定
522
+
523
+ - 帮他们概括 \`.trellis/spec/\`:这个团队实际要求哪些编码规范。
524
+ - 指给他们看 \`.trellis/tasks/archive/\` 里最近 5 条记录,作为实际工作节奏示例。
525
+ **如果 archive 为空**(项目刚开始),就跳过,不要编造例子。
526
+ - 这次入项引导不负责讲业务代码本身——那部分应由 README 和团队成员来承担。
527
+
528
+ ### 4. 他们当前被分配的工作
529
+
530
+ - 先检查 \`.trellis/workspace/${developer}/\` 是否已存在——如果存在,说明这是他们从另一台机器带来的日志,值得提醒。
531
+ - 运行 \`${pythonCmd} ./.trellis/scripts/task.py list --assignee ${developer}\`,展示分配给他们的任务。(如果名字含空格,记得加引号。)
532
+ - 提醒他们:“My Tasks” 区块会在每次新会话的 SessionStart 上下文里自动出现。
533
+
534
+ ---
535
+
536
+ ## 可选:带他们完整走一遍一个小任务
537
+
538
+ 如果他们想先练手再接正式任务,可以主动提议挑一个很小的 P3 任务或 typo 修复,
539
+ 一起完整走一遍:\`/trellis:continue\` → 你通过子代理实现 → \`/trellis:finish-work\`。
540
+
541
+ ---
542
+
543
+ ## 完成方式
544
+
545
+ 当他们觉得已经完成入项理解(或者以上四个主题已经有过充分来回交流)后,引导他们执行:
546
+
547
+ \`\`\`bash
548
+ ${pythonCmd} ./.trellis/scripts/task.py finish
549
+ ${pythonCmd} ./.trellis/scripts/task.py archive 00-join-${slug}
550
+ \`\`\`
551
+
552
+ ---
553
+
554
+ ## 建议开场白
555
+
556
+ “欢迎!这次 \`trellis init\` 已经让我可以带你完成这个项目的入项引导。我可以先讲工作流,
557
+ 也可以先讲底层运行机制、团队 spec,或者直接看你现在关心的问题——你想从哪一块开始?”
558
+ `;
559
+ }
560
+ /**
561
+ * Create joiner onboarding task for a new developer on an existing Trellis
562
+ * project. Task name is slugified to be filesystem-safe for arbitrary
563
+ * developer names (spaces, Unicode, punctuation).
564
+ */
565
+ function createJoinerOnboardingTask(cwd, developer, pythonCmd) {
566
+ const slug = slugifyDeveloperName(developer);
567
+ const taskName = `00-join-${slug}`;
568
+ const taskJson = getJoinerTaskJson(developer, taskName);
569
+ const prdContent = getJoinerPrdContent(developer, pythonCmd);
570
+ return writeTaskSkeleton(cwd, taskName, taskJson, prdContent);
571
+ }
572
+ /**
573
+ * Handle re-init when .trellis/ already exists.
574
+ * Returns true if handled (caller should return), false if user chose full re-init.
575
+ */
576
+ async function handleReinit(cwd, options, developerName, pythonCmd) {
577
+ const TOOLS = getInitToolChoices();
578
+ const configuredPlatforms = getConfiguredPlatforms(cwd);
579
+ const configuredNames = [...configuredPlatforms]
580
+ .map((id) => AI_TOOLS[id].name)
581
+ .join(", ");
582
+ // Determine explicit platform flags
583
+ const explicitTools = TOOLS.filter((t) => options[t.key]).map((t) => t.key);
584
+ let doAddPlatforms = explicitTools.length > 0;
585
+ let doAddDeveloper = !!options.user;
586
+ let platformsToAdd = explicitTools;
587
+ // No explicit flags → show menu
588
+ if (!doAddPlatforms && !doAddDeveloper) {
589
+ if (options.yes) {
590
+ console.log(chalk.gray(`Already initialized with: ${configuredNames}`));
591
+ console.log(chalk.gray("Use platform flags (e.g., --codex) or -u <name> to add platforms/developer."));
592
+ return true;
593
+ }
594
+ console.log(chalk.gray(`\n Already initialized with: ${configuredNames}\n`));
595
+ const { action } = await inquirer.prompt([
596
+ {
597
+ type: "list",
598
+ name: "action",
599
+ message: "Trellis is already initialized. What would you like to do?",
600
+ choices: [
601
+ { name: "Add AI platform(s)", value: "add-platform" },
602
+ {
603
+ name: "Set up developer identity on this device",
604
+ value: "add-developer",
605
+ },
606
+ { name: "Full re-initialize", value: "full" },
607
+ ],
608
+ },
609
+ ]);
610
+ if (action === "full") {
611
+ return false; // Fall through to full init
612
+ }
613
+ if (action === "add-platform")
614
+ doAddPlatforms = true;
615
+ if (action === "add-developer")
616
+ doAddDeveloper = true;
617
+ }
618
+ // --- Add platforms ---
619
+ if (doAddPlatforms) {
620
+ if (platformsToAdd.length === 0) {
621
+ // Interactive: show only unconfigured platforms
622
+ const unconfigured = TOOLS.filter((t) => {
623
+ const pid = resolveCliFlag(t.key);
624
+ return pid && !configuredPlatforms.has(pid);
625
+ });
626
+ if (unconfigured.length === 0) {
627
+ console.log(chalk.green("✓ All available platforms are already configured."));
628
+ }
629
+ else {
630
+ const answers = await inquirer.prompt([
631
+ {
632
+ type: "checkbox",
633
+ name: "tools",
634
+ message: "Select platforms to add:",
635
+ choices: unconfigured.map((t) => ({
636
+ name: t.name,
637
+ value: t.key,
638
+ })),
639
+ },
640
+ ]);
641
+ platformsToAdd = answers.tools;
642
+ }
643
+ }
644
+ const reinitWritten = startRecordingWrites(cwd);
645
+ try {
646
+ for (const tool of platformsToAdd) {
647
+ const platformId = resolveCliFlag(tool);
648
+ if (platformId) {
649
+ if (configuredPlatforms.has(platformId)) {
650
+ console.log(chalk.gray(` ○ ${AI_TOOLS[platformId].name} already configured, skipping`));
651
+ }
652
+ else {
653
+ console.log(chalk.blue(`📝 Configuring ${AI_TOOLS[platformId].name}...`));
654
+ await configurePlatform(platformId, cwd);
655
+ }
656
+ }
657
+ }
658
+ }
659
+ finally {
660
+ stopRecordingWrites();
661
+ }
662
+ // Update template hashes. Merge mode: preserve previously-tracked
663
+ // platforms' hashes, layer in the newly-added platform's writes.
664
+ const hashedCount = initializeHashes(cwd, {
665
+ trackedPaths: reinitWritten,
666
+ merge: true,
667
+ });
668
+ if (hashedCount > 0) {
669
+ console.log(chalk.gray(`📋 Tracking ${hashedCount} template files for updates`));
670
+ }
671
+ }
672
+ // --- Add developer ---
673
+ if (doAddDeveloper) {
674
+ let devName = developerName;
675
+ if (!devName) {
676
+ devName = await askInput("Your name: ");
677
+ while (!devName) {
678
+ console.log(chalk.yellow("Name is required"));
679
+ devName = await askInput("Your name: ");
680
+ }
681
+ }
682
+ // Capture pre-init state: if .developer did not exist before we ran
683
+ // init_developer.py, this checkout had no identity → treat as a new
684
+ // joiner onboarding onto an existing Trellis project.
685
+ const hadDeveloperFileBefore = fs.existsSync(path.join(cwd, DIR_NAMES.WORKFLOW, FILE_NAMES.DEVELOPER));
686
+ try {
687
+ const scriptPath = path.join(cwd, PATHS.SCRIPTS, "init_developer.py");
688
+ execSync(`${pythonCmd} "${scriptPath}" "${devName}"`, {
689
+ cwd,
690
+ stdio: "pipe",
691
+ });
692
+ console.log(chalk.green(`✓ Developer "${devName}" initialized`));
693
+ }
694
+ catch {
695
+ console.log(chalk.yellow("⚠ Could not initialize developer. Run manually:"));
696
+ console.log(chalk.gray(` ${pythonCmd} .trellis/scripts/init_developer.py ${devName}`));
697
+ }
698
+ // Create joiner onboarding task for fresh checkouts (no prior .developer).
699
+ // Runs outside the init_developer try/catch so failures surface as warnings.
700
+ if (!hadDeveloperFileBefore) {
701
+ try {
702
+ if (!createJoinerOnboardingTask(cwd, devName, pythonCmd)) {
703
+ console.warn(chalk.yellow("⚠ Failed to create joiner onboarding task"));
704
+ }
705
+ }
706
+ catch (err) {
707
+ console.warn(chalk.yellow(`⚠ Joiner onboarding setup failed: ${err instanceof Error ? err.message : String(err)}`));
708
+ }
709
+ }
710
+ }
711
+ return true;
712
+ }
713
+ const _cliFlagCheck = true;
714
+ /**
715
+ * Write monorepo package configuration to config.yaml (non-destructive patch).
716
+ * Appends packages: and default_package: without disturbing existing config.
717
+ */
718
+ function writeMonorepoConfig(cwd, packages) {
719
+ const configPath = path.join(cwd, DIR_NAMES.WORKFLOW, "config.yaml");
720
+ let content = "";
721
+ try {
722
+ content = fs.readFileSync(configPath, "utf-8");
723
+ }
724
+ catch {
725
+ // Config not created yet; will be created by createWorkflowStructure
726
+ return;
727
+ }
728
+ // Don't overwrite if packages: already exists (re-init case)
729
+ if (/^packages\s*:/m.test(content)) {
730
+ return;
731
+ }
732
+ const lines = ["\n# Auto-detected monorepo packages", "packages:"];
733
+ for (const pkg of packages) {
734
+ lines.push(` ${sanitizePkgName(pkg.name)}:`);
735
+ lines.push(` path: ${pkg.path}`);
736
+ if (pkg.isSubmodule) {
737
+ lines.push(" type: submodule");
738
+ }
739
+ else if (pkg.isGitRepo) {
740
+ lines.push(" git: true");
741
+ }
742
+ }
743
+ // Use first non-submodule package as default, fallback to first package
744
+ const defaultPkg = packages.find((p) => !p.isSubmodule)?.name ?? packages[0]?.name;
745
+ if (defaultPkg) {
746
+ lines.push(`default_package: ${defaultPkg}`);
747
+ }
748
+ fs.writeFileSync(configPath, content.trimEnd() + "\n" + lines.join("\n") + "\n", "utf-8");
749
+ }
750
+ export async function init(options) {
751
+ // Refuse to run in $HOME — running here would scoop platform runtime data
752
+ // (Claude/Codex/OpenCode session histories etc.) into the trellis hash
753
+ // manifest, and a subsequent `trellis uninstall` would wipe it.
754
+ if (isCwdHomedir() && !homedirBypassEnabled()) {
755
+ console.error(chalk.red(homedirGuardMessage("init")));
756
+ process.exit(1);
757
+ }
758
+ const cwd = process.cwd();
759
+ const isFirstInit = !fs.existsSync(path.join(cwd, DIR_NAMES.WORKFLOW));
760
+ // Captured here (before createWorkflowStructure + init_developer run) so
761
+ // the three-branch dispatch at the bottom can tell "fresh clone joiner"
762
+ // (.trellis/ exists, .developer missing) apart from "creator first init".
763
+ const hadDeveloperFileAtStart = fs.existsSync(path.join(cwd, DIR_NAMES.WORKFLOW, FILE_NAMES.DEVELOPER));
764
+ // Generate ASCII art banner dynamically using FIGlet "Rebel" font
765
+ const banner = figlet.textSync("Trellis", { font: "Rebel" });
766
+ console.log(chalk.cyan(`\n${banner.trimEnd()}`));
767
+ console.log(chalk.gray("\n All-in-one AI framework & toolkit for Claude Code & Cursor\n"));
768
+ // Set up proxy before any network calls
769
+ const proxyUrl = setupProxy();
770
+ if (proxyUrl) {
771
+ console.log(chalk.gray(` Using proxy: ${maskProxyUrl(proxyUrl)}\n`));
772
+ }
773
+ // Set write mode based on options
774
+ let writeMode = "ask";
775
+ if (options.force) {
776
+ writeMode = "force";
777
+ console.log(chalk.gray("Mode: Force overwrite existing files\n"));
778
+ }
779
+ else if (options.skipExisting) {
780
+ writeMode = "skip";
781
+ console.log(chalk.gray("Mode: Skip existing files\n"));
782
+ }
783
+ else if (options.yes) {
784
+ // -y implies non-interactive: never prompt on conflicts. Default to skip
785
+ // (preserve user files) — explicit --force is required to overwrite.
786
+ writeMode = "skip";
787
+ console.log(chalk.gray("Mode: Non-interactive (skip existing files)\n"));
788
+ }
789
+ setWriteMode(writeMode);
790
+ // Detect developer name from git config or options
791
+ let developerName = options.user;
792
+ if (!developerName) {
793
+ // Only detect from git if current directory is a git repo
794
+ const isGitRepo = fs.existsSync(path.join(cwd, ".git"));
795
+ if (isGitRepo) {
796
+ try {
797
+ developerName = execSync("git config user.name", {
798
+ cwd,
799
+ encoding: "utf-8",
800
+ }).trim();
801
+ }
802
+ catch {
803
+ // Git not available or no user.name configured
804
+ }
805
+ }
806
+ }
807
+ if (developerName) {
808
+ console.log(chalk.blue("👤 Developer:"), chalk.gray(developerName));
809
+ }
810
+ const { command: pythonCmd } = resolveSupportedPython();
811
+ // ==========================================================================
812
+ // Re-init fast path: skip full flow when .trellis/ already exists
813
+ // ==========================================================================
814
+ // Aborted-init recovery (issue #204): if .trellis/ exists but tasks/ is
815
+ // empty, the previous init never reached bootstrap creation. Fall through
816
+ // to the full flow so the main-dispatch tasksEmpty fallback fires —
817
+ // handleReinit's joiner branch would otherwise mis-route the recovery.
818
+ const tasksDirEarly = path.join(cwd, PATHS.TASKS);
819
+ const tasksEmptyEarly = !fs.existsSync(tasksDirEarly) || fs.readdirSync(tasksDirEarly).length === 0;
820
+ if (!isFirstInit &&
821
+ !options.force &&
822
+ !options.skipExisting &&
823
+ !tasksEmptyEarly) {
824
+ const reinitDone = await handleReinit(cwd, options, developerName, pythonCmd);
825
+ if (reinitDone)
826
+ return;
827
+ // reinitDone === false means user chose "full re-initialize" → fall through
828
+ }
829
+ if (!developerName && !options.yes) {
830
+ // Ask for developer name if not detected and not in yes mode
831
+ console.log(chalk.gray("\nTrellis supports team collaboration - each developer has their own\n" +
832
+ `workspace directory (${PATHS.WORKSPACE}/{name}/) to track AI sessions.\n` +
833
+ "Tip: Usually this is your git username (git config user.name).\n"));
834
+ developerName = await askInput("Your name: ");
835
+ while (!developerName) {
836
+ console.log(chalk.yellow("Name is required"));
837
+ developerName = await askInput("Your name: ");
838
+ }
839
+ console.log(chalk.blue("👤 Developer:"), chalk.gray(developerName));
840
+ }
841
+ // Detect project type (silent - no output)
842
+ const detectedType = detectProjectType(cwd);
843
+ // Parse custom registry source early (needed by both monorepo + single-repo flows)
844
+ let registry;
845
+ if (options.registry) {
846
+ try {
847
+ registry = parseRegistrySource(options.registry);
848
+ }
849
+ catch (error) {
850
+ console.log(chalk.red(error instanceof Error ? error.message : "Invalid registry source"));
851
+ return;
852
+ }
853
+ }
854
+ // Determine template strategy from flags (needed before monorepo template downloads)
855
+ let templateStrategy = "skip";
856
+ if (options.overwrite) {
857
+ templateStrategy = "overwrite";
858
+ }
859
+ else if (options.append) {
860
+ templateStrategy = "append";
861
+ }
862
+ // ==========================================================================
863
+ // Monorepo Detection
864
+ // ==========================================================================
865
+ let monorepoPackages;
866
+ let remoteSpecPackages;
867
+ if (options.monorepo !== false) {
868
+ // options.monorepo: true = --monorepo, false = --no-monorepo, undefined = auto
869
+ const detected = detectMonorepo(cwd);
870
+ if (options.monorepo === true && !detected) {
871
+ console.log(chalk.red("Error: --monorepo specified but no multi-package layout detected."));
872
+ console.log("");
873
+ console.log(chalk.gray("Checked:"));
874
+ console.log(chalk.gray(" ✗ pnpm-workspace.yaml"));
875
+ console.log(chalk.gray(" ✗ package.json workspaces"));
876
+ console.log(chalk.gray(" ✗ Cargo.toml [workspace]"));
877
+ console.log(chalk.gray(" ✗ go.work"));
878
+ console.log(chalk.gray(" ✗ pyproject.toml [tool.uv.workspace]"));
879
+ console.log(chalk.gray(" ✗ .gitmodules"));
880
+ console.log(chalk.gray(" ✗ sibling .git directories (need ≥ 2)"));
881
+ console.log("");
882
+ console.log("To configure manually, add to .trellis/config.yaml:");
883
+ console.log("");
884
+ console.log(chalk.cyan(" packages:"));
885
+ console.log(chalk.cyan(" frontend:"));
886
+ console.log(chalk.cyan(" path: ./frontend"));
887
+ console.log(chalk.cyan(" git: true # if it has its own .git"));
888
+ console.log(chalk.cyan(" backend:"));
889
+ console.log(chalk.cyan(" path: ./backend"));
890
+ console.log(chalk.cyan(" git: true"));
891
+ return;
892
+ }
893
+ if (detected && detected.length > 0) {
894
+ let enableMonorepo = false;
895
+ if (options.monorepo === true || options.yes) {
896
+ enableMonorepo = true;
897
+ }
898
+ else {
899
+ // Show detected packages and ask
900
+ console.log(chalk.blue("\n🔍 Detected monorepo packages:"));
901
+ for (const pkg of detected) {
902
+ const tag = pkg.isSubmodule
903
+ ? chalk.gray(" (submodule)")
904
+ : pkg.isGitRepo
905
+ ? chalk.gray(" (git repo)")
906
+ : "";
907
+ console.log(chalk.gray(` - ${pkg.name}`) +
908
+ chalk.gray(` (${pkg.path})`) +
909
+ chalk.gray(` [${pkg.type}]`) +
910
+ tag);
911
+ }
912
+ console.log("");
913
+ const { useMonorepo } = await inquirer.prompt([
914
+ {
915
+ type: "confirm",
916
+ name: "useMonorepo",
917
+ message: "Enable monorepo mode?",
918
+ default: true,
919
+ },
920
+ ]);
921
+ enableMonorepo = useMonorepo;
922
+ }
923
+ if (enableMonorepo) {
924
+ monorepoPackages = detected;
925
+ remoteSpecPackages = new Set();
926
+ // Per-package template selection (unless -y mode: all use blank spec)
927
+ if (!options.yes && !options.template) {
928
+ for (const pkg of detected) {
929
+ const { specSource } = await inquirer.prompt([
930
+ {
931
+ type: "list",
932
+ name: "specSource",
933
+ message: `Spec source for ${pkg.name} (${pkg.path}):`,
934
+ choices: [
935
+ { name: "From scratch (Trellis default)", value: "blank" },
936
+ { name: "Download remote template", value: "remote" },
937
+ ],
938
+ default: "blank",
939
+ },
940
+ ]);
941
+ if (specSource === "remote") {
942
+ // Use existing template download flow, targeting spec/<name>/
943
+ const destDir = path.join(cwd, PATHS.SPEC, sanitizePkgName(pkg.name));
944
+ console.log(chalk.blue(`📦 Select template for ${pkg.name}...`));
945
+ // Fetch templates if not already done
946
+ const templates = await fetchTemplateIndex();
947
+ const specTemplates = templates
948
+ .filter((t) => t.type === "spec")
949
+ .map((t) => ({
950
+ name: `${t.id} (${t.name})`,
951
+ value: t.id,
952
+ }));
953
+ if (specTemplates.length > 0) {
954
+ const { templateId } = await inquirer.prompt([
955
+ {
956
+ type: "list",
957
+ name: "templateId",
958
+ message: `Select template for ${pkg.name}:`,
959
+ choices: specTemplates,
960
+ },
961
+ ]);
962
+ const result = await downloadTemplateById(cwd, templateId, templateStrategy, templates.find((t) => t.id === templateId), undefined, destDir);
963
+ if (result.success) {
964
+ console.log(chalk.green(` ${result.message}`));
965
+ remoteSpecPackages.add(sanitizePkgName(pkg.name));
966
+ }
967
+ else {
968
+ console.log(chalk.yellow(` ${result.message}`));
969
+ console.log(chalk.gray(" Falling back to blank spec..."));
970
+ }
971
+ }
972
+ else {
973
+ console.log(chalk.gray(" No templates available. Using blank spec."));
974
+ }
975
+ }
976
+ }
977
+ }
978
+ else if (options.template) {
979
+ // --template as default for all packages
980
+ for (const pkg of detected) {
981
+ const destDir = path.join(cwd, PATHS.SPEC, sanitizePkgName(pkg.name));
982
+ const result = await downloadTemplateById(cwd, options.template, templateStrategy, undefined, registry, destDir);
983
+ if (result.success && !result.skipped) {
984
+ remoteSpecPackages.add(sanitizePkgName(pkg.name));
985
+ }
986
+ }
987
+ }
988
+ }
989
+ }
990
+ }
991
+ // Tool definitions derived from platform registry
992
+ const TOOLS = getInitToolChoices();
993
+ // Build tools from explicit flags
994
+ const explicitTools = TOOLS.filter((t) => options[t.key]).map((t) => t.key);
995
+ let tools;
996
+ if (explicitTools.length > 0) {
997
+ // Explicit flags take precedence (works with or without -y)
998
+ tools = explicitTools;
999
+ }
1000
+ else if (options.yes) {
1001
+ // No explicit tools + -y: default to Cursor and Claude
1002
+ tools = TOOLS.filter((t) => t.defaultChecked).map((t) => t.key);
1003
+ }
1004
+ else {
1005
+ // Interactive mode
1006
+ const answers = await inquirer.prompt([
1007
+ {
1008
+ type: "checkbox",
1009
+ name: "tools",
1010
+ message: "Select AI tools to configure:",
1011
+ choices: TOOLS.map((t) => ({
1012
+ name: t.name,
1013
+ value: t.key,
1014
+ checked: t.defaultChecked,
1015
+ })),
1016
+ },
1017
+ ]);
1018
+ tools = answers.tools;
1019
+ }
1020
+ // Treat unknown project type as fullstack
1021
+ const projectType = detectedType === "unknown" ? "fullstack" : detectedType;
1022
+ if (tools.length === 0) {
1023
+ console.log(chalk.yellow("No tools selected. At least one tool is required."));
1024
+ return;
1025
+ }
1026
+ // ==========================================================================
1027
+ // Template Selection (single-repo only; monorepo handles templates above)
1028
+ // ==========================================================================
1029
+ let selectedTemplate = null;
1030
+ // Pre-fetched templates list (used to pass selected SpecTemplate to downloadTemplateById)
1031
+ let fetchedTemplates = [];
1032
+ let registryBackend;
1033
+ // Determine the index URL based on registry
1034
+ const indexUrl = registry
1035
+ ? `${registry.rawBaseUrl}/index.json`
1036
+ : TEMPLATE_INDEX_URL;
1037
+ if (monorepoPackages) {
1038
+ // Monorepo: template selection already handled above
1039
+ }
1040
+ else if (options.template) {
1041
+ // Template specified via --template flag
1042
+ selectedTemplate = options.template;
1043
+ }
1044
+ else if (!options.yes) {
1045
+ // Interactive mode: show template selection
1046
+ const timeoutSec = TIMEOUTS.INDEX_FETCH_MS / 1000;
1047
+ const sourceLabel = registry ? registry.gigetSource : TEMPLATE_INDEX_URL;
1048
+ console.log(chalk.gray(` Fetching available templates from ${sourceLabel}`));
1049
+ let elapsed = 0;
1050
+ const ticker = setInterval(() => {
1051
+ elapsed++;
1052
+ process.stdout.write(`\r${chalk.gray(` Loading... ${elapsed}s/${timeoutSec}s`)}`);
1053
+ }, 1000);
1054
+ process.stdout.write(chalk.gray(` Loading... 0s/${timeoutSec}s`));
1055
+ let templates;
1056
+ let registryProbeNotFound = false;
1057
+ let registryProbeError;
1058
+ if (registry) {
1059
+ const probeResult = await probeRegistryIndex(indexUrl, registry);
1060
+ templates = probeResult.templates;
1061
+ registryProbeNotFound = probeResult.isNotFound;
1062
+ registryProbeError = probeResult.error;
1063
+ registryBackend = probeResult.backend;
1064
+ }
1065
+ else {
1066
+ templates = await fetchTemplateIndex(indexUrl);
1067
+ }
1068
+ clearInterval(ticker);
1069
+ // Clear the loading line
1070
+ process.stdout.write("\r\x1b[2K");
1071
+ fetchedTemplates = templates;
1072
+ if (templates.length === 0 && registry && registryProbeNotFound) {
1073
+ // Custom registry: confirmed no index.json — will try direct download later
1074
+ console.log(chalk.gray(" No index.json found at registry. Will download as direct spec template."));
1075
+ }
1076
+ else if (templates.length === 0 && registry) {
1077
+ // Custom registry: transient error (not a 404) — abort, don't misclassify
1078
+ console.log(chalk.red(` ${registryProbeError?.message ?? "Could not reach registry. Check your connection and try again."}`));
1079
+ return;
1080
+ }
1081
+ else if (templates.length === 0) {
1082
+ console.log(chalk.gray(" Could not fetch templates (offline or server unavailable)."));
1083
+ console.log(chalk.gray(" Using blank templates.\n"));
1084
+ }
1085
+ if (templates.length > 0) {
1086
+ // Build template choices
1087
+ const specTemplates = templates
1088
+ .filter((t) => t.type === "spec")
1089
+ .map((t) => ({
1090
+ name: `${t.id} (${t.name})`,
1091
+ value: t.id,
1092
+ }));
1093
+ const templateChoices = registry
1094
+ ? specTemplates
1095
+ : [
1096
+ {
1097
+ name: "from scratch (default)",
1098
+ value: "blank",
1099
+ },
1100
+ ...specTemplates,
1101
+ {
1102
+ name: "custom (enter a registry source)",
1103
+ value: "__custom__",
1104
+ },
1105
+ ];
1106
+ // Loop to allow returning from custom source input back to the picker
1107
+ let templatePicked = false;
1108
+ while (templateChoices.length > 0 && !templatePicked) {
1109
+ const templateAnswer = await inquirer.prompt([
1110
+ {
1111
+ type: "list",
1112
+ name: "template",
1113
+ message: "Select a spec template:",
1114
+ choices: templateChoices,
1115
+ default: registry ? undefined : "blank",
1116
+ },
1117
+ ]);
1118
+ if (templateAnswer.template === "__custom__") {
1119
+ // Prompt for custom registry source (empty → back to picker)
1120
+ const customSource = await askInput("Enter registry source (e.g., gh:myorg/myrepo/specs), or press Enter to go back: ");
1121
+ if (!customSource) {
1122
+ continue; // Back to picker
1123
+ }
1124
+ try {
1125
+ registry = parseRegistrySource(customSource);
1126
+ fetchedTemplates = []; // Reset so direct-download guard works correctly
1127
+ // Probe index.json to detect marketplace vs direct download
1128
+ const customIndexUrl = `${registry.rawBaseUrl}/index.json`;
1129
+ console.log(chalk.gray(` Checking for templates at ${registry.gigetSource}...`));
1130
+ const customProbe = await probeRegistryIndex(customIndexUrl, registry);
1131
+ const customTemplates = customProbe.templates;
1132
+ registryBackend = customProbe.backend;
1133
+ if (customTemplates.length > 0) {
1134
+ // Marketplace mode: show picker with custom templates
1135
+ fetchedTemplates = customTemplates;
1136
+ const customChoices = customTemplates
1137
+ .filter((t) => t.type === "spec")
1138
+ .map((t) => ({
1139
+ name: `${t.id} (${t.name})`,
1140
+ value: t.id,
1141
+ }));
1142
+ if (customChoices.length > 0) {
1143
+ const customAnswer = await inquirer.prompt([
1144
+ {
1145
+ type: "list",
1146
+ name: "template",
1147
+ message: "Select a spec template:",
1148
+ choices: customChoices,
1149
+ },
1150
+ ]);
1151
+ selectedTemplate = customAnswer.template;
1152
+ // Check if spec directory already exists and ask what to do
1153
+ const specDir = path.join(cwd, PATHS.SPEC);
1154
+ if (fs.existsSync(specDir) &&
1155
+ !options.overwrite &&
1156
+ !options.append) {
1157
+ const actionAnswer = await inquirer.prompt([
1158
+ {
1159
+ type: "list",
1160
+ name: "action",
1161
+ message: `Directory ${PATHS.SPEC} already exists. What do you want to do?`,
1162
+ choices: [
1163
+ { name: "Skip (keep existing)", value: "skip" },
1164
+ {
1165
+ name: "Overwrite (replace all)",
1166
+ value: "overwrite",
1167
+ },
1168
+ {
1169
+ name: "Append (add missing files only)",
1170
+ value: "append",
1171
+ },
1172
+ ],
1173
+ default: "skip",
1174
+ },
1175
+ ]);
1176
+ templateStrategy = actionAnswer.action;
1177
+ }
1178
+ }
1179
+ templatePicked = true;
1180
+ }
1181
+ else if (customProbe.isNotFound) {
1182
+ // No index.json → direct download mode
1183
+ templatePicked = true;
1184
+ }
1185
+ else {
1186
+ // Transient error (not 404) — loop back, don't misclassify
1187
+ console.log(chalk.yellow(` ${customProbe.error?.message ?? "Could not reach registry. Try again or enter a different source."}`));
1188
+ registry = undefined; // Reset so we don't fall through to direct download
1189
+ }
1190
+ }
1191
+ catch (error) {
1192
+ console.log(chalk.red(error instanceof Error
1193
+ ? error.message
1194
+ : "Invalid registry source"));
1195
+ // Loop back to picker
1196
+ }
1197
+ }
1198
+ else {
1199
+ templatePicked = true;
1200
+ if (templateAnswer.template !== "blank") {
1201
+ selectedTemplate = templateAnswer.template;
1202
+ // Check if spec directory already exists and ask what to do
1203
+ const specDir = path.join(cwd, PATHS.SPEC);
1204
+ if (fs.existsSync(specDir) &&
1205
+ !options.overwrite &&
1206
+ !options.append) {
1207
+ const actionAnswer = await inquirer.prompt([
1208
+ {
1209
+ type: "list",
1210
+ name: "action",
1211
+ message: `Directory ${PATHS.SPEC} already exists. What do you want to do?`,
1212
+ choices: [
1213
+ { name: "Skip (keep existing)", value: "skip" },
1214
+ { name: "Overwrite (replace all)", value: "overwrite" },
1215
+ {
1216
+ name: "Append (add missing files only)",
1217
+ value: "append",
1218
+ },
1219
+ ],
1220
+ default: "skip",
1221
+ },
1222
+ ]);
1223
+ templateStrategy = actionAnswer.action;
1224
+ }
1225
+ }
1226
+ }
1227
+ }
1228
+ }
1229
+ }
1230
+ // -y mode with --registry (no --template): probe index.json to detect mode
1231
+ // Skip when monorepo mode already handled templates above
1232
+ if (options.yes && registry && !selectedTemplate && !monorepoPackages) {
1233
+ const probeResult = await probeRegistryIndex(`${registry.rawBaseUrl}/index.json`, registry);
1234
+ registryBackend = probeResult.backend;
1235
+ if (probeResult.templates.length > 0) {
1236
+ // Marketplace mode requires interactive selection — can't auto-select
1237
+ console.log(chalk.red("Error: Registry is a marketplace with multiple templates. " +
1238
+ "Use --template <id> to specify which one, or remove -y for interactive selection."));
1239
+ return;
1240
+ }
1241
+ if (!probeResult.isNotFound) {
1242
+ // Transient error (not 404) — abort, don't misclassify as direct-download
1243
+ console.log(chalk.red(`Error: ${probeResult.error?.message ?? "Could not reach registry. Check your connection and try again."}`));
1244
+ return;
1245
+ }
1246
+ // isNotFound=true → no index.json, proceed with direct download (fetchedTemplates stays empty)
1247
+ }
1248
+ // ==========================================================================
1249
+ // Download Remote Template (if selected or direct registry download)
1250
+ // ==========================================================================
1251
+ let useRemoteTemplate = false;
1252
+ if (selectedTemplate) {
1253
+ // Marketplace mode: download specific template by ID
1254
+ console.log(chalk.blue(`📦 Downloading template "${selectedTemplate}"...`));
1255
+ console.log(chalk.gray(" This may take a moment on slow connections."));
1256
+ // Find pre-fetched SpecTemplate to avoid double-fetch
1257
+ const prefetched = fetchedTemplates.find((t) => t.id === selectedTemplate);
1258
+ const result = await downloadTemplateById(cwd, selectedTemplate, templateStrategy, prefetched, registry, undefined, registryBackend);
1259
+ if (result.success) {
1260
+ if (result.skipped) {
1261
+ console.log(chalk.gray(` ${result.message}`));
1262
+ }
1263
+ else {
1264
+ console.log(chalk.green(` ${result.message}`));
1265
+ useRemoteTemplate = true;
1266
+ }
1267
+ }
1268
+ else {
1269
+ console.log(chalk.yellow(` ${result.message}`));
1270
+ console.log(chalk.gray(" Falling back to blank templates..."));
1271
+ const retryCmd = registry
1272
+ ? `trellis init --registry ${registry.gigetSource} --template ${selectedTemplate}`
1273
+ : `trellis init --template ${selectedTemplate}`;
1274
+ console.log(chalk.gray(` You can retry later: ${retryCmd}`));
1275
+ }
1276
+ }
1277
+ else if (registry && fetchedTemplates.length === 0) {
1278
+ // Direct download mode: registry has no index.json, download directory directly
1279
+ console.log(chalk.blue(`📦 Downloading spec from ${registry.gigetSource}...`));
1280
+ console.log(chalk.gray(" This may take a moment on slow connections."));
1281
+ // Ask about existing spec dir in interactive mode
1282
+ if (!options.yes && !options.overwrite && !options.append) {
1283
+ const specDir = path.join(cwd, PATHS.SPEC);
1284
+ if (fs.existsSync(specDir)) {
1285
+ const actionAnswer = await inquirer.prompt([
1286
+ {
1287
+ type: "list",
1288
+ name: "action",
1289
+ message: `Directory ${PATHS.SPEC} already exists. What do you want to do?`,
1290
+ choices: [
1291
+ { name: "Skip (keep existing)", value: "skip" },
1292
+ { name: "Overwrite (replace all)", value: "overwrite" },
1293
+ { name: "Append (add missing files only)", value: "append" },
1294
+ ],
1295
+ default: "skip",
1296
+ },
1297
+ ]);
1298
+ templateStrategy = actionAnswer.action;
1299
+ }
1300
+ }
1301
+ const result = await downloadRegistryDirect(cwd, registry, templateStrategy, undefined, registryBackend);
1302
+ if (result.success) {
1303
+ if (result.skipped) {
1304
+ console.log(chalk.gray(` ${result.message}`));
1305
+ }
1306
+ else {
1307
+ console.log(chalk.green(` ${result.message}`));
1308
+ useRemoteTemplate = true;
1309
+ }
1310
+ }
1311
+ else {
1312
+ console.log(chalk.yellow(` ${result.message}`));
1313
+ console.log(chalk.gray(" Falling back to blank templates..."));
1314
+ console.log(chalk.gray(` You can retry later: trellis init --registry ${registry.gigetSource}`));
1315
+ }
1316
+ }
1317
+ // ==========================================================================
1318
+ // Resolve workflow template (default: native bundled)
1319
+ // ==========================================================================
1320
+ const workflowIdInput = options.workflow?.trim();
1321
+ const workflowId = workflowIdInput && workflowIdInput.length > 0
1322
+ ? workflowIdInput
1323
+ : NATIVE_WORKFLOW_ID;
1324
+ let workflowMdOverride;
1325
+ if (workflowId !== NATIVE_WORKFLOW_ID || options.workflowSource) {
1326
+ const resolved = await resolveWorkflowTemplate(workflowId, {
1327
+ source: options.workflowSource,
1328
+ });
1329
+ if (resolved.id !== NATIVE_WORKFLOW_ID) {
1330
+ workflowMdOverride = resolved.content;
1331
+ console.log(chalk.blue(`🧭 Using workflow template: ${chalk.cyan(resolved.id)}`));
1332
+ }
1333
+ }
1334
+ // ==========================================================================
1335
+ // Create Workflow Structure
1336
+ // ==========================================================================
1337
+ // Record every successful write from here through createRootFiles. The
1338
+ // captured set is the source of truth for `.template-hashes.json`'s
1339
+ // platform/root entries — replacing the previous "walk every managed dir"
1340
+ // approach that swept user-owned runtime files into the manifest
1341
+ // (.codex/sessions/, .claude/projects/, pre-existing AGENTS.md).
1342
+ const writtenPaths = startRecordingWrites(cwd);
1343
+ try {
1344
+ // Create workflow structure with project type
1345
+ console.log(chalk.blue("📁 Creating workflow structure..."));
1346
+ await createWorkflowStructure(cwd, {
1347
+ projectType,
1348
+ skipSpecTemplates: useRemoteTemplate,
1349
+ packages: monorepoPackages,
1350
+ remoteSpecPackages,
1351
+ workflowMdOverride,
1352
+ });
1353
+ // Write monorepo packages to config.yaml (non-destructive patch)
1354
+ if (monorepoPackages) {
1355
+ writeMonorepoConfig(cwd, monorepoPackages);
1356
+ console.log(chalk.blue("📦 Monorepo packages written to config.yaml"));
1357
+ }
1358
+ // Write version file for update tracking
1359
+ const versionPath = path.join(cwd, DIR_NAMES.WORKFLOW, ".version");
1360
+ fs.writeFileSync(versionPath, VERSION);
1361
+ // Configure selected tools by copying entire directories (dogfooding)
1362
+ for (const tool of tools) {
1363
+ const platformId = resolveCliFlag(tool);
1364
+ if (platformId) {
1365
+ console.log(chalk.blue(`📝 Configuring ${AI_TOOLS[platformId].name}...`));
1366
+ await configurePlatform(platformId, cwd);
1367
+ }
1368
+ }
1369
+ const pythonPlatforms = getPlatformsWithPythonHooks();
1370
+ const hasSelectedPythonPlatform = pythonPlatforms.some((id) => tools.includes(AI_TOOLS[id].cliFlag));
1371
+ if (hasSelectedPythonPlatform) {
1372
+ logPythonAdaptationNotice(pythonCmd);
1373
+ }
1374
+ // Create root files (skip if exists)
1375
+ await createRootFiles(cwd);
1376
+ }
1377
+ finally {
1378
+ stopRecordingWrites();
1379
+ }
1380
+ // Initialize template hashes for modification tracking
1381
+ const hashedCount = initializeHashes(cwd, { trackedPaths: writtenPaths });
1382
+ if (hashedCount > 0) {
1383
+ console.log(chalk.gray(`📋 Tracking ${hashedCount} template files for updates`));
1384
+ }
1385
+ // Non-native workflow is user-managed local content. Drop the
1386
+ // `.trellis/workflow.md` hash entry so `trellis update` classifies it as
1387
+ // modified and does not silently restore native bytes. See design.md
1388
+ // "Durable-state contract".
1389
+ if (workflowMdOverride !== undefined && workflowId !== NATIVE_WORKFLOW_ID) {
1390
+ removeHash(cwd, PATHS.WORKFLOW_GUIDE_FILE);
1391
+ }
1392
+ // Initialize developer identity (silent - no output)
1393
+ if (developerName) {
1394
+ try {
1395
+ const scriptPath = path.join(cwd, PATHS.SCRIPTS, "init_developer.py");
1396
+ execSync(`${pythonCmd} "${scriptPath}" "${developerName}"`, {
1397
+ cwd,
1398
+ stdio: "pipe", // Silent
1399
+ });
1400
+ }
1401
+ catch {
1402
+ // Silent failure - user can run init_developer.py manually
1403
+ }
1404
+ // Three-branch dispatch using flags captured at init() start (before
1405
+ // createWorkflowStructure/init_developer ran, so they reflect the disk
1406
+ // state of the user's checkout, not the state this init just produced):
1407
+ // isFirstInit=true → creator bootstrap (new project)
1408
+ // isFirstInit=false + no .developer file → joiner onboarding (fresh clone)
1409
+ // isFirstInit=false + .developer exists → same-dev re-init, no task
1410
+ //
1411
+ // Tasks-empty fallback (issue #204): if .trellis/ exists but tasks dir is
1412
+ // empty, the previous init aborted before creating the bootstrap task. Run
1413
+ // bootstrap creation regardless of isFirstInit. writeTaskSkeleton is
1414
+ // idempotent so repeated triggers are safe.
1415
+ //
1416
+ // Runs OUTSIDE the init_developer try/catch (which uses stdio: "pipe")
1417
+ // so joiner failures surface as warnings instead of being silently
1418
+ // swallowed.
1419
+ const tasksDir = path.join(cwd, PATHS.TASKS);
1420
+ const tasksEmpty = !fs.existsSync(tasksDir) || fs.readdirSync(tasksDir).length === 0;
1421
+ if (isFirstInit || tasksEmpty) {
1422
+ createBootstrapTask(cwd, developerName, pythonCmd, projectType, monorepoPackages);
1423
+ }
1424
+ else if (!hadDeveloperFileAtStart) {
1425
+ try {
1426
+ if (!createJoinerOnboardingTask(cwd, developerName, pythonCmd)) {
1427
+ console.warn(chalk.yellow("⚠ Failed to create joiner onboarding task"));
1428
+ }
1429
+ }
1430
+ catch (err) {
1431
+ console.warn(chalk.yellow(`⚠ Joiner onboarding setup failed: ${err instanceof Error ? err.message : String(err)}`));
1432
+ }
1433
+ }
1434
+ }
1435
+ }
1436
+ /**
1437
+ * Simple readline-based input (no flickering like inquirer)
1438
+ */
1439
+ function askInput(prompt) {
1440
+ const rl = readline.createInterface({
1441
+ input: process.stdin,
1442
+ output: process.stdout,
1443
+ });
1444
+ return new Promise((resolve) => {
1445
+ rl.question(prompt, (answer) => {
1446
+ rl.close();
1447
+ resolve(answer.trim());
1448
+ });
1449
+ });
1450
+ }
1451
+ async function createRootFiles(cwd) {
1452
+ const agentsPath = path.join(cwd, FILE_NAMES.AGENTS);
1453
+ // Write AGENTS.md from template
1454
+ const agentsWritten = await writeFile(agentsPath, agentsMdContent);
1455
+ if (agentsWritten) {
1456
+ console.log(chalk.blue("📄 Created AGENTS.md"));
1457
+ }
1458
+ }
1459
+ //# sourceMappingURL=init.js.map