trellis-herbivore 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (629) hide show
  1. package/bin/trellis.js +3 -0
  2. package/dist/cli/index.d.ts +3 -0
  3. package/dist/cli/index.d.ts.map +1 -0
  4. package/dist/cli/index.js +174 -0
  5. package/dist/cli/index.js.map +1 -0
  6. package/dist/commands/channel/adapters/claude.d.ts +38 -0
  7. package/dist/commands/channel/adapters/claude.d.ts.map +1 -0
  8. package/dist/commands/channel/adapters/claude.js +209 -0
  9. package/dist/commands/channel/adapters/claude.js.map +1 -0
  10. package/dist/commands/channel/adapters/codex.d.ts +77 -0
  11. package/dist/commands/channel/adapters/codex.d.ts.map +1 -0
  12. package/dist/commands/channel/adapters/codex.js +495 -0
  13. package/dist/commands/channel/adapters/codex.js.map +1 -0
  14. package/dist/commands/channel/adapters/index.d.ts +79 -0
  15. package/dist/commands/channel/adapters/index.d.ts.map +1 -0
  16. package/dist/commands/channel/adapters/index.js +109 -0
  17. package/dist/commands/channel/adapters/index.js.map +1 -0
  18. package/dist/commands/channel/adapters/types.d.ts +33 -0
  19. package/dist/commands/channel/adapters/types.d.ts.map +1 -0
  20. package/dist/commands/channel/adapters/types.js +2 -0
  21. package/dist/commands/channel/adapters/types.js.map +1 -0
  22. package/dist/commands/channel/agent-loader.d.ts +32 -0
  23. package/dist/commands/channel/agent-loader.d.ts.map +1 -0
  24. package/dist/commands/channel/agent-loader.js +154 -0
  25. package/dist/commands/channel/agent-loader.js.map +1 -0
  26. package/dist/commands/channel/context-loader.d.ts +26 -0
  27. package/dist/commands/channel/context-loader.d.ts.map +1 -0
  28. package/dist/commands/channel/context-loader.js +290 -0
  29. package/dist/commands/channel/context-loader.js.map +1 -0
  30. package/dist/commands/channel/context.d.ts +16 -0
  31. package/dist/commands/channel/context.d.ts.map +1 -0
  32. package/dist/commands/channel/context.js +83 -0
  33. package/dist/commands/channel/context.js.map +1 -0
  34. package/dist/commands/channel/create.d.ts +27 -0
  35. package/dist/commands/channel/create.d.ts.map +1 -0
  36. package/dist/commands/channel/create.js +39 -0
  37. package/dist/commands/channel/create.js.map +1 -0
  38. package/dist/commands/channel/dev-parse-trace.d.ts +14 -0
  39. package/dist/commands/channel/dev-parse-trace.d.ts.map +1 -0
  40. package/dist/commands/channel/dev-parse-trace.js +70 -0
  41. package/dist/commands/channel/dev-parse-trace.js.map +1 -0
  42. package/dist/commands/channel/index.d.ts +3 -0
  43. package/dist/commands/channel/index.d.ts.map +1 -0
  44. package/dist/commands/channel/index.js +496 -0
  45. package/dist/commands/channel/index.js.map +1 -0
  46. package/dist/commands/channel/kill.d.ts +7 -0
  47. package/dist/commands/channel/kill.d.ts.map +1 -0
  48. package/dist/commands/channel/kill.js +121 -0
  49. package/dist/commands/channel/kill.js.map +1 -0
  50. package/dist/commands/channel/list.d.ts +17 -0
  51. package/dist/commands/channel/list.d.ts.map +1 -0
  52. package/dist/commands/channel/list.js +233 -0
  53. package/dist/commands/channel/list.js.map +1 -0
  54. package/dist/commands/channel/messages.d.ts +16 -0
  55. package/dist/commands/channel/messages.d.ts.map +1 -0
  56. package/dist/commands/channel/messages.js +237 -0
  57. package/dist/commands/channel/messages.js.map +1 -0
  58. package/dist/commands/channel/rm.d.ts +27 -0
  59. package/dist/commands/channel/rm.d.ts.map +1 -0
  60. package/dist/commands/channel/rm.js +216 -0
  61. package/dist/commands/channel/rm.js.map +1 -0
  62. package/dist/commands/channel/run.d.ts +31 -0
  63. package/dist/commands/channel/run.d.ts.map +1 -0
  64. package/dist/commands/channel/run.js +137 -0
  65. package/dist/commands/channel/run.js.map +1 -0
  66. package/dist/commands/channel/send.d.ts +12 -0
  67. package/dist/commands/channel/send.d.ts.map +1 -0
  68. package/dist/commands/channel/send.js +24 -0
  69. package/dist/commands/channel/send.js.map +1 -0
  70. package/dist/commands/channel/spawn.d.ts +25 -0
  71. package/dist/commands/channel/spawn.d.ts.map +1 -0
  72. package/dist/commands/channel/spawn.js +192 -0
  73. package/dist/commands/channel/spawn.js.map +1 -0
  74. package/dist/commands/channel/store/events.d.ts +39 -0
  75. package/dist/commands/channel/store/events.d.ts.map +1 -0
  76. package/dist/commands/channel/store/events.js +87 -0
  77. package/dist/commands/channel/store/events.js.map +1 -0
  78. package/dist/commands/channel/store/filter.d.ts +3 -0
  79. package/dist/commands/channel/store/filter.d.ts.map +1 -0
  80. package/dist/commands/channel/store/filter.js +2 -0
  81. package/dist/commands/channel/store/filter.js.map +1 -0
  82. package/dist/commands/channel/store/lock.d.ts +23 -0
  83. package/dist/commands/channel/store/lock.d.ts.map +1 -0
  84. package/dist/commands/channel/store/lock.js +99 -0
  85. package/dist/commands/channel/store/lock.js.map +1 -0
  86. package/dist/commands/channel/store/paths.d.ts +63 -0
  87. package/dist/commands/channel/store/paths.d.ts.map +1 -0
  88. package/dist/commands/channel/store/paths.js +246 -0
  89. package/dist/commands/channel/store/paths.js.map +1 -0
  90. package/dist/commands/channel/store/schema.d.ts +27 -0
  91. package/dist/commands/channel/store/schema.d.ts.map +1 -0
  92. package/dist/commands/channel/store/schema.js +34 -0
  93. package/dist/commands/channel/store/schema.js.map +1 -0
  94. package/dist/commands/channel/store/thread-state.d.ts +5 -0
  95. package/dist/commands/channel/store/thread-state.d.ts.map +1 -0
  96. package/dist/commands/channel/store/thread-state.js +16 -0
  97. package/dist/commands/channel/store/thread-state.js.map +1 -0
  98. package/dist/commands/channel/store/watch.d.ts +19 -0
  99. package/dist/commands/channel/store/watch.d.ts.map +1 -0
  100. package/dist/commands/channel/store/watch.js +130 -0
  101. package/dist/commands/channel/store/watch.js.map +1 -0
  102. package/dist/commands/channel/supervisor/inbox.d.ts +25 -0
  103. package/dist/commands/channel/supervisor/inbox.d.ts.map +1 -0
  104. package/dist/commands/channel/supervisor/inbox.js +99 -0
  105. package/dist/commands/channel/supervisor/inbox.js.map +1 -0
  106. package/dist/commands/channel/supervisor/shutdown.d.ts +66 -0
  107. package/dist/commands/channel/supervisor/shutdown.d.ts.map +1 -0
  108. package/dist/commands/channel/supervisor/shutdown.js +143 -0
  109. package/dist/commands/channel/supervisor/shutdown.js.map +1 -0
  110. package/dist/commands/channel/supervisor/stdout.d.ts +49 -0
  111. package/dist/commands/channel/supervisor/stdout.d.ts.map +1 -0
  112. package/dist/commands/channel/supervisor/stdout.js +107 -0
  113. package/dist/commands/channel/supervisor/stdout.js.map +1 -0
  114. package/dist/commands/channel/supervisor.d.ts +47 -0
  115. package/dist/commands/channel/supervisor.d.ts.map +1 -0
  116. package/dist/commands/channel/supervisor.js +283 -0
  117. package/dist/commands/channel/supervisor.js.map +1 -0
  118. package/dist/commands/channel/text-body.d.ts +13 -0
  119. package/dist/commands/channel/text-body.d.ts.map +1 -0
  120. package/dist/commands/channel/text-body.js +47 -0
  121. package/dist/commands/channel/text-body.js.map +1 -0
  122. package/dist/commands/channel/threads.d.ts +39 -0
  123. package/dist/commands/channel/threads.d.ts.map +1 -0
  124. package/dist/commands/channel/threads.js +106 -0
  125. package/dist/commands/channel/threads.js.map +1 -0
  126. package/dist/commands/channel/title.d.ts +12 -0
  127. package/dist/commands/channel/title.d.ts.map +1 -0
  128. package/dist/commands/channel/title.js +24 -0
  129. package/dist/commands/channel/title.js.map +1 -0
  130. package/dist/commands/channel/wait.d.ts +18 -0
  131. package/dist/commands/channel/wait.d.ts.map +1 -0
  132. package/dist/commands/channel/wait.js +76 -0
  133. package/dist/commands/channel/wait.js.map +1 -0
  134. package/dist/commands/init.d.ts +57 -0
  135. package/dist/commands/init.d.ts.map +1 -0
  136. package/dist/commands/init.js +1466 -0
  137. package/dist/commands/init.js.map +1 -0
  138. package/dist/commands/mem.d.ts +234 -0
  139. package/dist/commands/mem.d.ts.map +1 -0
  140. package/dist/commands/mem.js +1869 -0
  141. package/dist/commands/mem.js.map +1 -0
  142. package/dist/commands/uninstall.d.ts +27 -0
  143. package/dist/commands/uninstall.d.ts.map +1 -0
  144. package/dist/commands/uninstall.js +339 -0
  145. package/dist/commands/uninstall.js.map +1 -0
  146. package/dist/commands/update.d.ts +72 -0
  147. package/dist/commands/update.d.ts.map +1 -0
  148. package/dist/commands/update.js +1926 -0
  149. package/dist/commands/update.js.map +1 -0
  150. package/dist/commands/upgrade.d.ts +28 -0
  151. package/dist/commands/upgrade.d.ts.map +1 -0
  152. package/dist/commands/upgrade.js +84 -0
  153. package/dist/commands/upgrade.js.map +1 -0
  154. package/dist/configurators/antigravity.d.ts +7 -0
  155. package/dist/configurators/antigravity.d.ts.map +1 -0
  156. package/dist/configurators/antigravity.js +19 -0
  157. package/dist/configurators/antigravity.js.map +1 -0
  158. package/dist/configurators/claude.d.ts +9 -0
  159. package/dist/configurators/claude.d.ts.map +1 -0
  160. package/dist/configurators/claude.js +72 -0
  161. package/dist/configurators/claude.js.map +1 -0
  162. package/dist/configurators/codebuddy.d.ts +10 -0
  163. package/dist/configurators/codebuddy.d.ts.map +1 -0
  164. package/dist/configurators/codebuddy.js +30 -0
  165. package/dist/configurators/codebuddy.js.map +1 -0
  166. package/dist/configurators/codex.d.ts +8 -0
  167. package/dist/configurators/codex.d.ts.map +1 -0
  168. package/dist/configurators/codex.js +87 -0
  169. package/dist/configurators/codex.js.map +1 -0
  170. package/dist/configurators/copilot.d.ts +10 -0
  171. package/dist/configurators/copilot.d.ts.map +1 -0
  172. package/dist/configurators/copilot.js +51 -0
  173. package/dist/configurators/copilot.js.map +1 -0
  174. package/dist/configurators/cursor.d.ts +10 -0
  175. package/dist/configurators/cursor.d.ts.map +1 -0
  176. package/dist/configurators/cursor.js +29 -0
  177. package/dist/configurators/cursor.js.map +1 -0
  178. package/dist/configurators/droid.d.ts +10 -0
  179. package/dist/configurators/droid.d.ts.map +1 -0
  180. package/dist/configurators/droid.js +30 -0
  181. package/dist/configurators/droid.js.map +1 -0
  182. package/dist/configurators/gemini.d.ts +16 -0
  183. package/dist/configurators/gemini.d.ts.map +1 -0
  184. package/dist/configurators/gemini.js +38 -0
  185. package/dist/configurators/gemini.js.map +1 -0
  186. package/dist/configurators/index.d.ts +65 -0
  187. package/dist/configurators/index.d.ts.map +1 -0
  188. package/dist/configurators/index.js +367 -0
  189. package/dist/configurators/index.js.map +1 -0
  190. package/dist/configurators/kilo.d.ts +7 -0
  191. package/dist/configurators/kilo.d.ts.map +1 -0
  192. package/dist/configurators/kilo.js +19 -0
  193. package/dist/configurators/kilo.js.map +1 -0
  194. package/dist/configurators/kiro.d.ts +8 -0
  195. package/dist/configurators/kiro.d.ts.map +1 -0
  196. package/dist/configurators/kiro.js +24 -0
  197. package/dist/configurators/kiro.js.map +1 -0
  198. package/dist/configurators/opencode.d.ts +14 -0
  199. package/dist/configurators/opencode.d.ts.map +1 -0
  200. package/dist/configurators/opencode.js +96 -0
  201. package/dist/configurators/opencode.js.map +1 -0
  202. package/dist/configurators/pi.d.ts +3 -0
  203. package/dist/configurators/pi.d.ts.map +1 -0
  204. package/dist/configurators/pi.js +45 -0
  205. package/dist/configurators/pi.js.map +1 -0
  206. package/dist/configurators/qoder.d.ts +11 -0
  207. package/dist/configurators/qoder.d.ts.map +1 -0
  208. package/dist/configurators/qoder.js +31 -0
  209. package/dist/configurators/qoder.js.map +1 -0
  210. package/dist/configurators/shared.d.ts +178 -0
  211. package/dist/configurators/shared.d.ts.map +1 -0
  212. package/dist/configurators/shared.js +538 -0
  213. package/dist/configurators/shared.js.map +1 -0
  214. package/dist/configurators/windsurf.d.ts +7 -0
  215. package/dist/configurators/windsurf.d.ts.map +1 -0
  216. package/dist/configurators/windsurf.js +19 -0
  217. package/dist/configurators/windsurf.js.map +1 -0
  218. package/dist/configurators/workflow.d.ts +29 -0
  219. package/dist/configurators/workflow.d.ts.map +1 -0
  220. package/dist/configurators/workflow.js +163 -0
  221. package/dist/configurators/workflow.js.map +1 -0
  222. package/dist/constants/paths.d.ts +70 -0
  223. package/dist/constants/paths.d.ts.map +1 -0
  224. package/dist/constants/paths.js +79 -0
  225. package/dist/constants/paths.js.map +1 -0
  226. package/dist/constants/version.d.ts +9 -0
  227. package/dist/constants/version.d.ts.map +1 -0
  228. package/dist/constants/version.js +15 -0
  229. package/dist/constants/version.js.map +1 -0
  230. package/dist/index.d.ts +9 -0
  231. package/dist/index.d.ts.map +1 -0
  232. package/dist/index.js +9 -0
  233. package/dist/index.js.map +1 -0
  234. package/dist/migrations/index.d.ts +62 -0
  235. package/dist/migrations/index.d.ts.map +1 -0
  236. package/dist/migrations/index.js +187 -0
  237. package/dist/migrations/index.js.map +1 -0
  238. package/dist/migrations/manifests/0.1.9.json +30 -0
  239. package/dist/migrations/manifests/0.2.0.json +49 -0
  240. package/dist/migrations/manifests/0.2.12.json +9 -0
  241. package/dist/migrations/manifests/0.2.13.json +9 -0
  242. package/dist/migrations/manifests/0.2.14.json +175 -0
  243. package/dist/migrations/manifests/0.2.15.json +33 -0
  244. package/dist/migrations/manifests/0.3.0-beta.0.json +297 -0
  245. package/dist/migrations/manifests/0.3.0-beta.1.json +9 -0
  246. package/dist/migrations/manifests/0.3.0-beta.10.json +9 -0
  247. package/dist/migrations/manifests/0.3.0-beta.11.json +9 -0
  248. package/dist/migrations/manifests/0.3.0-beta.12.json +9 -0
  249. package/dist/migrations/manifests/0.3.0-beta.13.json +9 -0
  250. package/dist/migrations/manifests/0.3.0-beta.14.json +9 -0
  251. package/dist/migrations/manifests/0.3.0-beta.15.json +9 -0
  252. package/dist/migrations/manifests/0.3.0-beta.16.json +9 -0
  253. package/dist/migrations/manifests/0.3.0-beta.2.json +9 -0
  254. package/dist/migrations/manifests/0.3.0-beta.3.json +9 -0
  255. package/dist/migrations/manifests/0.3.0-beta.4.json +9 -0
  256. package/dist/migrations/manifests/0.3.0-beta.5.json +9 -0
  257. package/dist/migrations/manifests/0.3.0-beta.6.json +9 -0
  258. package/dist/migrations/manifests/0.3.0-beta.7.json +11 -0
  259. package/dist/migrations/manifests/0.3.0-beta.8.json +9 -0
  260. package/dist/migrations/manifests/0.3.0-beta.9.json +9 -0
  261. package/dist/migrations/manifests/0.3.0-rc.0.json +9 -0
  262. package/dist/migrations/manifests/0.3.0-rc.1.json +9 -0
  263. package/dist/migrations/manifests/0.3.0-rc.2.json +9 -0
  264. package/dist/migrations/manifests/0.3.0-rc.3.json +9 -0
  265. package/dist/migrations/manifests/0.3.0-rc.4.json +9 -0
  266. package/dist/migrations/manifests/0.3.0-rc.5.json +9 -0
  267. package/dist/migrations/manifests/0.3.0-rc.6.json +9 -0
  268. package/dist/migrations/manifests/0.3.0.json +11 -0
  269. package/dist/migrations/manifests/0.3.1.json +9 -0
  270. package/dist/migrations/manifests/0.3.10.json +9 -0
  271. package/dist/migrations/manifests/0.3.2.json +9 -0
  272. package/dist/migrations/manifests/0.3.3.json +9 -0
  273. package/dist/migrations/manifests/0.3.4.json +21 -0
  274. package/dist/migrations/manifests/0.3.5.json +9 -0
  275. package/dist/migrations/manifests/0.3.6.json +9 -0
  276. package/dist/migrations/manifests/0.3.7.json +9 -0
  277. package/dist/migrations/manifests/0.3.8.json +9 -0
  278. package/dist/migrations/manifests/0.3.9.json +9 -0
  279. package/dist/migrations/manifests/0.4.0-beta.1.json +228 -0
  280. package/dist/migrations/manifests/0.4.0-beta.10.json +9 -0
  281. package/dist/migrations/manifests/0.4.0-beta.2.json +9 -0
  282. package/dist/migrations/manifests/0.4.0-beta.3.json +9 -0
  283. package/dist/migrations/manifests/0.4.0-beta.4.json +9 -0
  284. package/dist/migrations/manifests/0.4.0-beta.5.json +9 -0
  285. package/dist/migrations/manifests/0.4.0-beta.6.json +9 -0
  286. package/dist/migrations/manifests/0.4.0-beta.7.json +9 -0
  287. package/dist/migrations/manifests/0.4.0-beta.8.json +34 -0
  288. package/dist/migrations/manifests/0.4.0-beta.9.json +9 -0
  289. package/dist/migrations/manifests/0.4.0-rc.0.json +9 -0
  290. package/dist/migrations/manifests/0.4.0-rc.1.json +9 -0
  291. package/dist/migrations/manifests/0.4.0.json +9 -0
  292. package/dist/migrations/manifests/0.5.0-beta.0.json +1646 -0
  293. package/dist/migrations/manifests/0.5.0-beta.1.json +9 -0
  294. package/dist/migrations/manifests/0.5.0-beta.10.json +9 -0
  295. package/dist/migrations/manifests/0.5.0-beta.11.json +9 -0
  296. package/dist/migrations/manifests/0.5.0-beta.12.json +9 -0
  297. package/dist/migrations/manifests/0.5.0-beta.13.json +9 -0
  298. package/dist/migrations/manifests/0.5.0-beta.14.json +9 -0
  299. package/dist/migrations/manifests/0.5.0-beta.15.json +116 -0
  300. package/dist/migrations/manifests/0.5.0-beta.16.json +9 -0
  301. package/dist/migrations/manifests/0.5.0-beta.17.json +9 -0
  302. package/dist/migrations/manifests/0.5.0-beta.18.json +9 -0
  303. package/dist/migrations/manifests/0.5.0-beta.19.json +9 -0
  304. package/dist/migrations/manifests/0.5.0-beta.2.json +9 -0
  305. package/dist/migrations/manifests/0.5.0-beta.3.json +9 -0
  306. package/dist/migrations/manifests/0.5.0-beta.4.json +9 -0
  307. package/dist/migrations/manifests/0.5.0-beta.5.json +222 -0
  308. package/dist/migrations/manifests/0.5.0-beta.6.json +9 -0
  309. package/dist/migrations/manifests/0.5.0-beta.7.json +9 -0
  310. package/dist/migrations/manifests/0.5.0-beta.8.json +9 -0
  311. package/dist/migrations/manifests/0.5.0-beta.9.json +48 -0
  312. package/dist/migrations/manifests/0.5.0-rc.0.json +9 -0
  313. package/dist/migrations/manifests/0.5.0-rc.1.json +9 -0
  314. package/dist/migrations/manifests/0.5.0-rc.2.json +9 -0
  315. package/dist/migrations/manifests/0.5.0-rc.3.json +9 -0
  316. package/dist/migrations/manifests/0.5.0-rc.4.json +9 -0
  317. package/dist/migrations/manifests/0.5.0-rc.5.json +9 -0
  318. package/dist/migrations/manifests/0.5.0-rc.6.json +9 -0
  319. package/dist/migrations/manifests/0.5.0-rc.7.json +9 -0
  320. package/dist/migrations/manifests/0.5.0.json +9 -0
  321. package/dist/migrations/manifests/0.5.1.json +9 -0
  322. package/dist/migrations/manifests/0.5.10.json +9 -0
  323. package/dist/migrations/manifests/0.5.11.json +16 -0
  324. package/dist/migrations/manifests/0.5.12.json +9 -0
  325. package/dist/migrations/manifests/0.5.13.json +9 -0
  326. package/dist/migrations/manifests/0.5.14.json +9 -0
  327. package/dist/migrations/manifests/0.5.15.json +9 -0
  328. package/dist/migrations/manifests/0.5.2.json +9 -0
  329. package/dist/migrations/manifests/0.5.3.json +9 -0
  330. package/dist/migrations/manifests/0.5.4.json +9 -0
  331. package/dist/migrations/manifests/0.5.5.json +9 -0
  332. package/dist/migrations/manifests/0.5.6.json +9 -0
  333. package/dist/migrations/manifests/0.5.7.json +16 -0
  334. package/dist/migrations/manifests/0.5.8.json +9 -0
  335. package/dist/migrations/manifests/0.5.9.json +9 -0
  336. package/dist/migrations/manifests/0.6.0-beta.0.json +16 -0
  337. package/dist/migrations/manifests/0.6.0-beta.1.json +9 -0
  338. package/dist/migrations/manifests/0.6.0-beta.10.json +9 -0
  339. package/dist/migrations/manifests/0.6.0-beta.11.json +9 -0
  340. package/dist/migrations/manifests/0.6.0-beta.12.json +9 -0
  341. package/dist/migrations/manifests/0.6.0-beta.13.json +9 -0
  342. package/dist/migrations/manifests/0.6.0-beta.14.json +9 -0
  343. package/dist/migrations/manifests/0.6.0-beta.2.json +9 -0
  344. package/dist/migrations/manifests/0.6.0-beta.3.json +9 -0
  345. package/dist/migrations/manifests/0.6.0-beta.4.json +9 -0
  346. package/dist/migrations/manifests/0.6.0-beta.5.json +9 -0
  347. package/dist/migrations/manifests/0.6.0-beta.6.json +16 -0
  348. package/dist/migrations/manifests/0.6.0-beta.7.json +9 -0
  349. package/dist/migrations/manifests/0.6.0-beta.8.json +9 -0
  350. package/dist/migrations/manifests/0.6.0-beta.9.json +9 -0
  351. package/dist/templates/claude/agents/trellis-check.md +114 -0
  352. package/dist/templates/claude/agents/trellis-implement.md +113 -0
  353. package/dist/templates/claude/agents/trellis-research.md +137 -0
  354. package/dist/templates/claude/index.d.ts +22 -0
  355. package/dist/templates/claude/index.d.ts.map +1 -0
  356. package/dist/templates/claude/index.js +46 -0
  357. package/dist/templates/claude/index.js.map +1 -0
  358. package/dist/templates/claude/settings.json +73 -0
  359. package/dist/templates/codebuddy/agents/trellis-check.md +109 -0
  360. package/dist/templates/codebuddy/agents/trellis-implement.md +110 -0
  361. package/dist/templates/codebuddy/agents/trellis-research.md +137 -0
  362. package/dist/templates/codebuddy/index.d.ts +15 -0
  363. package/dist/templates/codebuddy/index.d.ts.map +1 -0
  364. package/dist/templates/codebuddy/index.js +15 -0
  365. package/dist/templates/codebuddy/index.js.map +1 -0
  366. package/dist/templates/codebuddy/settings.json +59 -0
  367. package/dist/templates/codex/agents/trellis-check.toml +84 -0
  368. package/dist/templates/codex/agents/trellis-implement.toml +65 -0
  369. package/dist/templates/codex/agents/trellis-research.toml +73 -0
  370. package/dist/templates/codex/config.toml +35 -0
  371. package/dist/templates/codex/hooks/session-start.py +545 -0
  372. package/dist/templates/codex/hooks.json +15 -0
  373. package/dist/templates/codex/index.d.ts +39 -0
  374. package/dist/templates/codex/index.d.ts.map +1 -0
  375. package/dist/templates/codex/index.js +85 -0
  376. package/dist/templates/codex/index.js.map +1 -0
  377. package/dist/templates/codex/skills/before-dev/SKILL.md +40 -0
  378. package/dist/templates/codex/skills/brainstorm/SKILL.md +112 -0
  379. package/dist/templates/codex/skills/break-loop/SKILL.md +130 -0
  380. package/dist/templates/codex/skills/check/SKILL.md +98 -0
  381. package/dist/templates/codex/skills/check-cross-layer/SKILL.md +158 -0
  382. package/dist/templates/codex/skills/create-command/SKILL.md +101 -0
  383. package/dist/templates/codex/skills/finish-work/SKILL.md +90 -0
  384. package/dist/templates/codex/skills/improve-ut/SKILL.md +69 -0
  385. package/dist/templates/codex/skills/integrate-skill/SKILL.md +221 -0
  386. package/dist/templates/codex/skills/onboard/SKILL.md +363 -0
  387. package/dist/templates/codex/skills/record-session/SKILL.md +67 -0
  388. package/dist/templates/codex/skills/start/SKILL.md +64 -0
  389. package/dist/templates/codex/skills/update-spec/SKILL.md +335 -0
  390. package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +73 -0
  391. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/add-project-local-conventions.md +83 -0
  392. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +54 -0
  393. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +84 -0
  394. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-hooks.md +57 -0
  395. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +78 -0
  396. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-spec-structure.md +83 -0
  397. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +90 -0
  398. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-workflow.md +65 -0
  399. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +55 -0
  400. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/context-injection.md +68 -0
  401. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +80 -0
  402. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/overview.md +51 -0
  403. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/spec-system.md +102 -0
  404. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +103 -0
  405. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workflow.md +75 -0
  406. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workspace-memory.md +71 -0
  407. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +80 -0
  408. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +69 -0
  409. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/overview.md +59 -0
  410. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +74 -0
  411. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +83 -0
  412. package/dist/templates/common/commands/continue.md +56 -0
  413. package/dist/templates/common/commands/finish-work.md +66 -0
  414. package/dist/templates/common/commands/start.md +59 -0
  415. package/dist/templates/common/index.d.ts +48 -0
  416. package/dist/templates/common/index.d.ts.map +1 -0
  417. package/dist/templates/common/index.js +104 -0
  418. package/dist/templates/common/index.js.map +1 -0
  419. package/dist/templates/common/skills/before-dev.md +35 -0
  420. package/dist/templates/common/skills/brainstorm.md +112 -0
  421. package/dist/templates/common/skills/break-loop.md +125 -0
  422. package/dist/templates/common/skills/check.md +93 -0
  423. package/dist/templates/common/skills/update-spec.md +351 -0
  424. package/dist/templates/copilot/hooks/session-start.py +547 -0
  425. package/dist/templates/copilot/hooks.json +19 -0
  426. package/dist/templates/copilot/index.d.ts +23 -0
  427. package/dist/templates/copilot/index.d.ts.map +1 -0
  428. package/dist/templates/copilot/index.js +54 -0
  429. package/dist/templates/copilot/index.js.map +1 -0
  430. package/dist/templates/copilot/prompts/before-dev.prompt.md +39 -0
  431. package/dist/templates/copilot/prompts/brainstorm.prompt.md +111 -0
  432. package/dist/templates/copilot/prompts/break-loop.prompt.md +129 -0
  433. package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +157 -0
  434. package/dist/templates/copilot/prompts/check.prompt.md +97 -0
  435. package/dist/templates/copilot/prompts/create-command.prompt.md +116 -0
  436. package/dist/templates/copilot/prompts/finish-work.prompt.md +99 -0
  437. package/dist/templates/copilot/prompts/integrate-skill.prompt.md +223 -0
  438. package/dist/templates/copilot/prompts/onboard.prompt.md +362 -0
  439. package/dist/templates/copilot/prompts/parallel.prompt.md +204 -0
  440. package/dist/templates/copilot/prompts/record-session.prompt.md +66 -0
  441. package/dist/templates/copilot/prompts/start.prompt.md +63 -0
  442. package/dist/templates/copilot/prompts/update-spec.prompt.md +358 -0
  443. package/dist/templates/cursor/agents/trellis-check.md +108 -0
  444. package/dist/templates/cursor/agents/trellis-implement.md +109 -0
  445. package/dist/templates/cursor/agents/trellis-research.md +136 -0
  446. package/dist/templates/cursor/hooks.json +30 -0
  447. package/dist/templates/cursor/index.d.ts +13 -0
  448. package/dist/templates/cursor/index.d.ts.map +1 -0
  449. package/dist/templates/cursor/index.js +13 -0
  450. package/dist/templates/cursor/index.js.map +1 -0
  451. package/dist/templates/droid/droids/trellis-check.md +101 -0
  452. package/dist/templates/droid/droids/trellis-implement.md +102 -0
  453. package/dist/templates/droid/droids/trellis-research.md +137 -0
  454. package/dist/templates/droid/index.d.ts +15 -0
  455. package/dist/templates/droid/index.d.ts.map +1 -0
  456. package/dist/templates/droid/index.js +15 -0
  457. package/dist/templates/droid/index.js.map +1 -0
  458. package/dist/templates/droid/settings.json +59 -0
  459. package/dist/templates/extract.d.ts +40 -0
  460. package/dist/templates/extract.d.ts.map +1 -0
  461. package/dist/templates/extract.js +106 -0
  462. package/dist/templates/extract.js.map +1 -0
  463. package/dist/templates/gemini/agents/trellis-check.md +101 -0
  464. package/dist/templates/gemini/agents/trellis-implement.md +102 -0
  465. package/dist/templates/gemini/agents/trellis-research.md +136 -0
  466. package/dist/templates/gemini/index.d.ts +13 -0
  467. package/dist/templates/gemini/index.d.ts.map +1 -0
  468. package/dist/templates/gemini/index.js +13 -0
  469. package/dist/templates/gemini/index.js.map +1 -0
  470. package/dist/templates/gemini/settings.json +28 -0
  471. package/dist/templates/kiro/agents/trellis-check.json +26 -0
  472. package/dist/templates/kiro/agents/trellis-implement.json +26 -0
  473. package/dist/templates/kiro/agents/trellis-research.json +30 -0
  474. package/dist/templates/kiro/index.d.ts +18 -0
  475. package/dist/templates/kiro/index.d.ts.map +1 -0
  476. package/dist/templates/kiro/index.js +18 -0
  477. package/dist/templates/kiro/index.js.map +1 -0
  478. package/dist/templates/markdown/agents.md +21 -0
  479. package/dist/templates/markdown/gitignore.txt +15 -0
  480. package/dist/templates/markdown/index.d.ts +27 -0
  481. package/dist/templates/markdown/index.d.ts.map +1 -0
  482. package/dist/templates/markdown/index.js +52 -0
  483. package/dist/templates/markdown/index.js.map +1 -0
  484. package/dist/templates/markdown/spec/backend/database-guidelines.md.txt +51 -0
  485. package/dist/templates/markdown/spec/backend/directory-structure.md.txt +54 -0
  486. package/dist/templates/markdown/spec/backend/error-handling.md.txt +51 -0
  487. package/dist/templates/markdown/spec/backend/index.md.txt +38 -0
  488. package/dist/templates/markdown/spec/backend/logging-guidelines.md.txt +51 -0
  489. package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +51 -0
  490. package/dist/templates/markdown/spec/frontend/component-guidelines.md.txt +59 -0
  491. package/dist/templates/markdown/spec/frontend/directory-structure.md.txt +54 -0
  492. package/dist/templates/markdown/spec/frontend/hook-guidelines.md.txt +51 -0
  493. package/dist/templates/markdown/spec/frontend/index.md.txt +39 -0
  494. package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +51 -0
  495. package/dist/templates/markdown/spec/frontend/state-management.md.txt +51 -0
  496. package/dist/templates/markdown/spec/frontend/type-safety.md.txt +51 -0
  497. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +223 -0
  498. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +259 -0
  499. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +595 -0
  500. package/dist/templates/markdown/spec/guides/index.md.txt +97 -0
  501. package/dist/templates/markdown/workspace-index.md +125 -0
  502. package/dist/templates/markdown/worktree.yaml.txt +58 -0
  503. package/dist/templates/opencode/agents/trellis-check.md +116 -0
  504. package/dist/templates/opencode/agents/trellis-implement.md +118 -0
  505. package/dist/templates/opencode/agents/trellis-research.md +145 -0
  506. package/dist/templates/opencode/lib/session-utils.js +521 -0
  507. package/dist/templates/opencode/lib/trellis-context.js +381 -0
  508. package/dist/templates/opencode/package.json +5 -0
  509. package/dist/templates/opencode/plugins/inject-subagent-context.js +513 -0
  510. package/dist/templates/opencode/plugins/inject-workflow-state.js +159 -0
  511. package/dist/templates/opencode/plugins/session-start.js +101 -0
  512. package/dist/templates/pi/agents/trellis-check.md +36 -0
  513. package/dist/templates/pi/agents/trellis-implement.md +41 -0
  514. package/dist/templates/pi/agents/trellis-research.md +25 -0
  515. package/dist/templates/pi/extensions/trellis/index.ts.txt +1174 -0
  516. package/dist/templates/pi/index.d.ts +5 -0
  517. package/dist/templates/pi/index.d.ts.map +1 -0
  518. package/dist/templates/pi/index.js +12 -0
  519. package/dist/templates/pi/index.js.map +1 -0
  520. package/dist/templates/pi/settings.json +21 -0
  521. package/dist/templates/qoder/agents/trellis-check.md +102 -0
  522. package/dist/templates/qoder/agents/trellis-implement.md +103 -0
  523. package/dist/templates/qoder/agents/trellis-research.md +137 -0
  524. package/dist/templates/qoder/index.d.ts +15 -0
  525. package/dist/templates/qoder/index.d.ts.map +1 -0
  526. package/dist/templates/qoder/index.js +15 -0
  527. package/dist/templates/qoder/index.js.map +1 -0
  528. package/dist/templates/qoder/settings.json +47 -0
  529. package/dist/templates/shared-hooks/index.d.ts +50 -0
  530. package/dist/templates/shared-hooks/index.d.ts.map +1 -0
  531. package/dist/templates/shared-hooks/index.js +89 -0
  532. package/dist/templates/shared-hooks/index.js.map +1 -0
  533. package/dist/templates/shared-hooks/inject-shell-session-context.py +183 -0
  534. package/dist/templates/shared-hooks/inject-subagent-context.py +771 -0
  535. package/dist/templates/shared-hooks/inject-workflow-state.py +363 -0
  536. package/dist/templates/shared-hooks/session-start.py +827 -0
  537. package/dist/templates/template-utils.d.ts +26 -0
  538. package/dist/templates/template-utils.d.ts.map +1 -0
  539. package/dist/templates/template-utils.js +60 -0
  540. package/dist/templates/template-utils.js.map +1 -0
  541. package/dist/templates/trellis/config.yaml +90 -0
  542. package/dist/templates/trellis/gitignore.txt +32 -0
  543. package/dist/templates/trellis/index.d.ts +52 -0
  544. package/dist/templates/trellis/index.d.ts.map +1 -0
  545. package/dist/templates/trellis/index.js +97 -0
  546. package/dist/templates/trellis/index.js.map +1 -0
  547. package/dist/templates/trellis/scripts/__init__.py +5 -0
  548. package/dist/templates/trellis/scripts/add_session.py +547 -0
  549. package/dist/templates/trellis/scripts/common/__init__.py +92 -0
  550. package/dist/templates/trellis/scripts/common/active_task.py +626 -0
  551. package/dist/templates/trellis/scripts/common/cli_adapter.py +811 -0
  552. package/dist/templates/trellis/scripts/common/config.py +445 -0
  553. package/dist/templates/trellis/scripts/common/developer.py +190 -0
  554. package/dist/templates/trellis/scripts/common/git.py +31 -0
  555. package/dist/templates/trellis/scripts/common/git_context.py +106 -0
  556. package/dist/templates/trellis/scripts/common/io.py +37 -0
  557. package/dist/templates/trellis/scripts/common/log.py +45 -0
  558. package/dist/templates/trellis/scripts/common/packages_context.py +238 -0
  559. package/dist/templates/trellis/scripts/common/paths.py +447 -0
  560. package/dist/templates/trellis/scripts/common/safe_commit.py +285 -0
  561. package/dist/templates/trellis/scripts/common/session_context.py +821 -0
  562. package/dist/templates/trellis/scripts/common/task_context.py +223 -0
  563. package/dist/templates/trellis/scripts/common/task_queue.py +188 -0
  564. package/dist/templates/trellis/scripts/common/task_store.py +698 -0
  565. package/dist/templates/trellis/scripts/common/task_utils.py +274 -0
  566. package/dist/templates/trellis/scripts/common/tasks.py +112 -0
  567. package/dist/templates/trellis/scripts/common/trellis_config.py +131 -0
  568. package/dist/templates/trellis/scripts/common/types.py +110 -0
  569. package/dist/templates/trellis/scripts/common/workflow_phase.py +212 -0
  570. package/dist/templates/trellis/scripts/get_context.py +16 -0
  571. package/dist/templates/trellis/scripts/get_developer.py +26 -0
  572. package/dist/templates/trellis/scripts/hooks/linear_sync.py +243 -0
  573. package/dist/templates/trellis/scripts/init_developer.py +51 -0
  574. package/dist/templates/trellis/scripts/task.py +500 -0
  575. package/dist/templates/trellis/tasks/.gitkeep +0 -0
  576. package/dist/templates/trellis/workflow.md +690 -0
  577. package/dist/types/ai-tools.d.ts +95 -0
  578. package/dist/types/ai-tools.d.ts.map +1 -0
  579. package/dist/types/ai-tools.js +280 -0
  580. package/dist/types/ai-tools.js.map +1 -0
  581. package/dist/types/migration.d.ts +125 -0
  582. package/dist/types/migration.d.ts.map +1 -0
  583. package/dist/types/migration.js +8 -0
  584. package/dist/types/migration.js.map +1 -0
  585. package/dist/utils/compare-versions.d.ts +12 -0
  586. package/dist/utils/compare-versions.d.ts.map +1 -0
  587. package/dist/utils/compare-versions.js +86 -0
  588. package/dist/utils/compare-versions.js.map +1 -0
  589. package/dist/utils/cwd-guard.d.ts +38 -0
  590. package/dist/utils/cwd-guard.d.ts.map +1 -0
  591. package/dist/utils/cwd-guard.js +62 -0
  592. package/dist/utils/cwd-guard.js.map +1 -0
  593. package/dist/utils/file-writer.d.ts +36 -0
  594. package/dist/utils/file-writer.d.ts.map +1 -0
  595. package/dist/utils/file-writer.js +203 -0
  596. package/dist/utils/file-writer.js.map +1 -0
  597. package/dist/utils/manifest-prune.d.ts +61 -0
  598. package/dist/utils/manifest-prune.d.ts.map +1 -0
  599. package/dist/utils/manifest-prune.js +136 -0
  600. package/dist/utils/manifest-prune.js.map +1 -0
  601. package/dist/utils/posix.d.ts +13 -0
  602. package/dist/utils/posix.d.ts.map +1 -0
  603. package/dist/utils/posix.js +15 -0
  604. package/dist/utils/posix.js.map +1 -0
  605. package/dist/utils/project-detector.d.ts +46 -0
  606. package/dist/utils/project-detector.d.ts.map +1 -0
  607. package/dist/utils/project-detector.js +666 -0
  608. package/dist/utils/project-detector.js.map +1 -0
  609. package/dist/utils/proxy.d.ts +25 -0
  610. package/dist/utils/proxy.d.ts.map +1 -0
  611. package/dist/utils/proxy.js +60 -0
  612. package/dist/utils/proxy.js.map +1 -0
  613. package/dist/utils/task-json.d.ts +13 -0
  614. package/dist/utils/task-json.d.ts.map +1 -0
  615. package/dist/utils/task-json.js +12 -0
  616. package/dist/utils/task-json.js.map +1 -0
  617. package/dist/utils/template-fetcher.d.ts +150 -0
  618. package/dist/utils/template-fetcher.d.ts.map +1 -0
  619. package/dist/utils/template-fetcher.js +907 -0
  620. package/dist/utils/template-fetcher.js.map +1 -0
  621. package/dist/utils/template-hash.d.ts +123 -0
  622. package/dist/utils/template-hash.d.ts.map +1 -0
  623. package/dist/utils/template-hash.js +334 -0
  624. package/dist/utils/template-hash.js.map +1 -0
  625. package/dist/utils/uninstall-scrubbers.d.ts +66 -0
  626. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -0
  627. package/dist/utils/uninstall-scrubbers.js +342 -0
  628. package/dist/utils/uninstall-scrubbers.js.map +1 -0
  629. package/package.json +90 -0
