@omega.js/manager 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (416) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/LICENSE +98 -0
  3. package/README.md +165 -0
  4. package/bin/omega +2 -0
  5. package/bin/omg +2 -0
  6. package/claude-plugin/.claude-plugin/plugin.json +11 -0
  7. package/claude-plugin/.mcp.json +9 -0
  8. package/claude-plugin/README.md +139 -0
  9. package/claude-plugin/hooks/gate/mark.sh +58 -0
  10. package/claude-plugin/hooks/gate/run.sh +97 -0
  11. package/claude-plugin/hooks/guard/run.sh +249 -0
  12. package/claude-plugin/hooks/hooks.json +63 -0
  13. package/claude-plugin/hooks/inject/run.sh +127 -0
  14. package/claude-plugin/hooks/lib/omega-gate.sh +20 -0
  15. package/claude-plugin/hooks/lib/omega-scope.sh +41 -0
  16. package/claude-plugin/hooks/lib/omega-skills.sh +209 -0
  17. package/claude-plugin/hooks/quality/run.sh +132 -0
  18. package/claude-plugin/hooks/shape/run.sh +45 -0
  19. package/claude-plugin/mcp-router-launch.js +29 -0
  20. package/claude-plugin/skills/README.md +48 -0
  21. package/claude-plugin/skills/accessibility/SKILL.md +31 -0
  22. package/claude-plugin/skills/analytics/SKILL.md +30 -0
  23. package/claude-plugin/skills/backend/SKILL.md +25 -0
  24. package/claude-plugin/skills/brandcheck/SKILL.md +29 -0
  25. package/claude-plugin/skills/browser/SKILL.md +64 -0
  26. package/claude-plugin/skills/client/SKILL.md +24 -0
  27. package/claude-plugin/skills/desktop/SKILL.md +25 -0
  28. package/claude-plugin/skills/extension/SKILL.md +25 -0
  29. package/claude-plugin/skills/main/SKILL.md +54 -0
  30. package/claude-plugin/skills/manager/SKILL.md +25 -0
  31. package/claude-plugin/skills/seo/SKILL.md +36 -0
  32. package/claude-plugin/skills/theme/SKILL.md +33 -0
  33. package/claude-plugin/skills/web/SKILL.md +29 -0
  34. package/dist/cli-run.js +34 -0
  35. package/dist/cli.js +29 -0
  36. package/dist/commands/build.js +17 -0
  37. package/dist/commands/clean.js +16 -0
  38. package/dist/commands/company.js +46 -0
  39. package/dist/commands/deploy.js +143 -0
  40. package/dist/commands/dev.js +437 -0
  41. package/dist/commands/devlog.js +25 -0
  42. package/dist/commands/help.js +48 -0
  43. package/dist/commands/manage.js +53 -0
  44. package/dist/commands/migrate.js +76 -0
  45. package/dist/commands/onboard.js +31 -0
  46. package/dist/commands/pipeline.js +319 -0
  47. package/dist/commands/test.js +380 -0
  48. package/dist/commands/update.js +128 -0
  49. package/dist/commands/version.js +8 -0
  50. package/dist/company-init.js +167 -0
  51. package/dist/company.js +283 -0
  52. package/dist/config.js +1359 -0
  53. package/dist/devlog/index.js +226 -0
  54. package/dist/devlog/lib/collect-commits.js +136 -0
  55. package/dist/devlog/lib/generate-post.js +201 -0
  56. package/dist/devlog/lib/ghostii.js +122 -0
  57. package/dist/devlog/lib/project-map.js +98 -0
  58. package/dist/devlog/lib/publish-website.js +88 -0
  59. package/dist/index.js +45 -0
  60. package/dist/lib/agents-md.js +201 -0
  61. package/dist/lib/analytics-secret.js +22 -0
  62. package/dist/lib/argv.js +21 -0
  63. package/dist/lib/auth-admin.js +120 -0
  64. package/dist/lib/automation-client.js +210 -0
  65. package/dist/lib/backend-marketing.js +49 -0
  66. package/dist/lib/brand.js +240 -0
  67. package/dist/lib/bundle-id.js +53 -0
  68. package/dist/lib/claude-settings.js +125 -0
  69. package/dist/lib/company-scaffold.js +213 -0
  70. package/dist/lib/company.js +227 -0
  71. package/dist/lib/config-flow.js +350 -0
  72. package/dist/lib/config-write.js +41 -0
  73. package/dist/lib/custom-target.js +75 -0
  74. package/dist/lib/domain-utils.js +19 -0
  75. package/dist/lib/duration.js +39 -0
  76. package/dist/lib/env-order.js +236 -0
  77. package/dist/lib/env-secret.js +78 -0
  78. package/dist/lib/firestore-rest.js +326 -0
  79. package/dist/lib/framework-bin.js +156 -0
  80. package/dist/lib/gitignore.js +63 -0
  81. package/dist/lib/google-auth.js +495 -0
  82. package/dist/lib/google-token.js +66 -0
  83. package/dist/lib/jwt.js +31 -0
  84. package/dist/lib/legacy-oauth.js +59 -0
  85. package/dist/lib/node-version.js +127 -0
  86. package/dist/lib/owner-plan.js +78 -0
  87. package/dist/lib/package-scripts.js +152 -0
  88. package/dist/lib/preflight.js +470 -0
  89. package/dist/lib/product-create.js +144 -0
  90. package/dist/lib/run-command.js +74 -0
  91. package/dist/lib/run-gates.js +70 -0
  92. package/dist/lib/run-output.js +42 -0
  93. package/dist/lib/run-summary.js +384 -0
  94. package/dist/lib/scaffold.js +427 -0
  95. package/dist/lib/service-input.js +207 -0
  96. package/dist/lib/service-runner.js +466 -0
  97. package/dist/lib/stale.js +24 -0
  98. package/dist/lib/target-selection.js +155 -0
  99. package/dist/lib/verb-fanout.js +128 -0
  100. package/dist/lib/verify-live.js +188 -0
  101. package/dist/manage.js +311 -0
  102. package/dist/omega-bin.js +7 -0
  103. package/dist/onboard.js +532 -0
  104. package/dist/services/account/ensure/users.js +259 -0
  105. package/dist/services/account/index.js +97 -0
  106. package/dist/services/account/lib/backend-client.js +107 -0
  107. package/dist/services/account/lib/password.js +36 -0
  108. package/dist/services/account/lib/resolve-password.js +100 -0
  109. package/dist/services/advertising/ensure/sites.js +101 -0
  110. package/dist/services/advertising/index.js +101 -0
  111. package/dist/services/advertising/lib/adsense-api.js +41 -0
  112. package/dist/services/ai/ensure/keys.js +21 -0
  113. package/dist/services/ai/index.js +36 -0
  114. package/dist/services/analytics/ensure/google-firebase-link.js +230 -0
  115. package/dist/services/analytics/ensure/google-streams.js +256 -0
  116. package/dist/services/analytics/ensure/meta-pixel.js +22 -0
  117. package/dist/services/analytics/ensure/tiktok-pixel.js +22 -0
  118. package/dist/services/analytics/index.js +134 -0
  119. package/dist/services/analytics/lib/analytics-api.js +156 -0
  120. package/dist/services/analytics/lib/meta-api.js +83 -0
  121. package/dist/services/analytics/lib/pixel-account.js +78 -0
  122. package/dist/services/analytics/lib/pixel-provision.js +152 -0
  123. package/dist/services/analytics/lib/pixel-specs.js +57 -0
  124. package/dist/services/analytics/lib/pixel-token.js +88 -0
  125. package/dist/services/analytics/lib/property-flow.js +77 -0
  126. package/dist/services/analytics/lib/tiktok-api.js +157 -0
  127. package/dist/services/analytics/lib/tiktok-auth.js +153 -0
  128. package/dist/services/assets/ensure/logo-gen.js +68 -0
  129. package/dist/services/assets/index.js +109 -0
  130. package/dist/services/assets/lib/assets-config.js +168 -0
  131. package/dist/services/assets/lib/brandmark-api.js +160 -0
  132. package/dist/services/assets/lib/derived.js +113 -0
  133. package/dist/services/assets/lib/font-loader.js +67 -0
  134. package/dist/services/assets/lib/reconcile.js +122 -0
  135. package/dist/services/assets/lib/reset.js +118 -0
  136. package/dist/services/assets/lib/svg-logo-generator.js +154 -0
  137. package/dist/services/assets/lib/svg-to-black.js +32 -0
  138. package/dist/services/assets/write/favicons.js +82 -0
  139. package/dist/services/assets/write/icons.js +64 -0
  140. package/dist/services/assets/write/process.js +96 -0
  141. package/dist/services/assets/write/reconcile.js +30 -0
  142. package/dist/services/assets/write/social-icons.js +108 -0
  143. package/dist/services/assets/write/templates.js +345 -0
  144. package/dist/services/bookmark/ensure/sync.js +255 -0
  145. package/dist/services/bookmark/index.js +17 -0
  146. package/dist/services/campaigns/ensure/contact-person.js +40 -0
  147. package/dist/services/campaigns/ensure/custom-fields.js +84 -0
  148. package/dist/services/campaigns/ensure/domain-auth.js +92 -0
  149. package/dist/services/campaigns/ensure/event-webhook.js +89 -0
  150. package/dist/services/campaigns/ensure/link-branding.js +187 -0
  151. package/dist/services/campaigns/ensure/list.js +50 -0
  152. package/dist/services/campaigns/ensure/segments.js +109 -0
  153. package/dist/services/campaigns/ensure/sender-identity.js +153 -0
  154. package/dist/services/campaigns/ensure/unsubscribe-groups.js +116 -0
  155. package/dist/services/campaigns/index.js +79 -0
  156. package/dist/services/campaigns/lib/dns-sync.js +81 -0
  157. package/dist/services/campaigns/lib/segment-query.js +138 -0
  158. package/dist/services/campaigns/lib/sendgrid-api.js +287 -0
  159. package/dist/services/captcha/ensure/site-key.js +83 -0
  160. package/dist/services/captcha/index.js +78 -0
  161. package/dist/services/captcha/lib/console-url.js +45 -0
  162. package/dist/services/captcha/lib/recaptcha-api.js +36 -0
  163. package/dist/services/certificates/ensure/api-key.js +22 -0
  164. package/dist/services/certificates/ensure/bundle-ids.js +115 -0
  165. package/dist/services/certificates/ensure/certificates.js +187 -0
  166. package/dist/services/certificates/ensure/profiles.js +143 -0
  167. package/dist/services/certificates/index.js +225 -0
  168. package/dist/services/certificates/lib/apple-api.js +183 -0
  169. package/dist/services/certificates/lib/certificate-manager.js +252 -0
  170. package/dist/services/certificates/lib/identifier-manager.js +107 -0
  171. package/dist/services/certificates/lib/keychain.js +135 -0
  172. package/dist/services/certificates/lib/manual-walkthrough.js +165 -0
  173. package/dist/services/certificates/lib/profile-manager.js +115 -0
  174. package/dist/services/chat/data/baseline-knowledge.md +96 -0
  175. package/dist/services/chat/ensure/chat.js +106 -0
  176. package/dist/services/chat/ensure/user.js +45 -0
  177. package/dist/services/chat/index.js +139 -0
  178. package/dist/services/chat/lib/baseline-knowledge.js +137 -0
  179. package/dist/services/cloud/ensure/authentication.js +267 -0
  180. package/dist/services/cloud/ensure/billing.js +120 -0
  181. package/dist/services/cloud/ensure/cloud-messaging.js +105 -0
  182. package/dist/services/cloud/ensure/database.js +60 -0
  183. package/dist/services/cloud/ensure/firestore.js +79 -0
  184. package/dist/services/cloud/ensure/functions.js +49 -0
  185. package/dist/services/cloud/ensure/hosting.js +398 -0
  186. package/dist/services/cloud/ensure/oauth-consent.js +248 -0
  187. package/dist/services/cloud/ensure/project-settings.js +72 -0
  188. package/dist/services/cloud/ensure/sdk-config.js +95 -0
  189. package/dist/services/cloud/ensure/service-account.js +152 -0
  190. package/dist/services/cloud/ensure/services.js +103 -0
  191. package/dist/services/cloud/ensure/storage.js +47 -0
  192. package/dist/services/cloud/index.js +130 -0
  193. package/dist/services/cloud/lib/access-heal.js +130 -0
  194. package/dist/services/cloud/lib/firebase-api.js +662 -0
  195. package/dist/services/cloud/lib/project-flow.js +102 -0
  196. package/dist/services/directory/ensure/entry.js +48 -0
  197. package/dist/services/directory/index.js +64 -0
  198. package/dist/services/directory/lib/blocks.js +104 -0
  199. package/dist/services/disperse/index.js +45 -0
  200. package/dist/services/disperse/write/certs.js +117 -0
  201. package/dist/services/domain/ensure/nameservers.js +179 -0
  202. package/dist/services/domain/index.js +69 -0
  203. package/dist/services/domain/lib/namecheap-api.js +167 -0
  204. package/dist/services/domain/lib/registrars.js +46 -0
  205. package/dist/services/domain/lib/whitelist-walkthrough.js +76 -0
  206. package/dist/services/edge/ensure/cache-rules.js +105 -0
  207. package/dist/services/edge/ensure/dns-records.js +304 -0
  208. package/dist/services/edge/ensure/email-routing.js +292 -0
  209. package/dist/services/edge/ensure/rules-configuration.js +98 -0
  210. package/dist/services/edge/ensure/rules-managed-transforms.js +101 -0
  211. package/dist/services/edge/ensure/rules-redirect.js +107 -0
  212. package/dist/services/edge/ensure/rules-response-headers.js +90 -0
  213. package/dist/services/edge/ensure/rules-security.js +142 -0
  214. package/dist/services/edge/ensure/speed-scheduled-tests.js +98 -0
  215. package/dist/services/edge/ensure/workers.js +219 -0
  216. package/dist/services/edge/ensure/zone-settings.js +108 -0
  217. package/dist/services/edge/ensure/zone.js +183 -0
  218. package/dist/services/edge/index.js +73 -0
  219. package/dist/services/edge/lib/cloudflare-api.js +84 -0
  220. package/dist/services/edge/lib/dns-records-helpers.js +518 -0
  221. package/dist/services/edge/lib/read-cache.js +16 -0
  222. package/dist/services/edge/lib/ruleset-helper.js +108 -0
  223. package/dist/services/edge/workers/omega-api-proxy.js +43 -0
  224. package/dist/services/email/data/baseline-filter.md +4 -0
  225. package/dist/services/email/data/baseline-knowledge.md +86 -0
  226. package/dist/services/email/ensure/agent.js +107 -0
  227. package/dist/services/email/ensure/user.js +45 -0
  228. package/dist/services/email/index.js +138 -0
  229. package/dist/services/email/lib/baseline.js +82 -0
  230. package/dist/services/email/lib/knowledge-file.js +53 -0
  231. package/dist/services/forms/ensure/form.js +46 -0
  232. package/dist/services/forms/ensure/user.js +45 -0
  233. package/dist/services/forms/index.js +138 -0
  234. package/dist/services/migrations/ensure/notifications.js +189 -0
  235. package/dist/services/migrations/ensure/orders.js +34 -0
  236. package/dist/services/migrations/ensure/payment-provider.js +132 -0
  237. package/dist/services/migrations/ensure/payments-intents.js +34 -0
  238. package/dist/services/migrations/ensure/state-retirement.js +266 -0
  239. package/dist/services/migrations/ensure/targets-rename.js +124 -0
  240. package/dist/services/migrations/ensure/users.js +1176 -0
  241. package/dist/services/migrations/index.js +101 -0
  242. package/dist/services/migrations/lib/attribution-touch.js +64 -0
  243. package/dist/services/migrations/lib/ensure-metadata.js +144 -0
  244. package/dist/services/migrations/lib/migration-runner.js +473 -0
  245. package/dist/services/migrations/lib/sanitize-strings.js +112 -0
  246. package/dist/services/migrations/lib/schema-validator.js +154 -0
  247. package/dist/services/monitoring/ensure/dsn.js +54 -0
  248. package/dist/services/monitoring/ensure/projects.js +135 -0
  249. package/dist/services/monitoring/index.js +54 -0
  250. package/dist/services/monitoring/lib/sentry-api.js +94 -0
  251. package/dist/services/newsletter/ensure/custom-fields.js +90 -0
  252. package/dist/services/newsletter/ensure/publication.js +94 -0
  253. package/dist/services/newsletter/ensure/segments.js +140 -0
  254. package/dist/services/newsletter/ensure/webhook.js +108 -0
  255. package/dist/services/newsletter/index.js +64 -0
  256. package/dist/services/newsletter/lib/beehiiv-api.js +124 -0
  257. package/dist/services/newsletter/lib/segment-automation.js +577 -0
  258. package/dist/services/payment/ensure/chargebee-account.js +30 -0
  259. package/dist/services/payment/ensure/chargebee-products.js +359 -0
  260. package/dist/services/payment/ensure/chargebee-webhook.js +113 -0
  261. package/dist/services/payment/ensure/paypal-account.js +36 -0
  262. package/dist/services/payment/ensure/paypal-products.js +374 -0
  263. package/dist/services/payment/ensure/paypal-webhook.js +128 -0
  264. package/dist/services/payment/ensure/stripe-account.js +93 -0
  265. package/dist/services/payment/ensure/stripe-disputes.js +56 -0
  266. package/dist/services/payment/ensure/stripe-products.js +245 -0
  267. package/dist/services/payment/ensure/stripe-radar.js +74 -0
  268. package/dist/services/payment/ensure/stripe-webhook.js +130 -0
  269. package/dist/services/payment/index.js +163 -0
  270. package/dist/services/payment/lib/chargebee-api.js +338 -0
  271. package/dist/services/payment/lib/payment-utils.js +116 -0
  272. package/dist/services/payment/lib/paypal-api.js +354 -0
  273. package/dist/services/payment/lib/provider-setup.js +134 -0
  274. package/dist/services/payment/lib/stripe-api.js +152 -0
  275. package/dist/services/repo/ensure/org.js +70 -0
  276. package/dist/services/repo/ensure/pages.js +72 -0
  277. package/dist/services/repo/ensure/repo.js +90 -0
  278. package/dist/services/repo/index.js +52 -0
  279. package/dist/services/repo/lib/github-api.js +196 -0
  280. package/dist/services/search/ensure/ga-link.js +57 -0
  281. package/dist/services/search/ensure/property.js +137 -0
  282. package/dist/services/search/ensure/sitemaps.js +70 -0
  283. package/dist/services/search/index.js +61 -0
  284. package/dist/services/search/lib/search-console-api.js +92 -0
  285. package/dist/services/seo/ensure/github-repos.js +271 -0
  286. package/dist/services/seo/index.js +55 -0
  287. package/dist/services/seo/lib/gh-api.js +161 -0
  288. package/dist/services/seo/templates/developer-tool/.github/workflows/maintenance.yml +25 -0
  289. package/dist/services/seo/templates/developer-tool/.nvmrc +1 -0
  290. package/dist/services/seo/templates/developer-tool/_README.md.js +121 -0
  291. package/dist/services/seo/templates/developer-tool/_package.json.js +33 -0
  292. package/dist/services/seo/templates/developer-tool/src/index.js +120 -0
  293. package/dist/services/seo/templates/index.js +57 -0
  294. package/dist/services/server/ensure/brands.js +63 -0
  295. package/dist/services/server/index.js +42 -0
  296. package/dist/services/testing/ensure/target-checks.js +97 -0
  297. package/dist/services/testing/index.js +11 -0
  298. package/dist/services/testing/lib/checks.js +467 -0
  299. package/dist/services/update/index.js +20 -0
  300. package/dist/services/update/lib/cache.js +59 -0
  301. package/dist/services/update/lib/fingerprint.js +157 -0
  302. package/dist/services/update/write/targets.js +206 -0
  303. package/dist/services/workspace/ensure/agents.js +42 -0
  304. package/dist/services/workspace/ensure/claude-settings.js +34 -0
  305. package/dist/services/workspace/ensure/config.js +54 -0
  306. package/dist/services/workspace/ensure/defaults.js +60 -0
  307. package/dist/services/workspace/ensure/env-keys.js +55 -0
  308. package/dist/services/workspace/ensure/env-order.js +66 -0
  309. package/dist/services/workspace/ensure/env-rules.js +97 -0
  310. package/dist/services/workspace/ensure/gitignore.js +18 -0
  311. package/dist/services/workspace/ensure/scripts.js +126 -0
  312. package/dist/services/workspace/ensure/structure.js +97 -0
  313. package/dist/services/workspace/ensure/translation-sdk.js +148 -0
  314. package/dist/services/workspace/ensure/workflows.js +54 -0
  315. package/dist/services/workspace/index.js +8 -0
  316. package/dist/vendor/account/engine.js +182 -0
  317. package/dist/vendor/account/features.js +220 -0
  318. package/dist/vendor/account/index.js +53 -0
  319. package/dist/vendor/account/schema.js +272 -0
  320. package/dist/vendor/account/subscription.js +38 -0
  321. package/dist/vendor/config/company.js +31 -0
  322. package/dist/vendor/config/defaults.js +173 -0
  323. package/dist/vendor/config/demo.js +18 -0
  324. package/dist/vendor/config/desktop-artifacts.js +110 -0
  325. package/dist/vendor/config/edit.js +769 -0
  326. package/dist/vendor/config/env-delivery.js +145 -0
  327. package/dist/vendor/config/env-rules.js +93 -0
  328. package/dist/vendor/config/env-schema.js +1078 -0
  329. package/dist/vendor/config/env.js +445 -0
  330. package/dist/vendor/config/hooks.js +97 -0
  331. package/dist/vendor/config/index.js +237 -0
  332. package/dist/vendor/config/instances.js +208 -0
  333. package/dist/vendor/config/load.js +490 -0
  334. package/dist/vendor/config/merge.js +68 -0
  335. package/dist/vendor/config/order.js +139 -0
  336. package/dist/vendor/config/ports.js +374 -0
  337. package/dist/vendor/config/providers.js +32 -0
  338. package/dist/vendor/config/repo.js +142 -0
  339. package/dist/vendor/config/retired-keys.js +430 -0
  340. package/dist/vendor/config/schema.js +1610 -0
  341. package/dist/vendor/config/secrets.js +50 -0
  342. package/dist/vendor/config/seed.js +34 -0
  343. package/dist/vendor/config/site-global.js +205 -0
  344. package/dist/vendor/config/validate.js +554 -0
  345. package/dist/vendor/config/winback.js +61 -0
  346. package/dist/vendor/devkit/attach-log-file.js +262 -0
  347. package/dist/vendor/devkit/certs.js +199 -0
  348. package/dist/vendor/devkit/ci-workflows.js +520 -0
  349. package/dist/vendor/devkit/cli-router.js +156 -0
  350. package/dist/vendor/devkit/command-path.js +46 -0
  351. package/dist/vendor/devkit/deploy-record.js +180 -0
  352. package/dist/vendor/devkit/flows.js +317 -0
  353. package/dist/vendor/devkit/local-https.js +360 -0
  354. package/dist/vendor/devkit/local.js +1905 -0
  355. package/dist/vendor/devkit/logger.js +128 -0
  356. package/dist/vendor/devkit/omega-bin.js +345 -0
  357. package/dist/vendor/devkit/prompt.js +187 -0
  358. package/dist/vendor/devkit/safe-install.js +18 -0
  359. package/dist/vendor/devkit/stop-signals.js +28 -0
  360. package/dist/vendor/devkit/test/scope.js +162 -0
  361. package/dist/vendor/devkit/translate/cache.js +84 -0
  362. package/dist/vendor/devkit/translate/engine.js +243 -0
  363. package/dist/vendor/devkit/translate/index.js +49 -0
  364. package/dist/vendor/devkit/translate/languages.js +134 -0
  365. package/dist/vendor/devkit/translate/providers.js +188 -0
  366. package/dist/vendor/devkit/update.js +569 -0
  367. package/docs/AGENTS.md +200 -0
  368. package/docs/account.md +45 -0
  369. package/docs/advertising.md +46 -0
  370. package/docs/ai.md +34 -0
  371. package/docs/analytics.md +69 -0
  372. package/docs/assets.md +57 -0
  373. package/docs/bookmark.md +26 -0
  374. package/docs/brand.md +151 -0
  375. package/docs/campaigns.md +96 -0
  376. package/docs/captcha.md +46 -0
  377. package/docs/certificates.md +68 -0
  378. package/docs/chat.md +47 -0
  379. package/docs/cloud.md +110 -0
  380. package/docs/company.md +64 -0
  381. package/docs/directory.md +140 -0
  382. package/docs/disperse.md +46 -0
  383. package/docs/domain.md +56 -0
  384. package/docs/edge.md +234 -0
  385. package/docs/email.md +42 -0
  386. package/docs/forms.md +48 -0
  387. package/docs/index.md +109 -0
  388. package/docs/migration.md +203 -0
  389. package/docs/migrations.md +56 -0
  390. package/docs/monitoring.md +41 -0
  391. package/docs/newsletter.md +48 -0
  392. package/docs/payment.md +83 -0
  393. package/docs/repo.md +48 -0
  394. package/docs/search.md +44 -0
  395. package/docs/seo.md +34 -0
  396. package/docs/server.md +37 -0
  397. package/docs/shared/agent-docs.md +89 -0
  398. package/docs/shared/analytics.md +612 -0
  399. package/docs/shared/brands.md +51 -0
  400. package/docs/shared/breaking-changes.md +497 -0
  401. package/docs/shared/config.md +1387 -0
  402. package/docs/shared/deploys.md +215 -0
  403. package/docs/shared/icons.md +201 -0
  404. package/docs/shared/local-dev.md +147 -0
  405. package/docs/shared/logging.md +202 -0
  406. package/docs/shared/monitoring.md +153 -0
  407. package/docs/shared/publishing.md +183 -0
  408. package/docs/shared/rulings.md +34 -0
  409. package/docs/shared/testing.md +147 -0
  410. package/docs/shared/theming.md +604 -0
  411. package/docs/shared/translation.md +291 -0
  412. package/docs/shared/updates.md +61 -0
  413. package/docs/testing.md +34 -0
  414. package/docs/update.md +36 -0
  415. package/docs/workspace.md +64 -0
  416. package/package.json +88 -0
