@happier-dev/stack 0.1.0-preview.74.1

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 (439) hide show
  1. package/README.md +501 -0
  2. package/bin/hstack.mjs +348 -0
  3. package/docs/codex-mcp-resume.md +129 -0
  4. package/docs/edison.md +74 -0
  5. package/docs/forking-and-branding.md +189 -0
  6. package/docs/happy-development.md +22 -0
  7. package/docs/isolated-linux-vm.md +243 -0
  8. package/docs/menubar.md +244 -0
  9. package/docs/mobile-ios.md +322 -0
  10. package/docs/monorepo-migration.md +20 -0
  11. package/docs/paths-and-env.md +154 -0
  12. package/docs/remote-access.md +43 -0
  13. package/docs/server-flavors.md +147 -0
  14. package/docs/stacks.md +330 -0
  15. package/docs/tauri.md +60 -0
  16. package/docs/worktrees-and-forks.md +133 -0
  17. package/extras/swiftbar/auth-login.sh +29 -0
  18. package/extras/swiftbar/git-cache-refresh.sh +122 -0
  19. package/extras/swiftbar/hstack-term.sh +133 -0
  20. package/extras/swiftbar/hstack.5s.sh +296 -0
  21. package/extras/swiftbar/hstack.sh +35 -0
  22. package/extras/swiftbar/icons/happy-green.png +0 -0
  23. package/extras/swiftbar/icons/happy-orange.png +0 -0
  24. package/extras/swiftbar/icons/happy-red.png +0 -0
  25. package/extras/swiftbar/icons/logo-white.png +0 -0
  26. package/extras/swiftbar/install.sh +265 -0
  27. package/extras/swiftbar/lib/git.sh +629 -0
  28. package/extras/swiftbar/lib/icons.sh +92 -0
  29. package/extras/swiftbar/lib/render.sh +999 -0
  30. package/extras/swiftbar/lib/system.sh +244 -0
  31. package/extras/swiftbar/lib/utils.sh +717 -0
  32. package/extras/swiftbar/set-interval.sh +65 -0
  33. package/extras/swiftbar/set-server-flavor.sh +61 -0
  34. package/extras/swiftbar/wt-pr.sh +140 -0
  35. package/node_modules/@happier-dev/cli-common/README.md +6 -0
  36. package/node_modules/@happier-dev/cli-common/dist/index.d.ts +4 -0
  37. package/node_modules/@happier-dev/cli-common/dist/index.d.ts.map +1 -0
  38. package/node_modules/@happier-dev/cli-common/dist/index.js +4 -0
  39. package/node_modules/@happier-dev/cli-common/dist/index.js.map +1 -0
  40. package/node_modules/@happier-dev/cli-common/dist/links/index.d.ts +18 -0
  41. package/node_modules/@happier-dev/cli-common/dist/links/index.d.ts.map +1 -0
  42. package/node_modules/@happier-dev/cli-common/dist/links/index.js +25 -0
  43. package/node_modules/@happier-dev/cli-common/dist/links/index.js.map +1 -0
  44. package/node_modules/@happier-dev/cli-common/dist/links.d.ts +2 -0
  45. package/node_modules/@happier-dev/cli-common/dist/links.d.ts.map +1 -0
  46. package/node_modules/@happier-dev/cli-common/dist/links.js +2 -0
  47. package/node_modules/@happier-dev/cli-common/dist/links.js.map +1 -0
  48. package/node_modules/@happier-dev/cli-common/dist/update/index.d.ts +67 -0
  49. package/node_modules/@happier-dev/cli-common/dist/update/index.d.ts.map +1 -0
  50. package/node_modules/@happier-dev/cli-common/dist/update/index.js +259 -0
  51. package/node_modules/@happier-dev/cli-common/dist/update/index.js.map +1 -0
  52. package/node_modules/@happier-dev/cli-common/dist/workspaces/index.d.ts +17 -0
  53. package/node_modules/@happier-dev/cli-common/dist/workspaces/index.d.ts.map +1 -0
  54. package/node_modules/@happier-dev/cli-common/dist/workspaces/index.js +80 -0
  55. package/node_modules/@happier-dev/cli-common/dist/workspaces/index.js.map +1 -0
  56. package/node_modules/@happier-dev/cli-common/package.json +26 -0
  57. package/package.json +77 -0
  58. package/scripts/auth.mjs +1829 -0
  59. package/scripts/auth_copy_from_pglite_lock_in_use.integration.test.mjs +90 -0
  60. package/scripts/auth_copy_from_runCapture.integration.test.mjs +447 -0
  61. package/scripts/auth_help_cmd.test.mjs +28 -0
  62. package/scripts/auth_login_flow_in_tty.test.mjs +100 -0
  63. package/scripts/auth_login_force_default.test.mjs +66 -0
  64. package/scripts/auth_login_guided_server_no_expo.test.mjs +126 -0
  65. package/scripts/auth_login_method_override.test.mjs +67 -0
  66. package/scripts/auth_login_print_includes_configure_links.test.mjs +99 -0
  67. package/scripts/auth_status_server_validation.integration.test.mjs +140 -0
  68. package/scripts/build.mjs +266 -0
  69. package/scripts/bundleWorkspaceDeps.mjs +38 -0
  70. package/scripts/bundleWorkspaceDeps.test.mjs +77 -0
  71. package/scripts/ci.mjs +135 -0
  72. package/scripts/ci.test.mjs +50 -0
  73. package/scripts/cli-link.mjs +57 -0
  74. package/scripts/completion.mjs +395 -0
  75. package/scripts/contrib.mjs +333 -0
  76. package/scripts/daemon.mjs +1160 -0
  77. package/scripts/daemon.status_scope.test.mjs +51 -0
  78. package/scripts/daemon_cmd.mjs +26 -0
  79. package/scripts/daemon_dist_guard.test.mjs +171 -0
  80. package/scripts/daemon_invalid_auth_reseed_stack_name.integration.test.mjs +608 -0
  81. package/scripts/daemon_server_scoped_state.test.mjs +49 -0
  82. package/scripts/daemon_start_verification.integration.test.mjs +296 -0
  83. package/scripts/dev.mjs +545 -0
  84. package/scripts/doctor.mjs +340 -0
  85. package/scripts/doctor_cmd.test.mjs +22 -0
  86. package/scripts/doctor_ui_index_missing.test.mjs +37 -0
  87. package/scripts/eas.mjs +367 -0
  88. package/scripts/eas_platform_parsing.test.mjs +63 -0
  89. package/scripts/edison.mjs +1848 -0
  90. package/scripts/env.mjs +149 -0
  91. package/scripts/env_cmd.test.mjs +118 -0
  92. package/scripts/exit_cleanup_kills_detached_children_on_crash.integration.test.mjs +80 -0
  93. package/scripts/happier.mjs +82 -0
  94. package/scripts/import.mjs +1327 -0
  95. package/scripts/init.mjs +464 -0
  96. package/scripts/install.mjs +550 -0
  97. package/scripts/lint.mjs +177 -0
  98. package/scripts/menubar.mjs +202 -0
  99. package/scripts/migrate.mjs +318 -0
  100. package/scripts/mobile.mjs +353 -0
  101. package/scripts/mobile_dev_client.mjs +87 -0
  102. package/scripts/monorepo.mjs +2234 -0
  103. package/scripts/monorepo_port.apply.integration.test.mjs +680 -0
  104. package/scripts/monorepo_port.conflicts.integration.test.mjs +454 -0
  105. package/scripts/monorepo_port.validation.integration.test.mjs +486 -0
  106. package/scripts/orchestrated_stack_auth_flow.test.mjs +134 -0
  107. package/scripts/orchestrated_stack_auth_flow_resolve_port.test.mjs +98 -0
  108. package/scripts/orchestrated_stack_auth_flow_webapp_url.test.mjs +119 -0
  109. package/scripts/pack.mjs +257 -0
  110. package/scripts/pack.test.mjs +68 -0
  111. package/scripts/pglite_lock.integration.test.mjs +152 -0
  112. package/scripts/provision/linux-ubuntu-e2e.sh +132 -0
  113. package/scripts/provision/linux-ubuntu-review-pr.sh +66 -0
  114. package/scripts/provision/macos-lima-happy-vm.sh +192 -0
  115. package/scripts/provision/macos-lima-hstack-e2e.sh +100 -0
  116. package/scripts/release.mjs +53 -0
  117. package/scripts/release_binary_smoke.integration.test.mjs +138 -0
  118. package/scripts/review.mjs +1752 -0
  119. package/scripts/review_pr.mjs +435 -0
  120. package/scripts/run.mjs +561 -0
  121. package/scripts/run_script_with_stack_env.restart_port_reuse.test.mjs +30 -0
  122. package/scripts/self.mjs +465 -0
  123. package/scripts/self_host.mjs +9 -0
  124. package/scripts/self_host_binary_smoke.integration.test.mjs +74 -0
  125. package/scripts/self_host_runtime.mjs +883 -0
  126. package/scripts/self_host_runtime.test.mjs +82 -0
  127. package/scripts/self_host_systemd.real.integration.test.mjs +367 -0
  128. package/scripts/server_flavor.mjs +148 -0
  129. package/scripts/service.mjs +868 -0
  130. package/scripts/service_mode_help.test.mjs +27 -0
  131. package/scripts/setup.mjs +1324 -0
  132. package/scripts/setup_non_interactive_flag.test.mjs +60 -0
  133. package/scripts/setup_pr.mjs +605 -0
  134. package/scripts/setup_pr_orchestrated_auth_flow_util_import.test.mjs +117 -0
  135. package/scripts/stack/command_arguments.mjs +91 -0
  136. package/scripts/stack/copy_auth_from_stack.mjs +111 -0
  137. package/scripts/stack/delegated_script_commands.mjs +92 -0
  138. package/scripts/stack/help_text.mjs +110 -0
  139. package/scripts/stack/port_reservation.mjs +74 -0
  140. package/scripts/stack/repo_checkout_resolution.mjs +31 -0
  141. package/scripts/stack/run_script_with_stack_env.mjs +634 -0
  142. package/scripts/stack/stack_daemon_command.mjs +219 -0
  143. package/scripts/stack/stack_delegated_help.mjs +81 -0
  144. package/scripts/stack/stack_environment.mjs +151 -0
  145. package/scripts/stack/stack_environment.sanitization.test.mjs +75 -0
  146. package/scripts/stack/stack_happier_passthrough_command.mjs +63 -0
  147. package/scripts/stack/stack_info_snapshot.mjs +167 -0
  148. package/scripts/stack/stack_mobile_install_command.mjs +61 -0
  149. package/scripts/stack/stack_resume_command.mjs +76 -0
  150. package/scripts/stack/stack_stop_command.mjs +34 -0
  151. package/scripts/stack/stack_workspace_command.mjs +83 -0
  152. package/scripts/stack/transient_repo_overrides.mjs +29 -0
  153. package/scripts/stack.mjs +2388 -0
  154. package/scripts/stack_archive_cmd.integration.test.mjs +31 -0
  155. package/scripts/stack_audit_fix_light_env.test.mjs +129 -0
  156. package/scripts/stack_background_pinned_stack_json.test.mjs +81 -0
  157. package/scripts/stack_copy_auth_server_scoped.test.mjs +243 -0
  158. package/scripts/stack_daemon_cmd.integration.test.mjs +484 -0
  159. package/scripts/stack_eas_help.test.mjs +72 -0
  160. package/scripts/stack_editor_workspace_monorepo_root.test.mjs +102 -0
  161. package/scripts/stack_env_cmd.test.mjs +107 -0
  162. package/scripts/stack_guided_login_bundle_error_parse.test.mjs +20 -0
  163. package/scripts/stack_guided_login_inner_invocation.test.mjs +46 -0
  164. package/scripts/stack_happy_cmd.integration.test.mjs +263 -0
  165. package/scripts/stack_info_snapshot_running_status.test.mjs +186 -0
  166. package/scripts/stack_interactive_monorepo_group.test.mjs +128 -0
  167. package/scripts/stack_monorepo_defaults.test.mjs +31 -0
  168. package/scripts/stack_monorepo_repo_dev_token.test.mjs +32 -0
  169. package/scripts/stack_monorepo_server_light_from_happy_spec.test.mjs +37 -0
  170. package/scripts/stack_new_name_normalize_cmd.test.mjs +38 -0
  171. package/scripts/stack_pr_name_normalize_cmd.test.mjs +84 -0
  172. package/scripts/stack_resume_cmd.integration.test.mjs +134 -0
  173. package/scripts/stack_server_flavors_defaults.test.mjs +64 -0
  174. package/scripts/stack_shorthand_cmd.integration.test.mjs +74 -0
  175. package/scripts/stack_stop_sweeps_legacy_infra_without_kind.integration.test.mjs +44 -0
  176. package/scripts/stack_stop_sweeps_when_runtime_missing.integration.test.mjs +42 -0
  177. package/scripts/stack_stop_sweeps_when_runtime_stale.integration.test.mjs +50 -0
  178. package/scripts/stack_wt_list.test.mjs +117 -0
  179. package/scripts/start_ui_required_default.test.mjs +63 -0
  180. package/scripts/stop.mjs +190 -0
  181. package/scripts/stopStackWithEnv_no_autosweep_when_runtime_missing.integration.test.mjs +95 -0
  182. package/scripts/swiftbar_git_monorepo_cmd.test.mjs +75 -0
  183. package/scripts/swiftbar_render_monorepo_wt_actions.integration.test.mjs +116 -0
  184. package/scripts/swiftbar_utils_cmd.test.mjs +92 -0
  185. package/scripts/swiftbar_wt_pr_backcompat.test.mjs +162 -0
  186. package/scripts/systemd_unit_info.test.mjs +24 -0
  187. package/scripts/tailscale.mjs +490 -0
  188. package/scripts/test_ci.mjs +36 -0
  189. package/scripts/test_cmd.mjs +274 -0
  190. package/scripts/test_cmd.test.mjs +133 -0
  191. package/scripts/test_integration.mjs +33 -0
  192. package/scripts/testkit/auth_testkit.mjs +121 -0
  193. package/scripts/testkit/doctor_testkit.mjs +68 -0
  194. package/scripts/testkit/monorepo_port_testkit.mjs +157 -0
  195. package/scripts/testkit/stack_archive_command_testkit.mjs +55 -0
  196. package/scripts/testkit/stack_new_monorepo_testkit.mjs +83 -0
  197. package/scripts/testkit/stack_script_command_testkit.mjs +27 -0
  198. package/scripts/testkit/stack_stop_sweeps_testkit.mjs +172 -0
  199. package/scripts/testkit/worktrees_monorepo_testkit.mjs +53 -0
  200. package/scripts/tools.mjs +70 -0
  201. package/scripts/tui.mjs +914 -0
  202. package/scripts/tui_stopStackForTuiExit_no_autosweep.integration.test.mjs +95 -0
  203. package/scripts/typecheck.mjs +178 -0
  204. package/scripts/ui_gateway.mjs +247 -0
  205. package/scripts/uninstall.mjs +179 -0
  206. package/scripts/utils/auth/credentials_paths.mjs +181 -0
  207. package/scripts/utils/auth/credentials_paths.test.mjs +187 -0
  208. package/scripts/utils/auth/daemon_gate.mjs +66 -0
  209. package/scripts/utils/auth/daemon_gate.test.mjs +116 -0
  210. package/scripts/utils/auth/decode_jwt_payload_unsafe.mjs +16 -0
  211. package/scripts/utils/auth/dev_key.mjs +163 -0
  212. package/scripts/utils/auth/files.mjs +56 -0
  213. package/scripts/utils/auth/guided_pr_auth.mjs +86 -0
  214. package/scripts/utils/auth/guided_stack_web_login.mjs +56 -0
  215. package/scripts/utils/auth/handy_master_secret.mjs +42 -0
  216. package/scripts/utils/auth/interactive_stack_auth.mjs +70 -0
  217. package/scripts/utils/auth/login_ux.mjs +105 -0
  218. package/scripts/utils/auth/orchestrated_stack_auth_flow.mjs +291 -0
  219. package/scripts/utils/auth/sources.mjs +28 -0
  220. package/scripts/utils/auth/stable_scope_id.mjs +91 -0
  221. package/scripts/utils/auth/stable_scope_id.test.mjs +51 -0
  222. package/scripts/utils/auth/stack_guided_login.mjs +438 -0
  223. package/scripts/utils/cli/arg_values.mjs +23 -0
  224. package/scripts/utils/cli/arg_values.test.mjs +43 -0
  225. package/scripts/utils/cli/args.mjs +17 -0
  226. package/scripts/utils/cli/cli.mjs +24 -0
  227. package/scripts/utils/cli/cli_registry.mjs +440 -0
  228. package/scripts/utils/cli/cwd_scope.mjs +158 -0
  229. package/scripts/utils/cli/cwd_scope.test.mjs +154 -0
  230. package/scripts/utils/cli/flags.mjs +17 -0
  231. package/scripts/utils/cli/log_forwarder.mjs +157 -0
  232. package/scripts/utils/cli/normalize.mjs +16 -0
  233. package/scripts/utils/cli/prereqs.mjs +103 -0
  234. package/scripts/utils/cli/prereqs.test.mjs +33 -0
  235. package/scripts/utils/cli/progress.mjs +141 -0
  236. package/scripts/utils/cli/smoke_help.mjs +44 -0
  237. package/scripts/utils/cli/verbosity.mjs +11 -0
  238. package/scripts/utils/cli/wizard.mjs +139 -0
  239. package/scripts/utils/cli/wizard_promptSelect.test.mjs +44 -0
  240. package/scripts/utils/cli/wizard_prompt_worktree_source_lazy.test.mjs +132 -0
  241. package/scripts/utils/cli/wizard_worktree_slug.test.mjs +33 -0
  242. package/scripts/utils/crypto/tokens.mjs +14 -0
  243. package/scripts/utils/dev/daemon.mjs +232 -0
  244. package/scripts/utils/dev/daemon_watch_resilience.test.mjs +224 -0
  245. package/scripts/utils/dev/expo_dev.buildEnv.test.mjs +35 -0
  246. package/scripts/utils/dev/expo_dev.mjs +478 -0
  247. package/scripts/utils/dev/expo_dev.test.mjs +89 -0
  248. package/scripts/utils/dev/expo_dev_restart_port_reservation.test.mjs +120 -0
  249. package/scripts/utils/dev/expo_dev_verbose_logs.test.mjs +60 -0
  250. package/scripts/utils/dev/server.mjs +180 -0
  251. package/scripts/utils/dev_auth_key.mjs +7 -0
  252. package/scripts/utils/edison/git_roots.mjs +30 -0
  253. package/scripts/utils/edison/git_roots.test.mjs +49 -0
  254. package/scripts/utils/env/config.mjs +52 -0
  255. package/scripts/utils/env/dotenv.mjs +32 -0
  256. package/scripts/utils/env/dotenv.test.mjs +32 -0
  257. package/scripts/utils/env/env.mjs +130 -0
  258. package/scripts/utils/env/env_file.mjs +98 -0
  259. package/scripts/utils/env/env_file.test.mjs +49 -0
  260. package/scripts/utils/env/env_local.mjs +25 -0
  261. package/scripts/utils/env/load_env_file.mjs +34 -0
  262. package/scripts/utils/env/read.mjs +30 -0
  263. package/scripts/utils/env/sandbox.mjs +13 -0
  264. package/scripts/utils/env/scrub_env.mjs +69 -0
  265. package/scripts/utils/env/scrub_env.test.mjs +102 -0
  266. package/scripts/utils/env/values.mjs +13 -0
  267. package/scripts/utils/expo/command.mjs +65 -0
  268. package/scripts/utils/expo/expo.mjs +139 -0
  269. package/scripts/utils/expo/expo_state_running.test.mjs +48 -0
  270. package/scripts/utils/expo/metro_ports.mjs +101 -0
  271. package/scripts/utils/expo/metro_ports.test.mjs +35 -0
  272. package/scripts/utils/fs/atomic_dir_swap.mjs +55 -0
  273. package/scripts/utils/fs/atomic_dir_swap.test.mjs +54 -0
  274. package/scripts/utils/fs/file_has_content.mjs +10 -0
  275. package/scripts/utils/fs/fs.mjs +11 -0
  276. package/scripts/utils/fs/json.mjs +25 -0
  277. package/scripts/utils/fs/ops.mjs +29 -0
  278. package/scripts/utils/fs/package_json.mjs +8 -0
  279. package/scripts/utils/fs/tail.mjs +12 -0
  280. package/scripts/utils/git/dev_checkout.mjs +127 -0
  281. package/scripts/utils/git/dev_checkout.test.mjs +115 -0
  282. package/scripts/utils/git/git.mjs +67 -0
  283. package/scripts/utils/git/parse_name_status_z.mjs +21 -0
  284. package/scripts/utils/git/refs.mjs +26 -0
  285. package/scripts/utils/git/worktrees.mjs +323 -0
  286. package/scripts/utils/git/worktrees_monorepo.test.mjs +60 -0
  287. package/scripts/utils/git/worktrees_pathstyle.test.mjs +53 -0
  288. package/scripts/utils/llm/assist.mjs +260 -0
  289. package/scripts/utils/llm/codex_exec.mjs +61 -0
  290. package/scripts/utils/llm/codex_exec.test.mjs +46 -0
  291. package/scripts/utils/llm/hstack_runner.mjs +59 -0
  292. package/scripts/utils/llm/tools.mjs +56 -0
  293. package/scripts/utils/llm/tools.test.mjs +67 -0
  294. package/scripts/utils/menubar/swiftbar.mjs +121 -0
  295. package/scripts/utils/menubar/swiftbar.test.mjs +85 -0
  296. package/scripts/utils/mobile/config.mjs +35 -0
  297. package/scripts/utils/mobile/dev_client_links.mjs +59 -0
  298. package/scripts/utils/mobile/identifiers.mjs +46 -0
  299. package/scripts/utils/mobile/identifiers.test.mjs +41 -0
  300. package/scripts/utils/mobile/ios_xcodeproj_patch.mjs +128 -0
  301. package/scripts/utils/mobile/ios_xcodeproj_patch.test.mjs +131 -0
  302. package/scripts/utils/net/bind_mode.mjs +39 -0
  303. package/scripts/utils/net/dns.mjs +10 -0
  304. package/scripts/utils/net/lan_ip.mjs +24 -0
  305. package/scripts/utils/net/ports.mjs +110 -0
  306. package/scripts/utils/net/tcp_forward.mjs +162 -0
  307. package/scripts/utils/net/url.mjs +30 -0
  308. package/scripts/utils/net/url.test.mjs +29 -0
  309. package/scripts/utils/paths/canonical_home.mjs +15 -0
  310. package/scripts/utils/paths/canonical_home.test.mjs +28 -0
  311. package/scripts/utils/paths/localhost_host.mjs +112 -0
  312. package/scripts/utils/paths/localhost_host.test.mjs +58 -0
  313. package/scripts/utils/paths/paths.mjs +302 -0
  314. package/scripts/utils/paths/paths_env_win32.test.mjs +36 -0
  315. package/scripts/utils/paths/paths_monorepo.test.mjs +58 -0
  316. package/scripts/utils/paths/paths_server_flavors.test.mjs +50 -0
  317. package/scripts/utils/paths/runtime.mjs +41 -0
  318. package/scripts/utils/pglite_lock.mjs +107 -0
  319. package/scripts/utils/proc/commands.mjs +33 -0
  320. package/scripts/utils/proc/exit_cleanup.mjs +57 -0
  321. package/scripts/utils/proc/happy_monorepo_deps.mjs +37 -0
  322. package/scripts/utils/proc/happy_monorepo_deps.test.mjs +89 -0
  323. package/scripts/utils/proc/ownership.mjs +217 -0
  324. package/scripts/utils/proc/ownership_killProcessGroupOwnedByStack.test.mjs +216 -0
  325. package/scripts/utils/proc/ownership_listPidsWithEnvNeedles.test.mjs +88 -0
  326. package/scripts/utils/proc/package_scripts.mjs +38 -0
  327. package/scripts/utils/proc/package_scripts.test.mjs +58 -0
  328. package/scripts/utils/proc/parallel.mjs +25 -0
  329. package/scripts/utils/proc/pids.mjs +11 -0
  330. package/scripts/utils/proc/pm.mjs +478 -0
  331. package/scripts/utils/proc/pm_spawn.integration.test.mjs +131 -0
  332. package/scripts/utils/proc/pm_stack_cache_env.test.mjs +313 -0
  333. package/scripts/utils/proc/proc.mjs +331 -0
  334. package/scripts/utils/proc/proc.test.mjs +85 -0
  335. package/scripts/utils/proc/terminate.mjs +69 -0
  336. package/scripts/utils/proc/terminate.test.mjs +54 -0
  337. package/scripts/utils/proc/watch.mjs +63 -0
  338. package/scripts/utils/review/augment_runner_integration.test.mjs +105 -0
  339. package/scripts/utils/review/base_ref.mjs +82 -0
  340. package/scripts/utils/review/base_ref.test.mjs +89 -0
  341. package/scripts/utils/review/chunks.mjs +55 -0
  342. package/scripts/utils/review/chunks.test.mjs +107 -0
  343. package/scripts/utils/review/detached_worktree.mjs +61 -0
  344. package/scripts/utils/review/detached_worktree.test.mjs +61 -0
  345. package/scripts/utils/review/findings.mjs +278 -0
  346. package/scripts/utils/review/findings.test.mjs +203 -0
  347. package/scripts/utils/review/head_slice.mjs +132 -0
  348. package/scripts/utils/review/head_slice.test.mjs +117 -0
  349. package/scripts/utils/review/instructions/deep.md +20 -0
  350. package/scripts/utils/review/prompts.mjs +279 -0
  351. package/scripts/utils/review/prompts.test.mjs +77 -0
  352. package/scripts/utils/review/run_reviewers_safe.mjs +12 -0
  353. package/scripts/utils/review/run_reviewers_safe.test.mjs +45 -0
  354. package/scripts/utils/review/runners/augment.mjs +91 -0
  355. package/scripts/utils/review/runners/augment.test.mjs +64 -0
  356. package/scripts/utils/review/runners/claude.mjs +92 -0
  357. package/scripts/utils/review/runners/claude.test.mjs +47 -0
  358. package/scripts/utils/review/runners/coderabbit.mjs +105 -0
  359. package/scripts/utils/review/runners/coderabbit.test.mjs +32 -0
  360. package/scripts/utils/review/runners/codex.mjs +129 -0
  361. package/scripts/utils/review/runners/codex.test.mjs +115 -0
  362. package/scripts/utils/review/slice_mode.mjs +20 -0
  363. package/scripts/utils/review/slice_mode.test.mjs +69 -0
  364. package/scripts/utils/review/sliced_runner.mjs +39 -0
  365. package/scripts/utils/review/sliced_runner.test.mjs +57 -0
  366. package/scripts/utils/review/slices.mjs +140 -0
  367. package/scripts/utils/review/slices.test.mjs +41 -0
  368. package/scripts/utils/review/targets.mjs +23 -0
  369. package/scripts/utils/review/targets.test.mjs +31 -0
  370. package/scripts/utils/review/tool_home_seed.mjs +106 -0
  371. package/scripts/utils/review/tool_home_seed.test.mjs +124 -0
  372. package/scripts/utils/review/uncommitted_ops.mjs +77 -0
  373. package/scripts/utils/review/uncommitted_ops.test.mjs +117 -0
  374. package/scripts/utils/sandbox/review_pr_sandbox.mjs +105 -0
  375. package/scripts/utils/server/apply_server_light_env_defaults.mjs +14 -0
  376. package/scripts/utils/server/flavor_scripts.mjs +138 -0
  377. package/scripts/utils/server/flavor_scripts.test.mjs +115 -0
  378. package/scripts/utils/server/infra/happy_server_infra.mjs +444 -0
  379. package/scripts/utils/server/mobile_api_url.mjs +60 -0
  380. package/scripts/utils/server/mobile_api_url.test.mjs +58 -0
  381. package/scripts/utils/server/port.mjs +55 -0
  382. package/scripts/utils/server/prisma_import.mjs +36 -0
  383. package/scripts/utils/server/prisma_import.test.mjs +78 -0
  384. package/scripts/utils/server/server.mjs +109 -0
  385. package/scripts/utils/server/ui_build_check.mjs +37 -0
  386. package/scripts/utils/server/ui_build_check.test.mjs +70 -0
  387. package/scripts/utils/server/ui_env.mjs +13 -0
  388. package/scripts/utils/server/ui_env.test.mjs +57 -0
  389. package/scripts/utils/server/urls.mjs +100 -0
  390. package/scripts/utils/server/validate.mjs +60 -0
  391. package/scripts/utils/server/validate.test.mjs +76 -0
  392. package/scripts/utils/service/autostart_darwin.mjs +198 -0
  393. package/scripts/utils/service/autostart_darwin.test.mjs +49 -0
  394. package/scripts/utils/service/autostart_darwin_keepalive.test.mjs +19 -0
  395. package/scripts/utils/stack/cli_identities.mjs +29 -0
  396. package/scripts/utils/stack/context.mjs +19 -0
  397. package/scripts/utils/stack/dirs.mjs +26 -0
  398. package/scripts/utils/stack/editor_workspace.mjs +126 -0
  399. package/scripts/utils/stack/interactive_stack_config.mjs +266 -0
  400. package/scripts/utils/stack/interactive_stack_config.port_validation.test.mjs +93 -0
  401. package/scripts/utils/stack/interactive_stack_config.remote_validation.test.mjs +122 -0
  402. package/scripts/utils/stack/interactive_stack_config.stack_name_validation.test.mjs +76 -0
  403. package/scripts/utils/stack/interactive_stack_config_testkit.mjs +18 -0
  404. package/scripts/utils/stack/names.mjs +27 -0
  405. package/scripts/utils/stack/names.test.mjs +26 -0
  406. package/scripts/utils/stack/pr_stack_name.mjs +16 -0
  407. package/scripts/utils/stack/runtime_state.mjs +88 -0
  408. package/scripts/utils/stack/stacks.mjs +40 -0
  409. package/scripts/utils/stack/startup.mjs +370 -0
  410. package/scripts/utils/stack/startup_server_light_dirs.test.mjs +119 -0
  411. package/scripts/utils/stack/startup_server_light_generate.test.mjs +20 -0
  412. package/scripts/utils/stack/startup_server_light_legacy.test.mjs +79 -0
  413. package/scripts/utils/stack/startup_server_light_testkit.mjs +106 -0
  414. package/scripts/utils/stack/stop.mjs +284 -0
  415. package/scripts/utils/stack_context.mjs +1 -0
  416. package/scripts/utils/stack_runtime_state.mjs +1 -0
  417. package/scripts/utils/stacks.mjs +1 -0
  418. package/scripts/utils/tailscale/ip.mjs +116 -0
  419. package/scripts/utils/tauri/stack_overrides.mjs +22 -0
  420. package/scripts/utils/test/collect_test_files.mjs +29 -0
  421. package/scripts/utils/time/get_today_ymd.mjs +7 -0
  422. package/scripts/utils/tui/cleanup.mjs +38 -0
  423. package/scripts/utils/ui/ansi.mjs +47 -0
  424. package/scripts/utils/ui/browser.mjs +31 -0
  425. package/scripts/utils/ui/browser.test.mjs +56 -0
  426. package/scripts/utils/ui/clipboard.mjs +38 -0
  427. package/scripts/utils/ui/layout.mjs +44 -0
  428. package/scripts/utils/ui/qr.mjs +17 -0
  429. package/scripts/utils/ui/terminal_launcher.mjs +129 -0
  430. package/scripts/utils/ui/text.mjs +16 -0
  431. package/scripts/utils/update/auto_update_notice.mjs +93 -0
  432. package/scripts/utils/validate.mjs +5 -0
  433. package/scripts/where.mjs +138 -0
  434. package/scripts/worktrees.mjs +2174 -0
  435. package/scripts/worktrees_archive_cmd.integration.test.mjs +228 -0
  436. package/scripts/worktrees_cursor_monorepo_root.test.mjs +23 -0
  437. package/scripts/worktrees_list_specs_no_recurse.test.mjs +32 -0
  438. package/scripts/worktrees_monorepo_testkit.test.mjs +29 -0
  439. package/scripts/worktrees_monorepo_use_group.test.mjs +41 -0