@@ -0,0 +1,907 @@
1
+ /**
2
+ * Remote template fetcher for Trellis CLI
3
+ *
4
+ * Fetches spec templates from the official marketplace:
5
+ * https://github.com/mindfold-ai/marketplace
6
+ */
7
+ import fs from "node:fs";
8
+ import os from "node:os";
9
+ import path from "node:path";
10
+ import { downloadTemplate } from "giget";
11
+ // =============================================================================
12
+ // Constants
13
+ // =============================================================================
14
+ export const TEMPLATE_INDEX_URL = "https://raw.githubusercontent.com/mindfold-ai/marketplace/main/index.json";
15
+ const TEMPLATE_REPO = "gh:mindfold-ai/marketplace";
16
+ /** Map template type to installation path */
17
+ const INSTALL_PATHS = {
18
+ spec: ".trellis/spec",
19
+ skill: ".agents/skills",
20
+ command: ".claude/commands",
21
+ full: ".", // Entire project root
22
+ };
23
+ /** Timeout constants for network operations */
24
+ export const TIMEOUTS = {
25
+ /** Timeout for fetching the template index (ms) */
26
+ INDEX_FETCH_MS: 5_000,
27
+ /** Timeout for downloading a template via giget (ms) */
28
+ DOWNLOAD_MS: 30_000,
29
+ };
30
+ export class RegistryBackendError extends Error {
31
+ kind;
32
+ constructor(kind, message) {
33
+ super(message);
34
+ this.name = "RegistryBackendError";
35
+ this.kind = kind;
36
+ }
37
+ }
38
+ // =============================================================================
39
+ // Registry Source Parsing
40
+ // =============================================================================
41
+ /** Maps provider prefixes to raw file URL patterns */
42
+ const RAW_URL_PATTERNS = {
43
+ gh: "https://raw.githubusercontent.com/{repo}/{ref}/{subdir}",
44
+ github: "https://raw.githubusercontent.com/{repo}/{ref}/{subdir}",
45
+ gitlab: "https://gitlab.com/{repo}/-/raw/{ref}/{subdir}",
46
+ bitbucket: "https://bitbucket.org/{repo}/raw/{ref}/{subdir}",
47
+ };
48
+ export const SUPPORTED_PROVIDERS = Object.keys(RAW_URL_PATTERNS);
49
+ /**
50
+ * Convert an HTTPS URL to giget-style source format.
51
+ * e.g. "https://github.com/user/repo" → "gh:user/repo"
52
+ * "https://github.com/user/repo/tree/branch/path" → "gh:user/repo/path#branch"
53
+ * Returns the original string if it's not a recognized HTTPS URL.
54
+ */
55
+ export function normalizeRegistrySource(source) {
56
+ const patterns = [
57
+ { re: /^https?:\/\/github\.com\//, prefix: "gh:" },
58
+ { re: /^https?:\/\/gitlab\.com\//, prefix: "gitlab:" },
59
+ { re: /^https?:\/\/bitbucket\.org\//, prefix: "bitbucket:" },
60
+ ];
61
+ for (const { re, prefix } of patterns) {
62
+ if (!re.test(source))
63
+ continue;
64
+ const path = source.replace(re, "");
65
+ // Handle /tree/<branch>/<subdir> format (GitHub browse URLs)
66
+ const treeMatch = path.match(/^([^/]+\/[^/]+)\/tree\/([^/]+)(?:\/(.+?))?(?:\.git)?\/?$/);
67
+ if (treeMatch) {
68
+ const [, repo, ref, subdir] = treeMatch;
69
+ return `${prefix}${repo}${subdir ? `/${subdir}` : ""}#${ref}`;
70
+ }
71
+ // Plain URL: strip trailing .git and /
72
+ const cleaned = path.replace(/\.git\/?$/, "").replace(/\/$/, "");
73
+ return `${prefix}${cleaned}`;
74
+ }
75
+ return source;
76
+ }
77
+ /** Known public domains that have dedicated giget provider prefixes */
78
+ const KNOWN_PUBLIC_DOMAINS = ["github.com", "gitlab.com", "bitbucket.org"];
79
+ /** Maps SSH/HTTPS domains of public providers to their giget prefix */
80
+ const PUBLIC_DOMAIN_TO_PREFIX = {
81
+ "github.com": "gh",
82
+ "gitlab.com": "gitlab",
83
+ "bitbucket.org": "bitbucket",
84
+ };
85
+ function buildRegistryGitUrl(input) {
86
+ if (input.sourceKind === "ssh" && input.sshHost) {
87
+ if (input.sshStyle === "ssh-url") {
88
+ const port = input.sshPort ? `:${input.sshPort}` : "";
89
+ return `ssh://git@${input.sshHost}${port}/${input.repo}.git`;
90
+ }
91
+ return `git@${input.sshHost}:${input.repo}.git`;
92
+ }
93
+ if (input.host) {
94
+ return `https://${input.host}/${input.repo}.git`;
95
+ }
96
+ switch (input.provider) {
97
+ case "gh":
98
+ case "github":
99
+ return `https://github.com/${input.repo}.git`;
100
+ case "gitlab":
101
+ return `https://gitlab.com/${input.repo}.git`;
102
+ case "bitbucket":
103
+ return `https://bitbucket.org/${input.repo}.git`;
104
+ default:
105
+ return input.repo;
106
+ }
107
+ }
108
+ /**
109
+ * Parse a giget-style registry source into its components.
110
+ *
111
+ * Supported input formats:
112
+ * | Format | Example | Provider | Host? |
113
+ * |-------------------------------------|----------------------------------------------|-----------|-----------|
114
+ * | giget prefix | gh:org/repo, gitlab:org/repo#ref | native | no |
115
+ * | Public HTTPS | https://github.com/org/repo | native | no |
116
+ * | Public SSH | git@github.com:org/repo | native | no |
117
+ * | Self-hosted HTTPS | https://git.corp.com/org/repo | gitlab | yes |
118
+ * | Self-hosted SSH | git@git.corp.com:org/repo | gitlab | yes |
119
+ * | ssh:// protocol (with/without port) | ssh://git@host:2222/org/repo | gitlab | yes |
120
+ * | HTTPS with port | https://host:8443/org/repo | gitlab | yes |
121
+ * | GitLab browse URL | https://host/org/repo/-/tree/branch/path | gitlab | yes |
122
+ *
123
+ * Ref defaults to "main" if not specified.
124
+ * Unknown domains default to GitLab URL patterns (covers self-hosted GitLab CE/EE).
125
+ *
126
+ * @throws Error if provider is unsupported
127
+ */
128
+ export function parseRegistrySource(source) {
129
+ // --- Self-hosted URL detection (SSH + unknown HTTPS) ---
130
+ let host;
131
+ let normalizedInput;
132
+ let sourceKind = "prefixed";
133
+ let sshHost;
134
+ let sshPort;
135
+ let sshStyle;
136
+ // SSH URL: git@host:org/repo[.git] or ssh://git@host[:port]/org/repo[.git]
137
+ const scpSshMatch = source.match(/^git@([^:]+):(.+?)(?:\.git)?\/?$/);
138
+ const protocolSshMatch = source.match(/^ssh:\/\/git@([^/:]+)(?::(\d+))?\/(.+?)(?:\.git)?\/?$/);
139
+ if (scpSshMatch || protocolSshMatch) {
140
+ sourceKind = "ssh";
141
+ sshStyle = scpSshMatch ? "scp" : "ssh-url";
142
+ const sshDomain = scpSshMatch?.[1] ?? protocolSshMatch?.[1] ?? "";
143
+ const sshPath = scpSshMatch?.[2] ?? protocolSshMatch?.[3] ?? "";
144
+ sshPort = protocolSshMatch?.[2];
145
+ sshHost = sshDomain;
146
+ const publicPrefix = PUBLIC_DOMAIN_TO_PREFIX[sshDomain];
147
+ if (publicPrefix) {
148
+ // Public provider SSH (e.g., git@github.com:org/repo) — use native prefix, no host
149
+ normalizedInput = `${publicPrefix}:${sshPath}`;
150
+ }
151
+ else {
152
+ // Self-hosted SSH — default to gitlab provider with host
153
+ host = sshDomain;
154
+ normalizedInput = `gitlab:${sshPath}`;
155
+ }
156
+ }
157
+ // HTTPS URL to unknown domain (not github.com/gitlab.com/bitbucket.org)
158
+ if (!normalizedInput) {
159
+ const httpsMatch = source.match(/^https?:\/\/([^/]+)\/(.+?)(?:\.git)?\/?$/);
160
+ if (httpsMatch) {
161
+ sourceKind = "https";
162
+ const domain = httpsMatch[1];
163
+ if (!KNOWN_PUBLIC_DOMAINS.includes(domain)) {
164
+ host = domain;
165
+ const pathPart = httpsMatch[2];
166
+ // Handle GitLab browse URLs: /org/repo/-/tree/branch/path
167
+ const treeMatch = pathPart.match(/^([^/]+\/[^/]+)(?:\/-)?\/tree\/([^/]+)(?:\/(.+?))?$/);
168
+ if (treeMatch) {
169
+ const [, repoPath, ref, subdir] = treeMatch;
170
+ normalizedInput = `gitlab:${repoPath}${subdir ? `/${subdir}` : ""}#${ref}`;
171
+ }
172
+ else {
173
+ normalizedInput = `gitlab:${pathPart}`;
174
+ }
175
+ }
176
+ }
177
+ }
178
+ // Auto-convert known HTTPS URLs to giget format (existing logic)
179
+ const normalized = normalizedInput ?? normalizeRegistrySource(source);
180
+ // Extract provider prefix
181
+ const colonIndex = normalized.indexOf(":");
182
+ if (colonIndex === -1) {
183
+ throw new Error(`Invalid registry source "${source}". Expected format: gh:user/repo/path`);
184
+ }
185
+ const provider = normalized.slice(0, colonIndex);
186
+ const rest = normalized.slice(colonIndex + 1);
187
+ // Check supported provider
188
+ const pattern = RAW_URL_PATTERNS[provider];
189
+ if (!pattern) {
190
+ const supported = [...new Set(Object.keys(RAW_URL_PATTERNS))].join(", ");
191
+ throw new Error(`Unsupported provider "${provider}". Supported: ${supported}`);
192
+ }
193
+ // Parse rest: user/repo/subdir#ref
194
+ // Match: user/repo (required), /subdir (optional), #ref (optional)
195
+ const refMatch = rest.match(/^([^#]+?)(?:#(.+))?$/);
196
+ if (!refMatch) {
197
+ throw new Error(`Invalid registry source "${normalized}". Expected format: ${provider}:user/repo/path`);
198
+ }
199
+ const pathPart = refMatch[1];
200
+ const ref = refMatch[2] ?? "main";
201
+ // Split into repo (first two segments) and subdir (rest)
202
+ const segments = pathPart.split("/").filter(Boolean);
203
+ if (segments.length < 2) {
204
+ throw new Error(`Invalid registry source "${normalized}". Must include user/repo at minimum.`);
205
+ }
206
+ const repo = `${segments[0]}/${segments[1]}`;
207
+ const subdir = segments.slice(2).join("/");
208
+ // Build raw base URL
209
+ let rawBaseUrl = pattern
210
+ .replace("{repo}", repo)
211
+ .replace("{ref}", ref)
212
+ .replace("{subdir}", subdir);
213
+ // Replace public domain with self-hosted host in rawBaseUrl
214
+ if (host && provider === "gitlab") {
215
+ rawBaseUrl = rawBaseUrl.replace("https://gitlab.com", `https://${host}`);
216
+ }
217
+ // Build giget source (use normalized format)
218
+ const gigetSource = normalized;
219
+ const gitUrl = buildRegistryGitUrl({
220
+ provider,
221
+ host,
222
+ repo,
223
+ sourceKind,
224
+ sshHost,
225
+ sshPort,
226
+ sshStyle,
227
+ });
228
+ const preferGit = sourceKind === "ssh" || host !== undefined;
229
+ return {
230
+ provider,
231
+ repo,
232
+ subdir,
233
+ ref,
234
+ rawBaseUrl,
235
+ gigetSource,
236
+ host,
237
+ gitUrl,
238
+ preferGit,
239
+ sourceKind,
240
+ };
241
+ }
242
+ // =============================================================================
243
+ // Helpers
244
+ // =============================================================================
245
+ /**
246
+ * Temporarily set `GIGET_GITLAB_URL` env var for self-hosted GitLab downloads.
247
+ * Restores the previous value (or deletes it) after the callback completes.
248
+ */
249
+ async function withGigetHost(host, fn) {
250
+ if (!host)
251
+ return fn();
252
+ const prev = process.env.GIGET_GITLAB_URL;
253
+ process.env.GIGET_GITLAB_URL = `https://${host}`;
254
+ try {
255
+ return await fn();
256
+ }
257
+ finally {
258
+ if (prev === undefined)
259
+ delete process.env.GIGET_GITLAB_URL;
260
+ else
261
+ process.env.GIGET_GITLAB_URL = prev;
262
+ }
263
+ }
264
+ /**
265
+ * Race a promise against a timeout.
266
+ * giget does not support AbortSignal, so we use Promise.race instead.
267
+ * The timer is cleaned up on success to avoid keeping the process alive.
268
+ */
269
+ function withTimeout(promise, ms, label) {
270
+ let timer;
271
+ const timeoutPromise = new Promise((_, reject) => {
272
+ timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms / 1000}s`)), ms);
273
+ });
274
+ return Promise.race([promise, timeoutPromise]).finally(() => {
275
+ clearTimeout(timer);
276
+ });
277
+ }
278
+ // =============================================================================
279
+ // Fetch Template Index
280
+ // =============================================================================
281
+ /**
282
+ * Fetch available templates from the remote index
283
+ * Returns empty array on network error or timeout (allows fallback to blank)
284
+ *
285
+ * @param indexUrl - URL to fetch index.json from (defaults to official marketplace)
286
+ */
287
+ export async function fetchTemplateIndex(indexUrl) {
288
+ try {
289
+ const url = indexUrl ?? TEMPLATE_INDEX_URL;
290
+ const res = await fetch(url, {
291
+ signal: AbortSignal.timeout(TIMEOUTS.INDEX_FETCH_MS),
292
+ });
293
+ if (!res.ok) {
294
+ throw new Error(`HTTP ${res.status}`);
295
+ }
296
+ const index = (await res.json());
297
+ return index.templates;
298
+ }
299
+ catch {
300
+ // Network error or timeout - return empty array, caller will fallback to blank
301
+ return [];
302
+ }
303
+ }
304
+ function isRecord(value) {
305
+ return typeof value === "object" && value !== null;
306
+ }
307
+ function parseTemplateIndex(raw, sourceLabel) {
308
+ let parsed;
309
+ try {
310
+ parsed = JSON.parse(raw);
311
+ }
312
+ catch (error) {
313
+ const detail = error instanceof Error ? error.message : String(error);
314
+ throw new RegistryBackendError("invalid-json", `${sourceLabel} is not valid JSON: ${detail}`);
315
+ }
316
+ if (!isRecord(parsed) || !Array.isArray(parsed.templates)) {
317
+ throw new RegistryBackendError("invalid-json", `${sourceLabel} must contain a templates array.`);
318
+ }
319
+ const templates = [];
320
+ for (const item of parsed.templates) {
321
+ if (!isRecord(item)) {
322
+ throw new RegistryBackendError("invalid-json", `${sourceLabel} contains an invalid template entry.`);
323
+ }
324
+ const { id, type, name, description, path: templatePath, tags } = item;
325
+ if (typeof id !== "string" ||
326
+ typeof type !== "string" ||
327
+ typeof name !== "string" ||
328
+ typeof templatePath !== "string") {
329
+ throw new RegistryBackendError("invalid-json", `${sourceLabel} template entries must include string id, type, name, and path fields.`);
330
+ }
331
+ templates.push({
332
+ id,
333
+ type,
334
+ name,
335
+ path: templatePath,
336
+ ...(typeof description === "string" ? { description } : {}),
337
+ ...(Array.isArray(tags) && tags.every((tag) => typeof tag === "string")
338
+ ? { tags }
339
+ : {}),
340
+ });
341
+ }
342
+ const version = typeof parsed.version === "number" ? parsed.version : 1;
343
+ return { version, templates };
344
+ }
345
+ function emptyProbeResult(backend, isNotFound, error) {
346
+ return { templates: [], isNotFound, backend, ...(error ? { error } : {}) };
347
+ }
348
+ function shouldFallbackToGit(registry, result) {
349
+ if (registry?.provider !== "gitlab")
350
+ return false;
351
+ if (result.backend !== "http" || result.isNotFound)
352
+ return false;
353
+ return result.error?.kind === "auth" || result.error?.kind === "invalid-json";
354
+ }
355
+ async function probeRegistryIndexHttp(indexUrl) {
356
+ try {
357
+ const res = await fetch(indexUrl, {
358
+ signal: AbortSignal.timeout(TIMEOUTS.INDEX_FETCH_MS),
359
+ });
360
+ if (res.status === 404) {
361
+ return emptyProbeResult("http", true);
362
+ }
363
+ if (res.status === 401 || res.status === 403) {
364
+ return emptyProbeResult("http", false, new RegistryBackendError("auth", `Registry index requires authentication (HTTP ${res.status}). Use a registry source accessible by your local Git credentials.`));
365
+ }
366
+ if (!res.ok) {
367
+ return emptyProbeResult("http", false, new RegistryBackendError("network", `Could not reach registry index (HTTP ${res.status}). Check your network connection and try again.`));
368
+ }
369
+ const index = parseTemplateIndex(await res.text(), "Registry index.json");
370
+ return { templates: index.templates, isNotFound: false, backend: "http" };
371
+ }
372
+ catch (error) {
373
+ if (error instanceof RegistryBackendError) {
374
+ return emptyProbeResult("http", false, error);
375
+ }
376
+ return emptyProbeResult("http", false, new RegistryBackendError("network", `Could not reach registry index: ${error instanceof Error ? error.message : String(error)}`));
377
+ }
378
+ }
379
+ /**
380
+ * Probe a registry's index.json, distinguishing "not found" from transient errors.
381
+ * Used by the registry flow to decide marketplace vs direct-download mode.
382
+ *
383
+ * - 404 → { templates: [], isNotFound: true }
384
+ * - Other HTTP error / network timeout → { templates: [], isNotFound: false }
385
+ * - 200 + valid JSON → { templates: [...], isNotFound: false }
386
+ */
387
+ export async function probeRegistryIndex(indexUrl, registry) {
388
+ if (registry?.preferGit) {
389
+ return probeRegistryIndexGit(registry);
390
+ }
391
+ const httpResult = await probeRegistryIndexHttp(indexUrl);
392
+ if (registry && shouldFallbackToGit(registry, httpResult)) {
393
+ return probeRegistryIndexGit(registry);
394
+ }
395
+ return httpResult;
396
+ }
397
+ async function runGit(args) {
398
+ const { execFile } = await import("node:child_process");
399
+ return new Promise((resolve, reject) => {
400
+ execFile("git", args, {
401
+ encoding: "utf-8",
402
+ maxBuffer: 10 * 1024 * 1024,
403
+ timeout: TIMEOUTS.DOWNLOAD_MS,
404
+ }, (error, stdout, stderr) => {
405
+ if (error) {
406
+ const commandError = error;
407
+ commandError.stdout = stdout;
408
+ commandError.stderr = stderr;
409
+ reject(commandError);
410
+ return;
411
+ }
412
+ resolve({ stdout, stderr });
413
+ });
414
+ });
415
+ }
416
+ function getCommandErrorText(error) {
417
+ if (!(error instanceof Error))
418
+ return String(error);
419
+ const commandError = error;
420
+ return [commandError.message, commandError.stderr, commandError.stdout]
421
+ .filter((part) => typeof part === "string" && part.length > 0)
422
+ .join("\n");
423
+ }
424
+ function classifyGitError(error, stage, registry) {
425
+ const text = getCommandErrorText(error);
426
+ const lower = text.toLowerCase();
427
+ if (lower.includes("enoent") ||
428
+ lower.includes("not found: git") ||
429
+ lower.includes("spawn git")) {
430
+ return new RegistryBackendError("git-unavailable", 'Git is required to access this registry, but the "git" command was not found.');
431
+ }
432
+ if (lower.includes("authentication failed") ||
433
+ lower.includes("permission denied") ||
434
+ lower.includes("access denied") ||
435
+ lower.includes("could not read from remote repository") ||
436
+ lower.includes("terminal prompts disabled")) {
437
+ return new RegistryBackendError("auth", `Authentication failed or registry is not accessible via Git. Check your local Git credentials for ${registry.gitUrl}.`);
438
+ }
439
+ if (stage !== "clone" &&
440
+ (lower.includes("couldn't find remote ref") ||
441
+ lower.includes("could not find remote ref") ||
442
+ lower.includes("invalid reference") ||
443
+ lower.includes("reference is not a tree") ||
444
+ lower.includes("pathspec"))) {
445
+ return new RegistryBackendError("ref-not-found", `Registry ref "${registry.ref}" was not found in ${registry.gitUrl}.`);
446
+ }
447
+ if (lower.includes("could not resolve host") ||
448
+ lower.includes("failed to connect") ||
449
+ lower.includes("network is unreachable") ||
450
+ lower.includes("operation timed out") ||
451
+ lower.includes("timed out") ||
452
+ lower.includes("connection refused")) {
453
+ return new RegistryBackendError("network", `Could not reach registry ${registry.gitUrl}. Check your network connection and Git remote access.`);
454
+ }
455
+ if (stage === "clone" &&
456
+ (lower.includes("repository not found") ||
457
+ lower.includes("does not appear to be a git repository") ||
458
+ lower.includes("could not read from remote repository"))) {
459
+ return new RegistryBackendError("not-found", `Registry repository was not found or is not accessible: ${registry.gitUrl}.`);
460
+ }
461
+ return new RegistryBackendError("unknown", `Git registry operation failed: ${text}`);
462
+ }
463
+ async function cloneRegistryRef(registry) {
464
+ const dir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "trellis-registry-"));
465
+ try {
466
+ try {
467
+ await runGit([
468
+ "clone",
469
+ "--filter=blob:none",
470
+ "--no-checkout",
471
+ registry.gitUrl,
472
+ dir,
473
+ ]);
474
+ }
475
+ catch (error) {
476
+ throw classifyGitError(error, "clone", registry);
477
+ }
478
+ try {
479
+ await runGit([
480
+ "-C",
481
+ dir,
482
+ "fetch",
483
+ "--depth",
484
+ "1",
485
+ "origin",
486
+ registry.ref,
487
+ ]);
488
+ await runGit(["-C", dir, "checkout", "--detach", "FETCH_HEAD"]);
489
+ }
490
+ catch (error) {
491
+ throw classifyGitError(error, "fetch", registry);
492
+ }
493
+ return {
494
+ dir,
495
+ cleanup: async () => {
496
+ await fs.promises.rm(dir, { recursive: true, force: true });
497
+ },
498
+ };
499
+ }
500
+ catch (error) {
501
+ await fs.promises.rm(dir, { recursive: true, force: true });
502
+ throw error;
503
+ }
504
+ }
505
+ function resolveInsideRegistryRoot(root, relativePath, label) {
506
+ const resolvedRoot = path.resolve(root);
507
+ const resolved = path.resolve(resolvedRoot, relativePath.length > 0 ? relativePath : ".");
508
+ const relative = path.relative(resolvedRoot, resolved);
509
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
510
+ throw new RegistryBackendError("path-not-found", `${label} path "${relativePath}" must stay inside the registry repository.`);
511
+ }
512
+ return resolved;
513
+ }
514
+ async function isDirectory(dir) {
515
+ try {
516
+ return (await fs.promises.stat(dir)).isDirectory();
517
+ }
518
+ catch {
519
+ return false;
520
+ }
521
+ }
522
+ async function isFile(filePath) {
523
+ try {
524
+ return (await fs.promises.stat(filePath)).isFile();
525
+ }
526
+ catch {
527
+ return false;
528
+ }
529
+ }
530
+ async function getGitRegistryRoot(checkoutDir, registry) {
531
+ const registryRoot = resolveInsideRegistryRoot(checkoutDir, registry.subdir, "Registry");
532
+ if (!(await isDirectory(registryRoot))) {
533
+ throw new RegistryBackendError("path-not-found", `Registry path "${registry.subdir.length > 0 ? registry.subdir : "."}" was not found in ${registry.gitUrl}#${registry.ref}.`);
534
+ }
535
+ return registryRoot;
536
+ }
537
+ async function probeRegistryIndexGit(registry) {
538
+ try {
539
+ const checkout = await cloneRegistryRef(registry);
540
+ try {
541
+ const registryRoot = await getGitRegistryRoot(checkout.dir, registry);
542
+ const indexPath = path.join(registryRoot, "index.json");
543
+ if (!(await isFile(indexPath))) {
544
+ return emptyProbeResult("git", true);
545
+ }
546
+ const index = parseTemplateIndex(await fs.promises.readFile(indexPath, "utf-8"), "Registry index.json");
547
+ return { templates: index.templates, isNotFound: false, backend: "git" };
548
+ }
549
+ finally {
550
+ await checkout.cleanup();
551
+ }
552
+ }
553
+ catch (error) {
554
+ const registryError = error instanceof RegistryBackendError
555
+ ? error
556
+ : new RegistryBackendError("unknown", `Git registry probe failed: ${error instanceof Error ? error.message : String(error)}`);
557
+ return emptyProbeResult("git", false, registryError);
558
+ }
559
+ }
560
+ /**
561
+ * Find a template by ID from the index
562
+ */
563
+ export async function findTemplate(templateId, indexUrl) {
564
+ const templates = await fetchTemplateIndex(indexUrl);
565
+ return templates.find((t) => t.id === templateId) ?? null;
566
+ }
567
+ // =============================================================================
568
+ // Download Template
569
+ // =============================================================================
570
+ /**
571
+ * Get the installation path for a template type
572
+ */
573
+ export function getInstallPath(cwd, templateType) {
574
+ const relativePath = INSTALL_PATHS[templateType] || INSTALL_PATHS.spec;
575
+ return path.join(cwd, relativePath);
576
+ }
577
+ /**
578
+ * Download a template with the specified strategy
579
+ *
580
+ * @param templatePath - Path in the docs repo (e.g., "marketplace/specs/electron-fullstack")
581
+ * OR a full giget source (e.g., "gh:myorg/myrepo/my-spec")
582
+ * @param destDir - Destination directory
583
+ * @param strategy - How to handle existing directory: skip, overwrite, or append
584
+ * @param repoSource - Optional giget repo source override. When set, templatePath is
585
+ * treated as relative to this repo. When not set, uses TEMPLATE_REPO.
586
+ * Pass null to use templatePath as a full giget source directly.
587
+ * @returns true if template was downloaded, false if skipped
588
+ */
589
+ export async function downloadWithStrategy(templatePath, destDir, strategy, repoSource) {
590
+ // Build the giget download source
591
+ const gigetSource = repoSource === null
592
+ ? templatePath // templatePath is already a full giget source
593
+ : `${repoSource ?? TEMPLATE_REPO}/${templatePath}`;
594
+ const exists = fs.existsSync(destDir);
595
+ // skip: Directory exists, don't download
596
+ if (strategy === "skip" && exists) {
597
+ return false;
598
+ }
599
+ // overwrite: Delete existing directory first
600
+ if (strategy === "overwrite" && exists) {
601
+ await fs.promises.rm(destDir, { recursive: true });
602
+ }
603
+ // append: Download to temp dir, then merge missing files
604
+ if (strategy === "append" && exists) {
605
+ const tempDir = path.join(os.tmpdir(), `trellis-template-${Date.now()}`);
606
+ try {
607
+ await withTimeout(downloadTemplate(gigetSource, {
608
+ dir: tempDir,
609
+ preferOffline: true,
610
+ }), TIMEOUTS.DOWNLOAD_MS, "Template download");
611
+ await copyMissing(tempDir, destDir);
612
+ }
613
+ catch (error) {
614
+ // Clean up partially written files on timeout.
615
+ // Note: giget does not support AbortSignal, so the background download may
616
+ // still be running. Removing the directory causes it to fail with ENOENT,
617
+ // which settles the orphaned promise harmlessly.
618
+ if (error instanceof Error && error.message.includes("timed out")) {
619
+ try {
620
+ fs.rmSync(tempDir, { recursive: true, force: true });
621
+ }
622
+ catch {
623
+ // Best-effort cleanup
624
+ }
625
+ }
626
+ throw error;
627
+ }
628
+ finally {
629
+ // Clean up temp directory
630
+ await fs.promises.rm(tempDir, { recursive: true, force: true });
631
+ }
632
+ return true;
633
+ }
634
+ // Default: Direct download (for new directory or after overwrite)
635
+ try {
636
+ await withTimeout(downloadTemplate(gigetSource, {
637
+ dir: destDir,
638
+ preferOffline: true,
639
+ }), TIMEOUTS.DOWNLOAD_MS, "Template download");
640
+ }
641
+ catch (error) {
642
+ // Clean up partially written files on timeout.
643
+ // Note: giget does not support AbortSignal, so the background download may
644
+ // still be running. Removing the directory causes it to fail with ENOENT,
645
+ // which settles the orphaned promise harmlessly.
646
+ if (error instanceof Error && error.message.includes("timed out")) {
647
+ try {
648
+ fs.rmSync(destDir, { recursive: true, force: true });
649
+ }
650
+ catch {
651
+ // Best-effort cleanup
652
+ }
653
+ }
654
+ throw error;
655
+ }
656
+ return true;
657
+ }
658
+ /**
659
+ * Copy only files that don't exist in the destination
660
+ */
661
+ async function copyMissing(src, dest) {
662
+ // Ensure destination exists
663
+ if (!fs.existsSync(dest)) {
664
+ await fs.promises.mkdir(dest, { recursive: true });
665
+ }
666
+ const entries = await fs.promises.readdir(src, { withFileTypes: true });
667
+ for (const entry of entries) {
668
+ const srcPath = path.join(src, entry.name);
669
+ const destPath = path.join(dest, entry.name);
670
+ if (entry.isDirectory()) {
671
+ // Recursively copy missing files in subdirectory
672
+ await copyMissing(srcPath, destPath);
673
+ }
674
+ else if (!fs.existsSync(destPath)) {
675
+ // Only copy if file doesn't exist
676
+ await fs.promises.copyFile(srcPath, destPath);
677
+ }
678
+ }
679
+ }
680
+ /**
681
+ * Copy all files from src to dest, overwriting existing files.
682
+ */
683
+ async function copyAll(src, dest) {
684
+ await fs.promises.mkdir(dest, { recursive: true });
685
+ const entries = await fs.promises.readdir(src, { withFileTypes: true });
686
+ for (const entry of entries) {
687
+ const srcPath = path.join(src, entry.name);
688
+ const destPath = path.join(dest, entry.name);
689
+ if (entry.isDirectory()) {
690
+ await copyAll(srcPath, destPath);
691
+ }
692
+ else {
693
+ await fs.promises.copyFile(srcPath, destPath);
694
+ }
695
+ }
696
+ }
697
+ async function copyDirectoryWithStrategy(srcDir, destDir, strategy) {
698
+ const exists = fs.existsSync(destDir);
699
+ if (strategy === "skip" && exists) {
700
+ return false;
701
+ }
702
+ if (strategy === "overwrite" && exists) {
703
+ await fs.promises.rm(destDir, { recursive: true, force: true });
704
+ }
705
+ if (strategy === "append" && exists) {
706
+ await copyMissing(srcDir, destDir);
707
+ return true;
708
+ }
709
+ await copyAll(srcDir, destDir);
710
+ return true;
711
+ }
712
+ async function downloadGitRegistryPath(registry, relativePath, destDir, strategy) {
713
+ const checkout = await cloneRegistryRef(registry);
714
+ try {
715
+ const sourceRoot = resolveInsideRegistryRoot(checkout.dir, relativePath, "Template");
716
+ if (!(await isDirectory(sourceRoot))) {
717
+ throw new RegistryBackendError("path-not-found", `Template path "${relativePath.length > 0 ? relativePath : "."}" was not found in ${registry.gitUrl}#${registry.ref}.`);
718
+ }
719
+ return await copyDirectoryWithStrategy(sourceRoot, destDir, strategy);
720
+ }
721
+ finally {
722
+ await checkout.cleanup();
723
+ }
724
+ }
725
+ async function downloadGitRegistryDirect(registry, destDir, strategy) {
726
+ const checkout = await cloneRegistryRef(registry);
727
+ try {
728
+ const sourceRoot = await getGitRegistryRoot(checkout.dir, registry);
729
+ return await copyDirectoryWithStrategy(sourceRoot, destDir, strategy);
730
+ }
731
+ finally {
732
+ await checkout.cleanup();
733
+ }
734
+ }
735
+ function resolveRegistryBackend(registry, backendOverride) {
736
+ return backendOverride ?? (registry.preferGit ? "git" : "http");
737
+ }
738
+ /**
739
+ * Download a template by ID
740
+ *
741
+ * @param cwd - Current working directory
742
+ * @param templateId - Template ID from the index
743
+ * @param strategy - How to handle existing directory
744
+ * @param template - Optional pre-fetched SpecTemplate to avoid double-fetch
745
+ * @param registry - Optional registry source (parsed). When set, uses the registry's
746
+ * repo as the giget source instead of the default TEMPLATE_REPO.
747
+ * @returns Object with success status and message
748
+ */
749
+ export async function downloadTemplateById(cwd, templateId, strategy, template, registry, destDirOverride, registryBackend) {
750
+ // Use pre-fetched template or find from index
751
+ let resolved = template;
752
+ let backend = registryBackend;
753
+ if (!resolved) {
754
+ const indexUrl = registry ? `${registry.rawBaseUrl}/index.json` : undefined;
755
+ if (registry && indexUrl) {
756
+ // Use probe to distinguish "template not in index" from "registry unreachable"
757
+ const probeResult = await probeRegistryIndex(indexUrl, registry);
758
+ backend = probeResult.backend;
759
+ if (probeResult.error) {
760
+ return {
761
+ success: false,
762
+ message: probeResult.error.message,
763
+ };
764
+ }
765
+ if (probeResult.templates.length === 0 && !probeResult.isNotFound) {
766
+ return {
767
+ success: false,
768
+ message: "Could not reach registry. Check your network connection and try again.",
769
+ };
770
+ }
771
+ if (probeResult.isNotFound) {
772
+ return {
773
+ success: false,
774
+ message: "Registry has no index.json. Remove --template to use direct download mode.",
775
+ };
776
+ }
777
+ resolved = probeResult.templates.find((t) => t.id === templateId);
778
+ }
779
+ else {
780
+ resolved = (await findTemplate(templateId, indexUrl)) ?? undefined;
781
+ }
782
+ }
783
+ if (!resolved) {
784
+ return {
785
+ success: false,
786
+ message: `Template "${templateId}" not found`,
787
+ };
788
+ }
789
+ // Only support spec type in MVP
790
+ if (resolved.type !== "spec") {
791
+ return {
792
+ success: false,
793
+ message: `Template type "${resolved.type}" is not supported yet (only "spec" is supported)`,
794
+ };
795
+ }
796
+ // Get destination path (use override for monorepo per-package downloads)
797
+ const destDir = destDirOverride ?? getInstallPath(cwd, resolved.type);
798
+ // Check if directory exists for skip strategy
799
+ if (strategy === "skip" && fs.existsSync(destDir)) {
800
+ return {
801
+ success: true,
802
+ skipped: true,
803
+ message: `Skipped: ${destDir} already exists`,
804
+ };
805
+ }
806
+ // Download template
807
+ try {
808
+ if (registry) {
809
+ if (resolveRegistryBackend(registry, backend) === "git") {
810
+ await downloadGitRegistryPath(registry, resolved.path, destDir, strategy);
811
+ }
812
+ else {
813
+ // Custom registry: build full giget source with ref at the end
814
+ // giget format: provider:user/repo/path#ref
815
+ const fullSource = `${registry.provider}:${registry.repo}/${resolved.path}#${registry.ref}`;
816
+ await withGigetHost(registry.host, () => downloadWithStrategy(fullSource, destDir, strategy, null));
817
+ }
818
+ }
819
+ else {
820
+ await downloadWithStrategy(resolved.path, destDir, strategy);
821
+ }
822
+ return {
823
+ success: true,
824
+ message: `Downloaded template "${templateId}" to ${destDir}`,
825
+ };
826
+ }
827
+ catch (error) {
828
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
829
+ if (error instanceof RegistryBackendError) {
830
+ return {
831
+ success: false,
832
+ message: error.message,
833
+ };
834
+ }
835
+ // Classify errors for user-friendly messages
836
+ if (errorMessage.includes("timed out")) {
837
+ return {
838
+ success: false,
839
+ message: "Download timed out. Check your network connection and try again.",
840
+ };
841
+ }
842
+ if (errorMessage.includes("Failed to download") ||
843
+ errorMessage.includes("Failed to fetch")) {
844
+ return {
845
+ success: false,
846
+ message: "Could not reach template server. Check your network connection.",
847
+ };
848
+ }
849
+ return {
850
+ success: false,
851
+ message: `Download failed: ${errorMessage}`,
852
+ };
853
+ }
854
+ }
855
+ /**
856
+ * Download a registry source directly to the spec directory (no index.json).
857
+ * Used when the registry source points to a spec directory, not a marketplace.
858
+ */
859
+ export async function downloadRegistryDirect(cwd, registry, strategy, destDirOverride, registryBackend) {
860
+ const destDir = destDirOverride ?? getInstallPath(cwd, "spec");
861
+ if (strategy === "skip" && fs.existsSync(destDir)) {
862
+ return {
863
+ success: true,
864
+ skipped: true,
865
+ message: `Skipped: ${destDir} already exists`,
866
+ };
867
+ }
868
+ try {
869
+ if (resolveRegistryBackend(registry, registryBackend) === "git") {
870
+ await downloadGitRegistryDirect(registry, destDir, strategy);
871
+ }
872
+ else {
873
+ await withGigetHost(registry.host, () => downloadWithStrategy(registry.gigetSource, destDir, strategy, null));
874
+ }
875
+ return {
876
+ success: true,
877
+ message: `Downloaded spec from ${registry.gigetSource} to ${destDir}`,
878
+ };
879
+ }
880
+ catch (error) {
881
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
882
+ if (error instanceof RegistryBackendError) {
883
+ return {
884
+ success: false,
885
+ message: error.message,
886
+ };
887
+ }
888
+ if (errorMessage.includes("timed out")) {
889
+ return {
890
+ success: false,
891
+ message: "Download timed out. Check your network connection and try again.",
892
+ };
893
+ }
894
+ if (errorMessage.includes("Failed to download") ||
895
+ errorMessage.includes("Failed to fetch")) {
896
+ return {
897
+ success: false,
898
+ message: "Could not reach template server. Check your network connection.",
899
+ };
900
+ }
901
+ return {
902
+ success: false,
903
+ message: `Download failed: ${errorMessage}`,
904
+ };
905
+ }
906
+ }
907
+ //# sourceMappingURL=template-fetcher.js.map