@@ -0,0 +1,532 @@
1
+ /**
2
+ * `omega onboard` — the brand-creation wizard: collect the brand's
3
+ * identity (flags win, prompts fill the gaps in a TTY, derivation covers the
4
+ * rest), scaffold the plan-§0 brand-monorepo skeleton, prove the config
5
+ * loads, then optionally hand off to manage.
6
+ *
7
+ * Context-sensitive on where it runs, mirroring manage:
8
+ * inside a company workspace → the new brand lands under brands.roots[0]
9
+ * (or a selected root) + gets the company stamp
10
+ * inside an existing brand → resume: no wizard, answers come from the
11
+ * brand's own config, only MISSING files fill in
12
+ * anywhere else → in-place: the cwd becomes the brand root
13
+ * (the "Use this template" story — clone, onboard)
14
+ *
15
+ * Non-interactive runs derive everything derivable from --id (name, url,
16
+ * email) and use the web+backend target default; only a missing, underivable
17
+ * id is an error. --dry-run never prompts and never writes — it prints the
18
+ * file plan. Scaffolding never overwrites: rerunning onboard converges.
19
+ */
20
+
21
+ const path = require('node:path');
22
+ const { spawn, spawnSync } = require('node:child_process');
23
+ const chalk = require('chalk').default;
24
+ const jetpack = require('fs-jetpack');
25
+
26
+ const { input, select, checkbox, confirm } = require('./vendor/devkit/prompt.js');
27
+ const { TARGETS, hasOmegaConfig } = require('./vendor/config/index.js');
28
+
29
+ const { TARGET_DIRS, TARGET_FRAMEWORKS, DEFAULTS } = require('./config.js');
30
+ const { DEFAULT_BRAND_ROOTS, resolveManageRoot, readRawConfig, stampCompanyMarker } = require('./lib/company.js');
31
+ const { loadBrand } = require('./lib/brand.js');
32
+ const { buildScaffoldPlan, applyScaffoldPlan, printPlanResults } = require('./lib/scaffold.js');
33
+ const { canPrompt } = require('./lib/run-gates.js');
34
+ const { deriveBundleIdPrefix } = require('./lib/bundle-id.js');
35
+ const { convertLegacyOAuthSecret } = require('./lib/legacy-oauth.js');
36
+
37
+ // New-brand ids are a conservative subset of the schema's brand.id pattern —
38
+ // always dir-, repo-, and URL-scheme-safe.
39
+ const ID_PATTERN = /^[a-z][a-z0-9-]*$/;
40
+ const ID_HINT = 'lowercase letters, numbers, dashes; starts with a letter';
41
+
42
+ const DEFAULT_TARGETS = ['web', 'backend'];
43
+
44
+ /**
45
+ * Derive a valid brand id from a directory name ('My Brand 2' → 'my-brand-2');
46
+ * null when nothing valid survives.
47
+ */
48
+ function deriveId(dirName) {
49
+ const id = String(dirName)
50
+ .toLowerCase()
51
+ .replace(/[^a-z0-9-]+/g, '-')
52
+ .replace(/-+/g, '-')
53
+ .replace(/^[^a-z]+|-+$/g, '');
54
+
55
+ return ID_PATTERN.test(id) ? id : null;
56
+ }
57
+
58
+ /** 'acme-demo' → 'Acme Demo' */
59
+ function deriveName(id) {
60
+ return id
61
+ .split('-')
62
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
63
+ .join(' ');
64
+ }
65
+
66
+ /** omega-manager's onboarding default: dashes drop out of the domain. */
67
+ function deriveUrl(id) {
68
+ return `https://${id.replace(/-/g, '')}.com`;
69
+ }
70
+
71
+ function deriveEmail(url) {
72
+ const host = url.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
73
+ return `support@${host}`;
74
+ }
75
+
76
+ /**
77
+ * The brand.contact.person block from the wizard's answers (#770): the NAME
78
+ * is the whole block's condition — a headshot or a link with nobody to sign
79
+ * is not a person, and no name at all writes nothing at all, because a human
80
+ * fact can never be derived (the manage walk's own gate is what catches the
81
+ * gap later, rather than a framework identity signing the brand's email).
82
+ *
83
+ * @returns {Object|null} - { name, image?, url? }, or null when unanswered
84
+ */
85
+ function buildPerson({ name, image, url }) {
86
+ const clean = (value) => (typeof value === 'string' ? value.trim() : '');
87
+
88
+ if (!clean(name)) {
89
+ return null;
90
+ }
91
+
92
+ return {
93
+ name: clean(name),
94
+ ...(clean(image) ? { image: clean(image) } : {}),
95
+ ...(clean(url) ? { url: clean(url) } : {}),
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Normalize a --targets flag value ('web,backend' or array) and validate
101
+ * every entry against the canonical target list.
102
+ */
103
+ function parseTargetsFlag(value) {
104
+ const targets = Array.isArray(value) ? value : String(value).split(',');
105
+ const cleaned = targets.map((t) => t.trim()).filter(Boolean);
106
+
107
+ const unknown = cleaned.filter((t) => !TARGETS.includes(t));
108
+ if (unknown.length > 0) {
109
+ throw new Error(`Unknown target(s): ${unknown.join(', ')} — valid targets: ${TARGETS.join(', ')}`);
110
+ }
111
+ if (cleaned.length === 0) {
112
+ throw new Error(`--targets needs at least one of: ${TARGETS.join(', ')}`);
113
+ }
114
+
115
+ return cleaned;
116
+ }
117
+
118
+ /**
119
+ * The admin-account list a fresh brand would inherit without writing
120
+ * anything: the company config's account.admins when onboarding into a
121
+ * company workspace, else the manager's built-in default.
122
+ */
123
+ function inheritedAdmins(companyRoot) {
124
+ const companyAdmins = companyRoot ? readRawConfig(companyRoot)?.account?.admins : null;
125
+
126
+ return Array.isArray(companyAdmins) && companyAdmins.length > 0
127
+ ? { list: companyAdmins, source: 'company config' }
128
+ : { list: DEFAULTS.account.admins, source: 'built-in default' };
129
+ }
130
+
131
+ /**
132
+ * The accounts step: show the list the brand inherits and let the owner
133
+ * keep it (nothing written — the source layer keeps owning it) or define
134
+ * the brand's own, which lands as account.admins in its omega.json5.
135
+ *
136
+ * @returns {Array|null} Customized entries, or null to inherit
137
+ */
138
+ async function collectAccountAdmins(inherited, interactive) {
139
+ if (!interactive) {
140
+ return null;
141
+ }
142
+
143
+ console.log('');
144
+ console.log(` Managed accounts the brand inherits ${chalk.dim(`(${inherited.source})`)}:`);
145
+ for (const entry of inherited.list) {
146
+ const flags = [entry.account && 'account', entry.marketing && 'marketing'].filter(Boolean).join(' + ');
147
+ console.log(` ${chalk.dim('•')} ${chalk.cyan(entry.email)} ${chalk.dim(`(${flags || 'nothing managed'})`)}`);
148
+ }
149
+
150
+ const keep = await confirm({
151
+ message: 'Keep this account list? (customizing writes account.admins into the brand config)',
152
+ default: true,
153
+ });
154
+ if (keep) {
155
+ return null;
156
+ }
157
+
158
+ const entries = [];
159
+ do {
160
+ const email = (await input({
161
+ message: `Account email (${'{domain}'} = brand domain):`,
162
+ ...(entries.length === 0 ? { default: 'support@{domain}' } : {}),
163
+ validate: (value) => (value.trim().includes('@') ? true : 'Must be an email address'),
164
+ })).trim();
165
+ const account = await confirm({ message: 'Manage the Firebase Auth account (create + admin role + top plan)?', default: true });
166
+ const marketing = await confirm({ message: 'Sync the contact to marketing providers?', default: true });
167
+ entries.push({ email, account, marketing });
168
+ } while (await confirm({ message: 'Add another account?', default: false }));
169
+
170
+ return entries;
171
+ }
172
+
173
+ /**
174
+ * Collect the wizard answers for a FRESH brand: flags win, prompts fill the
175
+ * gaps interactively, derivation covers the rest. `defaultId` comes from the
176
+ * in-place directory name (null in company mode); `companyRoot` (when
177
+ * onboarding into a company workspace) supplies the inherited account list.
178
+ */
179
+ async function collectAnswers(options, defaultId, interactive, companyRoot = null) {
180
+ // id — the only field with no universal derivation
181
+ let id = options.id ?? null;
182
+ if (id != null && !ID_PATTERN.test(String(id))) {
183
+ throw new Error(`Invalid brand id ${JSON.stringify(String(id))} — ${ID_HINT}`);
184
+ }
185
+ if (!id) {
186
+ if (interactive) {
187
+ id = await input({
188
+ message: 'Brand ID:',
189
+ ...(defaultId ? { default: defaultId } : {}),
190
+ validate: (value) => (ID_PATTERN.test(value.trim()) ? true : `Must be ${ID_HINT}`),
191
+ });
192
+ id = id.trim();
193
+ } else if (defaultId) {
194
+ id = defaultId;
195
+ } else {
196
+ throw new Error('Brand id required — pass --id=<id> (or run in an interactive terminal)');
197
+ }
198
+ }
199
+
200
+ // name / url — derivable from the id
201
+ let name = options.name ?? null;
202
+ if (!name) {
203
+ name = interactive
204
+ ? (await input({
205
+ message: 'Brand name:',
206
+ default: deriveName(id),
207
+ validate: (value) => (value.trim().length > 0 ? true : 'Required'),
208
+ })).trim()
209
+ : deriveName(id);
210
+ }
211
+
212
+ let url = options.url ?? null;
213
+ if (url != null && !/^https?:\/\//.test(String(url))) {
214
+ throw new Error(`Invalid brand url ${JSON.stringify(String(url))} — must start with http(s)://`);
215
+ }
216
+ if (!url) {
217
+ url = interactive
218
+ ? (await input({
219
+ message: 'Brand URL:',
220
+ default: deriveUrl(id),
221
+ validate: (value) => (/^https?:\/\//.test(value.trim()) ? true : 'Must start with http(s)://'),
222
+ })).trim()
223
+ : deriveUrl(id);
224
+ }
225
+
226
+ // description / tagline — optional; empty answers stay out of the config
227
+ let description = options.description ?? null;
228
+ if (description == null && interactive) {
229
+ description = (await input({ message: 'Brand description (optional):' })).trim();
230
+ }
231
+
232
+ let tagline = options.tagline ?? null;
233
+ if (tagline == null && interactive) {
234
+ tagline = (await input({ message: 'Brand tagline (optional, ~3 words):' })).trim();
235
+ }
236
+
237
+ // contact person — the human who signs the personal sends. The support
238
+ // email derives from the url; a NAME cannot, so the wizard asks for it and
239
+ // requires an answer, while a non-interactive run without --contactName
240
+ // writes no person at all rather than inventing one (#770). The headshot
241
+ // and the link are optional garnish on the same signoff.
242
+ let contactName = options.contactName ?? null;
243
+ if (contactName != null && (typeof contactName !== 'string' || !contactName.trim())) {
244
+ throw new Error('Invalid --contactName — pass the person\'s name (--contactName="Jane Doe")');
245
+ }
246
+ if (contactName == null && !interactive && (options.contactImage != null || options.contactUrl != null)) {
247
+ throw new Error('--contactImage/--contactUrl need a --contactName to belong to — nothing would be written');
248
+ }
249
+ if (contactName == null && interactive) {
250
+ contactName = await input({
251
+ message: 'Contact person (signs the personal emails: welcome, nudges, checkups):',
252
+ validate: (value) => (value.trim().length > 0 ? true : 'Required'),
253
+ });
254
+ }
255
+
256
+ let contactImage = options.contactImage ?? null;
257
+ if (contactImage == null && interactive && contactName) {
258
+ contactImage = await input({ message: 'Contact person headshot URL (optional):' });
259
+ }
260
+
261
+ let contactUrl = options.contactUrl ?? null;
262
+ if (contactUrl == null && interactive && contactName) {
263
+ contactUrl = await input({ message: 'Contact person link URL (optional):' });
264
+ }
265
+
266
+ // targets — checkbox in a TTY, web+backend otherwise
267
+ let targets = options.targets != null ? parseTargetsFlag(options.targets) : null;
268
+ if (!targets) {
269
+ targets = interactive
270
+ ? await checkbox({
271
+ message: 'Targets (key presence in omega.json5 = enabled):',
272
+ choices: TARGETS.map((target) => ({
273
+ name: `${target} — targets/${TARGET_DIRS[target] || target}${TARGET_FRAMEWORKS[target] ? ` (${TARGET_FRAMEWORKS[target]})` : ' (reserved — MAM overhaul pending)'}`,
274
+ value: target,
275
+ checked: DEFAULT_TARGETS.includes(target),
276
+ })),
277
+ validate: (selection) => (selection.length > 0 ? true : 'Select at least one target'),
278
+ })
279
+ : DEFAULT_TARGETS;
280
+ }
281
+
282
+ // accounts — inherit by default (null writes nothing); customizing lands
283
+ // the brand's own account.admins
284
+ const accountAdmins = await collectAccountAdmins(inheritedAdmins(companyRoot), interactive);
285
+
286
+ // Bundle-id prefix — reverse-DNS of the parent company's domain when
287
+ // onboarding into a company workspace, else the brand's own (seeded into
288
+ // config only for targets that sign apps)
289
+ const companyUrl = companyRoot ? readRawConfig(companyRoot)?.brand?.url : null;
290
+
291
+ return {
292
+ id,
293
+ name,
294
+ url,
295
+ description: description || null,
296
+ tagline: tagline || null,
297
+ email: deriveEmail(url),
298
+ person: buildPerson({ name: contactName, image: contactImage, url: contactUrl }),
299
+ targets,
300
+ accountAdmins,
301
+ bundleIdPrefix: deriveBundleIdPrefix(companyUrl || url),
302
+ };
303
+ }
304
+
305
+ /**
306
+ * Resolve where the new brand lives when onboarding from a company root:
307
+ * brands.roots[0], or a selected root when several are configured and the
308
+ * run is interactive.
309
+ */
310
+ async function resolveCompanyBrandsDir(companyRoot, interactive) {
311
+ const roots = readRawConfig(companyRoot)?.brands?.roots || DEFAULT_BRAND_ROOTS;
312
+
313
+ let rootSpec = roots[0];
314
+ if (roots.length > 1 && interactive) {
315
+ rootSpec = await select({
316
+ message: 'Where should the new brand live?',
317
+ choices: roots.map((r) => ({ value: r })),
318
+ });
319
+ }
320
+
321
+ if (path.isAbsolute(rootSpec)) {
322
+ throw new Error(`brands.roots entries must be RELATIVE to the company root — got absolute path: ${rootSpec}`);
323
+ }
324
+
325
+ return path.resolve(companyRoot, rootSpec);
326
+ }
327
+
328
+ /**
329
+ * Answers for an EXISTING brand (resume): no wizard — the brand's own config
330
+ * is the source, and the scaffold only fills missing files around it.
331
+ */
332
+ function answersFromBrand(brandRoot) {
333
+ const brand = loadBrand(brandRoot);
334
+ if (brand.configError) {
335
+ throw new Error(`This brand's config/omega.json5 does not load: ${brand.configError}`);
336
+ }
337
+
338
+ const id = brand.id;
339
+ const url = brand.config.brand?.url || deriveUrl(id);
340
+
341
+ return {
342
+ id,
343
+ name: brand.config.brand?.name || deriveName(id),
344
+ url,
345
+ description: brand.config.brand?.description || null,
346
+ tagline: brand.config.brand?.tagline || null,
347
+ email: brand.config.brand?.contact?.email || deriveEmail(url),
348
+ // The brand's own answer, kept as it stands — the scaffold never rewrites
349
+ // an existing config, so a rerun leaves the person byte-identical (#770)
350
+ person: brand.config.brand?.contact?.person || null,
351
+ targets: brand.enabledTargets.length > 0 ? brand.enabledTargets : DEFAULT_TARGETS,
352
+ bundleIdPrefix: brand.config.certificates?.providers?.apple?.bundleIdPrefix || deriveBundleIdPrefix(url),
353
+ };
354
+ }
355
+
356
+ /**
357
+ * Ensure the born brand is a git repository: init + stage + first commit
358
+ * when the brand root isn't already inside one (Ian 2026-07-17 — a brand
359
+ * should be committable from minute one, and the extension package task's
360
+ * source-zip step wants a repo). The scaffolded .gitignore is on disk
361
+ * before this runs, so the first commit can never capture .env/.omega.
362
+ * Inside an existing work tree ("Use this template" clones, brands in a
363
+ * monorepo) this is a clean skip.
364
+ *
365
+ * @param {string} brandRoot - The brand root directory
366
+ * @param {string} brandName - Display name for the first-commit message
367
+ * @returns {{ initialized: boolean, committed?: boolean, reason?: string }}
368
+ */
369
+ function ensureGitRepo(brandRoot, brandName) {
370
+ const git = (args) => spawnSync('git', args, { cwd: brandRoot, stdio: 'ignore' });
371
+
372
+ if (git(['rev-parse', '--is-inside-work-tree']).status === 0) {
373
+ return { initialized: false, reason: 'existing repository' };
374
+ }
375
+ if (git(['init']).status !== 0) {
376
+ return { initialized: false, reason: 'git init failed (is git installed?)' };
377
+ }
378
+ git(['add', '-A']);
379
+
380
+ // Commit with the owner's identity; machines without one get a neutral
381
+ // fallback so the first commit never blocks onboarding
382
+ const hasIdentity = git(['config', 'user.email']).status === 0;
383
+ const identity = hasIdentity ? [] : ['-c', 'user.name=OMEGA Onboard', '-c', 'user.email=onboard@localhost'];
384
+ const commit = git([...identity, 'commit', '-m', `Initial commit — ${brandName} born via omega onboard`]);
385
+
386
+ return { initialized: true, committed: commit.status === 0 };
387
+ }
388
+
389
+ /** Run manage in the new brand exactly as a user would — a real child process. */
390
+ function spawnManage(brandRoot) {
391
+ return new Promise((resolve) => {
392
+ // cli-run.js self-executes when spawned as main — spawning it directly
393
+ // deliberately bypasses the bin's dispatcher (#276): this child must run
394
+ // THIS manager, never re-dispatch on the child's cwd. The verb is
395
+ // explicit (#229): a bare invocation prints help and walks nothing.
396
+ const entry = path.join(__dirname, 'cli-run.js');
397
+ const child = spawn(process.execPath, [entry, 'manage'], { cwd: brandRoot, stdio: 'inherit' });
398
+ child.on('close', (code) => resolve(code ?? 1));
399
+ child.on('error', () => resolve(1));
400
+ });
401
+ }
402
+
403
+ function printNextSteps(answers) {
404
+ console.log('');
405
+ console.log(chalk.bold('Next steps'));
406
+
407
+ const frameworks = answers.targets
408
+ .filter((target) => TARGET_FRAMEWORKS[target])
409
+ .map((target) => `${TARGET_FRAMEWORKS[target]} → targets/${TARGET_DIRS[target]}`);
410
+ if (frameworks.length > 0) {
411
+ console.log(` 1. Install each target's framework and run its setup: ${frameworks.join(', ')}`);
412
+ }
413
+ console.log(` ${frameworks.length > 0 ? 2 : 1}. Fill in .env as the brand adopts external services (the stub lists every key)`);
414
+ console.log(` ${frameworks.length > 0 ? 3 : 2}. Run ${chalk.cyan('npm run manage')} to reconcile everything — rerun any time`);
415
+ }
416
+
417
+ /**
418
+ * Main onboard runner.
419
+ *
420
+ * @param {string} cwd - Where the command ran
421
+ * @param {Object} options - { id, name, url, description, tagline, contactName, contactImage, contactUrl, targets, dryRun, manage }
422
+ * @returns {Object} - { brandRoot, mode, created, kept, planned, valid, manageExitCode }
423
+ */
424
+ async function runOnboard(cwd, options = {}) {
425
+ console.log(chalk.bold.cyan('OMEGA Manager — Onboarding'));
426
+ console.log('');
427
+
428
+ const interactive = canPrompt(options);
429
+ const resolved = resolveManageRoot(cwd);
430
+
431
+ // Resolve the brand root + answers per context
432
+ let brandRoot;
433
+ let answers;
434
+ let mode;
435
+ let companyRoot = null;
436
+
437
+ if (resolved?.isCompany) {
438
+ companyRoot = resolved.root;
439
+ console.log(`${chalk.dim('→')} Company workspace: ${chalk.cyan(companyRoot)}`);
440
+
441
+ const brandsDir = await resolveCompanyBrandsDir(companyRoot, interactive);
442
+ answers = await collectAnswers(options, null, interactive, companyRoot);
443
+ brandRoot = path.join(brandsDir, answers.id);
444
+
445
+ if (hasOmegaConfig(brandRoot)) {
446
+ console.log(`${chalk.dim('→')} Brand ${chalk.cyan(answers.id)} already exists — filling missing files only`);
447
+ answers = answersFromBrand(brandRoot);
448
+ mode = 'resume';
449
+ } else {
450
+ mode = 'fresh';
451
+ }
452
+ } else if (resolved) {
453
+ brandRoot = resolved.root;
454
+ console.log(`${chalk.dim('→')} Existing brand: ${chalk.cyan(brandRoot)} — filling missing files only`);
455
+ answers = answersFromBrand(brandRoot);
456
+ mode = 'resume';
457
+ } else {
458
+ brandRoot = cwd;
459
+ answers = await collectAnswers(options, deriveId(path.basename(cwd)), interactive);
460
+ mode = 'fresh';
461
+ }
462
+
463
+ if (mode === 'fresh') {
464
+ console.log(`${chalk.dim('→')} New brand: ${chalk.cyan(answers.name)} ${chalk.dim(`(${answers.id})`)} → ${chalk.cyan(brandRoot)}`);
465
+ }
466
+ console.log('');
467
+
468
+ // Scaffold (fill-missing; dry-run plans only)
469
+ const plan = buildScaffoldPlan(answers);
470
+ const results = applyScaffoldPlan(brandRoot, plan, { dryRun: options.dryRun });
471
+ printPlanResults(results);
472
+
473
+ if (options.dryRun) {
474
+ console.log('');
475
+ console.log(chalk.dim('⊘ Dry run — nothing written'));
476
+ return { brandRoot, mode, ...results, valid: true };
477
+ }
478
+
479
+ // Port-time conversion: a carried legacy oauth.json becomes the canonical
480
+ // google-oauth.json once, here, so nothing downstream dual-reads it (#501)
481
+ const legacyOAuth = convertLegacyOAuthSecret(brandRoot);
482
+ if (legacyOAuth.converted) {
483
+ console.log(` ${chalk.green('✓')} google-oauth.json ${chalk.dim('← the carried legacy oauth.json (converted, legacy file removed)')}`);
484
+ } else if (legacyOAuth.reason) {
485
+ console.log(` ${chalk.yellow('⚠')} legacy oauth.json not converted ${chalk.dim(`(${legacyOAuth.reason})`)}`);
486
+ }
487
+
488
+ // Company link — brand-local runs now layer the company defaults
489
+ if (companyRoot) {
490
+ if (stampCompanyMarker(brandRoot, companyRoot)) {
491
+ console.log(` ${chalk.green('✓')} company stamp ${chalk.dim(`→ .omega/company.json (${companyRoot})`)}`);
492
+ }
493
+ }
494
+
495
+ // Prove the brand actually loads before calling it done
496
+ const brand = loadBrand(brandRoot);
497
+ const valid = !brand.configError && brand.configErrors.length === 0;
498
+ console.log('');
499
+ if (valid) {
500
+ console.log(`${chalk.green('✓')} Config loads and validates ${chalk.dim(`(targets: ${brand.enabledTargets.join(', ')})`)}`);
501
+ } else {
502
+ console.log(`${chalk.red('✗')} Config problem: ${brand.configError || brand.configErrors.join('; ')}`);
503
+ }
504
+
505
+ // Git — a born brand is committable from minute one
506
+ const git = ensureGitRepo(brandRoot, answers.name);
507
+ if (git.initialized) {
508
+ console.log(`${chalk.green('✓')} git repository initialized ${chalk.dim(git.committed ? '(first commit made)' : '(commit skipped — set your git identity and commit)')}`);
509
+ } else {
510
+ console.log(`${chalk.dim('•')} git ${chalk.dim(`(${git.reason})`)}`);
511
+ }
512
+
513
+ printNextSteps(answers);
514
+
515
+ // Manage handoff — never in a dry run, never implicitly without a TTY
516
+ let manageExitCode = null;
517
+ const runNow = options.manage
518
+ ?? (interactive && valid
519
+ ? await confirm({ message: 'Run manage now?', default: true })
520
+ : false);
521
+
522
+ if (runNow) {
523
+ console.log('');
524
+ console.log(`${chalk.dim('→')} Starting management…`);
525
+ console.log(chalk.dim('━'.repeat(70)));
526
+ manageExitCode = await spawnManage(brandRoot);
527
+ }
528
+
529
+ return { brandRoot, mode, ...results, valid, git, legacyOAuth, manageExitCode };
530
+ }
531
+
532
+ module.exports = { runOnboard, deriveId, deriveName, deriveUrl };