@@ -0,0 +1,31 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { stat } from 'node:fs/promises';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { runNodeCapture } from './testkit/stack_script_command_testkit.mjs';
7
+ import { createStackArchiveFixture } from './testkit/stack_archive_command_testkit.mjs';
8
+
9
+ test('hstack stack archive moves the stack and archives its referenced worktrees', async (t) => {
10
+ const scriptsDir = dirname(fileURLToPath(import.meta.url));
11
+ const rootDir = dirname(scriptsDir);
12
+ const fixture = await createStackArchiveFixture(t, { stackName: 'exp-test', worktreeSlug: 'archived-by-stack' });
13
+
14
+ const date = '2000-01-04';
15
+ const nodeEnv = { ...fixture.baseEnv, PATH: '' };
16
+ const res = await runNodeCapture([join(rootDir, 'scripts', 'stack.mjs'), 'archive', fixture.stackName, `--date=${date}`, '--json'], {
17
+ cwd: rootDir,
18
+ env: nodeEnv,
19
+ });
20
+ assert.equal(res.code, 0, `expected stack archive exit 0\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
21
+ const parsed = JSON.parse(res.stdout);
22
+ assert.equal(parsed.ok, true, `expected ok=true JSON output\n${res.stdout}`);
23
+
24
+ const archivedStackDir = join(fixture.storageDir, '.archived', date, fixture.stackName);
25
+ assert.equal(parsed.archivedStackDir, archivedStackDir, `expected archivedStackDir in JSON output\n${res.stdout}`);
26
+ await stat(join(archivedStackDir, 'env'));
27
+
28
+ const archivedWorktreeDir = join(fixture.workspaceDir, 'archive', 'worktrees', date, 'pr', 'archived-by-stack');
29
+ const gitStat = await stat(join(archivedWorktreeDir, '.git'));
30
+ assert.ok(gitStat.isDirectory(), 'expected archived worktree to be detached (standalone .git dir)');
31
+ });
@@ -0,0 +1,129 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ import { dirname, join } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { runNodeCapture } from './testkit/stack_script_command_testkit.mjs';
8
+
9
+ const scriptsDir = dirname(fileURLToPath(import.meta.url));
10
+ const rootDir = dirname(scriptsDir);
11
+
12
+ async function createLightAuditFixture(t, { stackName = 'exp1', envLines }) {
13
+ const tmp = await mkdtemp(join(tmpdir(), 'hstack-stack-audit-light-'));
14
+ t.after(async () => {
15
+ await rm(tmp, { recursive: true, force: true });
16
+ });
17
+
18
+ const homeDir = join(tmp, 'home');
19
+ const storageDir = join(tmp, 'storage');
20
+ const workspaceDir = join(tmp, 'workspace');
21
+ const repoDir = join(workspaceDir, 'main');
22
+ const baseDir = join(storageDir, stackName);
23
+ const envPath = join(baseDir, 'env');
24
+
25
+ await mkdir(homeDir, { recursive: true });
26
+ await mkdir(storageDir, { recursive: true });
27
+ await mkdir(repoDir, { recursive: true });
28
+ await mkdir(baseDir, { recursive: true });
29
+ await writeFile(envPath, [...envLines, ''].join('\n'), 'utf-8');
30
+
31
+ return {
32
+ baseDir,
33
+ envPath,
34
+ env: {
35
+ ...process.env,
36
+ HAPPIER_STACK_HOME_DIR: homeDir,
37
+ HAPPIER_STACK_STORAGE_DIR: storageDir,
38
+ HAPPIER_STACK_WORKSPACE_DIR: workspaceDir,
39
+ },
40
+ };
41
+ }
42
+
43
+ async function runAuditFixPaths({ env }) {
44
+ return await runNodeCapture([join(rootDir, 'scripts', 'stack.mjs'), 'audit', '--fix-paths', '--json'], { cwd: rootDir, env });
45
+ }
46
+
47
+ test('hstack stack audit --fix-paths prunes legacy DATABASE_URL for light stacks and sets HAPPIER_SERVER_LIGHT_DB_DIR', async (t) => {
48
+ const stackName = 'exp1';
49
+ const fixture = await createLightAuditFixture(t, {
50
+ stackName,
51
+ envLines: (() => {
52
+ const dataDir = join(join(join(tmpdir(), 'unused'), stackName), 'server-light');
53
+ return [
54
+ `HAPPIER_STACK_STACK=${stackName}`,
55
+ `HAPPIER_STACK_SERVER_COMPONENT=happier-server-light`,
56
+ `HAPPIER_STACK_UI_BUILD_DIR=${join(join(tmpdir(), 'unused'), stackName, 'ui')}`,
57
+ `HAPPIER_STACK_CLI_HOME_DIR=${join(join(tmpdir(), 'unused'), stackName, 'cli')}`,
58
+ `HAPPIER_STACK_REPO_DIR=${join(tmpdir(), 'unused', 'main')}`,
59
+ `HAPPIER_SERVER_LIGHT_DATA_DIR=${dataDir}`,
60
+ `HAPPIER_SERVER_LIGHT_FILES_DIR=${join(dataDir, 'files')}`,
61
+ `DATABASE_URL=file:${join(dataDir, 'happier-server-light.sqlite')}`,
62
+ ];
63
+ })(),
64
+ });
65
+
66
+ const res = await runAuditFixPaths({ env: fixture.env });
67
+ assert.equal(res.code, 0, `expected exit 0, got ${res.code}\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
68
+
69
+ const raw = await readFile(fixture.envPath, 'utf-8');
70
+ assert.ok(raw.includes('HAPPIER_SERVER_LIGHT_DB_DIR='), `expected HAPPIER_SERVER_LIGHT_DB_DIR to be set\n${raw}`);
71
+ assert.ok(!raw.includes('\nDATABASE_URL='), `expected legacy DATABASE_URL to be pruned for light stacks\n${raw}`);
72
+ });
73
+
74
+ test('hstack stack audit --fix-paths normalizes mixed legacy light paths and removes DATABASE_URL', async (t) => {
75
+ const stackName = 'exp-mixed';
76
+ const fixture = await createLightAuditFixture(t, {
77
+ stackName,
78
+ envLines: [
79
+ `HAPPIER_STACK_STACK=${stackName}`,
80
+ `HAPPIER_STACK_SERVER_COMPONENT=happier-server-light`,
81
+ `HAPPIER_STACK_UI_BUILD_DIR=${join(fixturePathPlaceholder(), 'ui')}`,
82
+ `HAPPIER_STACK_CLI_HOME_DIR=${join(fixturePathPlaceholder(), 'cli')}`,
83
+ `HAPPIER_STACK_REPO_DIR=${join(fixturePathPlaceholder(), 'repo')}`,
84
+ `HAPPIER_SERVER_LIGHT_DATA_DIR=${join(fixturePathPlaceholder(), 'legacy-light')}`,
85
+ `HAPPIER_SERVER_LIGHT_FILES_DIR=${join(fixturePathPlaceholder(), 'legacy-light', 'files-old')}`,
86
+ `DATABASE_URL=file:${join(fixturePathPlaceholder(), 'legacy-light', 'legacy.sqlite')}`,
87
+ ],
88
+ });
89
+
90
+ const expectedDataDir = join(fixture.baseDir, 'server-light');
91
+ const expectedFilesDir = join(expectedDataDir, 'files');
92
+ const expectedDbDir = join(expectedDataDir, 'pglite');
93
+
94
+ const res = await runAuditFixPaths({ env: fixture.env });
95
+ assert.equal(res.code, 0, `expected exit 0, got ${res.code}\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
96
+
97
+ const raw = await readFile(fixture.envPath, 'utf-8');
98
+ assert.ok(raw.includes(`HAPPIER_SERVER_LIGHT_DATA_DIR=${expectedDataDir}`), `expected normalized data dir\n${raw}`);
99
+ assert.ok(raw.includes(`HAPPIER_SERVER_LIGHT_FILES_DIR=${expectedFilesDir}`), `expected normalized files dir\n${raw}`);
100
+ assert.ok(raw.includes(`HAPPIER_SERVER_LIGHT_DB_DIR=${expectedDbDir}`), `expected normalized db dir\n${raw}`);
101
+ assert.ok(!raw.includes('\nDATABASE_URL='), `expected legacy DATABASE_URL to be removed\n${raw}`);
102
+ });
103
+
104
+ test('hstack stack audit --fix-paths is idempotent for migrated light env files', async (t) => {
105
+ const stackName = 'exp-idempotent';
106
+ const fixture = await createLightAuditFixture(t, {
107
+ stackName,
108
+ envLines: [
109
+ `HAPPIER_STACK_STACK=${stackName}`,
110
+ `HAPPIER_STACK_SERVER_COMPONENT=happier-server-light`,
111
+ `HAPPIER_STACK_REPO_DIR=${join(fixturePathPlaceholder(), 'repo')}`,
112
+ `DATABASE_URL=file:${join(fixturePathPlaceholder(), 'legacy.sqlite')}`,
113
+ ],
114
+ });
115
+
116
+ const first = await runAuditFixPaths({ env: fixture.env });
117
+ assert.equal(first.code, 0, `expected first audit exit 0, got ${first.code}\nstdout:\n${first.stdout}\nstderr:\n${first.stderr}`);
118
+ const afterFirst = await readFile(fixture.envPath, 'utf-8');
119
+
120
+ const second = await runAuditFixPaths({ env: fixture.env });
121
+ assert.equal(second.code, 0, `expected second audit exit 0, got ${second.code}\nstdout:\n${second.stdout}\nstderr:\n${second.stderr}`);
122
+ const afterSecond = await readFile(fixture.envPath, 'utf-8');
123
+
124
+ assert.equal(afterSecond, afterFirst, 'expected second --fix-paths run to be idempotent');
125
+ });
126
+
127
+ function fixturePathPlaceholder() {
128
+ return join(tmpdir(), 'hstack-stack-audit-placeholder');
129
+ }
@@ -0,0 +1,81 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { spawn } from 'node:child_process';
4
+ import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
5
+ import { constants as osConstants, tmpdir } from 'node:os';
6
+ import { dirname, join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ function exitCodeFromSignal(signal) {
10
+ if (!signal) return 0;
11
+ const n = osConstants?.signals?.[signal];
12
+ return 128 + (typeof n === 'number' && Number.isFinite(n) ? n : 1);
13
+ }
14
+
15
+ function runNode(args, { cwd, env } = {}) {
16
+ return new Promise((resolve, reject) => {
17
+ const proc = spawn(process.execPath, args, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'] });
18
+ let stdout = '';
19
+ let stderr = '';
20
+ proc.stdout.on('data', (d) => (stdout += String(d)));
21
+ proc.stderr.on('data', (d) => (stderr += String(d)));
22
+ proc.on('error', reject);
23
+ proc.on('exit', (code, signal) => resolve({ code: code ?? exitCodeFromSignal(signal), stdout, stderr }));
24
+ });
25
+ }
26
+
27
+ async function ensureMinimalMonorepo({ monoRoot }) {
28
+ await mkdir(join(monoRoot, 'apps', 'ui'), { recursive: true });
29
+ await mkdir(join(monoRoot, 'apps', 'cli'), { recursive: true });
30
+ await mkdir(join(monoRoot, 'apps', 'server'), { recursive: true });
31
+ await writeFile(join(monoRoot, 'apps', 'ui', 'package.json'), '{}\n', 'utf-8');
32
+ await writeFile(join(monoRoot, 'apps', 'cli', 'package.json'), '{}\n', 'utf-8');
33
+ await writeFile(join(monoRoot, 'apps', 'server', 'package.json'), '{}\n', 'utf-8');
34
+ }
35
+
36
+ test('hstack stack dev <pinned> --background --json does not fail fast (prints dev config)', async () => {
37
+ const scriptsDir = dirname(fileURLToPath(import.meta.url));
38
+ const rootDir = dirname(scriptsDir);
39
+
40
+ const tmp = await mkdtemp(join(tmpdir(), 'hstack-stack-bg-pinned-'));
41
+ try {
42
+ const storageDir = join(tmp, 'storage');
43
+ const monoRoot = join(tmp, 'happier');
44
+
45
+ await ensureMinimalMonorepo({ monoRoot });
46
+
47
+ await mkdir(join(storageDir, 'main'), { recursive: true });
48
+ const envPath = join(storageDir, 'main', 'env');
49
+ await writeFile(
50
+ envPath,
51
+ [
52
+ 'HAPPIER_STACK_STACK=main',
53
+ `HAPPIER_STACK_REPO_DIR=${monoRoot}`,
54
+ 'HAPPIER_STACK_SERVER_PORT=4101',
55
+ // Avoid external probes (tailscale) so --json stays hermetic.
56
+ 'HAPPIER_STACK_TAILSCALE_PREFER_PUBLIC_URL=0',
57
+ 'HAPPIER_STACK_TAILSCALE_SERVE=0',
58
+ '',
59
+ ].join('\n'),
60
+ 'utf-8'
61
+ );
62
+
63
+ const env = {
64
+ ...process.env,
65
+ HAPPIER_STACK_STORAGE_DIR: storageDir,
66
+ HAPPIER_STACK_STACK: 'main',
67
+ HAPPIER_STACK_ENV_FILE: envPath,
68
+ };
69
+
70
+ const res = await runNode(
71
+ [join(rootDir, 'bin', 'hstack.mjs'), 'stack', 'dev', 'main', '--background', '--json'],
72
+ { cwd: rootDir, env }
73
+ );
74
+ assert.equal(res.code, 0, `expected exit 0, got ${res.code}\nstderr:\n${res.stderr}\nstdout:\n${res.stdout}`);
75
+ const parsed = JSON.parse(res.stdout.trim());
76
+ assert.equal(parsed?.mode, 'dev');
77
+ assert.equal(parsed?.serverPort, 4101);
78
+ } finally {
79
+ await rm(tmp, { recursive: true, force: true });
80
+ }
81
+ });
@@ -0,0 +1,243 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { spawn } from 'node:child_process';
4
+ import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
5
+ import { tmpdir } from 'node:os';
6
+ import { dirname, join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ import { resolveStackCredentialPaths } from './utils/auth/credentials_paths.mjs';
10
+
11
+ function runNode(args, { cwd, env }) {
12
+ return new Promise((resolve, reject) => {
13
+ const proc = spawn(process.execPath, args, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'] });
14
+ let stdout = '';
15
+ let stderr = '';
16
+ proc.stdout.on('data', (d) => (stdout += String(d)));
17
+ proc.stderr.on('data', (d) => (stderr += String(d)));
18
+ proc.on('error', reject);
19
+ proc.on('exit', (code, signal) => resolve({ code: code ?? (signal ? 1 : 0), signal: signal ?? null, stdout, stderr }));
20
+ });
21
+ }
22
+
23
+ test('hstack stack new copies server-scoped credentials from source stack', async () => {
24
+ const scriptsDir = dirname(fileURLToPath(import.meta.url));
25
+ const rootDir = dirname(scriptsDir);
26
+ const tmp = await mkdtemp(join(tmpdir(), 'hstack-stack-copy-auth-server-scoped-'));
27
+
28
+ const workspaceDir = join(tmp, 'workspace');
29
+ const storageDir = join(tmp, 'storage');
30
+ const homeDir = join(tmp, 'home');
31
+ const sandboxDir = join(tmp, 'sandbox');
32
+ const sourceStack = 'seed-auth';
33
+ const targetStack = 'exp-copy-auth';
34
+ const serverPort = 4101;
35
+ const serverUrl = `http://127.0.0.1:${serverPort}`;
36
+
37
+ const monoRoot = join(workspaceDir, 'tmp', 'leeroy-wip');
38
+ await mkdir(join(monoRoot, 'apps', 'ui'), { recursive: true });
39
+ await mkdir(join(monoRoot, 'apps', 'cli'), { recursive: true });
40
+ await mkdir(join(monoRoot, 'apps', 'server', 'prisma', 'sqlite'), { recursive: true });
41
+ await writeFile(join(monoRoot, 'apps', 'ui', 'package.json'), '{}\n', 'utf-8');
42
+ await writeFile(join(monoRoot, 'apps', 'cli', 'package.json'), '{}\n', 'utf-8');
43
+ await writeFile(join(monoRoot, 'apps', 'server', 'package.json'), '{}\n', 'utf-8');
44
+ await writeFile(join(monoRoot, 'apps', 'server', 'prisma', 'schema.prisma'), 'datasource db { provider = "postgresql" }\n', 'utf-8');
45
+ await writeFile(join(monoRoot, 'apps', 'server', 'prisma', 'sqlite', 'schema.prisma'), 'datasource db { provider = "sqlite" }\n', 'utf-8');
46
+
47
+ const sourceCliHome = join(storageDir, sourceStack, 'cli');
48
+ const sourceCred = resolveStackCredentialPaths({ cliHomeDir: sourceCliHome, serverUrl });
49
+ await mkdir(dirname(sourceCred.serverScopedPath), { recursive: true });
50
+ await writeFile(sourceCred.serverScopedPath, 'seed-credential\n', 'utf-8');
51
+ await writeFile(join(sourceCliHome, 'settings.json'), JSON.stringify({ machineId: 'seed-machine' }) + '\n', 'utf-8');
52
+ await writeFile(
53
+ join(storageDir, sourceStack, 'env'),
54
+ [
55
+ `HAPPIER_STACK_REPO_DIR=${monoRoot}`,
56
+ `HAPPIER_STACK_CLI_HOME_DIR=${sourceCliHome}`,
57
+ `HAPPIER_STACK_SERVER_PORT=${serverPort}`,
58
+ '',
59
+ ].join('\n'),
60
+ 'utf-8'
61
+ );
62
+
63
+ const env = {
64
+ ...process.env,
65
+ HAPPIER_STACK_HOME_DIR: homeDir,
66
+ HAPPIER_STACK_WORKSPACE_DIR: workspaceDir,
67
+ HAPPIER_STACK_STORAGE_DIR: storageDir,
68
+ HAPPIER_STACK_SANDBOX_DIR: sandboxDir,
69
+ };
70
+
71
+ const res = await runNode(
72
+ [
73
+ join(rootDir, 'scripts', 'stack.mjs'),
74
+ 'new',
75
+ targetStack,
76
+ `--repo=${monoRoot}`,
77
+ '--server=happier-server-light',
78
+ `--copy-auth-from=${sourceStack}`,
79
+ `--port=${serverPort}`,
80
+ '--force-port',
81
+ '--json',
82
+ ],
83
+ { cwd: rootDir, env }
84
+ );
85
+ assert.equal(res.code, 0, `expected exit 0, got ${res.code}\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
86
+
87
+ const targetCliHome = join(storageDir, targetStack, 'cli');
88
+ const targetStableEnv = { HAPPIER_ACTIVE_SERVER_ID: `stack_${targetStack}__id_default` };
89
+ const targetCred = resolveStackCredentialPaths({ cliHomeDir: targetCliHome, serverUrl, env: targetStableEnv });
90
+ const targetCredRaw = await readFile(targetCred.serverScopedPath, 'utf-8');
91
+ assert.equal(targetCredRaw.trim(), 'seed-credential');
92
+
93
+ await rm(tmp, { recursive: true, force: true });
94
+ });
95
+
96
+ test('hstack stack new copy-auth prefers source server-scoped credentials over unrelated legacy access.key', async () => {
97
+ const scriptsDir = dirname(fileURLToPath(import.meta.url));
98
+ const rootDir = dirname(scriptsDir);
99
+ const tmp = await mkdtemp(join(tmpdir(), 'hstack-stack-copy-auth-prefer-server-scoped-'));
100
+
101
+ const workspaceDir = join(tmp, 'workspace');
102
+ const storageDir = join(tmp, 'storage');
103
+ const homeDir = join(tmp, 'home');
104
+ const sandboxDir = join(tmp, 'sandbox');
105
+ const sourceStack = 'seed-auth';
106
+ const targetStack = 'exp-copy-auth';
107
+ const serverPort = 4101;
108
+ const serverUrl = `http://127.0.0.1:${serverPort}`;
109
+
110
+ const monoRoot = join(workspaceDir, 'tmp', 'leeroy-wip');
111
+ await mkdir(join(monoRoot, 'apps', 'ui'), { recursive: true });
112
+ await mkdir(join(monoRoot, 'apps', 'cli'), { recursive: true });
113
+ await mkdir(join(monoRoot, 'apps', 'server', 'prisma', 'sqlite'), { recursive: true });
114
+ await writeFile(join(monoRoot, 'apps', 'ui', 'package.json'), '{}\n', 'utf-8');
115
+ await writeFile(join(monoRoot, 'apps', 'cli', 'package.json'), '{}\n', 'utf-8');
116
+ await writeFile(join(monoRoot, 'apps', 'server', 'package.json'), '{}\n', 'utf-8');
117
+ await writeFile(join(monoRoot, 'apps', 'server', 'prisma', 'schema.prisma'), 'datasource db { provider = "postgresql" }\n', 'utf-8');
118
+ await writeFile(join(monoRoot, 'apps', 'server', 'prisma', 'sqlite', 'schema.prisma'), 'datasource db { provider = "sqlite" }\n', 'utf-8');
119
+
120
+ const sourceCliHome = join(storageDir, sourceStack, 'cli');
121
+ const sourceCred = resolveStackCredentialPaths({ cliHomeDir: sourceCliHome, serverUrl });
122
+ await mkdir(dirname(sourceCred.serverScopedPath), { recursive: true });
123
+ await writeFile(join(sourceCliHome, 'access.key'), 'legacy-wrong\n', 'utf-8');
124
+ await writeFile(sourceCred.serverScopedPath, 'server-scoped-correct\n', 'utf-8');
125
+ await writeFile(join(sourceCliHome, 'settings.json'), JSON.stringify({ machineId: 'seed-machine' }) + '\n', 'utf-8');
126
+ await writeFile(
127
+ join(storageDir, sourceStack, 'env'),
128
+ [
129
+ `HAPPIER_STACK_REPO_DIR=${monoRoot}`,
130
+ `HAPPIER_STACK_CLI_HOME_DIR=${sourceCliHome}`,
131
+ `HAPPIER_STACK_SERVER_PORT=${serverPort}`,
132
+ '',
133
+ ].join('\n'),
134
+ 'utf-8'
135
+ );
136
+
137
+ const env = {
138
+ ...process.env,
139
+ HAPPIER_STACK_HOME_DIR: homeDir,
140
+ HAPPIER_STACK_WORKSPACE_DIR: workspaceDir,
141
+ HAPPIER_STACK_STORAGE_DIR: storageDir,
142
+ HAPPIER_STACK_SANDBOX_DIR: sandboxDir,
143
+ };
144
+
145
+ const res = await runNode(
146
+ [
147
+ join(rootDir, 'scripts', 'stack.mjs'),
148
+ 'new',
149
+ targetStack,
150
+ `--repo=${monoRoot}`,
151
+ '--server=happier-server-light',
152
+ `--copy-auth-from=${sourceStack}`,
153
+ `--port=${serverPort}`,
154
+ '--force-port',
155
+ '--json',
156
+ ],
157
+ { cwd: rootDir, env }
158
+ );
159
+ assert.equal(res.code, 0, `expected exit 0, got ${res.code}\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
160
+
161
+ const targetCliHome = join(storageDir, targetStack, 'cli');
162
+ const targetStableEnv = { HAPPIER_ACTIVE_SERVER_ID: `stack_${targetStack}__id_default` };
163
+ const targetCred = resolveStackCredentialPaths({ cliHomeDir: targetCliHome, serverUrl, env: targetStableEnv });
164
+ const targetCredRaw = await readFile(targetCred.serverScopedPath, 'utf-8');
165
+ assert.equal(targetCredRaw.trim(), 'server-scoped-correct');
166
+
167
+ await rm(tmp, { recursive: true, force: true });
168
+ });
169
+
170
+ test('hstack stack new copy-auth copies stable-scope credentials from source stack', async () => {
171
+ const scriptsDir = dirname(fileURLToPath(import.meta.url));
172
+ const rootDir = dirname(scriptsDir);
173
+ const tmp = await mkdtemp(join(tmpdir(), 'hstack-stack-copy-auth-stable-scope-'));
174
+
175
+ const workspaceDir = join(tmp, 'workspace');
176
+ const storageDir = join(tmp, 'storage');
177
+ const homeDir = join(tmp, 'home');
178
+ const sandboxDir = join(tmp, 'sandbox');
179
+ const sourceStack = 'seed-auth';
180
+ const targetStack = 'exp-copy-auth';
181
+ const serverPort = 4101;
182
+ const serverUrl = `http://127.0.0.1:${serverPort}`;
183
+
184
+ const monoRoot = join(workspaceDir, 'tmp', 'leeroy-wip');
185
+ await mkdir(join(monoRoot, 'apps', 'ui'), { recursive: true });
186
+ await mkdir(join(monoRoot, 'apps', 'cli'), { recursive: true });
187
+ await mkdir(join(monoRoot, 'apps', 'server', 'prisma', 'sqlite'), { recursive: true });
188
+ await writeFile(join(monoRoot, 'apps', 'ui', 'package.json'), '{}\n', 'utf-8');
189
+ await writeFile(join(monoRoot, 'apps', 'cli', 'package.json'), '{}\n', 'utf-8');
190
+ await writeFile(join(monoRoot, 'apps', 'server', 'package.json'), '{}\n', 'utf-8');
191
+ await writeFile(join(monoRoot, 'apps', 'server', 'prisma', 'schema.prisma'), 'datasource db { provider = \"postgresql\" }\n', 'utf-8');
192
+ await writeFile(join(monoRoot, 'apps', 'server', 'prisma', 'sqlite', 'schema.prisma'), 'datasource db { provider = \"sqlite\" }\n', 'utf-8');
193
+
194
+ const sourceCliHome = join(storageDir, sourceStack, 'cli');
195
+ const sourceStableEnv = { HAPPIER_ACTIVE_SERVER_ID: `stack_${sourceStack}__id_default` };
196
+ const sourceCredStable = resolveStackCredentialPaths({ cliHomeDir: sourceCliHome, serverUrl, env: sourceStableEnv });
197
+ await mkdir(dirname(sourceCredStable.serverScopedPath), { recursive: true });
198
+ await writeFile(join(sourceCliHome, 'access.key'), 'legacy-wrong\n', 'utf-8');
199
+ await writeFile(sourceCredStable.serverScopedPath, 'stable-correct\n', 'utf-8');
200
+ await writeFile(join(sourceCliHome, 'settings.json'), JSON.stringify({ machineId: 'seed-machine' }) + '\n', 'utf-8');
201
+ await writeFile(
202
+ join(storageDir, sourceStack, 'env'),
203
+ [
204
+ `HAPPIER_STACK_REPO_DIR=${monoRoot}`,
205
+ `HAPPIER_STACK_CLI_HOME_DIR=${sourceCliHome}`,
206
+ `HAPPIER_STACK_SERVER_PORT=${serverPort}`,
207
+ '',
208
+ ].join('\n'),
209
+ 'utf-8'
210
+ );
211
+
212
+ const env = {
213
+ ...process.env,
214
+ HAPPIER_STACK_HOME_DIR: homeDir,
215
+ HAPPIER_STACK_WORKSPACE_DIR: workspaceDir,
216
+ HAPPIER_STACK_STORAGE_DIR: storageDir,
217
+ HAPPIER_STACK_SANDBOX_DIR: sandboxDir,
218
+ };
219
+
220
+ const res = await runNode(
221
+ [
222
+ join(rootDir, 'scripts', 'stack.mjs'),
223
+ 'new',
224
+ targetStack,
225
+ `--repo=${monoRoot}`,
226
+ '--server=happier-server-light',
227
+ `--copy-auth-from=${sourceStack}`,
228
+ `--port=${serverPort}`,
229
+ '--force-port',
230
+ '--json',
231
+ ],
232
+ { cwd: rootDir, env }
233
+ );
234
+ assert.equal(res.code, 0, `expected exit 0, got ${res.code}\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
235
+
236
+ const targetCliHome = join(storageDir, targetStack, 'cli');
237
+ const targetStableEnv = { HAPPIER_ACTIVE_SERVER_ID: `stack_${targetStack}__id_default` };
238
+ const targetCredStable = resolveStackCredentialPaths({ cliHomeDir: targetCliHome, serverUrl, env: targetStableEnv });
239
+ const targetCredRaw = await readFile(targetCredStable.serverScopedPath, 'utf-8');
240
+ assert.equal(targetCredRaw.trim(), 'stable-correct');
241
+
242
+ await rm(tmp, { recursive: true, force: true });
243
+ });