bsmnt 0.4.3 → 0.6.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 (266) hide show
  1. package/README.md +1 -0
  2. package/dist/application/add-hooks/index.d.ts.map +1 -1
  3. package/dist/application/add-hooks/index.js +1 -0
  4. package/dist/application/add-hooks/index.js.map +1 -1
  5. package/dist/application/add-integration/index.d.ts +33 -8
  6. package/dist/application/add-integration/index.d.ts.map +1 -1
  7. package/dist/application/add-integration/index.js +221 -90
  8. package/dist/application/add-integration/index.js.map +1 -1
  9. package/dist/application/create-project/build-context.d.ts +3 -1
  10. package/dist/application/create-project/build-context.d.ts.map +1 -1
  11. package/dist/application/create-project/build-context.js +2 -1
  12. package/dist/application/create-project/build-context.js.map +1 -1
  13. package/dist/application/create-project/index.d.ts +3 -1
  14. package/dist/application/create-project/index.d.ts.map +1 -1
  15. package/dist/application/create-project/index.js +4 -2
  16. package/dist/application/create-project/index.js.map +1 -1
  17. package/dist/application/create-project/map-selection-to-registry.js +1 -1
  18. package/dist/application/create-project/map-selection-to-registry.js.map +1 -1
  19. package/dist/application/install-skills/index.js +1 -1
  20. package/dist/application/install-skills/index.js.map +1 -1
  21. package/dist/application/shared/project-detection.d.ts +2 -2
  22. package/dist/application/shared/project-detection.d.ts.map +1 -1
  23. package/dist/application/shared/project-detection.js +2 -2
  24. package/dist/application/shared/project-detection.js.map +1 -1
  25. package/dist/core/create/execute-plan.d.ts +4 -0
  26. package/dist/core/create/execute-plan.d.ts.map +1 -1
  27. package/dist/core/create/execute-plan.js +8 -0
  28. package/dist/core/create/execute-plan.js.map +1 -1
  29. package/dist/core/create/internal-ids.d.ts +4 -1
  30. package/dist/core/create/internal-ids.d.ts.map +1 -1
  31. package/dist/core/create/internal-ids.js +6 -1
  32. package/dist/core/create/internal-ids.js.map +1 -1
  33. package/dist/core/create/types.d.ts +2 -0
  34. package/dist/core/create/types.d.ts.map +1 -1
  35. package/dist/domain/cms.d.ts +0 -7
  36. package/dist/domain/cms.d.ts.map +1 -1
  37. package/dist/domain/cms.js.map +1 -1
  38. package/dist/domain/forms.d.ts +17 -0
  39. package/dist/domain/forms.d.ts.map +1 -0
  40. package/dist/domain/forms.js +13 -0
  41. package/dist/domain/forms.js.map +1 -0
  42. package/dist/domain/integration.d.ts +30 -0
  43. package/dist/domain/integration.d.ts.map +1 -0
  44. package/dist/domain/integration.js +12 -0
  45. package/dist/domain/integration.js.map +1 -0
  46. package/dist/domain/skills.d.ts +58 -0
  47. package/dist/domain/skills.d.ts.map +1 -1
  48. package/dist/domain/skills.js +52 -0
  49. package/dist/domain/skills.js.map +1 -1
  50. package/dist/index.js +114 -38
  51. package/dist/index.js.map +1 -1
  52. package/dist/infrastructure/agents/register-sanity-mcp.js +9 -6
  53. package/dist/infrastructure/agents/register-sanity-mcp.js.map +1 -1
  54. package/dist/infrastructure/create/apply-package-json.d.ts.map +1 -1
  55. package/dist/infrastructure/create/apply-package-json.js +19 -0
  56. package/dist/infrastructure/create/apply-package-json.js.map +1 -1
  57. package/dist/infrastructure/create/executor.d.ts.map +1 -1
  58. package/dist/infrastructure/create/executor.js +5 -0
  59. package/dist/infrastructure/create/executor.js.map +1 -1
  60. package/dist/infrastructure/create/install-dependencies.d.ts.map +1 -1
  61. package/dist/infrastructure/create/install-dependencies.js +4 -0
  62. package/dist/infrastructure/create/install-dependencies.js.map +1 -1
  63. package/dist/infrastructure/create/lifecycle-registry.d.ts.map +1 -1
  64. package/dist/infrastructure/create/lifecycle-registry.js +2 -0
  65. package/dist/infrastructure/create/lifecycle-registry.js.map +1 -1
  66. package/dist/infrastructure/create/merge-registry.d.ts.map +1 -1
  67. package/dist/infrastructure/create/merge-registry.js +8 -0
  68. package/dist/infrastructure/create/merge-registry.js.map +1 -1
  69. package/dist/infrastructure/create/setup-agent.d.ts +2 -0
  70. package/dist/infrastructure/create/setup-agent.d.ts.map +1 -1
  71. package/dist/infrastructure/create/setup-agent.js +32 -8
  72. package/dist/infrastructure/create/setup-agent.js.map +1 -1
  73. package/dist/infrastructure/create/setup-dotenvx.d.ts +41 -0
  74. package/dist/infrastructure/create/setup-dotenvx.d.ts.map +1 -0
  75. package/dist/infrastructure/create/setup-dotenvx.js +502 -0
  76. package/dist/infrastructure/create/setup-dotenvx.js.map +1 -0
  77. package/dist/infrastructure/create/setup-sanity.d.ts +13 -1
  78. package/dist/infrastructure/create/setup-sanity.d.ts.map +1 -1
  79. package/dist/infrastructure/create/setup-sanity.js +74 -31
  80. package/dist/infrastructure/create/setup-sanity.js.map +1 -1
  81. package/dist/infrastructure/integrations/inject-integration.d.ts +3 -3
  82. package/dist/infrastructure/integrations/inject-integration.d.ts.map +1 -1
  83. package/dist/infrastructure/integrations/inject-integration.js +81 -37
  84. package/dist/infrastructure/integrations/inject-integration.js.map +1 -1
  85. package/dist/modules/features/cms/sanity/config.d.ts.map +1 -1
  86. package/dist/modules/features/cms/sanity/config.js +13 -6
  87. package/dist/modules/features/cms/sanity/config.js.map +1 -1
  88. package/dist/modules/features/cms/sanity/index.js +8 -2
  89. package/dist/modules/features/cms/sanity/index.js.map +1 -1
  90. package/dist/modules/features/cms/sanity/mergers/ci-workflow.d.ts +13 -0
  91. package/dist/modules/features/cms/sanity/mergers/ci-workflow.d.ts.map +1 -0
  92. package/dist/modules/features/cms/sanity/mergers/ci-workflow.js +38 -0
  93. package/dist/modules/features/cms/sanity/mergers/ci-workflow.js.map +1 -0
  94. package/dist/modules/features/cms/sanity/mergers/knip.d.ts +12 -0
  95. package/dist/modules/features/cms/sanity/mergers/knip.d.ts.map +1 -0
  96. package/dist/modules/features/cms/sanity/mergers/knip.js +38 -0
  97. package/dist/modules/features/cms/sanity/mergers/knip.js.map +1 -0
  98. package/dist/modules/features/cms/sanity/mergers/layout.d.ts.map +1 -1
  99. package/dist/modules/features/cms/sanity/mergers/layout.js +17 -3
  100. package/dist/modules/features/cms/sanity/mergers/layout.js.map +1 -1
  101. package/dist/modules/features/cms/sanity/mergers/sitemap.d.ts +3 -3
  102. package/dist/modules/features/cms/sanity/mergers/sitemap.d.ts.map +1 -1
  103. package/dist/modules/features/cms/sanity/mergers/sitemap.js +48 -62
  104. package/dist/modules/features/cms/sanity/mergers/sitemap.js.map +1 -1
  105. package/dist/modules/features/cms/sanity-pagebuilder/config.d.ts.map +1 -1
  106. package/dist/modules/features/cms/sanity-pagebuilder/config.js +8 -5
  107. package/dist/modules/features/cms/sanity-pagebuilder/config.js.map +1 -1
  108. package/dist/modules/features/cms/sanity-pagebuilder/index.js +2 -2
  109. package/dist/modules/features/cms/sanity-pagebuilder/index.js.map +1 -1
  110. package/dist/modules/features/env/dotenvx/config.d.ts +13 -0
  111. package/dist/modules/features/env/dotenvx/config.d.ts.map +1 -0
  112. package/dist/modules/features/env/dotenvx/config.js +47 -0
  113. package/dist/modules/features/env/dotenvx/config.js.map +1 -0
  114. package/dist/modules/features/forms/hubspot/config.d.ts +31 -0
  115. package/dist/modules/features/forms/hubspot/config.d.ts.map +1 -0
  116. package/dist/modules/features/forms/hubspot/config.js +25 -0
  117. package/dist/modules/features/forms/hubspot/config.js.map +1 -0
  118. package/dist/paths.d.ts +16 -0
  119. package/dist/paths.d.ts.map +1 -1
  120. package/dist/paths.js +16 -0
  121. package/dist/paths.js.map +1 -1
  122. package/package.json +21 -9
  123. package/src/agent-skills/json-ld/SKILL.md +85 -0
  124. package/src/agent-skills/manage-env/SKILL.md +67 -0
  125. package/src/assets/check-node-version.mjs +22 -0
  126. package/src/modules/features/cms/sanity/files/app/blog/[slug]/page.tsx +48 -21
  127. package/src/modules/features/cms/sanity/files/app/blog/page.tsx +54 -0
  128. package/src/modules/features/cms/sanity/files/app/feed.xml/route.ts +36 -0
  129. package/src/modules/features/cms/sanity/files/components/sanity/structured-data.tsx +83 -0
  130. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/components/disable-draft-mode.tsx +3 -3
  131. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/queries.ts +14 -0
  132. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/sanity.config.ts +19 -3
  133. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/sanity.types.ts +1 -0
  134. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/schemas/article.ts +7 -7
  135. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/schemas/index.ts +36 -1
  136. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/schemas/metadata.ts +12 -1
  137. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/schemas/structured-data-blocks.ts +416 -0
  138. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/schemas/structured-data.ts +100 -0
  139. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/structure.ts +37 -2
  140. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/utils/page-json-ld.ts +208 -0
  141. package/src/modules/features/cms/sanity/files/lib/scripts/setup-ci-secrets.ts +117 -0
  142. package/src/modules/features/cms/sanity/files/lib/utils/rss.ts +96 -0
  143. package/src/modules/features/cms/sanity-pagebuilder/files/.knip.json +8 -0
  144. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/[[...slug]]/route.ts +100 -0
  145. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/draft-mode/disable/route.ts +7 -0
  146. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/draft-mode/enable/route.ts +20 -0
  147. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/revalidate/route.ts +126 -0
  148. package/src/modules/features/cms/sanity-pagebuilder/files/app/feed.xml/route.ts +40 -0
  149. package/src/modules/features/cms/sanity-pagebuilder/files/app/sitemap.md/route.ts +124 -0
  150. package/src/modules/features/cms/sanity-pagebuilder/files/app/studio/[[...tool]]/page.tsx +8 -0
  151. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/draft-mode-toggle.tsx +27 -0
  152. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/rich-text.tsx +87 -0
  153. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/visual-editing.tsx +28 -0
  154. package/src/modules/features/cms/sanity-pagebuilder/files/components/ui/sanity-image/index.tsx +41 -0
  155. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/check-integration.ts +5 -0
  156. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/client.ts +27 -0
  157. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/disable-draft-mode.tsx +23 -0
  158. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-builder-input.tsx +39 -0
  159. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-category-input.tsx +50 -0
  160. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/same-as-input.tsx +66 -0
  161. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-field.tsx +18 -0
  162. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-input.tsx +14 -0
  163. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/confirm-publish-action.ts +40 -0
  164. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/env.ts +34 -0
  165. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/layout.ts +67 -0
  166. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/redirects.ts +17 -0
  167. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/icons.ts +79 -0
  168. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/live/index.tsx +88 -0
  169. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/markdown-proxy.config.ts +50 -0
  170. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-builder-config.ts +130 -0
  171. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-category.ts +28 -0
  172. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-tree-pane.tsx +420 -0
  173. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/pinned-pages.ts +54 -0
  174. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/presentation.ts +119 -0
  175. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/queries.ts +407 -0
  176. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/sanity.cli.ts +29 -0
  177. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/sanity.config.ts +113 -0
  178. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/index.ts +4 -0
  179. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/blog-content.ts +89 -0
  180. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/description.ts +29 -0
  181. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/hero.ts +28 -0
  182. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/singleton/content-collection.ts +45 -0
  183. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/content/author.ts +70 -0
  184. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/content/blog-category.ts +55 -0
  185. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/index.ts +124 -0
  186. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/company-data.ts +70 -0
  187. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/footer.ts +79 -0
  188. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/navbar.ts +75 -0
  189. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/redirect.ts +120 -0
  190. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/structured-data.ts +124 -0
  191. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/link.ts +125 -0
  192. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/logo-field.ts +9 -0
  193. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/metadata.ts +79 -0
  194. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/nav-objects.ts +192 -0
  195. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page-builder.ts +39 -0
  196. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page-folder.ts +124 -0
  197. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page.ts +242 -0
  198. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/richText.ts +63 -0
  199. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/structured-data-blocks.ts +436 -0
  200. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/singletons.ts +51 -0
  201. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/structure.ts +330 -0
  202. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/image.ts +8 -0
  203. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/link.ts +140 -0
  204. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/page-builder-markdown.ts +81 -0
  205. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/page-json-ld.ts +208 -0
  206. package/src/modules/features/cms/sanity-pagebuilder/files/lib/scripts/sanity-typegen.ts +45 -0
  207. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/check-redirect.ts +30 -0
  208. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/json-ld.tsx +521 -0
  209. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/metadata.ts +167 -0
  210. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/rss.ts +96 -0
  211. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/sitemap.ts +40 -0
  212. package/src/modules/features/cms/sanity-pagebuilder/files/proxy.ts +81 -0
  213. package/src/modules/features/env/dotenvx/files/decrypt-env.mjs +45 -0
  214. package/src/modules/features/env/dotenvx/files/gitleaks.yml +25 -0
  215. package/src/modules/features/env/dotenvx/files/instrumentation.ts +14 -0
  216. package/src/modules/features/env/dotenvx/files/push-keys.mjs +129 -0
  217. package/src/modules/features/env/dotenvx/files/save-key.mjs +108 -0
  218. package/src/modules/features/env/dotenvx/files/setup-env.mjs +67 -0
  219. package/src/modules/features/env/dotenvx/files/vercelignore +4 -0
  220. package/src/modules/features/env/dotenvx/files/write-env.mjs +47 -0
  221. package/src/templates/next-default/.env.example +14 -6
  222. package/src/templates/next-default/.github/workflows/ci.yml +83 -0
  223. package/src/templates/next-default/.vscode/extensions.json +3 -0
  224. package/src/templates/next-default/README.md +53 -0
  225. package/src/templates/next-default/knip.json +14 -0
  226. package/src/templates/next-default/lefthook.yml +5 -0
  227. package/src/templates/next-default/lib/utils/json-ld.tsx +327 -21
  228. package/src/templates/next-default/next.config.ts +6 -0
  229. package/src/templates/next-default/package.json +8 -4
  230. package/src/templates/next-experiments/.env.example +14 -6
  231. package/src/templates/next-experiments/.github/workflows/ci.yml +83 -0
  232. package/src/templates/next-experiments/.vscode/extensions.json +3 -0
  233. package/src/templates/next-experiments/README.md +53 -0
  234. package/src/templates/next-experiments/knip.json +14 -0
  235. package/src/templates/next-experiments/lefthook.yml +30 -0
  236. package/src/templates/next-experiments/lib/utils/json-ld.tsx +327 -21
  237. package/src/templates/next-experiments/next.config.ts +6 -0
  238. package/src/templates/next-experiments/package.json +10 -2
  239. package/src/templates/next-pagebuilder/.env.example +19 -1
  240. package/src/templates/next-pagebuilder/.github/workflows/ci.yml +90 -0
  241. package/src/templates/next-pagebuilder/.vscode/extensions.json +3 -0
  242. package/src/templates/next-pagebuilder/README.md +65 -0
  243. package/src/templates/next-pagebuilder/app/(content)/[[...slug]]/page.tsx +6 -1
  244. package/src/templates/next-pagebuilder/app/(content)/layout.tsx +3 -12
  245. package/src/templates/next-pagebuilder/components/layout/footer/index.tsx +11 -10
  246. package/src/templates/next-pagebuilder/components/layout/header/index.tsx +5 -3
  247. package/src/templates/next-pagebuilder/components/layout/json-ld/index.tsx +38 -26
  248. package/src/templates/next-pagebuilder/components/layout/wrapper/index.tsx +3 -11
  249. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/index.tsx +1 -1
  250. package/src/templates/next-pagebuilder/components/page-document/index.tsx +38 -0
  251. package/src/templates/next-pagebuilder/knip.json +27 -0
  252. package/src/templates/next-pagebuilder/lefthook.yml +5 -0
  253. package/src/templates/next-pagebuilder/lib/integrations/sanity/sanity.types.ts +36 -2
  254. package/src/templates/next-pagebuilder/lib/scripts/setup-ci-secrets.ts +117 -0
  255. package/src/templates/next-pagebuilder/next.config.ts +6 -0
  256. package/src/templates/next-pagebuilder/package.json +9 -3
  257. package/src/templates/next-webgl/.env.example +14 -6
  258. package/src/templates/next-webgl/.github/workflows/ci.yml +83 -0
  259. package/src/templates/next-webgl/.vscode/extensions.json +3 -0
  260. package/src/templates/next-webgl/README.md +53 -0
  261. package/src/templates/next-webgl/knip.json +13 -0
  262. package/src/templates/next-webgl/lefthook.yml +5 -0
  263. package/src/templates/next-webgl/lib/utils/json-ld.tsx +327 -21
  264. package/src/templates/next-webgl/next.config.ts +6 -0
  265. package/src/templates/next-webgl/package.json +8 -4
  266. package/src/modules/features/cms/sanity/files/lib/utils/json-ld.tsx +0 -244
