@happier-dev/stack 0.1.0-preview.5.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 +159 -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 +94 -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
package/bin/hstack.mjs ADDED
@@ -0,0 +1,348 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from 'node:child_process';
4
+ import { spawn } from 'node:child_process';
5
+ import { existsSync } from 'node:fs';
6
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
7
+ import { homedir } from 'node:os';
8
+ import { dirname, join } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { commandHelpArgs, renderhstackRootHelp, resolvehstackCommand } from '../scripts/utils/cli/cli_registry.mjs';
11
+ import { expandHome, getCanonicalHomeEnvPathFromEnv } from '../scripts/utils/paths/canonical_home.mjs';
12
+ import { resolveStackEnvPath } from '../scripts/utils/paths/paths.mjs';
13
+ import { SANDBOX_PRESERVE_KEYS, scrubHappierStackEnv } from '../scripts/utils/env/scrub_env.mjs';
14
+ import { maybeAutoUpdateNotice as maybeAutoUpdateNoticeShared } from '../scripts/utils/update/auto_update_notice.mjs';
15
+
16
+ function getCliRootDir() {
17
+ return dirname(dirname(fileURLToPath(import.meta.url)));
18
+ }
19
+
20
+ // expandHome is imported from scripts/utils/paths/canonical_home.mjs
21
+
22
+ function dotenvGetQuick(envPath, key) {
23
+ try {
24
+ if (!envPath || !existsSync(envPath)) return '';
25
+ const lines = readFileSync(envPath, 'utf-8').split('\n');
26
+ for (const line of lines) {
27
+ const trimmed = line.trim();
28
+ if (!trimmed || trimmed.startsWith('#')) continue;
29
+ if (!trimmed.startsWith(`${key}=`)) continue;
30
+ let v = trimmed.slice(`${key}=`.length).trim();
31
+ if (v.startsWith('"') && v.endsWith('"')) v = v.slice(1, -1);
32
+ if (v.startsWith("'") && v.endsWith("'")) v = v.slice(1, -1);
33
+ return v;
34
+ }
35
+ } catch {
36
+ // ignore
37
+ }
38
+ return '';
39
+ }
40
+
41
+ function resolveCliRootDir() {
42
+ const fromEnv = (
43
+ process.env.HAPPIER_STACK_CLI_ROOT_DIR ??
44
+ process.env.HAPPIER_STACK_DEV_CLI_ROOT_DIR ??
45
+ ''
46
+ ).trim();
47
+ if (fromEnv) return expandHome(fromEnv);
48
+
49
+ // Stable pointer file: even if the real home dir is elsewhere, `hstack init` writes the pointer here.
50
+ const canonicalEnv = getCanonicalHomeEnvPathFromEnv(process.env);
51
+ const v =
52
+ dotenvGetQuick(canonicalEnv, 'HAPPIER_STACK_CLI_ROOT_DIR') ||
53
+ dotenvGetQuick(canonicalEnv, 'HAPPIER_STACK_DEV_CLI_ROOT_DIR') ||
54
+ '';
55
+ return v ? expandHome(v) : '';
56
+ }
57
+
58
+ function maybeReexecToCliRoot(cliRootDir) {
59
+ if ((process.env.HAPPIER_STACK_CLI_REEXEC ?? process.env.HAPPIER_STACK_DEV_REEXEC ?? '') === '1') return;
60
+ if ((process.env.HAPPIER_STACK_CLI_ROOT_DISABLE ?? process.env.HAPPIER_STACK_DEV_CLI_DISABLE ?? '') === '1') return;
61
+
62
+ const cliRoot = resolveCliRootDir();
63
+ if (!cliRoot) return;
64
+ if (cliRoot === cliRootDir) return;
65
+
66
+ const cliBin = join(cliRoot, 'bin', 'hstack.mjs');
67
+ if (!existsSync(cliBin)) return;
68
+
69
+ const argv = process.argv.slice(2);
70
+ const res = spawnSync(process.execPath, [cliBin, ...argv], {
71
+ stdio: 'inherit',
72
+ cwd: cliRoot,
73
+ env: {
74
+ ...process.env,
75
+ HAPPIER_STACK_CLI_REEXEC: '1',
76
+ HAPPIER_STACK_CLI_ROOT_DIR: cliRoot,
77
+ },
78
+ });
79
+ process.exit(res.status ?? 1);
80
+ }
81
+
82
+ function resolveHomeDir() {
83
+ const fromEnv = (process.env.HAPPIER_STACK_HOME_DIR ?? '').trim();
84
+ if (fromEnv) return expandHome(fromEnv);
85
+
86
+ // Stable pointer file: even if the real home dir is elsewhere, `hstack init` writes the pointer here.
87
+ const canonicalEnv = getCanonicalHomeEnvPathFromEnv(process.env);
88
+ const v = dotenvGetQuick(canonicalEnv, 'HAPPIER_STACK_HOME_DIR') || '';
89
+ return v ? expandHome(v) : join(homedir(), '.happier-stack');
90
+ }
91
+
92
+ function stripGlobalOpt(argv, { name, aliases = [] }) {
93
+ const names = [name, ...aliases];
94
+ for (const n of names) {
95
+ const eq = `${n}=`;
96
+ const iEq = argv.findIndex((a) => a.startsWith(eq));
97
+ if (iEq >= 0) {
98
+ const value = argv[iEq].slice(eq.length);
99
+ const next = [...argv.slice(0, iEq), ...argv.slice(iEq + 1)];
100
+ return { value, argv: next };
101
+ }
102
+ const i = argv.indexOf(n);
103
+ if (i >= 0 && argv[i + 1] && !argv[i + 1].startsWith('-')) {
104
+ const value = argv[i + 1];
105
+ const next = [...argv.slice(0, i), ...argv.slice(i + 2)];
106
+ return { value, argv: next };
107
+ }
108
+ }
109
+ return { value: '', argv };
110
+ }
111
+
112
+ function applyVerbosityIfRequested(argv) {
113
+ // Global verbosity:
114
+ // - supports -v/-vv/-vvv anywhere before/after the command
115
+ // - supports --verbose and --verbose=N
116
+ //
117
+ // We set HAPPIER_STACK_VERBOSE (0-3) and strip these args so downstream scripts don't need to support them.
118
+ let level = Number.isFinite(Number(process.env.HAPPIER_STACK_VERBOSE)) ? Number(process.env.HAPPIER_STACK_VERBOSE) : null;
119
+ let next = [];
120
+ for (const a of argv) {
121
+ if (a === '-v' || a === '-vv' || a === '-vvv') {
122
+ const n = a.length - 1;
123
+ level = Math.max(level ?? 0, n);
124
+ continue;
125
+ }
126
+ if (a === '--verbose') {
127
+ level = Math.max(level ?? 0, 1);
128
+ continue;
129
+ }
130
+ if (a.startsWith('--verbose=')) {
131
+ const raw = a.slice('--verbose='.length).trim();
132
+ const n = Number(raw);
133
+ if (Number.isFinite(n)) {
134
+ level = Math.max(level ?? 0, Math.max(0, Math.min(3, Math.floor(n))));
135
+ } else {
136
+ level = Math.max(level ?? 0, 1);
137
+ }
138
+ continue;
139
+ }
140
+ next.push(a);
141
+ }
142
+ if (level != null) {
143
+ process.env.HAPPIER_STACK_VERBOSE = String(Math.max(0, Math.min(3, Math.floor(level))));
144
+ }
145
+ return next;
146
+ }
147
+
148
+ function applySandboxDirIfRequested(argv) {
149
+ const explicit = (process.env.HAPPIER_STACK_SANDBOX_DIR ?? '').trim();
150
+ const { value, argv: nextArgv } = stripGlobalOpt(argv, { name: '--sandbox-dir', aliases: ['--sandbox'] });
151
+ const raw = value || explicit;
152
+ if (!raw) return { argv: nextArgv, enabled: false };
153
+
154
+ const sandboxDir = expandHome(raw);
155
+ const allowGlobalRaw = (process.env.HAPPIER_STACK_SANDBOX_ALLOW_GLOBAL ?? '').trim().toLowerCase();
156
+ const allowGlobal = allowGlobalRaw === '1' || allowGlobalRaw === 'true' || allowGlobalRaw === 'yes' || allowGlobalRaw === 'y';
157
+ // Keep all state under one folder that can be deleted to reset completely.
158
+ const canonicalHomeDir = join(sandboxDir, 'canonical');
159
+ const homeDir = join(sandboxDir, 'home');
160
+ const workspaceDir = join(sandboxDir, 'workspace');
161
+ const runtimeDir = join(sandboxDir, 'runtime');
162
+ const storageDir = join(sandboxDir, 'storage');
163
+
164
+ // Sandbox isolation MUST win over any pre-exported hstack env vars.
165
+ // Otherwise sandbox runs can accidentally read/write "real" machine state.
166
+ //
167
+ // Keep only a tiny set of sandbox-safe globals; everything else should be driven by flags
168
+ // and stack env files inside the sandbox.
169
+ const preserved = new Map();
170
+ for (const k of SANDBOX_PRESERVE_KEYS) {
171
+ if (process.env[k] != null && String(process.env[k]).trim() !== '') {
172
+ preserved.set(k, process.env[k]);
173
+ }
174
+ }
175
+ const scrubbed = scrubHappierStackEnv(process.env, {
176
+ keepHappierStackKeys: Array.from(preserved.keys()),
177
+ clearUnprefixedKeys: ['HAPPIER_HOME_DIR', 'HAPPIER_SERVER_URL', 'HAPPIER_WEBAPP_URL'],
178
+ });
179
+ for (const k of Object.keys(process.env)) {
180
+ if (!(k in scrubbed)) delete process.env[k];
181
+ }
182
+ for (const [k, v] of Object.entries(scrubbed)) {
183
+ process.env[k] = v;
184
+ }
185
+
186
+ process.env.HAPPIER_STACK_SANDBOX_DIR = sandboxDir;
187
+ process.env.HAPPIER_STACK_CLI_ROOT_DISABLE = '1'; // never re-exec into a user's "real" install when sandboxing
188
+
189
+ // In sandbox mode, we MUST force all state directories into the sandbox, even if the user
190
+ // exported HAPPIER_STACK_* in their shell. Otherwise sandbox runs can accidentally read/write
191
+ // "real" machine state (breaking isolation).
192
+ process.env.HAPPIER_STACK_CANONICAL_HOME_DIR = canonicalHomeDir;
193
+
194
+ process.env.HAPPIER_STACK_HOME_DIR = homeDir;
195
+
196
+ process.env.HAPPIER_STACK_WORKSPACE_DIR = workspaceDir;
197
+
198
+ process.env.HAPPIER_STACK_RUNTIME_DIR = runtimeDir;
199
+
200
+ process.env.HAPPIER_STACK_STORAGE_DIR = storageDir;
201
+
202
+ // Sandbox default: disallow global side effects unless explicitly opted in.
203
+ // This keeps sandbox runs fast, deterministic, and isolated.
204
+ if (!allowGlobal) {
205
+ // Network-y UX (background update checks) are not useful in a temporary sandbox.
206
+ process.env.HAPPIER_STACK_UPDATE_CHECK = '0';
207
+ process.env.HAPPIER_STACK_UPDATE_CHECK_INTERVAL_MS = '0';
208
+ process.env.HAPPIER_STACK_UPDATE_NOTIFY_INTERVAL_MS = '0';
209
+
210
+ // Never auto-enable or reset Tailscale Serve in sandbox.
211
+ // (Tailscale is global machine state; sandbox runs must not touch it.)
212
+ process.env.HAPPIER_STACK_TAILSCALE_SERVE = '0';
213
+ process.env.HAPPIER_STACK_TAILSCALE_RESET_ON_EXIT = '0';
214
+ }
215
+
216
+ return { argv: nextArgv, enabled: true };
217
+ }
218
+
219
+ function maybeAutoUpdateNotice(cliRootDir, cmd) {
220
+ maybeAutoUpdateNoticeShared({
221
+ cliRootDir,
222
+ cmd,
223
+ homeDir: resolveHomeDir(),
224
+ isTTY: Boolean(process.stdout.isTTY),
225
+ env: process.env,
226
+ });
227
+ }
228
+
229
+ function usage() {
230
+ return renderhstackRootHelp();
231
+ }
232
+
233
+ function runNodeScript(cliRootDir, scriptRelPath, args) {
234
+ const scriptPath = join(cliRootDir, scriptRelPath);
235
+ if (!existsSync(scriptPath)) {
236
+ console.error(`[hstack] missing script: ${scriptPath}`);
237
+ process.exit(1);
238
+ }
239
+ const res = spawnSync(process.execPath, [scriptPath, ...args], {
240
+ stdio: 'inherit',
241
+ env: process.env,
242
+ cwd: cliRootDir,
243
+ });
244
+ process.exit(res.status ?? 1);
245
+ }
246
+
247
+ function main() {
248
+ const cliRootDir = getCliRootDir();
249
+ const initialArgv = process.argv.slice(2);
250
+ const argv0 = applyVerbosityIfRequested(initialArgv);
251
+ const { argv, enabled: sandboxed } = applySandboxDirIfRequested(argv0);
252
+ void sandboxed;
253
+
254
+ // Preserve the original working directory across re-exec to the CLI root so commands can infer
255
+ // component/worktree context even when the actual scripts run with cwd=cliRootDir.
256
+ if (!(process.env.HAPPIER_STACK_INVOKED_CWD ?? '').trim()) {
257
+ process.env.HAPPIER_STACK_INVOKED_CWD = process.cwd();
258
+ }
259
+
260
+ maybeReexecToCliRoot(cliRootDir);
261
+
262
+ // If the user passed only flags (common via `npx --yes -p @happier-dev/stack hstack --help`),
263
+ // treat it as root help rather than `help --help` (which would look like
264
+ // "unknown command: --help").
265
+ const cmd = argv.find((a) => !a.startsWith('--')) ?? 'help';
266
+ const cmdIndex = argv.indexOf(cmd);
267
+ const rest = cmdIndex >= 0 ? argv.slice(cmdIndex + 1) : [];
268
+
269
+ maybeAutoUpdateNotice(cliRootDir, cmd);
270
+
271
+ if (cmd === 'help' || cmd === '--help' || cmd === '-h') {
272
+ const target = rest[0];
273
+ if (!target || target.startsWith('-')) {
274
+ console.log(usage());
275
+ return;
276
+ }
277
+ const targetCmd = resolvehstackCommand(target);
278
+ if (!targetCmd || targetCmd.kind !== 'node') {
279
+ console.error(`[hstack] unknown command: ${target}`);
280
+ console.error('');
281
+ console.log(usage());
282
+ process.exit(1);
283
+ }
284
+ const helpArgs = commandHelpArgs(target) ?? ['--help'];
285
+ return runNodeScript(cliRootDir, targetCmd.scriptRelPath, helpArgs);
286
+ }
287
+
288
+ let resolved = resolvehstackCommand(cmd);
289
+ if (!resolved) {
290
+ // Stack shorthand:
291
+ // If the first token is not a known command, but it *is* an existing stack name,
292
+ // treat `hstack <stack> <command> ...` as `hstack stack <command> <stack> ...`.
293
+ const stackName = cmd;
294
+ const { envPath } = resolveStackEnvPath(stackName, process.env);
295
+ const stackExists = existsSync(envPath);
296
+ if (stackExists) {
297
+ const cmdIdx = rest.findIndex((a) => !a.startsWith('-'));
298
+ if (cmdIdx < 0) {
299
+ if (rest.includes('--help') || rest.includes('-h')) {
300
+ const stackCmd = resolvehstackCommand('stack');
301
+ if (!stackCmd || stackCmd.kind !== 'node') {
302
+ console.error('[hstack] internal error: missing stack command');
303
+ process.exit(1);
304
+ }
305
+ return runNodeScript(cliRootDir, stackCmd.scriptRelPath, ['--help']);
306
+ }
307
+ console.error(`[hstack] missing command after stack name: ${stackName}`);
308
+ console.error('');
309
+ console.error('Try one of:');
310
+ console.error(` hstack ${stackName} env list`);
311
+ console.error(` hstack ${stackName} dev`);
312
+ console.error(` hstack ${stackName} start`);
313
+ console.error('');
314
+ console.error('Equivalent long form:');
315
+ console.error(` hstack stack <command> ${stackName} ...`);
316
+ process.exit(1);
317
+ }
318
+
319
+ const stackSubcmd = rest[cmdIdx];
320
+ const preFlags = rest.slice(0, cmdIdx);
321
+ const post = rest.slice(cmdIdx + 1);
322
+ const stackArgs = [stackSubcmd, stackName, ...preFlags, ...post];
323
+
324
+ resolved = resolvehstackCommand('stack');
325
+ if (!resolved || resolved.kind !== 'node') {
326
+ console.error('[hstack] internal error: missing stack command');
327
+ process.exit(1);
328
+ }
329
+ return runNodeScript(cliRootDir, resolved.scriptRelPath, stackArgs);
330
+ }
331
+
332
+ console.error(`[hstack] unknown command: ${cmd}`);
333
+ console.error('');
334
+ console.error(usage());
335
+ process.exit(1);
336
+ }
337
+
338
+ if (resolved.kind === 'external') {
339
+ const args = resolved.external?.argsFromRest ? resolved.external.argsFromRest(rest) : rest;
340
+ const res = spawnSync(resolved.external.cmd, args, { stdio: 'inherit', env: process.env });
341
+ process.exit(res.status ?? 1);
342
+ }
343
+
344
+ const args = resolved.argsFromRest ? resolved.argsFromRest(rest) : rest;
345
+ return runNodeScript(cliRootDir, resolved.scriptRelPath, args);
346
+ }
347
+
348
+ main();
@@ -0,0 +1,129 @@
1
+ # Codex MCP resume (experimental) — Happier integration spec
2
+
3
+ This document describes how to integrate an **experimental** Codex MCP server fork
4
+ that can **resume sessions from rollout JSONL** after restarts/crashes.
5
+
6
+ ## Goals
7
+
8
+ - Allow Happier to use a Codex MCP server that supports **resume-from-rollout**.
9
+ - Keep the feature **opt-in** and **safe by default** (pinned versions, clear UI).
10
+ - Avoid requiring users to replace their global `codex` install.
11
+
12
+ ## Non-goals
13
+
14
+ - Replacing OpenAI’s `@openai/codex` installation for general CLI usage.
15
+ - Auto-updating to “latest” without user opt-in.
16
+
17
+ ## Key facts (implementation reality)
18
+
19
+ - `codex-mcp-server` is **not** a thin wrapper around a user-installed `codex` binary.
20
+ It embeds Codex core and runs threads internally.
21
+ - Therefore, when Happier uses this MCP server, the “engine” for those sessions is
22
+ the forked Codex Rust core shipped with the MCP server build.
23
+
24
+ ## Distribution model (recommended)
25
+
26
+ Ship the MCP server as an npm package that bundles native binaries:
27
+
28
+ - **npm package**: `@leeroy/codex-mcp-resume`
29
+ - **binary launcher**: `npx -y @leeroy/codex-mcp-resume@<pinned-version>`
30
+ - **native payload**: `vendor/<targetTriple>/codex-mcp-server/codex-mcp-server[.exe]`
31
+
32
+ This mirrors how upstream Codex ships platform binaries via npm.
33
+
34
+ ## Versioning + updates
35
+
36
+ ### Pinned by Happier (default)
37
+
38
+ - Happier pins an exact semver (example): `0.84.0-resume.123.a1`
39
+ - Happier invokes:
40
+ - `npx -y @leeroy/codex-mcp-resume@0.84.0-resume.123.a1`
41
+
42
+ **Update path**: users get new MCP builds when they update Happier (or when Happier updates the pin).
43
+
44
+ ### Optional “auto-update experimental tools” (opt-in)
45
+
46
+ If you want faster iteration:
47
+
48
+ - Happier uses `@latest` or a dedicated dist-tag (e.g. `resume`)
49
+ - This should be opt-in, clearly labeled “may break”.
50
+
51
+ ## Happier-side feature flag / settings
52
+
53
+ ### Setting name (proposed)
54
+
55
+ - **Config**: `experimental.codexMcpResume = true|false`
56
+ - **Env override** (optional): `HAPPIER_EXPERIMENTAL_CODEX_RESUME=1`
57
+
58
+ ### UX (proposed)
59
+
60
+ - Settings → Experimental → “Use Codex MCP resume fork”
61
+ - Subtext: “Runs a separate MCP server shipped by Happier; may differ from your global Codex install.”
62
+
63
+ ## Process + wiring
64
+
65
+ ### 1) Resolve the MCP server command
66
+
67
+ When feature enabled, Happier should register an MCP server entry equivalent to:
68
+
69
+ ```toml
70
+ [mcp_servers.codex_resume]
71
+ command = "npx"
72
+ args = ["-y", "@leeroy/codex-mcp-resume@0.84.0-resume.123.a1"]
73
+ ```
74
+
75
+ Notes:
76
+ - The MCP server is stdio-based; Happier should spawn it and speak MCP JSON-RPC over stdin/stdout.
77
+ - Use `-y` to make it non-interactive.
78
+ - Ensure Happier’s environment does **not** leak secrets in logs.
79
+
80
+ ### 2) Decide the Codex “home” used for sessions
81
+
82
+ Session restoration depends on reading rollout JSONL files.
83
+
84
+ Two viable modes:
85
+
86
+ - **Shared home (recommended for seamless resume)**:
87
+ - Run with the user’s default Codex home (usually `~/.codex`), so the MCP server sees the same rollouts.
88
+ - **Happier-managed home (more isolated)**:
89
+ - Provide a separate home (e.g. `~/.happier/codex`) but then “resume existing Codex sessions” won’t work unless you import/migrate.
90
+
91
+ If you need an explicit home, pass env vars when spawning the MCP server:
92
+
93
+ - `CODEX_HOME=<path>`
94
+
95
+ ### 3) Tool usage from Happier
96
+
97
+ Happier should call MCP tools:
98
+
99
+ - **Start new session**: `tools/call name="codex"` with desired config (model, cwd, approval_policy/sandbox, etc).
100
+ - **Continue session**: `tools/call name="codex-reply"` with `{ threadId, prompt }`.
101
+
102
+ The forked MCP server handles:
103
+
104
+ - in-memory threads normally
105
+ - on restart/crash: “resume thread” by reading rollout history
106
+
107
+ ### 4) Concurrency expectations
108
+
109
+ Happier may issue multiple `codex-reply` calls concurrently (UI retries, double-submit, etc).
110
+ The MCP server should serialize per-thread reply/resume to avoid event-stream races.
111
+
112
+ ## CI + publishing requirements (for maintainers)
113
+
114
+ The fork’s CI should:
115
+
116
+ - build `codex-mcp-server` for macOS/Linux/Windows (arm64 + x64 where relevant)
117
+ - pack those into an npm tarball
118
+ - publish via npm trusted publishing (OIDC)
119
+
120
+ ## What Happier devs need to implement (checklist)
121
+
122
+ - Add a feature flag + settings surface (opt-in).
123
+ - Add MCP server registry entry for `codex_resume` (command: `npx`, args pinned).
124
+ - Decide and document which Codex home dir is used (shared vs isolated).
125
+ - Route Codex session creation + reply calls through the selected MCP server.
126
+ - Add telemetry/logging that records:
127
+ - selected MCP server (default vs resume fork)
128
+ - package version (pinned version string)
129
+ - threadId for correlation (no prompt contents)
package/docs/edison.md ADDED
@@ -0,0 +1,74 @@
1
+ ## Edison in hstack
2
+
3
+ This doc explains how we use **Edison** with **hstack** for task/QA/evidence/validation, while keeping isolation strictly enforced via **stacks + repo worktrees**.
4
+
5
+ ---
6
+
7
+ ## What Edison is (in one sentence)
8
+
9
+ Edison is a **task + QA + evidence + validation workflow layer** with generated role prompts and trusted evidence capture.
10
+
11
+ Edison is **not** responsible for isolation in this repo; isolation is provided by **hstack**.
12
+
13
+ ---
14
+
15
+ ## Isolation model in this repo
16
+
17
+ - **Repo worktrees** live under `<workspace>/{dev,pr,local,tmp}/...`
18
+ - **Stacks** live under `~/.happier/stacks/<stack>/...` and each stack has its own ports/db/cli-home/etc.
19
+ - Edison worktrees are **disabled** for this project (to avoid conflicting with hstack worktrees)
20
+
21
+ Practical implications:
22
+
23
+ - Do **not** edit the stable checkout (typically `<workspace>/main`).
24
+ - Do all implementation work in `<workspace>/dev` or a worktree under `<workspace>/{pr,local,tmp}/...`.
25
+ - Run evidence/validation in the correct stack context.
26
+
27
+ ---
28
+
29
+ ## The one correct entrypoint: `hstack edison`
30
+
31
+ Do not run `edison ...` directly in this repo.
32
+
33
+ Use:
34
+
35
+ - `hstack edison -- <edison args...>`
36
+ - `hstack edison --stack=<stack> -- <edison args...>` (recommended)
37
+
38
+ Why this wrapper is mandatory:
39
+
40
+ - Loads the correct stack env (`HAPPIER_STACK_STACK` + `HAPPIER_STACK_ENV_FILE`)
41
+ - Enforces fail-closed guardrails (right stack + right repo worktree)
42
+ - Makes evidence capture deterministic (fingerprints the actual repo checkout the stack uses)
43
+
44
+ Reference:
45
+
46
+ - `.edison/guidelines/agents/HAPPIER_STACK_EDISON_WRAPPER.md`
47
+
48
+ ---
49
+
50
+ ## Task model (hstack)
51
+
52
+ We keep a strict structure so stacks/worktrees are never “forgotten”.
53
+
54
+ - **Parent task** (`hs_kind: parent`)
55
+ - planning umbrella (not claimable)
56
+ - **Track task** (`hs_kind: track`)
57
+ - owns exactly one stack (`stack: <name>`)
58
+ - declares track (`track: upstream|fork|integration`) and scope (`components: [...]`)
59
+ - **Component task** (`hs_kind: component`)
60
+ - child of a track task
61
+ - targets exactly one area (`component: ...`)
62
+
63
+ Note: “component” here means a **service/package scope** in the monorepo (apps/ui, apps/server, packages/*), not a separate git repo.
64
+
65
+ ---
66
+
67
+ ## Where Edison stores things
68
+
69
+ - Config + overlays: `.edison/`
70
+ - Project config: `.edison/config/*.yml`
71
+ - Validator overlays: `.edison/validators/overlays/*.md`
72
+ - Packs/guards: `.edison/packs/**`
73
+ - Generated content (do not edit): `.edison/_generated/`
74
+ - Local task/QA state (gitignored): `.project/**`