thevoidforge 21.0.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 (328) hide show
  1. package/dist/scripts/vault-read.d.ts +11 -0
  2. package/dist/scripts/vault-read.js +89 -0
  3. package/dist/scripts/voidforge.d.ts +20 -0
  4. package/dist/scripts/voidforge.js +404 -0
  5. package/dist/tsconfig.tsbuildinfo +1 -0
  6. package/dist/wizard/api/auth.d.ts +5 -0
  7. package/dist/wizard/api/auth.js +133 -0
  8. package/dist/wizard/api/blueprint.d.ts +45 -0
  9. package/dist/wizard/api/blueprint.js +184 -0
  10. package/dist/wizard/api/cloud-providers.d.ts +16 -0
  11. package/dist/wizard/api/cloud-providers.js +363 -0
  12. package/dist/wizard/api/credentials.d.ts +1 -0
  13. package/dist/wizard/api/credentials.js +258 -0
  14. package/dist/wizard/api/danger-room.d.ts +18 -0
  15. package/dist/wizard/api/danger-room.js +401 -0
  16. package/dist/wizard/api/deploy.d.ts +4 -0
  17. package/dist/wizard/api/deploy.js +164 -0
  18. package/dist/wizard/api/prd.d.ts +1 -0
  19. package/dist/wizard/api/prd.js +363 -0
  20. package/dist/wizard/api/project.d.ts +1 -0
  21. package/dist/wizard/api/project.js +239 -0
  22. package/dist/wizard/api/projects.d.ts +6 -0
  23. package/dist/wizard/api/projects.js +648 -0
  24. package/dist/wizard/api/provision.d.ts +4 -0
  25. package/dist/wizard/api/provision.js +535 -0
  26. package/dist/wizard/api/terminal.d.ts +25 -0
  27. package/dist/wizard/api/terminal.js +241 -0
  28. package/dist/wizard/api/users.d.ts +6 -0
  29. package/dist/wizard/api/users.js +244 -0
  30. package/dist/wizard/api/war-room.d.ts +14 -0
  31. package/dist/wizard/api/war-room.js +45 -0
  32. package/dist/wizard/lib/ad-platform-core.d.ts +6 -0
  33. package/dist/wizard/lib/ad-platform-core.js +1 -0
  34. package/dist/wizard/lib/adapters/index.d.ts +52 -0
  35. package/dist/wizard/lib/adapters/index.js +38 -0
  36. package/dist/wizard/lib/adapters/sandbox-bank.d.ts +17 -0
  37. package/dist/wizard/lib/adapters/sandbox-bank.js +77 -0
  38. package/dist/wizard/lib/adapters/sandbox.d.ts +39 -0
  39. package/dist/wizard/lib/adapters/sandbox.js +174 -0
  40. package/dist/wizard/lib/adapters/stripe.d.ts +19 -0
  41. package/dist/wizard/lib/adapters/stripe.js +143 -0
  42. package/dist/wizard/lib/adapters/types.d.ts +9 -0
  43. package/dist/wizard/lib/adapters/types.js +10 -0
  44. package/dist/wizard/lib/agent-memory.d.ts +36 -0
  45. package/dist/wizard/lib/agent-memory.js +114 -0
  46. package/dist/wizard/lib/anomaly-detection.d.ts +59 -0
  47. package/dist/wizard/lib/anomaly-detection.js +122 -0
  48. package/dist/wizard/lib/anthropic.d.ts +21 -0
  49. package/dist/wizard/lib/anthropic.js +105 -0
  50. package/dist/wizard/lib/asset-scanner.d.ts +23 -0
  51. package/dist/wizard/lib/asset-scanner.js +107 -0
  52. package/dist/wizard/lib/audit-log.d.ts +23 -0
  53. package/dist/wizard/lib/audit-log.js +70 -0
  54. package/dist/wizard/lib/autonomy-controller.d.ts +76 -0
  55. package/dist/wizard/lib/autonomy-controller.js +183 -0
  56. package/dist/wizard/lib/body-parser.d.ts +2 -0
  57. package/dist/wizard/lib/body-parser.js +36 -0
  58. package/dist/wizard/lib/build-analytics.d.ts +39 -0
  59. package/dist/wizard/lib/build-analytics.js +91 -0
  60. package/dist/wizard/lib/build-step.d.ts +21 -0
  61. package/dist/wizard/lib/build-step.js +104 -0
  62. package/dist/wizard/lib/campaign-proposer.d.ts +39 -0
  63. package/dist/wizard/lib/campaign-proposer.js +180 -0
  64. package/dist/wizard/lib/campaign-state-machine.d.ts +63 -0
  65. package/dist/wizard/lib/campaign-state-machine.js +114 -0
  66. package/dist/wizard/lib/ci-generator.d.ts +14 -0
  67. package/dist/wizard/lib/ci-generator.js +187 -0
  68. package/dist/wizard/lib/claude-merge.d.ts +38 -0
  69. package/dist/wizard/lib/claude-merge.js +115 -0
  70. package/dist/wizard/lib/codegen/erd-gen.d.ts +16 -0
  71. package/dist/wizard/lib/codegen/erd-gen.js +98 -0
  72. package/dist/wizard/lib/codegen/integrations.d.ts +18 -0
  73. package/dist/wizard/lib/codegen/integrations.js +189 -0
  74. package/dist/wizard/lib/codegen/openapi-gen.d.ts +15 -0
  75. package/dist/wizard/lib/codegen/openapi-gen.js +79 -0
  76. package/dist/wizard/lib/codegen/prisma-types.d.ts +15 -0
  77. package/dist/wizard/lib/codegen/prisma-types.js +44 -0
  78. package/dist/wizard/lib/codegen/seed-gen.d.ts +16 -0
  79. package/dist/wizard/lib/codegen/seed-gen.js +128 -0
  80. package/dist/wizard/lib/compliance.d.ts +51 -0
  81. package/dist/wizard/lib/compliance.js +112 -0
  82. package/dist/wizard/lib/correlation-engine.d.ts +59 -0
  83. package/dist/wizard/lib/correlation-engine.js +151 -0
  84. package/dist/wizard/lib/cost-estimator.d.ts +22 -0
  85. package/dist/wizard/lib/cost-estimator.js +72 -0
  86. package/dist/wizard/lib/cost-tracker.d.ts +27 -0
  87. package/dist/wizard/lib/cost-tracker.js +37 -0
  88. package/dist/wizard/lib/daemon-aggregator.d.ts +71 -0
  89. package/dist/wizard/lib/daemon-aggregator.js +204 -0
  90. package/dist/wizard/lib/daemon-core.d.ts +6 -0
  91. package/dist/wizard/lib/daemon-core.js +5 -0
  92. package/dist/wizard/lib/dashboard-data.d.ts +132 -0
  93. package/dist/wizard/lib/dashboard-data.js +336 -0
  94. package/dist/wizard/lib/dashboard-ws.d.ts +25 -0
  95. package/dist/wizard/lib/dashboard-ws.js +91 -0
  96. package/dist/wizard/lib/deep-current.d.ts +77 -0
  97. package/dist/wizard/lib/deep-current.js +234 -0
  98. package/dist/wizard/lib/deploy-coordinator.d.ts +40 -0
  99. package/dist/wizard/lib/deploy-coordinator.js +86 -0
  100. package/dist/wizard/lib/deploy-log.d.ts +28 -0
  101. package/dist/wizard/lib/deploy-log.js +52 -0
  102. package/dist/wizard/lib/desktop-notify.d.ts +27 -0
  103. package/dist/wizard/lib/desktop-notify.js +98 -0
  104. package/dist/wizard/lib/dns/cloudflare-dns.d.ts +35 -0
  105. package/dist/wizard/lib/dns/cloudflare-dns.js +216 -0
  106. package/dist/wizard/lib/dns/cloudflare-registrar.d.ts +31 -0
  107. package/dist/wizard/lib/dns/cloudflare-registrar.js +148 -0
  108. package/dist/wizard/lib/dns/types.d.ts +22 -0
  109. package/dist/wizard/lib/dns/types.js +4 -0
  110. package/dist/wizard/lib/document-discovery.d.ts +33 -0
  111. package/dist/wizard/lib/document-discovery.js +145 -0
  112. package/dist/wizard/lib/env-validator.d.ts +14 -0
  113. package/dist/wizard/lib/env-validator.js +205 -0
  114. package/dist/wizard/lib/env-writer.d.ts +13 -0
  115. package/dist/wizard/lib/env-writer.js +26 -0
  116. package/dist/wizard/lib/exec.d.ts +30 -0
  117. package/dist/wizard/lib/exec.js +52 -0
  118. package/dist/wizard/lib/experiment.d.ts +70 -0
  119. package/dist/wizard/lib/experiment.js +169 -0
  120. package/dist/wizard/lib/extensions.d.ts +20 -0
  121. package/dist/wizard/lib/extensions.js +183 -0
  122. package/dist/wizard/lib/financial/adapter-factory.d.ts +47 -0
  123. package/dist/wizard/lib/financial/adapter-factory.js +225 -0
  124. package/dist/wizard/lib/financial/billing/base.d.ts +6 -0
  125. package/dist/wizard/lib/financial/billing/base.js +1 -0
  126. package/dist/wizard/lib/financial/billing/google-billing.d.ts +56 -0
  127. package/dist/wizard/lib/financial/billing/google-billing.js +298 -0
  128. package/dist/wizard/lib/financial/billing/meta-billing.d.ts +54 -0
  129. package/dist/wizard/lib/financial/billing/meta-billing.js +243 -0
  130. package/dist/wizard/lib/financial/billing/tiktok-billing.d.ts +54 -0
  131. package/dist/wizard/lib/financial/billing/tiktok-billing.js +260 -0
  132. package/dist/wizard/lib/financial/campaign/base.d.ts +13 -0
  133. package/dist/wizard/lib/financial/campaign/base.js +1 -0
  134. package/dist/wizard/lib/financial/campaign/google-campaign.d.ts +42 -0
  135. package/dist/wizard/lib/financial/campaign/google-campaign.js +388 -0
  136. package/dist/wizard/lib/financial/campaign/meta-campaign.d.ts +41 -0
  137. package/dist/wizard/lib/financial/campaign/meta-campaign.js +311 -0
  138. package/dist/wizard/lib/financial/campaign/sandbox-campaign.d.ts +45 -0
  139. package/dist/wizard/lib/financial/campaign/sandbox-campaign.js +261 -0
  140. package/dist/wizard/lib/financial/campaign/tiktok-campaign.d.ts +40 -0
  141. package/dist/wizard/lib/financial/campaign/tiktok-campaign.js +350 -0
  142. package/dist/wizard/lib/financial/funding-auto.d.ts +44 -0
  143. package/dist/wizard/lib/financial/funding-auto.js +52 -0
  144. package/dist/wizard/lib/financial/funding-policy.d.ts +60 -0
  145. package/dist/wizard/lib/financial/funding-policy.js +179 -0
  146. package/dist/wizard/lib/financial/platform-planner.d.ts +47 -0
  147. package/dist/wizard/lib/financial/platform-planner.js +134 -0
  148. package/dist/wizard/lib/financial/reconciliation-engine.d.ts +78 -0
  149. package/dist/wizard/lib/financial/reconciliation-engine.js +193 -0
  150. package/dist/wizard/lib/financial/registry.d.ts +22 -0
  151. package/dist/wizard/lib/financial/registry.js +26 -0
  152. package/dist/wizard/lib/financial/reporting.d.ts +96 -0
  153. package/dist/wizard/lib/financial/reporting.js +198 -0
  154. package/dist/wizard/lib/financial/stablecoin/base.d.ts +6 -0
  155. package/dist/wizard/lib/financial/stablecoin/base.js +1 -0
  156. package/dist/wizard/lib/financial/stablecoin/circle.d.ts +54 -0
  157. package/dist/wizard/lib/financial/stablecoin/circle.js +367 -0
  158. package/dist/wizard/lib/financial/stablecoin/mercury.d.ts +24 -0
  159. package/dist/wizard/lib/financial/stablecoin/mercury.js +171 -0
  160. package/dist/wizard/lib/financial/stablecoin/sandbox-stablecoin.d.ts +47 -0
  161. package/dist/wizard/lib/financial/stablecoin/sandbox-stablecoin.js +202 -0
  162. package/dist/wizard/lib/financial/treasury-planner.d.ts +52 -0
  163. package/dist/wizard/lib/financial/treasury-planner.js +128 -0
  164. package/dist/wizard/lib/financial-core.d.ts +6 -0
  165. package/dist/wizard/lib/financial-core.js +5 -0
  166. package/dist/wizard/lib/financial-vault.d.ts +34 -0
  167. package/dist/wizard/lib/financial-vault.js +199 -0
  168. package/dist/wizard/lib/frontmatter.d.ts +30 -0
  169. package/dist/wizard/lib/frontmatter.js +96 -0
  170. package/dist/wizard/lib/gap-analysis.d.ts +37 -0
  171. package/dist/wizard/lib/gap-analysis.js +218 -0
  172. package/dist/wizard/lib/github.d.ts +22 -0
  173. package/dist/wizard/lib/github.js +261 -0
  174. package/dist/wizard/lib/headless-deploy.d.ts +14 -0
  175. package/dist/wizard/lib/headless-deploy.js +452 -0
  176. package/dist/wizard/lib/health-monitor.d.ts +15 -0
  177. package/dist/wizard/lib/health-monitor.js +91 -0
  178. package/dist/wizard/lib/health-poller.d.ts +9 -0
  179. package/dist/wizard/lib/health-poller.js +123 -0
  180. package/dist/wizard/lib/heartbeat.d.ts +15 -0
  181. package/dist/wizard/lib/heartbeat.js +827 -0
  182. package/dist/wizard/lib/http-helpers.d.ts +9 -0
  183. package/dist/wizard/lib/http-helpers.js +24 -0
  184. package/dist/wizard/lib/image-gen.d.ts +56 -0
  185. package/dist/wizard/lib/image-gen.js +159 -0
  186. package/dist/wizard/lib/instance-sizing.d.ts +26 -0
  187. package/dist/wizard/lib/instance-sizing.js +51 -0
  188. package/dist/wizard/lib/kongo/analytics.d.ts +29 -0
  189. package/dist/wizard/lib/kongo/analytics.js +179 -0
  190. package/dist/wizard/lib/kongo/campaigns.d.ts +52 -0
  191. package/dist/wizard/lib/kongo/campaigns.js +91 -0
  192. package/dist/wizard/lib/kongo/client.d.ts +58 -0
  193. package/dist/wizard/lib/kongo/client.js +221 -0
  194. package/dist/wizard/lib/kongo/jobs.d.ts +57 -0
  195. package/dist/wizard/lib/kongo/jobs.js +122 -0
  196. package/dist/wizard/lib/kongo/pages.d.ts +60 -0
  197. package/dist/wizard/lib/kongo/pages.js +150 -0
  198. package/dist/wizard/lib/kongo/provisioner.d.ts +64 -0
  199. package/dist/wizard/lib/kongo/provisioner.js +116 -0
  200. package/dist/wizard/lib/kongo/seed.d.ts +49 -0
  201. package/dist/wizard/lib/kongo/seed.js +237 -0
  202. package/dist/wizard/lib/kongo/types.d.ts +323 -0
  203. package/dist/wizard/lib/kongo/types.js +11 -0
  204. package/dist/wizard/lib/kongo/variants.d.ts +57 -0
  205. package/dist/wizard/lib/kongo/variants.js +88 -0
  206. package/dist/wizard/lib/kongo/webhooks.d.ts +41 -0
  207. package/dist/wizard/lib/kongo/webhooks.js +112 -0
  208. package/dist/wizard/lib/marker.d.ts +28 -0
  209. package/dist/wizard/lib/marker.js +79 -0
  210. package/dist/wizard/lib/migrator.d.ts +35 -0
  211. package/dist/wizard/lib/migrator.js +190 -0
  212. package/dist/wizard/lib/natural-language-deploy.d.ts +30 -0
  213. package/dist/wizard/lib/natural-language-deploy.js +186 -0
  214. package/dist/wizard/lib/network.d.ts +22 -0
  215. package/dist/wizard/lib/network.js +72 -0
  216. package/dist/wizard/lib/oauth-core.d.ts +6 -0
  217. package/dist/wizard/lib/oauth-core.js +5 -0
  218. package/dist/wizard/lib/open-browser.d.ts +1 -0
  219. package/dist/wizard/lib/open-browser.js +26 -0
  220. package/dist/wizard/lib/patterns/ad-billing-adapter.d.ts +209 -0
  221. package/dist/wizard/lib/patterns/ad-billing-adapter.js +269 -0
  222. package/dist/wizard/lib/patterns/ad-platform-adapter.d.ts +200 -0
  223. package/dist/wizard/lib/patterns/ad-platform-adapter.js +212 -0
  224. package/dist/wizard/lib/patterns/daemon-process.d.ts +88 -0
  225. package/dist/wizard/lib/patterns/daemon-process.js +271 -0
  226. package/dist/wizard/lib/patterns/financial-transaction.d.ts +161 -0
  227. package/dist/wizard/lib/patterns/financial-transaction.js +132 -0
  228. package/dist/wizard/lib/patterns/funding-plan.d.ts +136 -0
  229. package/dist/wizard/lib/patterns/funding-plan.js +200 -0
  230. package/dist/wizard/lib/patterns/oauth-token-lifecycle.d.ts +94 -0
  231. package/dist/wizard/lib/patterns/oauth-token-lifecycle.js +139 -0
  232. package/dist/wizard/lib/patterns/outbound-rate-limiter.d.ts +67 -0
  233. package/dist/wizard/lib/patterns/outbound-rate-limiter.js +216 -0
  234. package/dist/wizard/lib/patterns/revenue-source-adapter.d.ts +96 -0
  235. package/dist/wizard/lib/patterns/revenue-source-adapter.js +182 -0
  236. package/dist/wizard/lib/patterns/stablecoin-adapter.d.ts +218 -0
  237. package/dist/wizard/lib/patterns/stablecoin-adapter.js +264 -0
  238. package/dist/wizard/lib/prd-validator.d.ts +39 -0
  239. package/dist/wizard/lib/prd-validator.js +137 -0
  240. package/dist/wizard/lib/project-init.d.ts +24 -0
  241. package/dist/wizard/lib/project-init.js +193 -0
  242. package/dist/wizard/lib/project-registry.d.ts +86 -0
  243. package/dist/wizard/lib/project-registry.js +359 -0
  244. package/dist/wizard/lib/provision-manifest.d.ts +44 -0
  245. package/dist/wizard/lib/provision-manifest.js +164 -0
  246. package/dist/wizard/lib/provisioner-registry.d.ts +15 -0
  247. package/dist/wizard/lib/provisioner-registry.js +34 -0
  248. package/dist/wizard/lib/provisioners/aws-vps.d.ts +6 -0
  249. package/dist/wizard/lib/provisioners/aws-vps.js +643 -0
  250. package/dist/wizard/lib/provisioners/cloudflare.d.ts +6 -0
  251. package/dist/wizard/lib/provisioners/cloudflare.js +300 -0
  252. package/dist/wizard/lib/provisioners/docker.d.ts +6 -0
  253. package/dist/wizard/lib/provisioners/docker.js +75 -0
  254. package/dist/wizard/lib/provisioners/http-client.d.ts +20 -0
  255. package/dist/wizard/lib/provisioners/http-client.js +79 -0
  256. package/dist/wizard/lib/provisioners/railway.d.ts +6 -0
  257. package/dist/wizard/lib/provisioners/railway.js +413 -0
  258. package/dist/wizard/lib/provisioners/scripts/caddyfile.d.ts +10 -0
  259. package/dist/wizard/lib/provisioners/scripts/caddyfile.js +54 -0
  260. package/dist/wizard/lib/provisioners/scripts/deploy-vps.d.ts +10 -0
  261. package/dist/wizard/lib/provisioners/scripts/deploy-vps.js +112 -0
  262. package/dist/wizard/lib/provisioners/scripts/docker-compose.d.ts +11 -0
  263. package/dist/wizard/lib/provisioners/scripts/docker-compose.js +91 -0
  264. package/dist/wizard/lib/provisioners/scripts/dockerfile.d.ts +5 -0
  265. package/dist/wizard/lib/provisioners/scripts/dockerfile.js +185 -0
  266. package/dist/wizard/lib/provisioners/scripts/ecosystem-config.d.ts +10 -0
  267. package/dist/wizard/lib/provisioners/scripts/ecosystem-config.js +36 -0
  268. package/dist/wizard/lib/provisioners/scripts/provision-vps.d.ts +14 -0
  269. package/dist/wizard/lib/provisioners/scripts/provision-vps.js +202 -0
  270. package/dist/wizard/lib/provisioners/scripts/rollback-vps.d.ts +10 -0
  271. package/dist/wizard/lib/provisioners/scripts/rollback-vps.js +67 -0
  272. package/dist/wizard/lib/provisioners/self-deploy.d.ts +41 -0
  273. package/dist/wizard/lib/provisioners/self-deploy.js +185 -0
  274. package/dist/wizard/lib/provisioners/static-s3.d.ts +6 -0
  275. package/dist/wizard/lib/provisioners/static-s3.js +235 -0
  276. package/dist/wizard/lib/provisioners/types.d.ts +40 -0
  277. package/dist/wizard/lib/provisioners/types.js +4 -0
  278. package/dist/wizard/lib/provisioners/vercel.d.ts +6 -0
  279. package/dist/wizard/lib/provisioners/vercel.js +287 -0
  280. package/dist/wizard/lib/pty-manager.d.ts +42 -0
  281. package/dist/wizard/lib/pty-manager.js +231 -0
  282. package/dist/wizard/lib/rate-limiter-core.d.ts +5 -0
  283. package/dist/wizard/lib/rate-limiter-core.js +5 -0
  284. package/dist/wizard/lib/reconciliation.d.ts +43 -0
  285. package/dist/wizard/lib/reconciliation.js +173 -0
  286. package/dist/wizard/lib/revenue-types.d.ts +5 -0
  287. package/dist/wizard/lib/revenue-types.js +1 -0
  288. package/dist/wizard/lib/route-optimizer.d.ts +28 -0
  289. package/dist/wizard/lib/route-optimizer.js +93 -0
  290. package/dist/wizard/lib/s3-deploy.d.ts +19 -0
  291. package/dist/wizard/lib/s3-deploy.js +156 -0
  292. package/dist/wizard/lib/safety-tiers.d.ts +76 -0
  293. package/dist/wizard/lib/safety-tiers.js +134 -0
  294. package/dist/wizard/lib/sentry-generator.d.ts +15 -0
  295. package/dist/wizard/lib/sentry-generator.js +116 -0
  296. package/dist/wizard/lib/server-config.d.ts +13 -0
  297. package/dist/wizard/lib/server-config.js +23 -0
  298. package/dist/wizard/lib/service-install.d.ts +18 -0
  299. package/dist/wizard/lib/service-install.js +182 -0
  300. package/dist/wizard/lib/site-scanner.d.ts +80 -0
  301. package/dist/wizard/lib/site-scanner.js +262 -0
  302. package/dist/wizard/lib/ssh-deploy.d.ts +25 -0
  303. package/dist/wizard/lib/ssh-deploy.js +225 -0
  304. package/dist/wizard/lib/templates.d.ts +24 -0
  305. package/dist/wizard/lib/templates.js +219 -0
  306. package/dist/wizard/lib/totp.d.ts +35 -0
  307. package/dist/wizard/lib/totp.js +276 -0
  308. package/dist/wizard/lib/tower-auth.d.ts +43 -0
  309. package/dist/wizard/lib/tower-auth.js +352 -0
  310. package/dist/wizard/lib/tower-rate-limit.d.ts +14 -0
  311. package/dist/wizard/lib/tower-rate-limit.js +61 -0
  312. package/dist/wizard/lib/tower-session.d.ts +28 -0
  313. package/dist/wizard/lib/tower-session.js +119 -0
  314. package/dist/wizard/lib/treasury-backup.d.ts +23 -0
  315. package/dist/wizard/lib/treasury-backup.js +126 -0
  316. package/dist/wizard/lib/treasury-heartbeat.d.ts +82 -0
  317. package/dist/wizard/lib/treasury-heartbeat.js +1104 -0
  318. package/dist/wizard/lib/updater.d.ts +29 -0
  319. package/dist/wizard/lib/updater.js +190 -0
  320. package/dist/wizard/lib/user-manager.d.ts +39 -0
  321. package/dist/wizard/lib/user-manager.js +182 -0
  322. package/dist/wizard/lib/vault.d.ts +26 -0
  323. package/dist/wizard/lib/vault.js +161 -0
  324. package/dist/wizard/router.d.ts +5 -0
  325. package/dist/wizard/router.js +15 -0
  326. package/dist/wizard/server.d.ts +18 -0
  327. package/dist/wizard/server.js +436 -0
  328. package/package.json +59 -0
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Cloudflare DNS provisioning — zone lookup, record CRUD.
3
+ * Uses the shared HTTP client. No external dependencies.
4
+ */
5
+ import { httpsGet, httpsPost, httpsDelete, safeJsonParse } from '../provisioners/http-client.js';
6
+ import { recordResourcePending, recordResourceCreated } from '../provision-manifest.js';
7
+ const CF_API = 'api.cloudflare.com';
8
+ function cfHeaders(token) {
9
+ return {
10
+ 'Authorization': `Bearer ${token}`,
11
+ 'Content-Type': 'application/json',
12
+ };
13
+ }
14
+ /**
15
+ * Extract the root zone from a hostname.
16
+ * "app.voidforge.dev" → "voidforge.dev"
17
+ * "voidforge.dev" → "voidforge.dev"
18
+ */
19
+ export function extractZoneName(hostname) {
20
+ const parts = hostname.replace(/\.$/, '').split('.');
21
+ if (parts.length <= 2)
22
+ return parts.join('.');
23
+ return parts.slice(-2).join('.');
24
+ }
25
+ /**
26
+ * Find the Cloudflare zone for a hostname.
27
+ * Accepts zones in any status (active, pending, etc.) — Cloudflare allows DNS
28
+ * record creation on pending zones, which is needed after fresh domain registration
29
+ * where zones start as pending until nameservers are verified (Kusanagi-5).
30
+ */
31
+ export async function findZone(token, hostname) {
32
+ const zoneName = extractZoneName(hostname);
33
+ const res = await httpsGet(CF_API, `/client/v4/zones?name=${encodeURIComponent(zoneName)}`, cfHeaders(token));
34
+ if (res.status === 403) {
35
+ throw new Error('Cloudflare token lacks Zone:Read permission. Create a token with Zone:DNS:Edit at dash.cloudflare.com/profile/api-tokens');
36
+ }
37
+ if (res.status !== 200) {
38
+ throw new Error(`Cloudflare zones API returned ${res.status}`);
39
+ }
40
+ const data = safeJsonParse(res.body);
41
+ if (!data?.success || !data.result || data.result.length === 0) {
42
+ return null;
43
+ }
44
+ const zone = data.result[0];
45
+ return { id: zone.id, name: zone.name, status: zone.status };
46
+ }
47
+ /** List existing DNS records for a hostname. */
48
+ export async function listRecords(token, zoneId, hostname) {
49
+ const res = await httpsGet(CF_API, `/client/v4/zones/${zoneId}/dns_records?name=${encodeURIComponent(hostname)}&type=A,AAAA,CNAME`, cfHeaders(token));
50
+ if (res.status !== 200)
51
+ return [];
52
+ const data = safeJsonParse(res.body);
53
+ return (data?.result ?? []).map((r) => ({
54
+ id: r.id,
55
+ type: r.type,
56
+ name: r.name,
57
+ content: r.content,
58
+ proxied: r.proxied,
59
+ ttl: r.ttl,
60
+ }));
61
+ }
62
+ /** Create a DNS record. */
63
+ export async function createRecord(token, zoneId, type, name, content, proxied) {
64
+ const body = JSON.stringify({ type, name, content, proxied, ttl: 1 }); // ttl=1 = auto
65
+ const res = await httpsPost(CF_API, `/client/v4/zones/${zoneId}/dns_records`, cfHeaders(token), body);
66
+ if (res.status !== 200 && res.status !== 201) {
67
+ const data = safeJsonParse(res.body);
68
+ throw new Error(data?.errors?.[0]?.message || `DNS record creation returned ${res.status}`);
69
+ }
70
+ const data = safeJsonParse(res.body);
71
+ const r = data?.result;
72
+ if (!r)
73
+ throw new Error('No record returned from Cloudflare');
74
+ return {
75
+ id: r.id,
76
+ type: r.type,
77
+ name: r.name,
78
+ content: r.content,
79
+ proxied: r.proxied,
80
+ ttl: r.ttl,
81
+ };
82
+ }
83
+ /** Delete a DNS record. */
84
+ export async function deleteRecord(token, zoneId, recordId) {
85
+ await httpsDelete(CF_API, `/client/v4/zones/${zoneId}/dns_records/${recordId}`, cfHeaders(token));
86
+ }
87
+ /**
88
+ * Determine the DNS record type and content based on deploy target outputs.
89
+ *
90
+ * VPS → A record pointing to EC2 public IP
91
+ * Cloudflare Pages → CNAME pointing to slug.pages.dev
92
+ * Static S3 → CNAME pointing to S3 website URL
93
+ * Vercel → CNAME pointing to cname.vercel-dns.com
94
+ * Railway → CNAME pointing to railway project URL
95
+ */
96
+ function resolveRecordTarget(deployTarget, outputs) {
97
+ switch (deployTarget) {
98
+ case 'vps': {
99
+ const ip = outputs['SSH_HOST'];
100
+ if (!ip)
101
+ return null;
102
+ return { type: 'A', content: ip, proxied: true };
103
+ }
104
+ case 'cloudflare': {
105
+ const url = outputs['CF_PROJECT_URL'];
106
+ if (!url)
107
+ return null;
108
+ // Extract hostname from https://slug.pages.dev
109
+ const host = url.replace(/^https?:\/\//, '');
110
+ return { type: 'CNAME', content: host, proxied: true };
111
+ }
112
+ case 'static': {
113
+ const url = outputs['S3_WEBSITE_URL'];
114
+ if (!url)
115
+ return null;
116
+ const host = url.replace(/^https?:\/\//, '');
117
+ return { type: 'CNAME', content: host, proxied: true };
118
+ }
119
+ case 'vercel': {
120
+ return { type: 'CNAME', content: 'cname.vercel-dns.com', proxied: false };
121
+ }
122
+ case 'railway': {
123
+ // Railway custom domains need a CNAME to the project's railway.app subdomain
124
+ const projectName = outputs['RAILWAY_PROJECT_NAME'];
125
+ const content = projectName
126
+ ? `${projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-')}.up.railway.app`
127
+ : 'railway.app';
128
+ return { type: 'CNAME', content, proxied: false };
129
+ }
130
+ default:
131
+ return null;
132
+ }
133
+ }
134
+ /**
135
+ * Full DNS provisioning flow — called as a post-provision step.
136
+ * Non-fatal: returns success=false with error message, never throws.
137
+ */
138
+ export async function provisionDns(runId, token, hostname, deployTarget, outputs, emit) {
139
+ const records = [];
140
+ let zoneId = '';
141
+ // Step 1: Find zone
142
+ emit({ step: 'dns-zone', status: 'started', message: `Looking up Cloudflare zone for ${hostname}` });
143
+ try {
144
+ const zone = await findZone(token, hostname);
145
+ if (!zone) {
146
+ emit({ step: 'dns-zone', status: 'error', message: `Zone not found for "${extractZoneName(hostname)}". Add your domain at dash.cloudflare.com first.` });
147
+ return { success: false, records, zoneId, error: 'Zone not found on Cloudflare' };
148
+ }
149
+ zoneId = zone.id;
150
+ emit({ step: 'dns-zone', status: 'done', message: `Zone found: ${zone.name} (${zone.status})` });
151
+ }
152
+ catch (err) {
153
+ emit({ step: 'dns-zone', status: 'error', message: 'Failed to look up DNS zone', detail: err.message });
154
+ return { success: false, records, zoneId, error: err.message };
155
+ }
156
+ // Step 2: Determine record target
157
+ const target = resolveRecordTarget(deployTarget, outputs);
158
+ if (!target) {
159
+ emit({ step: 'dns-records', status: 'error', message: `Cannot determine DNS target for deploy type "${deployTarget}". Infrastructure may still be provisioning.` });
160
+ return { success: false, records, zoneId, error: 'No DNS target available' };
161
+ }
162
+ // Step 3: Check for existing records
163
+ emit({ step: 'dns-records', status: 'started', message: `Creating ${target.type} record: ${hostname} → ${target.content}` });
164
+ try {
165
+ const existing = await listRecords(token, zoneId, hostname);
166
+ if (existing.length > 0) {
167
+ // Delete conflicting records before creating new ones
168
+ for (const record of existing) {
169
+ await deleteRecord(token, zoneId, record.id);
170
+ }
171
+ emit({ step: 'dns-cleanup', status: 'done', message: `Replaced ${existing.length} existing record(s) for ${hostname}` });
172
+ }
173
+ // Step 4: Create root record
174
+ await recordResourcePending(runId, 'dns-record', hostname, 'global');
175
+ const rootRecord = await createRecord(token, zoneId, target.type, hostname, target.content, target.proxied);
176
+ records.push(rootRecord);
177
+ await recordResourceCreated(runId, 'dns-record', `${zoneId}:${rootRecord.id}`, 'global');
178
+ // Step 5: Create www record (if root domain)
179
+ const parts = hostname.split('.');
180
+ if (parts.length === 2) {
181
+ const wwwHostname = `www.${hostname}`;
182
+ const existingWww = await listRecords(token, zoneId, wwwHostname);
183
+ for (const record of existingWww) {
184
+ await deleteRecord(token, zoneId, record.id);
185
+ }
186
+ await recordResourcePending(runId, 'dns-record', wwwHostname, 'global');
187
+ const wwwRecord = await createRecord(token, zoneId, 'CNAME', wwwHostname, hostname, target.proxied);
188
+ records.push(wwwRecord);
189
+ await recordResourceCreated(runId, 'dns-record', `${zoneId}:${wwwRecord.id}`, 'global');
190
+ }
191
+ const recordNames = records.map((r) => r.name).join(', ');
192
+ emit({ step: 'dns-records', status: 'done', message: `DNS configured: ${recordNames} → ${target.content}` });
193
+ }
194
+ catch (err) {
195
+ emit({ step: 'dns-records', status: 'error', message: 'Failed to create DNS records', detail: err.message });
196
+ return { success: false, records, zoneId, error: err.message };
197
+ }
198
+ return { success: true, records, zoneId };
199
+ }
200
+ /**
201
+ * Clean up DNS records created during provisioning.
202
+ * Resource IDs are stored as "zoneId:recordId".
203
+ */
204
+ export async function cleanupDnsRecords(token, resourceIds) {
205
+ for (const resourceId of resourceIds) {
206
+ const [zoneId, recordId] = resourceId.split(':');
207
+ if (zoneId && recordId) {
208
+ try {
209
+ await deleteRecord(token, zoneId, recordId);
210
+ }
211
+ catch (err) {
212
+ console.error(`Failed to cleanup DNS record ${recordId}:`, err.message);
213
+ }
214
+ }
215
+ }
216
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Cloudflare Registrar — domain availability check and registration.
3
+ * Uses the shared HTTP client. No external dependencies.
4
+ */
5
+ import type { ProvisionEmitter } from '../provisioners/types.js';
6
+ export interface DomainCheckResult {
7
+ available: boolean;
8
+ premium: boolean;
9
+ price?: number;
10
+ currency?: string;
11
+ canRegister: boolean;
12
+ error?: string;
13
+ }
14
+ export interface DomainRegistrationResult {
15
+ success: boolean;
16
+ domain?: string;
17
+ expiresAt?: string;
18
+ autoRenew?: boolean;
19
+ error?: string;
20
+ }
21
+ /**
22
+ * Check domain availability via Cloudflare Registrar API.
23
+ * Returns availability status and price if registrable.
24
+ */
25
+ export declare function checkDomainAvailability(token: string, accountId: string, domain: string): Promise<DomainCheckResult>;
26
+ /**
27
+ * Register a domain via Cloudflare Registrar.
28
+ * Auto-renew is enabled by default.
29
+ * This is IRREVERSIBLE — domains cannot be deleted via API.
30
+ */
31
+ export declare function registerDomain(token: string, accountId: string, domain: string, emit: ProvisionEmitter): Promise<DomainRegistrationResult>;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Cloudflare Registrar — domain availability check and registration.
3
+ * Uses the shared HTTP client. No external dependencies.
4
+ */
5
+ import { httpsGet, httpsPost, safeJsonParse } from '../provisioners/http-client.js';
6
+ const CF_API = 'api.cloudflare.com';
7
+ /** Cloudflare account IDs are 32-character hex strings */
8
+ const ACCOUNT_ID_RE = /^[a-f0-9]{32}$/i;
9
+ /** Valid domain format: labels separated by dots, each 1-63 chars of alphanumeric/hyphens */
10
+ const DOMAIN_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/i;
11
+ function cfHeaders(token) {
12
+ return {
13
+ 'Authorization': `Bearer ${token}`,
14
+ 'Content-Type': 'application/json',
15
+ };
16
+ }
17
+ /**
18
+ * Check domain availability via Cloudflare Registrar API.
19
+ * Returns availability status and price if registrable.
20
+ */
21
+ export async function checkDomainAvailability(token, accountId, domain) {
22
+ if (!ACCOUNT_ID_RE.test(accountId)) {
23
+ return { available: false, premium: false, canRegister: false, error: 'Invalid Cloudflare account ID format (expected 32-char hex)' };
24
+ }
25
+ if (!DOMAIN_RE.test(domain)) {
26
+ return { available: false, premium: false, canRegister: false, error: 'Invalid domain format' };
27
+ }
28
+ const res = await httpsGet(CF_API, `/client/v4/accounts/${encodeURIComponent(accountId)}/registrar/domains?query=${encodeURIComponent(domain)}`, cfHeaders(token));
29
+ if (res.status === 403) {
30
+ return { available: false, premium: false, canRegister: false, error: 'Token lacks Registrar:Read permission' };
31
+ }
32
+ if (res.status !== 200) {
33
+ return { available: false, premium: false, canRegister: false, error: `Registrar API returned ${res.status}` };
34
+ }
35
+ const data = safeJsonParse(res.body);
36
+ if (!data?.success || !data.result || data.result.length === 0) {
37
+ // If no results, try the domain status endpoint directly
38
+ const statusRes = await httpsGet(CF_API, `/client/v4/accounts/${encodeURIComponent(accountId)}/registrar/domains/${encodeURIComponent(domain)}`, cfHeaders(token));
39
+ if (statusRes.status === 404) {
40
+ // Domain not registered with this account — may be available.
41
+ // This is a best-guess: 404 means "not on THIS account", not "globally available."
42
+ // canRegister: true is a "proceed to try" signal — the registration API will
43
+ // correctly reject if the domain isn't actually available.
44
+ return { available: true, premium: false, canRegister: true, error: 'Domain not found on this account — availability is unconfirmed until registration is attempted' };
45
+ }
46
+ if (statusRes.status === 200) {
47
+ // Domain already registered with this account
48
+ return { available: false, premium: false, canRegister: false, error: 'Domain already registered on this account' };
49
+ }
50
+ return { available: false, premium: false, canRegister: false, error: data?.errors?.[0]?.message || 'Could not check availability' };
51
+ }
52
+ const result = data.result[0];
53
+ const price = result.pricing?.registration?.price;
54
+ const currency = result.pricing?.registration?.currency;
55
+ return {
56
+ available: result.available ?? false,
57
+ premium: result.premium ?? false,
58
+ price,
59
+ currency,
60
+ canRegister: (result.available ?? false) && !(result.premium ?? false),
61
+ };
62
+ }
63
+ /**
64
+ * Register a domain via Cloudflare Registrar.
65
+ * Auto-renew is enabled by default.
66
+ * This is IRREVERSIBLE — domains cannot be deleted via API.
67
+ */
68
+ export async function registerDomain(token, accountId, domain, emit) {
69
+ if (!ACCOUNT_ID_RE.test(accountId)) {
70
+ return { success: false, error: 'Invalid Cloudflare account ID format (expected 32-char hex)' };
71
+ }
72
+ if (!DOMAIN_RE.test(domain)) {
73
+ return { success: false, error: 'Invalid domain format' };
74
+ }
75
+ // Step 1: Check availability
76
+ emit({ step: 'registrar-check', status: 'started', message: `Checking availability of ${domain}` });
77
+ const check = await checkDomainAvailability(token, accountId, domain);
78
+ if (!check.canRegister) {
79
+ const reason = check.error || (check.premium ? 'Premium domain — manual registration required' : 'Domain is not available');
80
+ emit({ step: 'registrar-check', status: 'error', message: reason });
81
+ return { success: false, error: reason };
82
+ }
83
+ const priceDisplay = check.price ? ` ($${(check.price / 100).toFixed(2)} ${check.currency || 'USD'}/year)` : '';
84
+ emit({ step: 'registrar-check', status: 'done', message: `${domain} is available${priceDisplay}` });
85
+ // Step 2: Register the domain
86
+ emit({ step: 'registrar-register', status: 'started', message: `Registering ${domain}...` });
87
+ const body = JSON.stringify({
88
+ name: domain,
89
+ auto_renew: true,
90
+ });
91
+ // Use a longer timeout (60s) for domain registration — this is an irreversible
92
+ // financial transaction. A 30-second timeout could mask a successful purchase.
93
+ let res;
94
+ try {
95
+ res = await httpsPost(CF_API, `/client/v4/accounts/${encodeURIComponent(accountId)}/registrar/domains/${encodeURIComponent(domain)}/register`, cfHeaders(token), body, 60000);
96
+ }
97
+ catch (regError) {
98
+ // Registration call failed (timeout, network error, etc.).
99
+ // Verify whether the domain was actually purchased before reporting failure.
100
+ emit({ step: 'registrar-register', status: 'started', message: 'Registration request failed — verifying whether domain was purchased...' });
101
+ const verifyCheck = await checkDomainAvailability(token, accountId, domain);
102
+ if (!verifyCheck.available) {
103
+ // Domain is no longer available — the purchase likely went through
104
+ emit({ step: 'registrar-register', status: 'done', message: `Domain ${domain} registered (confirmed after transient error) — auto-renew enabled` });
105
+ return { success: true, domain, autoRenew: true };
106
+ }
107
+ // Domain is still available — the purchase genuinely failed
108
+ const errMsg = `Registration failed: ${regError.message}`;
109
+ emit({ step: 'registrar-register', status: 'error', message: errMsg });
110
+ return { success: false, error: errMsg };
111
+ }
112
+ if (res.status === 403) {
113
+ const errMsg = 'Token lacks Registrar:Edit permission — update your Cloudflare API token';
114
+ emit({ step: 'registrar-register', status: 'error', message: errMsg });
115
+ return { success: false, error: errMsg };
116
+ }
117
+ if (res.status !== 200 && res.status !== 201) {
118
+ // Non-success status — verify whether the domain was actually purchased despite the error
119
+ const verifyCheck = await checkDomainAvailability(token, accountId, domain);
120
+ if (!verifyCheck.available) {
121
+ emit({ step: 'registrar-register', status: 'done', message: `Domain ${domain} registered (confirmed after API error) — auto-renew enabled` });
122
+ return { success: true, domain, autoRenew: true };
123
+ }
124
+ const data = safeJsonParse(res.body);
125
+ const errMsg = data?.errors?.[0]?.message || `Registration returned ${res.status}`;
126
+ emit({ step: 'registrar-register', status: 'error', message: `Registration failed: ${errMsg}` });
127
+ return { success: false, error: errMsg };
128
+ }
129
+ const data = safeJsonParse(res.body);
130
+ if (!data?.success) {
131
+ const errMsg = data?.errors?.[0]?.message || 'Registration failed';
132
+ emit({ step: 'registrar-register', status: 'error', message: errMsg });
133
+ return { success: false, error: errMsg };
134
+ }
135
+ const registered = data.result;
136
+ emit({
137
+ step: 'registrar-register',
138
+ status: 'done',
139
+ message: `Domain ${domain} registered — auto-renew enabled`,
140
+ detail: registered?.expires_at ? `Expires: ${registered.expires_at}` : undefined,
141
+ });
142
+ return {
143
+ success: true,
144
+ domain: registered?.domain_name || domain,
145
+ expiresAt: registered?.expires_at,
146
+ autoRenew: registered?.auto_renew ?? true,
147
+ };
148
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * DNS provisioning types — shared across DNS providers.
3
+ */
4
+ export interface DnsRecord {
5
+ id: string;
6
+ type: 'A' | 'AAAA' | 'CNAME';
7
+ name: string;
8
+ content: string;
9
+ proxied: boolean;
10
+ ttl: number;
11
+ }
12
+ export interface ZoneInfo {
13
+ id: string;
14
+ name: string;
15
+ status: string;
16
+ }
17
+ export interface DnsProvisionResult {
18
+ success: boolean;
19
+ records: DnsRecord[];
20
+ zoneId: string;
21
+ error?: string;
22
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * DNS provisioning types — shared across DNS providers.
3
+ */
4
+ export {};
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Document Discovery — Wong's supporting document scanner.
3
+ *
4
+ * Discovers and catalogs all supporting documents in a project directory
5
+ * following the Blueprint Path convention. Used by /blueprint, /campaign,
6
+ * and /build to load context beyond the PRD.
7
+ *
8
+ * Convention:
9
+ * docs/PRD.md — Required. Product specification.
10
+ * docs/PROJECT-DIRECTIVES.md — Optional. Appended to CLAUDE.md.
11
+ * docs/OPERATIONS.md — Optional. Business context for Sisko.
12
+ * docs/ADR/*.md — Optional. Architecture decisions for Picard.
13
+ * docs/reference/* — Optional. Available to all agents.
14
+ *
15
+ * PRD Reference: RFC-blueprint-path.md
16
+ */
17
+ export interface DiscoveredDocuments {
18
+ prd: string | null;
19
+ projectDirectives: string | null;
20
+ operations: string | null;
21
+ adrs: string[];
22
+ references: string[];
23
+ total: number;
24
+ }
25
+ /**
26
+ * Discover all supporting documents in the project directory.
27
+ * Returns a catalog of found files with their paths and total count.
28
+ */
29
+ export declare function discoverDocuments(projectRoot: string): Promise<DiscoveredDocuments>;
30
+ /**
31
+ * Produce a human-readable summary of discovered documents.
32
+ */
33
+ export declare function summarizeDiscovery(docs: DiscoveredDocuments): string;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Document Discovery — Wong's supporting document scanner.
3
+ *
4
+ * Discovers and catalogs all supporting documents in a project directory
5
+ * following the Blueprint Path convention. Used by /blueprint, /campaign,
6
+ * and /build to load context beyond the PRD.
7
+ *
8
+ * Convention:
9
+ * docs/PRD.md — Required. Product specification.
10
+ * docs/PROJECT-DIRECTIVES.md — Optional. Appended to CLAUDE.md.
11
+ * docs/OPERATIONS.md — Optional. Business context for Sisko.
12
+ * docs/ADR/*.md — Optional. Architecture decisions for Picard.
13
+ * docs/reference/* — Optional. Available to all agents.
14
+ *
15
+ * PRD Reference: RFC-blueprint-path.md
16
+ */
17
+ import { existsSync } from 'node:fs';
18
+ import { readdir, lstat } from 'node:fs/promises';
19
+ import { join } from 'node:path';
20
+ const MAX_WALK_DEPTH = 10;
21
+ const MAX_WALK_FILES = 1000;
22
+ // ── Directive file search paths (checked in order) ──
23
+ const DIRECTIVE_PATHS = [
24
+ 'docs/PROJECT-DIRECTIVES.md',
25
+ 'docs/PROJECT-CLAUDE.md',
26
+ 'docs/DIRECTIVES.md',
27
+ 'PROJECT-CLAUDE.md',
28
+ 'PROJECT-DIRECTIVES.md',
29
+ ];
30
+ // ── Discovery ───────────────────────────────────────
31
+ /**
32
+ * Discover all supporting documents in the project directory.
33
+ * Returns a catalog of found files with their paths and total count.
34
+ */
35
+ export async function discoverDocuments(projectRoot) {
36
+ const result = {
37
+ prd: null,
38
+ projectDirectives: null,
39
+ operations: null,
40
+ adrs: [],
41
+ references: [],
42
+ total: 0,
43
+ };
44
+ // PRD (required for /blueprint, optional for other commands)
45
+ const prdPath = join(projectRoot, 'docs/PRD.md');
46
+ if (existsSync(prdPath)) {
47
+ result.prd = 'docs/PRD.md';
48
+ result.total++;
49
+ }
50
+ // Project-specific directives (checked in priority order)
51
+ for (const relativePath of DIRECTIVE_PATHS) {
52
+ const fullPath = join(projectRoot, relativePath);
53
+ if (existsSync(fullPath)) {
54
+ result.projectDirectives = relativePath;
55
+ result.total++;
56
+ break;
57
+ }
58
+ }
59
+ // Operations playbook
60
+ const opsPath = join(projectRoot, 'docs/OPERATIONS.md');
61
+ if (existsSync(opsPath)) {
62
+ result.operations = 'docs/OPERATIONS.md';
63
+ result.total++;
64
+ }
65
+ // Architecture Decision Records
66
+ const adrDir = join(projectRoot, 'docs/ADR');
67
+ if (existsSync(adrDir)) {
68
+ try {
69
+ const files = await readdir(adrDir);
70
+ result.adrs = files
71
+ .filter(f => f.endsWith('.md'))
72
+ .sort()
73
+ .map(f => `docs/ADR/${f}`);
74
+ result.total += result.adrs.length;
75
+ }
76
+ catch { /* directory unreadable */ }
77
+ }
78
+ // Also check docs/adrs/ (lowercase variant)
79
+ const adrsDir = join(projectRoot, 'docs/adrs');
80
+ if (existsSync(adrsDir) && result.adrs.length === 0) {
81
+ try {
82
+ const files = await readdir(adrsDir);
83
+ result.adrs = files
84
+ .filter(f => f.endsWith('.md'))
85
+ .sort()
86
+ .map(f => `docs/adrs/${f}`);
87
+ result.total += result.adrs.length;
88
+ }
89
+ catch { /* directory unreadable */ }
90
+ }
91
+ // Reference materials (recursive scan, all file types)
92
+ const refDir = join(projectRoot, 'docs/reference');
93
+ if (existsSync(refDir)) {
94
+ try {
95
+ result.references = await walkDirectory(refDir, 'docs/reference');
96
+ result.total += result.references.length;
97
+ }
98
+ catch { /* directory unreadable */ }
99
+ }
100
+ return result;
101
+ }
102
+ /**
103
+ * Recursively walk a directory and return all file paths relative to the project root.
104
+ */
105
+ async function walkDirectory(dirPath, relativeTo, depth = 0, fileCount = { count: 0 }) {
106
+ if (depth > MAX_WALK_DEPTH || fileCount.count > MAX_WALK_FILES)
107
+ return [];
108
+ const entries = await readdir(dirPath);
109
+ const files = [];
110
+ for (const entry of entries) {
111
+ if (fileCount.count > MAX_WALK_FILES)
112
+ break;
113
+ const fullPath = join(dirPath, entry);
114
+ const entryStat = await lstat(fullPath); // lstat: don't follow symlinks
115
+ if (entryStat.isSymbolicLink())
116
+ continue; // Skip symlinks — prevent escape
117
+ if (entryStat.isDirectory()) {
118
+ const subFiles = await walkDirectory(fullPath, `${relativeTo}/${entry}`, depth + 1, fileCount);
119
+ files.push(...subFiles);
120
+ }
121
+ else {
122
+ files.push(`${relativeTo}/${entry}`);
123
+ fileCount.count++;
124
+ }
125
+ }
126
+ return files.sort();
127
+ }
128
+ /**
129
+ * Produce a human-readable summary of discovered documents.
130
+ */
131
+ export function summarizeDiscovery(docs) {
132
+ const lines = [];
133
+ if (docs.prd)
134
+ lines.push(` PRD: ${docs.prd}`);
135
+ if (docs.projectDirectives)
136
+ lines.push(` Project directives: ${docs.projectDirectives}`);
137
+ if (docs.operations)
138
+ lines.push(` Operations playbook: ${docs.operations}`);
139
+ if (docs.adrs.length > 0)
140
+ lines.push(` ADRs: ${docs.adrs.length} architecture decision records`);
141
+ if (docs.references.length > 0)
142
+ lines.push(` References: ${docs.references.length} supporting files`);
143
+ lines.push(` Total: ${docs.total} documents discovered`);
144
+ return lines.join('\n');
145
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Startup environment validation script generator (ADR-018).
3
+ * Generates a script that checks required env vars at boot time.
4
+ * No dependencies — pure Node.js stdlib or Python stdlib.
5
+ */
6
+ export interface EnvValidatorResult {
7
+ success: boolean;
8
+ file: string;
9
+ error?: string;
10
+ }
11
+ /**
12
+ * Generate an environment validation script based on the project's .env file.
13
+ */
14
+ export declare function generateEnvValidator(projectDir: string, framework: string): Promise<EnvValidatorResult>;