@@ -1,11 +1,13 @@
1
1
  import { execSync } from "node:child_process";
2
2
  import path from "node:path";
3
3
  import fs from "fs-extra";
4
+ import ora from "ora";
4
5
  import prompts from "prompts";
5
6
  import { AGENTS } from "../../domain/agents.js";
6
7
  import { CMS } from "../../domain/cms.js";
7
- import { BASE_SKILL_IDS, buildSkillInstallCommand, } from "../../domain/skills.js";
8
+ import { BASE_SKILL_IDS, buildSkillInstallCommand, LOCAL_SKILLS, SKILLS, } from "../../domain/skills.js";
8
9
  import { TEMPLATES } from "../../domain/templates.js";
10
+ import { AGENT_SKILLS_DIR } from "../../paths.js";
9
11
  import { registerSanityMcp } from "../agents/register-sanity-mcp.js";
10
12
  /**
11
13
  * Set up AI agent skills and configuration in the target project.
@@ -21,12 +23,17 @@ export async function setupAgent(targetDir, options) {
21
23
  const selectedAgents = [...new Set(options.agents)];
22
24
  if (selectedAgents.length === 0)
23
25
  return;
24
- // Create skills directories for each agent
26
+ // Create skills directories for each agent and copy locally shipped skills
25
27
  for (const agent of selectedAgents) {
26
28
  const { folder: agentFolder } = AGENTS[agent];
27
29
  const skillsPath = path.join(targetDir, agentFolder, "skills");
28
30
  await fs.ensureDir(skillsPath);
29
31
  await fs.writeFile(path.join(skillsPath, ".gitkeep"), "");
32
+ for (const skill of Object.values(LOCAL_SKILLS)) {
33
+ if (skill.dotenvxOnly && !options.dotenvx)
34
+ continue;
35
+ await fs.copy(path.join(AGENT_SKILLS_DIR, skill.dir), path.join(skillsPath, skill.dir));
36
+ }
30
37
  }
31
38
  const skillIds = new Set(BASE_SKILL_IDS);
32
39
  if (options.templateType !== null) {
@@ -34,14 +41,24 @@ export async function setupAgent(targetDir, options) {
34
41
  skillIds.add(skillId);
35
42
  }
36
43
  }
37
- if (options.cms !== null) {
38
- for (const skillId of CMS[options.cms].skills) {
44
+ // The page-builder preset bundles Sanity even though `cms` is null, so treat
45
+ // it as a Sanity project for skills + MCP setup.
46
+ const hasSanity = options.cms === "sanity" || options.templateType === "next-pagebuilder";
47
+ if (hasSanity) {
48
+ for (const skillId of CMS.sanity.skills) {
39
49
  skillIds.add(skillId);
40
50
  }
41
51
  }
52
+ const skillList = [...skillIds];
53
+ const totalInstalls = selectedAgents.length * skillList.length;
54
+ const failed = [];
55
+ let installed = 0;
56
+ const spinner = ora("Installing agent skills...").start();
42
57
  for (const agent of selectedAgents) {
43
- const { cliArg: agentArg } = AGENTS[agent];
44
- for (const skillId of skillIds) {
58
+ const { cliArg: agentArg, label: agentLabel } = AGENTS[agent];
59
+ for (const skillId of skillList) {
60
+ installed += 1;
61
+ spinner.text = `Installing agent skills: ${SKILLS[skillId].name} for ${agentLabel} (${installed}/${totalInstalls})`;
45
62
  const command = buildSkillInstallCommand(skillId, {
46
63
  agentArg,
47
64
  });
@@ -53,10 +70,17 @@ export async function setupAgent(targetDir, options) {
53
70
  }
54
71
  catch {
55
72
  // Non-fatal: skills install may fail due to network issues
73
+ failed.push(`${SKILLS[skillId].name} (${agentLabel})`);
56
74
  }
57
75
  }
58
76
  }
59
- if (options.cms === "sanity" && selectedAgents.length > 0) {
77
+ if (failed.length > 0) {
78
+ spinner.warn(`Agent skills installed, ${failed.length} skipped (network): ${failed.join(", ")}`);
79
+ }
80
+ else {
81
+ spinner.succeed("Agent skills installed.");
82
+ }
83
+ if (hasSanity) {
60
84
  const { mcpAgents } = (await prompts({
61
85
  type: "multiselect",
62
86
  name: "mcpAgents",
@@ -67,7 +91,7 @@ export async function setupAgent(targetDir, options) {
67
91
  selected: true,
68
92
  })),
69
93
  min: 1,
70
- hint: "Space to select, Enter to confirm",
94
+ instructions: false,
71
95
  }));
72
96
  if (mcpAgents && mcpAgents.length > 0) {
73
97
  await registerSanityMcp(targetDir, mcpAgents);
@@ -1 +1 @@
1
- {"version":3,"file":"setup-agent.js","sourceRoot":"","sources":["../../../src/infrastructure/create/setup-agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"setup-agent.js","sourceRoot":"","sources":["../../../src/infrastructure/create/setup-agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /** Wrap runtime scripts with `dotenvx run --`. Skips already-wrapped scripts. */
2
+ export declare function wrapDotenvxScripts(scripts: Record<string, string>): Record<string, string>;
3
+ /**
4
+ * Chain dotenvx's onboarding restore after any preinstall the scaffold already
5
+ * owns (e.g. the Node-version guard) instead of replacing it. Idempotent: a
6
+ * preinstall that already runs setup-env is returned untouched.
7
+ */
8
+ export declare function composePreinstall(existing: string | undefined): string;
9
+ export declare function applyGitignoreRules(content: string): string;
10
+ export declare function applyDotenvxArtifacts(targetDir: string): Promise<void>;
11
+ /** Parse `KEY=value` assignments from a `.env`-style file (ignores comments). */
12
+ export declare function parseEnvKeys(content: string): Set<string>;
13
+ /** `KEY=value` lines with a non-empty value (multiline-aware; via dotenv parser). */
14
+ export declare function nonEmptyAssignments(content: string): {
15
+ key: string;
16
+ line: string;
17
+ }[];
18
+ export declare function isEncryptableSecret(key: string): boolean;
19
+ export type NextConfigTracingStatus = "patched" | "present" | "manual";
20
+ /**
21
+ * Decide whether/how to add `outputFileTracingIncludes` to next.config source.
22
+ * Pure: returns the (possibly rewritten) content and a status — "present" if it
23
+ * already has the key, "manual" if the shape can't be safely extended.
24
+ */
25
+ export declare function patchNextConfigContent(content: string): {
26
+ status: NextConfigTracingStatus;
27
+ content: string;
28
+ };
29
+ /**
30
+ * Add `outputFileTracingIncludes` to an existing `next.config` so the encrypted
31
+ * `.env*` is readable by `instrumentation.ts` at runtime on Vercel. Best-effort:
32
+ * returns "manual" (caller should print the snippet) when no config is found or
33
+ * its shape can't be safely extended.
34
+ */
35
+ export declare function ensureNextConfigTracing(targetDir: string): Promise<{
36
+ status: NextConfigTracingStatus;
37
+ file: string | null;
38
+ }>;
39
+ /** Migrate plaintext, encrypt `.env`, print the key + next steps. Idempotent. */
40
+ export declare function setupDotenvx(targetDir: string): Promise<boolean>;
41
+ //# sourceMappingURL=setup-dotenvx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-dotenvx.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/create/setup-dotenvx.ts"],"names":[],"mappings":"AAyCA,iFAAiF;AACjF,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAsBxB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAMtE;AA8ED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAc3D;AAoFD,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK5E;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAEzD;AAgBD,qFAAqF;AACrF,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,MAAM,GACb;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAOjC;AAkED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD;AAgCD,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEvE;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG;IACxD,MAAM,EAAE,uBAAuB,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;CAChB,CAcA;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC5C,SAAS,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,MAAM,EAAE,uBAAuB,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAYnE;AAsJD,iFAAiF;AACjF,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAsBtE"}
@@ -0,0 +1,502 @@
1
+ import { execFileSync, execSync } from "node:child_process";
2
+ import { randomBytes } from "node:crypto";
3
+ import path from "node:path";
4
+ import { parse as parseDotenv } from "dotenv";
5
+ import fs from "fs-extra";
6
+ import ora from "ora";
7
+ import pc from "picocolors";
8
+ import prompts from "prompts";
9
+ import { dotenvxConfig } from "../../modules/features/env/dotenvx/config.js";
10
+ import { DOTENVX_FILES_DIR } from "../../paths.js";
11
+ // Env file first (first-match wins), then the shared `.env` base. VERCEL_ENV is
12
+ // set by Vercel; unset locally → `.env.` is skipped, leaving the base.
13
+ const DOTENVX_RUN_FILES = "-f .env.$VERCEL_ENV -f .env";
14
+ const DOTENVX_RUN_PREFIX = `dotenvx run ${DOTENVX_RUN_FILES} -- `;
15
+ // Compound commands (`a && b`, `a; b`, `cd … && …`) must run through `sh -c`,
16
+ // else only the first command inherits the decrypted env and the rest run bare.
17
+ const COMPOUND_COMMAND_RE = /\bcd\s|&&|\|\||;/;
18
+ function wrapWithDotenvx(command) {
19
+ if (COMPOUND_COMMAND_RE.test(command)) {
20
+ return `dotenvx run ${DOTENVX_RUN_FILES} -- sh -c '${command.replaceAll("'", "'\\''")}'`;
21
+ }
22
+ return DOTENVX_RUN_PREFIX + command;
23
+ }
24
+ // dotenvx injects decrypted env, so drop the now-redundant `--env-file`.
25
+ function wrapSanityScript(value) {
26
+ if (value.startsWith("dotenvx run"))
27
+ return value;
28
+ return wrapWithDotenvx(value.replaceAll(" --env-file ../../../.env.local", ""));
29
+ }
30
+ /** Wrap runtime scripts with `dotenvx run --`. Skips already-wrapped scripts. */
31
+ export function wrapDotenvxScripts(scripts) {
32
+ const next = { ...scripts };
33
+ for (const key of dotenvxConfig.wrapScripts) {
34
+ const value = next[key];
35
+ if (typeof value === "string" &&
36
+ value.length > 0 &&
37
+ !value.startsWith("dotenvx run")) {
38
+ next[key] = wrapWithDotenvx(value);
39
+ }
40
+ }
41
+ for (const key of dotenvxConfig.sanityScripts) {
42
+ const value = next[key];
43
+ if (typeof value === "string" && value.length > 0) {
44
+ next[key] = wrapSanityScript(value);
45
+ }
46
+ }
47
+ return next;
48
+ }
49
+ /**
50
+ * Chain dotenvx's onboarding restore after any preinstall the scaffold already
51
+ * owns (e.g. the Node-version guard) instead of replacing it. Idempotent: a
52
+ * preinstall that already runs setup-env is returned untouched.
53
+ */
54
+ export function composePreinstall(existing) {
55
+ const onboarding = dotenvxConfig.addScripts.preinstall;
56
+ if (!onboarding)
57
+ return existing ?? "";
58
+ if (!existing || existing === onboarding)
59
+ return onboarding;
60
+ if (existing.includes("scripts/setup-env.mjs"))
61
+ return existing;
62
+ return `${existing} && ${onboarding}`;
63
+ }
64
+ /** Add the dotenvx dependency, wrap scripts, and add the `env:setup` script. */
65
+ async function applyPackageJson(targetDir) {
66
+ const pkgPath = path.join(targetDir, "package.json");
67
+ if (!fs.existsSync(pkgPath))
68
+ return;
69
+ const pkg = (await fs.readJson(pkgPath));
70
+ // Capture the preinstall the scaffold set (e.g. the Node-version guard)
71
+ // before the merge below, so chaining ours onto it doesn't drop it.
72
+ const existingPreinstall = pkg.scripts?.preinstall;
73
+ pkg.dependencies = { ...dotenvxConfig.dependencies, ...pkg.dependencies };
74
+ pkg.scripts = {
75
+ ...dotenvxConfig.addScripts,
76
+ ...wrapDotenvxScripts(pkg.scripts ?? {}),
77
+ };
78
+ pkg.scripts.preinstall = composePreinstall(existingPreinstall);
79
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
80
+ }
81
+ // Printed when a project already has its own instrumentation.ts: we won't touch
82
+ // it, but without this call serverless functions on Vercel never decrypt.
83
+ const INSTRUMENTATION_SNIPPET = `export async function register() {
84
+ if (process.env.NEXT_RUNTIME !== "nodejs") return
85
+ const { config } = await import("@dotenvx/dotenvx")
86
+ const vercelEnv = process.env.VERCEL_ENV
87
+ config({
88
+ path: vercelEnv ? [".env", \`.env.\${vercelEnv}\`] : [".env"],
89
+ overload: true,
90
+ ignore: ["MISSING_ENV_FILE"],
91
+ })
92
+ }`;
93
+ function warnExistingInstrumentation(content) {
94
+ if (content.includes("@dotenvx/dotenvx"))
95
+ return; // already wired
96
+ console.log(pc.yellow("\n ⚠ instrumentation.ts already exists — left untouched. Without dotenvx's"));
97
+ console.log(pc.yellow(" register() your encrypted .env won't decrypt in Vercel serverless functions."));
98
+ console.log(pc.dim(" Add this to your register() (Node runtime only):\n"));
99
+ console.log(INSTRUMENTATION_SNIPPET.split("\n")
100
+ .map((line) => ` ${line}`)
101
+ .join("\n"));
102
+ console.log();
103
+ }
104
+ /** Copy the gitleaks workflow + helper scripts (skip if already present). */
105
+ async function copyArtifacts(targetDir) {
106
+ for (const [dest, source] of Object.entries(dotenvxConfig.files)) {
107
+ const destPath = path.join(targetDir, dest);
108
+ if (fs.existsSync(destPath)) {
109
+ // Silently skipping our scripts is fine, but a pre-existing
110
+ // instrumentation.ts means dotenvx never gets wired — flag it.
111
+ if (dest === "instrumentation.ts") {
112
+ warnExistingInstrumentation(await fs.readFile(destPath, "utf-8"));
113
+ }
114
+ continue;
115
+ }
116
+ await fs.ensureDir(path.dirname(destPath));
117
+ await fs.copy(path.join(DOTENVX_FILES_DIR, source), destPath);
118
+ }
119
+ }
120
+ // Commit the encrypted `.env`: drop the exact lines that ignore it, re-ensure the
121
+ // private keys and plaintext local files stay ignored, then append a `!.env`
122
+ // negation so broader globs an existing project may use (`**/.env`, `*.env`)
123
+ // can't keep it ignored — the negation is last, so it wins. Pure.
124
+ export function applyGitignoreRules(content) {
125
+ const ignoresEnv = new Set([".env", "/.env", ".env*", "/.env*"]);
126
+ const kept = content
127
+ .split("\n")
128
+ .filter((line) => !ignoresEnv.has(line.trim()));
129
+ const present = new Set(kept.map((line) => line.trim()));
130
+ for (const entry of [".env.keys", ".env.local", ".env*.local", "!.env"]) {
131
+ if (!present.has(entry))
132
+ kept.push(entry);
133
+ }
134
+ let next = kept.join("\n");
135
+ if (!next.endsWith("\n"))
136
+ next += "\n";
137
+ return next;
138
+ }
139
+ async function ensureGitignore(targetDir) {
140
+ const gitignorePath = path.join(targetDir, ".gitignore");
141
+ const content = fs.existsSync(gitignorePath)
142
+ ? await fs.readFile(gitignorePath, "utf-8")
143
+ : "";
144
+ await fs.writeFile(gitignorePath, applyGitignoreRules(content));
145
+ }
146
+ // gitleaks (a Go binary, not an npm dep) must be on PATH; the hook fails with an
147
+ // install hint if missing. Flags only real secrets, so plaintext vars commit fine.
148
+ const GITLEAKS_HOOK_RUN = 'command -v gitleaks >/dev/null || { echo "✖ gitleaks required — install: https://github.com/gitleaks/gitleaks#installing"; exit 1; }; gitleaks git --pre-commit --redact --staged --verbose';
149
+ /** Add the gitleaks secret scan to lefthook's pre-commit stage. */
150
+ async function ensureGitleaksHook(targetDir) {
151
+ const lefthookPath = path.join(targetDir, "lefthook.yml");
152
+ const hook = [" gitleaks:", ` run: '${GITLEAKS_HOOK_RUN}'`];
153
+ if (!fs.existsSync(lefthookPath)) {
154
+ await fs.writeFile(lefthookPath, `pre-commit:\n commands:\n${hook.join("\n")}\n`);
155
+ return;
156
+ }
157
+ const content = await fs.readFile(lefthookPath, "utf-8");
158
+ if (content.includes("gitleaks git --pre-commit"))
159
+ return; // idempotent
160
+ const lines = content.split("\n");
161
+ const preCommitIdx = lines.findIndex((l) => l.trimEnd() === "pre-commit:");
162
+ if (preCommitIdx === -1) {
163
+ const suffix = content.endsWith("\n") ? "" : "\n";
164
+ await fs.writeFile(lefthookPath, `${content}${suffix}\npre-commit:\n commands:\n${hook.join("\n")}\n`);
165
+ return;
166
+ }
167
+ // Find `commands:` inside the pre-commit section (before the next top-level key).
168
+ let commandsIdx = -1;
169
+ for (let i = preCommitIdx + 1; i < lines.length; i++) {
170
+ const line = lines[i] ?? "";
171
+ if (/^\S/.test(line))
172
+ break;
173
+ if (line.trim() === "commands:") {
174
+ commandsIdx = i;
175
+ break;
176
+ }
177
+ }
178
+ if (commandsIdx === -1) {
179
+ lines.splice(preCommitIdx + 1, 0, " commands:", ...hook);
180
+ }
181
+ else {
182
+ lines.splice(commandsIdx + 1, 0, ...hook);
183
+ }
184
+ await fs.writeFile(lefthookPath, lines.join("\n"));
185
+ }
186
+ const OP_REF_BLOCK = `# 1Password ref to .env.keys for \`bun run env:setup\`. Format: op://Development/<item>/<field>
187
+ DOTENV_PRIVATE_KEY_OP_REF=
188
+ `;
189
+ // Seed the committed `.env` with the shared 1Password pointer so `env:setup`
190
+ // finds it (Bun auto-loads .env). Kept plaintext by runEncrypt's --exclude-key,
191
+ // so it's readable without the keys it points to. Non-destructive + idempotent.
192
+ async function ensureEnvOpRef(targetDir) {
193
+ const envPath = path.join(targetDir, ".env");
194
+ if (!fs.existsSync(envPath)) {
195
+ await fs.writeFile(envPath, OP_REF_BLOCK);
196
+ return;
197
+ }
198
+ const content = await fs.readFile(envPath, "utf-8");
199
+ if (content.includes("DOTENV_PRIVATE_KEY_OP_REF"))
200
+ return;
201
+ const sep = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
202
+ await fs.appendFile(envPath, `${sep}\n${OP_REF_BLOCK}`);
203
+ }
204
+ // Idempotent; shared by the create flow and `bsmnt integrate --dotenvx` so the
205
+ // generated output never drifts between the two.
206
+ export async function applyDotenvxArtifacts(targetDir) {
207
+ await applyPackageJson(targetDir);
208
+ await copyArtifacts(targetDir);
209
+ await ensureGitignore(targetDir);
210
+ await ensureGitleaksHook(targetDir);
211
+ }
212
+ /** Parse `KEY=value` assignments from a `.env`-style file (ignores comments). */
213
+ export function parseEnvKeys(content) {
214
+ return new Set(Object.keys(parseDotenv(content)));
215
+ }
216
+ // Re-serialize a parsed value into a `.env` line. Double-quote (escaping `\`, `"`
217
+ // and newlines) when the value spans lines or has whitespace edges, so multiline
218
+ // secrets like PEM keys survive the round-trip instead of being truncated.
219
+ function serializeEnvValue(value) {
220
+ if (/[\n"\\]/.test(value) || value.trim() !== value) {
221
+ const escaped = value
222
+ .replaceAll("\\", "\\\\")
223
+ .replaceAll('"', '\\"')
224
+ .replaceAll("\n", "\\n");
225
+ return `"${escaped}"`;
226
+ }
227
+ return value;
228
+ }
229
+ /** `KEY=value` lines with a non-empty value (multiline-aware; via dotenv parser). */
230
+ export function nonEmptyAssignments(content) {
231
+ const out = [];
232
+ for (const [key, value] of Object.entries(parseDotenv(content))) {
233
+ if (value === "")
234
+ continue;
235
+ out.push({ key, line: `${key}=${serializeEnvValue(value)}` });
236
+ }
237
+ return out;
238
+ }
239
+ // Copy real `.env.local` values into `.env` for encryption without clobbering
240
+ // existing keys; `.env.local` stays as plaintext local overrides.
241
+ async function migratePlaintextEnv(targetDir) {
242
+ const envPath = path.join(targetDir, ".env");
243
+ const localPath = path.join(targetDir, ".env.local");
244
+ if (!fs.existsSync(localPath))
245
+ return;
246
+ const envContent = fs.existsSync(envPath)
247
+ ? await fs.readFile(envPath, "utf-8")
248
+ : "";
249
+ const existingKeys = parseEnvKeys(envContent);
250
+ const localContent = await fs.readFile(localPath, "utf-8");
251
+ const linesToAdd = nonEmptyAssignments(localContent)
252
+ // `DOTENV_*` are dotenvx's own keys/pointers (e.g. the op ref) — never
253
+ // encrypt them into the committed `.env`.
254
+ .filter(({ key }) => !existingKeys.has(key) && !key.startsWith("DOTENV_"))
255
+ .map(({ line }) => line);
256
+ if (linesToAdd.length === 0)
257
+ return;
258
+ let merged = envContent;
259
+ if (merged.length > 0 && !merged.endsWith("\n"))
260
+ merged += "\n";
261
+ merged += `${linesToAdd.join("\n")}\n`;
262
+ await fs.writeFile(envPath, merged);
263
+ }
264
+ const ENV_HEADER = "# Encrypted with dotenvx — safe to commit.\n";
265
+ // dotenvx can't initialize an empty `.env` (0-byte → dummy sample secrets;
266
+ // comment-only → no public key bound), so encrypt only once it has ≥1 real value.
267
+ // Migrate from `.env.local`, else seed `.env.example`. False = nothing to encrypt.
268
+ async function prepareEnvForEncryption(targetDir) {
269
+ await migratePlaintextEnv(targetDir);
270
+ const envPath = path.join(targetDir, ".env");
271
+ const examplePath = path.join(targetDir, ".env.example");
272
+ const content = fs.existsSync(envPath)
273
+ ? await fs.readFile(envPath, "utf-8")
274
+ : "";
275
+ if (parseEnvKeys(content).size > 0)
276
+ return true;
277
+ const seed = fs.existsSync(examplePath)
278
+ ? nonEmptyAssignments(await fs.readFile(examplePath, "utf-8")).map(({ line }) => line)
279
+ : [];
280
+ if (seed.length === 0)
281
+ return false;
282
+ await fs.writeFile(envPath, `${ENV_HEADER}${seed.join("\n")}\n`);
283
+ return true;
284
+ }
285
+ // A throwaway key whose only job is to give `dotenvx encrypt` something to
286
+ // encrypt so it mints the keypair and binds the public key to `.env`. Needed
287
+ // when every committed value is plaintext (NEXT_PUBLIC_*) — without it dotenvx
288
+ // writes a private key but no public key, leaving a malformed half-keypair.
289
+ const KEYGEN_SEED_KEY = "BSMNT_KEYGEN_SEED";
290
+ // Keys dotenvx leaves plaintext (mirrors runEncrypt's --exclude-key flags):
291
+ // its own DOTENV_* machinery and browser-public NEXT_PUBLIC_*. A key outside
292
+ // these is a real secret that encryption would bind the keypair to.
293
+ export function isEncryptableSecret(key) {
294
+ return !key.startsWith("DOTENV_") && !key.startsWith("NEXT_PUBLIC_");
295
+ }
296
+ async function hasEncryptableSecret(targetDir) {
297
+ const envPath = path.join(targetDir, ".env");
298
+ if (!fs.existsSync(envPath))
299
+ return false;
300
+ const content = await fs.readFile(envPath, "utf-8");
301
+ for (const key of parseEnvKeys(content)) {
302
+ if (isEncryptableSecret(key))
303
+ return true;
304
+ }
305
+ return false;
306
+ }
307
+ const NEXT_CONFIG_CANDIDATES = [
308
+ "next.config.ts",
309
+ "next.config.mts",
310
+ "next.config.js",
311
+ "next.config.mjs",
312
+ "next.config.cjs",
313
+ ];
314
+ // Bundle the committed encrypted .env* into serverless functions so
315
+ // instrumentation.ts can decrypt them at runtime (templates ship this baked in;
316
+ // integrate has to add it to the project's existing config).
317
+ const TRACING_PROPERTY = ` // dotenvx: bundle the committed encrypted .env* into serverless functions
318
+ // so instrumentation.ts can decrypt them at runtime.
319
+ outputFileTracingIncludes: { "/**": ["./.env", "./.env.production", "./.env.preview"] },`;
320
+ // Matches the opening brace of a top-level config object we can safely extend:
321
+ // `const x[: Type] = {`, `export default {`, or `module.exports = {`.
322
+ const CONFIG_OBJECT_RE = /(const\s+\w+\s*(?::[^=]+)?=\s*\{|export\s+default\s*\{|module\.exports\s*=\s*\{)/;
323
+ /**
324
+ * Decide whether/how to add `outputFileTracingIncludes` to next.config source.
325
+ * Pure: returns the (possibly rewritten) content and a status — "present" if it
326
+ * already has the key, "manual" if the shape can't be safely extended.
327
+ */
328
+ export function patchNextConfigContent(content) {
329
+ if (content.includes("outputFileTracingIncludes")) {
330
+ return { status: "present", content };
331
+ }
332
+ if (!CONFIG_OBJECT_RE.test(content)) {
333
+ return { status: "manual", content };
334
+ }
335
+ return {
336
+ status: "patched",
337
+ content: content.replace(CONFIG_OBJECT_RE, (match) => `${match}\n${TRACING_PROPERTY}`),
338
+ };
339
+ }
340
+ /**
341
+ * Add `outputFileTracingIncludes` to an existing `next.config` so the encrypted
342
+ * `.env*` is readable by `instrumentation.ts` at runtime on Vercel. Best-effort:
343
+ * returns "manual" (caller should print the snippet) when no config is found or
344
+ * its shape can't be safely extended.
345
+ */
346
+ export async function ensureNextConfigTracing(targetDir) {
347
+ const file = NEXT_CONFIG_CANDIDATES.find((name) => fs.existsSync(path.join(targetDir, name)));
348
+ if (!file)
349
+ return { status: "manual", file: null };
350
+ const configPath = path.join(targetDir, file);
351
+ const result = patchNextConfigContent(await fs.readFile(configPath, "utf-8"));
352
+ if (result.status === "patched") {
353
+ await fs.writeFile(configPath, result.content);
354
+ }
355
+ return { status: result.status, file };
356
+ }
357
+ /**
358
+ * Append a random throwaway secret when `.env` has nothing encryptable, so the
359
+ * encrypt pass mints a keypair. Returns whether a seed was added (the caller
360
+ * strips it once the keypair exists). No-op when a real secret is present.
361
+ */
362
+ async function ensureKeygenSeed(targetDir) {
363
+ if (await hasEncryptableSecret(targetDir))
364
+ return false;
365
+ const envPath = path.join(targetDir, ".env");
366
+ let content = fs.existsSync(envPath)
367
+ ? await fs.readFile(envPath, "utf-8")
368
+ : ENV_HEADER;
369
+ if (content.length > 0 && !content.endsWith("\n"))
370
+ content += "\n";
371
+ content += `${KEYGEN_SEED_KEY}=${randomBytes(16).toString("hex")}\n`;
372
+ await fs.writeFile(envPath, content);
373
+ return true;
374
+ }
375
+ /** Drop a single `KEY=…` line from `.env` (removes the keygen seed post-encrypt). */
376
+ async function removeEnvKey(targetDir, key) {
377
+ const envPath = path.join(targetDir, ".env");
378
+ if (!fs.existsSync(envPath))
379
+ return;
380
+ const content = await fs.readFile(envPath, "utf-8");
381
+ const pattern = new RegExp(`^\\s*${key}\\s*=`);
382
+ const kept = content.split("\n").filter((line) => !pattern.test(line));
383
+ await fs.writeFile(envPath, kept.join("\n").replace(/\n+$/, "\n"));
384
+ }
385
+ /** Resolve the project-local dotenvx binary, or null if not installed yet. */
386
+ function resolveDotenvxBin(targetDir) {
387
+ const localBin = path.join(targetDir, "node_modules", ".bin", process.platform === "win32" ? "dotenvx.cmd" : "dotenvx");
388
+ return fs.existsSync(localBin) ? localBin : null;
389
+ }
390
+ /** Run `dotenvx encrypt` in the target project (idempotent — re-runs are safe). */
391
+ function runEncrypt(targetDir) {
392
+ // Keep two kinds of key plaintext:
393
+ // - the op ref, so it's readable without the keys it points to;
394
+ // - NEXT_PUBLIC_*, which ship to the browser anyway — encrypting buys
395
+ // nothing, and plaintext lets keyless builds (CI, fresh clones) read
396
+ // them at build time instead of choking on an `encrypted:…` string.
397
+ const args = [
398
+ "encrypt",
399
+ "--exclude-key",
400
+ "DOTENV_PRIVATE_KEY_OP_REF",
401
+ "NEXT_PUBLIC_*",
402
+ ];
403
+ const bin = resolveDotenvxBin(targetDir);
404
+ if (bin) {
405
+ execFileSync(bin, args, { cwd: targetDir, stdio: "pipe" });
406
+ return;
407
+ }
408
+ // Fallback when the local bin isn't present (e.g. install was skipped).
409
+ // Quote glob args so the shell doesn't expand them.
410
+ const quoted = args.map((a) => (/[*?]/.test(a) ? `'${a}'` : a)).join(" ");
411
+ execSync(`bunx @dotenvx/dotenvx ${quoted}`, {
412
+ cwd: targetDir,
413
+ stdio: "pipe",
414
+ });
415
+ }
416
+ /** Print the one-time private keys and the 1Password / CI / Vercel next steps. */
417
+ async function printKeyAndNextSteps(targetDir) {
418
+ const keysPath = path.join(targetDir, ".env.keys");
419
+ const keysContent = fs.existsSync(keysPath)
420
+ ? await fs.readFile(keysPath, "utf-8")
421
+ : "";
422
+ console.log(`\n ${pc.bold("🔐 dotenvx — your .env is encrypted and committable.")}`);
423
+ console.log();
424
+ if (keysContent) {
425
+ const indented = keysContent
426
+ .trimEnd()
427
+ .split("\n")
428
+ .map((line) => (line ? ` ${line}` : line))
429
+ .join("\n");
430
+ console.log(pc.dim(" Private keys (.env.keys):"));
431
+ console.log(pc.yellow(indented));
432
+ }
433
+ console.log(pc.red(" ⚠ Save the private key in 1Password now — it is the ONLY way to decrypt .env."));
434
+ console.log();
435
+ console.log(" Then wire it up:");
436
+ console.log(pc.cyan(" - 1Password: `bun run env:save` (stores the key + sets the op-ref; teammates restore with `bun run env:setup`)"));
437
+ console.log(pc.cyan(" - GitHub (CI): `bun run env:setup-remote github` (needs the gh CLI, signed in)"));
438
+ console.log(pc.cyan(" - Vercel: `bun run env:setup-remote vercel`, then redeploy (needs the vercel CLI, linked)"));
439
+ console.log();
440
+ console.log(pc.dim(" Per-environment secrets (development/preview/production) layer on top of .env:"));
441
+ console.log(pc.cyan(" - Add one: bun run env:set KEY value --prod (creates an encrypted"));
442
+ console.log(pc.cyan(" .env.production + a DOTENV_PRIVATE_KEY_PRODUCTION in .env.keys)"));
443
+ console.log(pc.cyan(" - Then set DOTENV_PRIVATE_KEY_PRODUCTION on Vercel's Production env (and the"));
444
+ console.log(pc.cyan(" _PREVIEW key on Preview)."));
445
+ console.log();
446
+ console.log(pc.dim(" A gitleaks pre-commit hook guards against leaking secrets — install the CLI:"));
447
+ console.log(pc.cyan(" - macOS: brew install gitleaks"));
448
+ console.log(pc.cyan(" - Windows: scoop install gitleaks (or choco install gitleaks)"));
449
+ console.log(pc.cyan(" - other: https://github.com/gitleaks/gitleaks/releases"));
450
+ console.log();
451
+ }
452
+ /** Migrate plaintext, encrypt `.env`, print the key + next steps. Idempotent. */
453
+ export async function setupDotenvx(targetDir) {
454
+ const spinner = ora("Encrypting .env with dotenvx...").start();
455
+ try {
456
+ // Migrate any plaintext `.env.local` and ensure the op-ref exists, then
457
+ // always mint a keypair: when there's nothing real to encrypt yet,
458
+ // ensureKeygenSeed encrypts a throwaway value just to bind the keys.
459
+ await prepareEnvForEncryption(targetDir);
460
+ await ensureEnvOpRef(targetDir);
461
+ const seeded = await ensureKeygenSeed(targetDir);
462
+ runEncrypt(targetDir);
463
+ if (seeded)
464
+ await removeEnvKey(targetDir, KEYGEN_SEED_KEY);
465
+ spinner.succeed(pc.green(".env encrypted with dotenvx."));
466
+ }
467
+ catch (error) {
468
+ const message = error instanceof Error ? error.message : String(error);
469
+ spinner.fail(pc.yellow(`dotenvx encryption skipped: ${message}`));
470
+ return false;
471
+ }
472
+ await printKeyAndNextSteps(targetDir);
473
+ await offerSaveToOnePassword(targetDir);
474
+ return true;
475
+ }
476
+ // Offer to save the key to 1Password now (runs `env:save`). Skipped when
477
+ // non-interactive or `op` isn't installed — the printed steps cover that.
478
+ async function offerSaveToOnePassword(targetDir) {
479
+ if (!process.stdin.isTTY)
480
+ return;
481
+ try {
482
+ execFileSync("op", ["--version"], { stdio: "ignore" });
483
+ }
484
+ catch {
485
+ return;
486
+ }
487
+ const { save } = (await prompts({
488
+ type: "confirm",
489
+ name: "save",
490
+ message: "Save the private key to 1Password now?",
491
+ initial: true,
492
+ }));
493
+ if (!save)
494
+ return;
495
+ try {
496
+ execSync("bun run env:save", { cwd: targetDir, stdio: "inherit" });
497
+ }
498
+ catch {
499
+ // env:save prints its own guidance; don't fail the scaffold.
500
+ }
501
+ }
502
+ //# sourceMappingURL=setup-dotenvx.js.map