@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.
- package/README.md +501 -0
- package/bin/hstack.mjs +348 -0
- package/docs/codex-mcp-resume.md +129 -0
- package/docs/edison.md +74 -0
- package/docs/forking-and-branding.md +189 -0
- package/docs/happy-development.md +22 -0
- package/docs/isolated-linux-vm.md +243 -0
- package/docs/menubar.md +244 -0
- package/docs/mobile-ios.md +322 -0
- package/docs/monorepo-migration.md +20 -0
- package/docs/paths-and-env.md +154 -0
- package/docs/remote-access.md +43 -0
- package/docs/server-flavors.md +147 -0
- package/docs/stacks.md +330 -0
- package/docs/tauri.md +60 -0
- package/docs/worktrees-and-forks.md +133 -0
- package/extras/swiftbar/auth-login.sh +29 -0
- package/extras/swiftbar/git-cache-refresh.sh +122 -0
- package/extras/swiftbar/hstack-term.sh +133 -0
- package/extras/swiftbar/hstack.5s.sh +296 -0
- package/extras/swiftbar/hstack.sh +35 -0
- package/extras/swiftbar/icons/happy-green.png +0 -0
- package/extras/swiftbar/icons/happy-orange.png +0 -0
- package/extras/swiftbar/icons/happy-red.png +0 -0
- package/extras/swiftbar/icons/logo-white.png +0 -0
- package/extras/swiftbar/install.sh +265 -0
- package/extras/swiftbar/lib/git.sh +629 -0
- package/extras/swiftbar/lib/icons.sh +92 -0
- package/extras/swiftbar/lib/render.sh +999 -0
- package/extras/swiftbar/lib/system.sh +244 -0
- package/extras/swiftbar/lib/utils.sh +717 -0
- package/extras/swiftbar/set-interval.sh +65 -0
- package/extras/swiftbar/set-server-flavor.sh +61 -0
- package/extras/swiftbar/wt-pr.sh +140 -0
- package/node_modules/@happier-dev/cli-common/README.md +6 -0
- package/node_modules/@happier-dev/cli-common/dist/index.d.ts +4 -0
- package/node_modules/@happier-dev/cli-common/dist/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/index.js +4 -0
- package/node_modules/@happier-dev/cli-common/dist/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.d.ts +18 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.js +25 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links.d.ts +2 -0
- package/node_modules/@happier-dev/cli-common/dist/links.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links.js +2 -0
- package/node_modules/@happier-dev/cli-common/dist/links.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.d.ts +67 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.js +259 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.d.ts +17 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.js +80 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/package.json +26 -0
- package/package.json +77 -0
- package/scripts/auth.mjs +1829 -0
- package/scripts/auth_copy_from_pglite_lock_in_use.integration.test.mjs +90 -0
- package/scripts/auth_copy_from_runCapture.integration.test.mjs +447 -0
- package/scripts/auth_help_cmd.test.mjs +28 -0
- package/scripts/auth_login_flow_in_tty.test.mjs +100 -0
- package/scripts/auth_login_force_default.test.mjs +66 -0
- package/scripts/auth_login_guided_server_no_expo.test.mjs +126 -0
- package/scripts/auth_login_method_override.test.mjs +67 -0
- package/scripts/auth_login_print_includes_configure_links.test.mjs +99 -0
- package/scripts/auth_status_server_validation.integration.test.mjs +140 -0
- package/scripts/build.mjs +266 -0
- package/scripts/bundleWorkspaceDeps.mjs +38 -0
- package/scripts/bundleWorkspaceDeps.test.mjs +77 -0
- package/scripts/ci.mjs +135 -0
- package/scripts/ci.test.mjs +50 -0
- package/scripts/cli-link.mjs +57 -0
- package/scripts/completion.mjs +395 -0
- package/scripts/contrib.mjs +333 -0
- package/scripts/daemon.mjs +1160 -0
- package/scripts/daemon.status_scope.test.mjs +51 -0
- package/scripts/daemon_cmd.mjs +26 -0
- package/scripts/daemon_dist_guard.test.mjs +171 -0
- package/scripts/daemon_invalid_auth_reseed_stack_name.integration.test.mjs +608 -0
- package/scripts/daemon_server_scoped_state.test.mjs +49 -0
- package/scripts/daemon_start_verification.integration.test.mjs +296 -0
- package/scripts/dev.mjs +545 -0
- package/scripts/doctor.mjs +340 -0
- package/scripts/doctor_cmd.test.mjs +22 -0
- package/scripts/doctor_ui_index_missing.test.mjs +37 -0
- package/scripts/eas.mjs +367 -0
- package/scripts/eas_platform_parsing.test.mjs +63 -0
- package/scripts/edison.mjs +1848 -0
- package/scripts/env.mjs +149 -0
- package/scripts/env_cmd.test.mjs +118 -0
- package/scripts/exit_cleanup_kills_detached_children_on_crash.integration.test.mjs +80 -0
- package/scripts/happier.mjs +82 -0
- package/scripts/import.mjs +1327 -0
- package/scripts/init.mjs +464 -0
- package/scripts/install.mjs +550 -0
- package/scripts/lint.mjs +177 -0
- package/scripts/menubar.mjs +202 -0
- package/scripts/migrate.mjs +318 -0
- package/scripts/mobile.mjs +353 -0
- package/scripts/mobile_dev_client.mjs +87 -0
- package/scripts/monorepo.mjs +2234 -0
- package/scripts/monorepo_port.apply.integration.test.mjs +680 -0
- package/scripts/monorepo_port.conflicts.integration.test.mjs +454 -0
- package/scripts/monorepo_port.validation.integration.test.mjs +486 -0
- package/scripts/orchestrated_stack_auth_flow.test.mjs +134 -0
- package/scripts/orchestrated_stack_auth_flow_resolve_port.test.mjs +98 -0
- package/scripts/orchestrated_stack_auth_flow_webapp_url.test.mjs +119 -0
- package/scripts/pack.mjs +257 -0
- package/scripts/pack.test.mjs +68 -0
- package/scripts/pglite_lock.integration.test.mjs +152 -0
- package/scripts/provision/linux-ubuntu-e2e.sh +132 -0
- package/scripts/provision/linux-ubuntu-review-pr.sh +66 -0
- package/scripts/provision/macos-lima-happy-vm.sh +192 -0
- package/scripts/provision/macos-lima-hstack-e2e.sh +100 -0
- package/scripts/release.mjs +53 -0
- package/scripts/release_binary_smoke.integration.test.mjs +159 -0
- package/scripts/review.mjs +1752 -0
- package/scripts/review_pr.mjs +435 -0
- package/scripts/run.mjs +561 -0
- package/scripts/run_script_with_stack_env.restart_port_reuse.test.mjs +30 -0
- package/scripts/self.mjs +465 -0
- package/scripts/self_host.mjs +9 -0
- package/scripts/self_host_binary_smoke.integration.test.mjs +94 -0
- package/scripts/self_host_runtime.mjs +883 -0
- package/scripts/self_host_runtime.test.mjs +82 -0
- package/scripts/self_host_systemd.real.integration.test.mjs +367 -0
- package/scripts/server_flavor.mjs +148 -0
- package/scripts/service.mjs +868 -0
- package/scripts/service_mode_help.test.mjs +27 -0
- package/scripts/setup.mjs +1324 -0
- package/scripts/setup_non_interactive_flag.test.mjs +60 -0
- package/scripts/setup_pr.mjs +605 -0
- package/scripts/setup_pr_orchestrated_auth_flow_util_import.test.mjs +117 -0
- package/scripts/stack/command_arguments.mjs +91 -0
- package/scripts/stack/copy_auth_from_stack.mjs +111 -0
- package/scripts/stack/delegated_script_commands.mjs +92 -0
- package/scripts/stack/help_text.mjs +110 -0
- package/scripts/stack/port_reservation.mjs +74 -0
- package/scripts/stack/repo_checkout_resolution.mjs +31 -0
- package/scripts/stack/run_script_with_stack_env.mjs +634 -0
- package/scripts/stack/stack_daemon_command.mjs +219 -0
- package/scripts/stack/stack_delegated_help.mjs +81 -0
- package/scripts/stack/stack_environment.mjs +151 -0
- package/scripts/stack/stack_environment.sanitization.test.mjs +75 -0
- package/scripts/stack/stack_happier_passthrough_command.mjs +63 -0
- package/scripts/stack/stack_info_snapshot.mjs +167 -0
- package/scripts/stack/stack_mobile_install_command.mjs +61 -0
- package/scripts/stack/stack_resume_command.mjs +76 -0
- package/scripts/stack/stack_stop_command.mjs +34 -0
- package/scripts/stack/stack_workspace_command.mjs +83 -0
- package/scripts/stack/transient_repo_overrides.mjs +29 -0
- package/scripts/stack.mjs +2388 -0
- package/scripts/stack_archive_cmd.integration.test.mjs +31 -0
- package/scripts/stack_audit_fix_light_env.test.mjs +129 -0
- package/scripts/stack_background_pinned_stack_json.test.mjs +81 -0
- package/scripts/stack_copy_auth_server_scoped.test.mjs +243 -0
- package/scripts/stack_daemon_cmd.integration.test.mjs +484 -0
- package/scripts/stack_eas_help.test.mjs +72 -0
- package/scripts/stack_editor_workspace_monorepo_root.test.mjs +102 -0
- package/scripts/stack_env_cmd.test.mjs +107 -0
- package/scripts/stack_guided_login_bundle_error_parse.test.mjs +20 -0
- package/scripts/stack_guided_login_inner_invocation.test.mjs +46 -0
- package/scripts/stack_happy_cmd.integration.test.mjs +263 -0
- package/scripts/stack_info_snapshot_running_status.test.mjs +186 -0
- package/scripts/stack_interactive_monorepo_group.test.mjs +128 -0
- package/scripts/stack_monorepo_defaults.test.mjs +31 -0
- package/scripts/stack_monorepo_repo_dev_token.test.mjs +32 -0
- package/scripts/stack_monorepo_server_light_from_happy_spec.test.mjs +37 -0
- package/scripts/stack_new_name_normalize_cmd.test.mjs +38 -0
- package/scripts/stack_pr_name_normalize_cmd.test.mjs +84 -0
- package/scripts/stack_resume_cmd.integration.test.mjs +134 -0
- package/scripts/stack_server_flavors_defaults.test.mjs +64 -0
- package/scripts/stack_shorthand_cmd.integration.test.mjs +74 -0
- package/scripts/stack_stop_sweeps_legacy_infra_without_kind.integration.test.mjs +44 -0
- package/scripts/stack_stop_sweeps_when_runtime_missing.integration.test.mjs +42 -0
- package/scripts/stack_stop_sweeps_when_runtime_stale.integration.test.mjs +50 -0
- package/scripts/stack_wt_list.test.mjs +117 -0
- package/scripts/start_ui_required_default.test.mjs +63 -0
- package/scripts/stop.mjs +190 -0
- package/scripts/stopStackWithEnv_no_autosweep_when_runtime_missing.integration.test.mjs +95 -0
- package/scripts/swiftbar_git_monorepo_cmd.test.mjs +75 -0
- package/scripts/swiftbar_render_monorepo_wt_actions.integration.test.mjs +116 -0
- package/scripts/swiftbar_utils_cmd.test.mjs +92 -0
- package/scripts/swiftbar_wt_pr_backcompat.test.mjs +162 -0
- package/scripts/systemd_unit_info.test.mjs +24 -0
- package/scripts/tailscale.mjs +490 -0
- package/scripts/test_ci.mjs +36 -0
- package/scripts/test_cmd.mjs +274 -0
- package/scripts/test_cmd.test.mjs +133 -0
- package/scripts/test_integration.mjs +33 -0
- package/scripts/testkit/auth_testkit.mjs +121 -0
- package/scripts/testkit/doctor_testkit.mjs +68 -0
- package/scripts/testkit/monorepo_port_testkit.mjs +157 -0
- package/scripts/testkit/stack_archive_command_testkit.mjs +55 -0
- package/scripts/testkit/stack_new_monorepo_testkit.mjs +83 -0
- package/scripts/testkit/stack_script_command_testkit.mjs +27 -0
- package/scripts/testkit/stack_stop_sweeps_testkit.mjs +172 -0
- package/scripts/testkit/worktrees_monorepo_testkit.mjs +53 -0
- package/scripts/tools.mjs +70 -0
- package/scripts/tui.mjs +914 -0
- package/scripts/tui_stopStackForTuiExit_no_autosweep.integration.test.mjs +95 -0
- package/scripts/typecheck.mjs +178 -0
- package/scripts/ui_gateway.mjs +247 -0
- package/scripts/uninstall.mjs +179 -0
- package/scripts/utils/auth/credentials_paths.mjs +181 -0
- package/scripts/utils/auth/credentials_paths.test.mjs +187 -0
- package/scripts/utils/auth/daemon_gate.mjs +66 -0
- package/scripts/utils/auth/daemon_gate.test.mjs +116 -0
- package/scripts/utils/auth/decode_jwt_payload_unsafe.mjs +16 -0
- package/scripts/utils/auth/dev_key.mjs +163 -0
- package/scripts/utils/auth/files.mjs +56 -0
- package/scripts/utils/auth/guided_pr_auth.mjs +86 -0
- package/scripts/utils/auth/guided_stack_web_login.mjs +56 -0
- package/scripts/utils/auth/handy_master_secret.mjs +42 -0
- package/scripts/utils/auth/interactive_stack_auth.mjs +70 -0
- package/scripts/utils/auth/login_ux.mjs +105 -0
- package/scripts/utils/auth/orchestrated_stack_auth_flow.mjs +291 -0
- package/scripts/utils/auth/sources.mjs +28 -0
- package/scripts/utils/auth/stable_scope_id.mjs +91 -0
- package/scripts/utils/auth/stable_scope_id.test.mjs +51 -0
- package/scripts/utils/auth/stack_guided_login.mjs +438 -0
- package/scripts/utils/cli/arg_values.mjs +23 -0
- package/scripts/utils/cli/arg_values.test.mjs +43 -0
- package/scripts/utils/cli/args.mjs +17 -0
- package/scripts/utils/cli/cli.mjs +24 -0
- package/scripts/utils/cli/cli_registry.mjs +440 -0
- package/scripts/utils/cli/cwd_scope.mjs +158 -0
- package/scripts/utils/cli/cwd_scope.test.mjs +154 -0
- package/scripts/utils/cli/flags.mjs +17 -0
- package/scripts/utils/cli/log_forwarder.mjs +157 -0
- package/scripts/utils/cli/normalize.mjs +16 -0
- package/scripts/utils/cli/prereqs.mjs +103 -0
- package/scripts/utils/cli/prereqs.test.mjs +33 -0
- package/scripts/utils/cli/progress.mjs +141 -0
- package/scripts/utils/cli/smoke_help.mjs +44 -0
- package/scripts/utils/cli/verbosity.mjs +11 -0
- package/scripts/utils/cli/wizard.mjs +139 -0
- package/scripts/utils/cli/wizard_promptSelect.test.mjs +44 -0
- package/scripts/utils/cli/wizard_prompt_worktree_source_lazy.test.mjs +132 -0
- package/scripts/utils/cli/wizard_worktree_slug.test.mjs +33 -0
- package/scripts/utils/crypto/tokens.mjs +14 -0
- package/scripts/utils/dev/daemon.mjs +232 -0
- package/scripts/utils/dev/daemon_watch_resilience.test.mjs +224 -0
- package/scripts/utils/dev/expo_dev.buildEnv.test.mjs +35 -0
- package/scripts/utils/dev/expo_dev.mjs +478 -0
- package/scripts/utils/dev/expo_dev.test.mjs +89 -0
- package/scripts/utils/dev/expo_dev_restart_port_reservation.test.mjs +120 -0
- package/scripts/utils/dev/expo_dev_verbose_logs.test.mjs +60 -0
- package/scripts/utils/dev/server.mjs +180 -0
- package/scripts/utils/dev_auth_key.mjs +7 -0
- package/scripts/utils/edison/git_roots.mjs +30 -0
- package/scripts/utils/edison/git_roots.test.mjs +49 -0
- package/scripts/utils/env/config.mjs +52 -0
- package/scripts/utils/env/dotenv.mjs +32 -0
- package/scripts/utils/env/dotenv.test.mjs +32 -0
- package/scripts/utils/env/env.mjs +130 -0
- package/scripts/utils/env/env_file.mjs +98 -0
- package/scripts/utils/env/env_file.test.mjs +49 -0
- package/scripts/utils/env/env_local.mjs +25 -0
- package/scripts/utils/env/load_env_file.mjs +34 -0
- package/scripts/utils/env/read.mjs +30 -0
- package/scripts/utils/env/sandbox.mjs +13 -0
- package/scripts/utils/env/scrub_env.mjs +69 -0
- package/scripts/utils/env/scrub_env.test.mjs +102 -0
- package/scripts/utils/env/values.mjs +13 -0
- package/scripts/utils/expo/command.mjs +65 -0
- package/scripts/utils/expo/expo.mjs +139 -0
- package/scripts/utils/expo/expo_state_running.test.mjs +48 -0
- package/scripts/utils/expo/metro_ports.mjs +101 -0
- package/scripts/utils/expo/metro_ports.test.mjs +35 -0
- package/scripts/utils/fs/atomic_dir_swap.mjs +55 -0
- package/scripts/utils/fs/atomic_dir_swap.test.mjs +54 -0
- package/scripts/utils/fs/file_has_content.mjs +10 -0
- package/scripts/utils/fs/fs.mjs +11 -0
- package/scripts/utils/fs/json.mjs +25 -0
- package/scripts/utils/fs/ops.mjs +29 -0
- package/scripts/utils/fs/package_json.mjs +8 -0
- package/scripts/utils/fs/tail.mjs +12 -0
- package/scripts/utils/git/dev_checkout.mjs +127 -0
- package/scripts/utils/git/dev_checkout.test.mjs +115 -0
- package/scripts/utils/git/git.mjs +67 -0
- package/scripts/utils/git/parse_name_status_z.mjs +21 -0
- package/scripts/utils/git/refs.mjs +26 -0
- package/scripts/utils/git/worktrees.mjs +323 -0
- package/scripts/utils/git/worktrees_monorepo.test.mjs +60 -0
- package/scripts/utils/git/worktrees_pathstyle.test.mjs +53 -0
- package/scripts/utils/llm/assist.mjs +260 -0
- package/scripts/utils/llm/codex_exec.mjs +61 -0
- package/scripts/utils/llm/codex_exec.test.mjs +46 -0
- package/scripts/utils/llm/hstack_runner.mjs +59 -0
- package/scripts/utils/llm/tools.mjs +56 -0
- package/scripts/utils/llm/tools.test.mjs +67 -0
- package/scripts/utils/menubar/swiftbar.mjs +121 -0
- package/scripts/utils/menubar/swiftbar.test.mjs +85 -0
- package/scripts/utils/mobile/config.mjs +35 -0
- package/scripts/utils/mobile/dev_client_links.mjs +59 -0
- package/scripts/utils/mobile/identifiers.mjs +46 -0
- package/scripts/utils/mobile/identifiers.test.mjs +41 -0
- package/scripts/utils/mobile/ios_xcodeproj_patch.mjs +128 -0
- package/scripts/utils/mobile/ios_xcodeproj_patch.test.mjs +131 -0
- package/scripts/utils/net/bind_mode.mjs +39 -0
- package/scripts/utils/net/dns.mjs +10 -0
- package/scripts/utils/net/lan_ip.mjs +24 -0
- package/scripts/utils/net/ports.mjs +110 -0
- package/scripts/utils/net/tcp_forward.mjs +162 -0
- package/scripts/utils/net/url.mjs +30 -0
- package/scripts/utils/net/url.test.mjs +29 -0
- package/scripts/utils/paths/canonical_home.mjs +15 -0
- package/scripts/utils/paths/canonical_home.test.mjs +28 -0
- package/scripts/utils/paths/localhost_host.mjs +112 -0
- package/scripts/utils/paths/localhost_host.test.mjs +58 -0
- package/scripts/utils/paths/paths.mjs +302 -0
- package/scripts/utils/paths/paths_env_win32.test.mjs +36 -0
- package/scripts/utils/paths/paths_monorepo.test.mjs +58 -0
- package/scripts/utils/paths/paths_server_flavors.test.mjs +50 -0
- package/scripts/utils/paths/runtime.mjs +41 -0
- package/scripts/utils/pglite_lock.mjs +107 -0
- package/scripts/utils/proc/commands.mjs +33 -0
- package/scripts/utils/proc/exit_cleanup.mjs +57 -0
- package/scripts/utils/proc/happy_monorepo_deps.mjs +37 -0
- package/scripts/utils/proc/happy_monorepo_deps.test.mjs +89 -0
- package/scripts/utils/proc/ownership.mjs +217 -0
- package/scripts/utils/proc/ownership_killProcessGroupOwnedByStack.test.mjs +216 -0
- package/scripts/utils/proc/ownership_listPidsWithEnvNeedles.test.mjs +88 -0
- package/scripts/utils/proc/package_scripts.mjs +38 -0
- package/scripts/utils/proc/package_scripts.test.mjs +58 -0
- package/scripts/utils/proc/parallel.mjs +25 -0
- package/scripts/utils/proc/pids.mjs +11 -0
- package/scripts/utils/proc/pm.mjs +478 -0
- package/scripts/utils/proc/pm_spawn.integration.test.mjs +131 -0
- package/scripts/utils/proc/pm_stack_cache_env.test.mjs +313 -0
- package/scripts/utils/proc/proc.mjs +331 -0
- package/scripts/utils/proc/proc.test.mjs +85 -0
- package/scripts/utils/proc/terminate.mjs +69 -0
- package/scripts/utils/proc/terminate.test.mjs +54 -0
- package/scripts/utils/proc/watch.mjs +63 -0
- package/scripts/utils/review/augment_runner_integration.test.mjs +105 -0
- package/scripts/utils/review/base_ref.mjs +82 -0
- package/scripts/utils/review/base_ref.test.mjs +89 -0
- package/scripts/utils/review/chunks.mjs +55 -0
- package/scripts/utils/review/chunks.test.mjs +107 -0
- package/scripts/utils/review/detached_worktree.mjs +61 -0
- package/scripts/utils/review/detached_worktree.test.mjs +61 -0
- package/scripts/utils/review/findings.mjs +278 -0
- package/scripts/utils/review/findings.test.mjs +203 -0
- package/scripts/utils/review/head_slice.mjs +132 -0
- package/scripts/utils/review/head_slice.test.mjs +117 -0
- package/scripts/utils/review/instructions/deep.md +20 -0
- package/scripts/utils/review/prompts.mjs +279 -0
- package/scripts/utils/review/prompts.test.mjs +77 -0
- package/scripts/utils/review/run_reviewers_safe.mjs +12 -0
- package/scripts/utils/review/run_reviewers_safe.test.mjs +45 -0
- package/scripts/utils/review/runners/augment.mjs +91 -0
- package/scripts/utils/review/runners/augment.test.mjs +64 -0
- package/scripts/utils/review/runners/claude.mjs +92 -0
- package/scripts/utils/review/runners/claude.test.mjs +47 -0
- package/scripts/utils/review/runners/coderabbit.mjs +105 -0
- package/scripts/utils/review/runners/coderabbit.test.mjs +32 -0
- package/scripts/utils/review/runners/codex.mjs +129 -0
- package/scripts/utils/review/runners/codex.test.mjs +115 -0
- package/scripts/utils/review/slice_mode.mjs +20 -0
- package/scripts/utils/review/slice_mode.test.mjs +69 -0
- package/scripts/utils/review/sliced_runner.mjs +39 -0
- package/scripts/utils/review/sliced_runner.test.mjs +57 -0
- package/scripts/utils/review/slices.mjs +140 -0
- package/scripts/utils/review/slices.test.mjs +41 -0
- package/scripts/utils/review/targets.mjs +23 -0
- package/scripts/utils/review/targets.test.mjs +31 -0
- package/scripts/utils/review/tool_home_seed.mjs +106 -0
- package/scripts/utils/review/tool_home_seed.test.mjs +124 -0
- package/scripts/utils/review/uncommitted_ops.mjs +77 -0
- package/scripts/utils/review/uncommitted_ops.test.mjs +117 -0
- package/scripts/utils/sandbox/review_pr_sandbox.mjs +105 -0
- package/scripts/utils/server/apply_server_light_env_defaults.mjs +14 -0
- package/scripts/utils/server/flavor_scripts.mjs +138 -0
- package/scripts/utils/server/flavor_scripts.test.mjs +115 -0
- package/scripts/utils/server/infra/happy_server_infra.mjs +444 -0
- package/scripts/utils/server/mobile_api_url.mjs +60 -0
- package/scripts/utils/server/mobile_api_url.test.mjs +58 -0
- package/scripts/utils/server/port.mjs +55 -0
- package/scripts/utils/server/prisma_import.mjs +36 -0
- package/scripts/utils/server/prisma_import.test.mjs +78 -0
- package/scripts/utils/server/server.mjs +109 -0
- package/scripts/utils/server/ui_build_check.mjs +37 -0
- package/scripts/utils/server/ui_build_check.test.mjs +70 -0
- package/scripts/utils/server/ui_env.mjs +13 -0
- package/scripts/utils/server/ui_env.test.mjs +57 -0
- package/scripts/utils/server/urls.mjs +100 -0
- package/scripts/utils/server/validate.mjs +60 -0
- package/scripts/utils/server/validate.test.mjs +76 -0
- package/scripts/utils/service/autostart_darwin.mjs +198 -0
- package/scripts/utils/service/autostart_darwin.test.mjs +49 -0
- package/scripts/utils/service/autostart_darwin_keepalive.test.mjs +19 -0
- package/scripts/utils/stack/cli_identities.mjs +29 -0
- package/scripts/utils/stack/context.mjs +19 -0
- package/scripts/utils/stack/dirs.mjs +26 -0
- package/scripts/utils/stack/editor_workspace.mjs +126 -0
- package/scripts/utils/stack/interactive_stack_config.mjs +266 -0
- package/scripts/utils/stack/interactive_stack_config.port_validation.test.mjs +93 -0
- package/scripts/utils/stack/interactive_stack_config.remote_validation.test.mjs +122 -0
- package/scripts/utils/stack/interactive_stack_config.stack_name_validation.test.mjs +76 -0
- package/scripts/utils/stack/interactive_stack_config_testkit.mjs +18 -0
- package/scripts/utils/stack/names.mjs +27 -0
- package/scripts/utils/stack/names.test.mjs +26 -0
- package/scripts/utils/stack/pr_stack_name.mjs +16 -0
- package/scripts/utils/stack/runtime_state.mjs +88 -0
- package/scripts/utils/stack/stacks.mjs +40 -0
- package/scripts/utils/stack/startup.mjs +370 -0
- package/scripts/utils/stack/startup_server_light_dirs.test.mjs +119 -0
- package/scripts/utils/stack/startup_server_light_generate.test.mjs +20 -0
- package/scripts/utils/stack/startup_server_light_legacy.test.mjs +79 -0
- package/scripts/utils/stack/startup_server_light_testkit.mjs +106 -0
- package/scripts/utils/stack/stop.mjs +284 -0
- package/scripts/utils/stack_context.mjs +1 -0
- package/scripts/utils/stack_runtime_state.mjs +1 -0
- package/scripts/utils/stacks.mjs +1 -0
- package/scripts/utils/tailscale/ip.mjs +116 -0
- package/scripts/utils/tauri/stack_overrides.mjs +22 -0
- package/scripts/utils/test/collect_test_files.mjs +29 -0
- package/scripts/utils/time/get_today_ymd.mjs +7 -0
- package/scripts/utils/tui/cleanup.mjs +38 -0
- package/scripts/utils/ui/ansi.mjs +47 -0
- package/scripts/utils/ui/browser.mjs +31 -0
- package/scripts/utils/ui/browser.test.mjs +56 -0
- package/scripts/utils/ui/clipboard.mjs +38 -0
- package/scripts/utils/ui/layout.mjs +44 -0
- package/scripts/utils/ui/qr.mjs +17 -0
- package/scripts/utils/ui/terminal_launcher.mjs +129 -0
- package/scripts/utils/ui/text.mjs +16 -0
- package/scripts/utils/update/auto_update_notice.mjs +93 -0
- package/scripts/utils/validate.mjs +5 -0
- package/scripts/where.mjs +138 -0
- package/scripts/worktrees.mjs +2174 -0
- package/scripts/worktrees_archive_cmd.integration.test.mjs +228 -0
- package/scripts/worktrees_cursor_monorepo_root.test.mjs +23 -0
- package/scripts/worktrees_list_specs_no_recurse.test.mjs +32 -0
- package/scripts/worktrees_monorepo_testkit.test.mjs +29 -0
- package/scripts/worktrees_monorepo_use_group.test.mjs +41 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { run, runCapture } from './utils/proc/proc.mjs';
|
|
7
|
+
import { withTempRoot, gitEnv, spawnNodeWithCapture } from './testkit/monorepo_port_testkit.mjs';
|
|
8
|
+
|
|
9
|
+
test('monorepo port status reports the current patch and conflicted files during git am', async (t) => {
|
|
10
|
+
const root = await withTempRoot(t);
|
|
11
|
+
const target = join(root, 'target-mono');
|
|
12
|
+
const sourceCli = join(root, 'source-cli');
|
|
13
|
+
|
|
14
|
+
// Target monorepo stub with cli/hello.txt="value=target".
|
|
15
|
+
await mkdir(target, { recursive: true });
|
|
16
|
+
await run('git', ['init', '-q'], { cwd: target, env: gitEnv() });
|
|
17
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: target, env: gitEnv() });
|
|
18
|
+
await mkdir(join(target, 'apps', 'ui'), { recursive: true });
|
|
19
|
+
await mkdir(join(target, 'apps', 'cli'), { recursive: true });
|
|
20
|
+
await mkdir(join(target, 'apps', 'server'), { recursive: true });
|
|
21
|
+
await writeFile(join(target, 'apps', 'ui', 'package.json'), '{}\n', 'utf-8');
|
|
22
|
+
await writeFile(join(target, 'apps', 'cli', 'package.json'), '{}\n', 'utf-8');
|
|
23
|
+
await writeFile(join(target, 'apps', 'server', 'package.json'), '{}\n', 'utf-8');
|
|
24
|
+
await writeFile(join(target, 'apps', 'cli', 'hello.txt'), 'value=target\n', 'utf-8');
|
|
25
|
+
await run('git', ['add', '.'], { cwd: target, env: gitEnv() });
|
|
26
|
+
await run('git', ['commit', '-q', '-m', 'chore: init monorepo'], { cwd: target, env: gitEnv() });
|
|
27
|
+
|
|
28
|
+
// Source CLI repo with base="value=base" and a commit changing to "value=source".
|
|
29
|
+
await mkdir(sourceCli, { recursive: true });
|
|
30
|
+
await run('git', ['init', '-q'], { cwd: sourceCli, env: gitEnv() });
|
|
31
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: sourceCli, env: gitEnv() });
|
|
32
|
+
await writeFile(join(sourceCli, 'package.json'), '{}\n', 'utf-8');
|
|
33
|
+
await writeFile(join(sourceCli, 'hello.txt'), 'value=base\n', 'utf-8');
|
|
34
|
+
await run('git', ['add', '.'], { cwd: sourceCli, env: gitEnv() });
|
|
35
|
+
await run('git', ['commit', '-q', '-m', 'chore: init cli'], { cwd: sourceCli, env: gitEnv() });
|
|
36
|
+
const base = (await runCapture('git', ['rev-parse', 'HEAD'], { cwd: sourceCli, env: gitEnv() })).trim();
|
|
37
|
+
await writeFile(join(sourceCli, 'hello.txt'), 'value=source\n', 'utf-8');
|
|
38
|
+
await run('git', ['add', '.'], { cwd: sourceCli, env: gitEnv() });
|
|
39
|
+
await run('git', ['commit', '-q', '-m', 'feat: update hello'], { cwd: sourceCli, env: gitEnv() });
|
|
40
|
+
|
|
41
|
+
// Start a port that will stop with an am conflict.
|
|
42
|
+
await assert.rejects(
|
|
43
|
+
async () =>
|
|
44
|
+
await runCapture(
|
|
45
|
+
process.execPath,
|
|
46
|
+
[
|
|
47
|
+
join(process.cwd(), 'scripts', 'monorepo.mjs'),
|
|
48
|
+
'port',
|
|
49
|
+
`--target=${target}`,
|
|
50
|
+
`--branch=port/test-status`,
|
|
51
|
+
`--from-happy-cli=${sourceCli}`,
|
|
52
|
+
`--from-happy-cli-base=${base}`,
|
|
53
|
+
'--3way',
|
|
54
|
+
],
|
|
55
|
+
{ cwd: process.cwd(), env: gitEnv() }
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const out = await runCapture(
|
|
60
|
+
process.execPath,
|
|
61
|
+
[join(process.cwd(), 'scripts', 'monorepo.mjs'), 'port', 'status', `--target=${target}`, '--json'],
|
|
62
|
+
{ cwd: process.cwd(), env: gitEnv() }
|
|
63
|
+
);
|
|
64
|
+
const parsed = JSON.parse(out.trim());
|
|
65
|
+
assert.equal(parsed.ok, true);
|
|
66
|
+
assert.equal(parsed.inProgress, true);
|
|
67
|
+
assert.ok(parsed.currentPatch?.subject?.includes('feat: update hello'), `expected subject in status\n${out}`);
|
|
68
|
+
// Depending on git's 3-way behavior, it may stop without creating unmerged entries.
|
|
69
|
+
// In that case, status should still expose the file(s) touched by the current patch.
|
|
70
|
+
assert.ok(
|
|
71
|
+
parsed.conflictedFiles.includes('apps/cli/hello.txt') || parsed.currentPatch?.files?.includes('apps/cli/hello.txt'),
|
|
72
|
+
`expected apps/cli/hello.txt in conflictedFiles or currentPatch.files\n${out}`
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('monorepo port continue runs git am --continue after conflicts are resolved', async (t) => {
|
|
77
|
+
const root = await withTempRoot(t);
|
|
78
|
+
const target = join(root, 'target-mono');
|
|
79
|
+
const sourceCli = join(root, 'source-cli');
|
|
80
|
+
|
|
81
|
+
// Target monorepo stub with cli/hello.txt="value=target".
|
|
82
|
+
await mkdir(target, { recursive: true });
|
|
83
|
+
await run('git', ['init', '-q'], { cwd: target, env: gitEnv() });
|
|
84
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: target, env: gitEnv() });
|
|
85
|
+
await mkdir(join(target, 'apps', 'ui'), { recursive: true });
|
|
86
|
+
await mkdir(join(target, 'apps', 'cli'), { recursive: true });
|
|
87
|
+
await mkdir(join(target, 'apps', 'server'), { recursive: true });
|
|
88
|
+
await writeFile(join(target, 'apps', 'ui', 'package.json'), '{}\n', 'utf-8');
|
|
89
|
+
await writeFile(join(target, 'apps', 'cli', 'package.json'), '{}\n', 'utf-8');
|
|
90
|
+
await writeFile(join(target, 'apps', 'server', 'package.json'), '{}\n', 'utf-8');
|
|
91
|
+
await writeFile(join(target, 'apps', 'cli', 'hello.txt'), 'value=target\n', 'utf-8');
|
|
92
|
+
await run('git', ['add', '.'], { cwd: target, env: gitEnv() });
|
|
93
|
+
await run('git', ['commit', '-q', '-m', 'chore: init monorepo'], { cwd: target, env: gitEnv() });
|
|
94
|
+
|
|
95
|
+
// Source CLI repo with base="value=base" and a commit changing to "value=source".
|
|
96
|
+
await mkdir(sourceCli, { recursive: true });
|
|
97
|
+
await run('git', ['init', '-q'], { cwd: sourceCli, env: gitEnv() });
|
|
98
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: sourceCli, env: gitEnv() });
|
|
99
|
+
await writeFile(join(sourceCli, 'package.json'), '{}\n', 'utf-8');
|
|
100
|
+
await writeFile(join(sourceCli, 'hello.txt'), 'value=base\n', 'utf-8');
|
|
101
|
+
await run('git', ['add', '.'], { cwd: sourceCli, env: gitEnv() });
|
|
102
|
+
await run('git', ['commit', '-q', '-m', 'chore: init cli'], { cwd: sourceCli, env: gitEnv() });
|
|
103
|
+
const base = (await runCapture('git', ['rev-parse', 'HEAD'], { cwd: sourceCli, env: gitEnv() })).trim();
|
|
104
|
+
await writeFile(join(sourceCli, 'hello.txt'), 'value=source\n', 'utf-8');
|
|
105
|
+
await run('git', ['add', '.'], { cwd: sourceCli, env: gitEnv() });
|
|
106
|
+
await run('git', ['commit', '-q', '-m', 'feat: update hello'], { cwd: sourceCli, env: gitEnv() });
|
|
107
|
+
|
|
108
|
+
// Start a port that will stop with an am conflict.
|
|
109
|
+
await assert.rejects(
|
|
110
|
+
async () =>
|
|
111
|
+
await runCapture(
|
|
112
|
+
process.execPath,
|
|
113
|
+
[
|
|
114
|
+
join(process.cwd(), 'scripts', 'monorepo.mjs'),
|
|
115
|
+
'port',
|
|
116
|
+
`--target=${target}`,
|
|
117
|
+
`--branch=port/test-continue-helper`,
|
|
118
|
+
`--from-happy-cli=${sourceCli}`,
|
|
119
|
+
`--from-happy-cli-base=${base}`,
|
|
120
|
+
'--3way',
|
|
121
|
+
],
|
|
122
|
+
{ cwd: process.cwd(), env: gitEnv() }
|
|
123
|
+
)
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Resolve the conflict by choosing "value=source".
|
|
127
|
+
await writeFile(join(target, 'apps', 'cli', 'hello.txt'), 'value=source\n', 'utf-8');
|
|
128
|
+
await run('git', ['add', 'apps/cli/hello.txt'], { cwd: target, env: gitEnv() });
|
|
129
|
+
|
|
130
|
+
const out = await runCapture(
|
|
131
|
+
process.execPath,
|
|
132
|
+
[join(process.cwd(), 'scripts', 'monorepo.mjs'), 'port', 'continue', `--target=${target}`, '--json'],
|
|
133
|
+
{ cwd: process.cwd(), env: gitEnv() }
|
|
134
|
+
);
|
|
135
|
+
const parsed = JSON.parse(out.trim());
|
|
136
|
+
assert.equal(parsed.ok, true);
|
|
137
|
+
assert.equal(parsed.inProgress, false);
|
|
138
|
+
|
|
139
|
+
const content = (await readFile(join(target, 'apps', 'cli', 'hello.txt'), 'utf-8')).toString();
|
|
140
|
+
assert.equal(content, 'value=source\n');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('monorepo port continue --stage stages conflicted files before continuing', async (t) => {
|
|
144
|
+
const root = await withTempRoot(t);
|
|
145
|
+
const target = join(root, 'target-mono');
|
|
146
|
+
const sourceCli = join(root, 'source-cli');
|
|
147
|
+
|
|
148
|
+
// Target monorepo stub with cli/hello.txt="value=target".
|
|
149
|
+
await mkdir(target, { recursive: true });
|
|
150
|
+
await run('git', ['init', '-q'], { cwd: target, env: gitEnv() });
|
|
151
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: target, env: gitEnv() });
|
|
152
|
+
await mkdir(join(target, 'apps', 'ui'), { recursive: true });
|
|
153
|
+
await mkdir(join(target, 'apps', 'cli'), { recursive: true });
|
|
154
|
+
await mkdir(join(target, 'apps', 'server'), { recursive: true });
|
|
155
|
+
await writeFile(join(target, 'apps', 'ui', 'package.json'), '{}\n', 'utf-8');
|
|
156
|
+
await writeFile(join(target, 'apps', 'cli', 'package.json'), '{}\n', 'utf-8');
|
|
157
|
+
await writeFile(join(target, 'apps', 'server', 'package.json'), '{}\n', 'utf-8');
|
|
158
|
+
await writeFile(join(target, 'apps', 'cli', 'hello.txt'), 'value=target\n', 'utf-8');
|
|
159
|
+
await run('git', ['add', '.'], { cwd: target, env: gitEnv() });
|
|
160
|
+
await run('git', ['commit', '-q', '-m', 'chore: init monorepo'], { cwd: target, env: gitEnv() });
|
|
161
|
+
|
|
162
|
+
// Source CLI repo with base="value=base" and a commit changing to "value=source".
|
|
163
|
+
await mkdir(sourceCli, { recursive: true });
|
|
164
|
+
await run('git', ['init', '-q'], { cwd: sourceCli, env: gitEnv() });
|
|
165
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: sourceCli, env: gitEnv() });
|
|
166
|
+
await writeFile(join(sourceCli, 'package.json'), '{}\n', 'utf-8');
|
|
167
|
+
await writeFile(join(sourceCli, 'hello.txt'), 'value=base\n', 'utf-8');
|
|
168
|
+
await run('git', ['add', '.'], { cwd: sourceCli, env: gitEnv() });
|
|
169
|
+
await run('git', ['commit', '-q', '-m', 'chore: init cli'], { cwd: sourceCli, env: gitEnv() });
|
|
170
|
+
const base = (await runCapture('git', ['rev-parse', 'HEAD'], { cwd: sourceCli, env: gitEnv() })).trim();
|
|
171
|
+
await writeFile(join(sourceCli, 'hello.txt'), 'value=source\n', 'utf-8');
|
|
172
|
+
await run('git', ['add', '.'], { cwd: sourceCli, env: gitEnv() });
|
|
173
|
+
await run('git', ['commit', '-q', '-m', 'feat: update hello'], { cwd: sourceCli, env: gitEnv() });
|
|
174
|
+
|
|
175
|
+
// Start a port that will stop with an am conflict.
|
|
176
|
+
await assert.rejects(
|
|
177
|
+
async () =>
|
|
178
|
+
await runCapture(
|
|
179
|
+
process.execPath,
|
|
180
|
+
[
|
|
181
|
+
join(process.cwd(), 'scripts', 'monorepo.mjs'),
|
|
182
|
+
'port',
|
|
183
|
+
`--target=${target}`,
|
|
184
|
+
`--branch=port/test-continue-stage`,
|
|
185
|
+
`--from-happy-cli=${sourceCli}`,
|
|
186
|
+
`--from-happy-cli-base=${base}`,
|
|
187
|
+
'--3way',
|
|
188
|
+
],
|
|
189
|
+
{ cwd: process.cwd(), env: gitEnv() }
|
|
190
|
+
)
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// Resolve the conflict by choosing "value=source", but DO NOT stage it.
|
|
194
|
+
await writeFile(join(target, 'apps', 'cli', 'hello.txt'), 'value=source\n', 'utf-8');
|
|
195
|
+
|
|
196
|
+
const out = await runCapture(
|
|
197
|
+
process.execPath,
|
|
198
|
+
[join(process.cwd(), 'scripts', 'monorepo.mjs'), 'port', 'continue', `--target=${target}`, '--stage', '--json'],
|
|
199
|
+
{ cwd: process.cwd(), env: gitEnv() }
|
|
200
|
+
);
|
|
201
|
+
const parsed = JSON.parse(out.trim());
|
|
202
|
+
assert.equal(parsed.ok, true);
|
|
203
|
+
assert.equal(parsed.inProgress, false);
|
|
204
|
+
|
|
205
|
+
const content = (await readFile(join(target, 'apps', 'cli', 'hello.txt'), 'utf-8')).toString();
|
|
206
|
+
assert.equal(content, 'value=source\n');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('monorepo port guide refuses to run in non-tty mode', async (t) => {
|
|
210
|
+
const root = await withTempRoot(t);
|
|
211
|
+
const target = join(root, 'target-mono');
|
|
212
|
+
await mkdir(target, { recursive: true });
|
|
213
|
+
await run('git', ['init', '-q'], { cwd: target, env: gitEnv() });
|
|
214
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: target, env: gitEnv() });
|
|
215
|
+
await mkdir(join(target, 'apps', 'ui'), { recursive: true });
|
|
216
|
+
await mkdir(join(target, 'apps', 'cli'), { recursive: true });
|
|
217
|
+
await mkdir(join(target, 'apps', 'server'), { recursive: true });
|
|
218
|
+
await writeFile(join(target, 'apps', 'ui', 'package.json'), '{}\n', 'utf-8');
|
|
219
|
+
await writeFile(join(target, 'apps', 'cli', 'package.json'), '{}\n', 'utf-8');
|
|
220
|
+
await writeFile(join(target, 'apps', 'server', 'package.json'), '{}\n', 'utf-8');
|
|
221
|
+
await run('git', ['add', '.'], { cwd: target, env: gitEnv() });
|
|
222
|
+
await run('git', ['commit', '-q', '-m', 'chore: init monorepo'], { cwd: target, env: gitEnv() });
|
|
223
|
+
|
|
224
|
+
await assert.rejects(
|
|
225
|
+
async () =>
|
|
226
|
+
await runCapture(
|
|
227
|
+
process.execPath,
|
|
228
|
+
[join(process.cwd(), 'scripts', 'monorepo.mjs'), 'port', 'guide', `--target=${target}`],
|
|
229
|
+
{ cwd: process.cwd(), env: gitEnv() }
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test('monorepo port guide can wait for conflict resolution and finish the port', async (t) => {
|
|
235
|
+
const root = await withTempRoot(t);
|
|
236
|
+
const target = join(root, 'target-mono');
|
|
237
|
+
const sourceCli = join(root, 'source-cli');
|
|
238
|
+
|
|
239
|
+
// Target monorepo stub with cli/hello.txt="value=target".
|
|
240
|
+
await mkdir(target, { recursive: true });
|
|
241
|
+
await run('git', ['init', '-q'], { cwd: target, env: gitEnv() });
|
|
242
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: target, env: gitEnv() });
|
|
243
|
+
await mkdir(join(target, 'apps', 'ui'), { recursive: true });
|
|
244
|
+
await mkdir(join(target, 'apps', 'cli'), { recursive: true });
|
|
245
|
+
await mkdir(join(target, 'apps', 'server'), { recursive: true });
|
|
246
|
+
await writeFile(join(target, 'apps', 'ui', 'package.json'), '{}\n', 'utf-8');
|
|
247
|
+
await writeFile(join(target, 'apps', 'cli', 'package.json'), '{}\n', 'utf-8');
|
|
248
|
+
await writeFile(join(target, 'apps', 'server', 'package.json'), '{}\n', 'utf-8');
|
|
249
|
+
await writeFile(join(target, 'apps', 'cli', 'hello.txt'), 'value=target\n', 'utf-8');
|
|
250
|
+
await run('git', ['add', '.'], { cwd: target, env: gitEnv() });
|
|
251
|
+
await run('git', ['commit', '-q', '-m', 'chore: init monorepo'], { cwd: target, env: gitEnv() });
|
|
252
|
+
|
|
253
|
+
// Source CLI repo: keep main at base commit, then branch for the change.
|
|
254
|
+
await mkdir(sourceCli, { recursive: true });
|
|
255
|
+
await run('git', ['init', '-q'], { cwd: sourceCli, env: gitEnv() });
|
|
256
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: sourceCli, env: gitEnv() });
|
|
257
|
+
await writeFile(join(sourceCli, 'package.json'), '{}\n', 'utf-8');
|
|
258
|
+
await writeFile(join(sourceCli, 'hello.txt'), 'value=base\n', 'utf-8');
|
|
259
|
+
await run('git', ['add', '.'], { cwd: sourceCli, env: gitEnv() });
|
|
260
|
+
await run('git', ['commit', '-q', '-m', 'chore: init cli'], { cwd: sourceCli, env: gitEnv() });
|
|
261
|
+
await run('git', ['checkout', '-q', '-b', 'feature'], { cwd: sourceCli, env: gitEnv() });
|
|
262
|
+
await writeFile(join(sourceCli, 'hello.txt'), 'value=source\n', 'utf-8');
|
|
263
|
+
await run('git', ['add', '.'], { cwd: sourceCli, env: gitEnv() });
|
|
264
|
+
await run('git', ['commit', '-q', '-m', 'feat: update hello'], { cwd: sourceCli, env: gitEnv() });
|
|
265
|
+
|
|
266
|
+
// Run guide in "test TTY" mode so it can prompt even under non-interactive test runners.
|
|
267
|
+
const scriptPath = join(process.cwd(), 'scripts', 'monorepo.mjs');
|
|
268
|
+
const inputLines = [
|
|
269
|
+
target, // Target monorepo path
|
|
270
|
+
'port/test-guide', // New branch name
|
|
271
|
+
'1', // Use 3-way merge: yes
|
|
272
|
+
// Sources: since we provide --from-happy-cli via the prompts in this test, guide will still prompt.
|
|
273
|
+
'', // Path to old happy (skip)
|
|
274
|
+
sourceCli, // Path to old happy-cli
|
|
275
|
+
'', // Path to old happy-server (skip)
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
const guide = spawnNodeWithCapture(
|
|
279
|
+
process.execPath,
|
|
280
|
+
[scriptPath, 'port', 'guide'],
|
|
281
|
+
{
|
|
282
|
+
cwd: process.cwd(),
|
|
283
|
+
env: { ...gitEnv(), HAPPIER_STACK_TEST_TTY: '1', HAPPIER_STACK_DISABLE_LLM_AUTOEXEC: '1' },
|
|
284
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
285
|
+
}
|
|
286
|
+
);
|
|
287
|
+
t.after(() => {
|
|
288
|
+
guide.kill('SIGKILL');
|
|
289
|
+
});
|
|
290
|
+
let conflictSeen = false;
|
|
291
|
+
|
|
292
|
+
const sendLine = (line) => guide.sendLine(line);
|
|
293
|
+
|
|
294
|
+
// Feed the wizard answers step-by-step (readline can be picky under non-tty runners).
|
|
295
|
+
await guide.waitForText('Target monorepo path:', 15_000);
|
|
296
|
+
sendLine(inputLines[0]);
|
|
297
|
+
await guide.waitForText('New branch name:', 15_000);
|
|
298
|
+
sendLine(inputLines[1]);
|
|
299
|
+
await guide.waitForText('Use 3-way merge', 15_000);
|
|
300
|
+
sendLine(inputLines[2]);
|
|
301
|
+
await guide.waitForText('old happy (UI)', 15_000);
|
|
302
|
+
sendLine(inputLines[3]);
|
|
303
|
+
await guide.waitForText('old happy-cli', 15_000);
|
|
304
|
+
sendLine(inputLines[4]);
|
|
305
|
+
await guide.waitForText('old happy-server', 15_000);
|
|
306
|
+
sendLine(inputLines[5]);
|
|
307
|
+
|
|
308
|
+
// Preflight now runs before starting the port. Accept the default (guided) mode.
|
|
309
|
+
await guide.waitForText('Preflight detected conflicts', 10_000);
|
|
310
|
+
sendLine('');
|
|
311
|
+
|
|
312
|
+
// Wait for the guide to detect a conflict and start waiting for user action.
|
|
313
|
+
await Promise.race([guide.waitForText('guide: conflict detected', 10_000), guide.waitForText('guide: waiting for conflict resolution', 10_000)]);
|
|
314
|
+
conflictSeen = true;
|
|
315
|
+
|
|
316
|
+
// Wait until the guide is actually prompting for the action.
|
|
317
|
+
await guide.waitForText('Resolve conflicts, then choose an action:', 10_000);
|
|
318
|
+
|
|
319
|
+
// Resolve conflict in target repo by choosing value=source and staging.
|
|
320
|
+
await writeFile(join(target, 'apps', 'cli', 'hello.txt'), 'value=source\n', 'utf-8');
|
|
321
|
+
await run('git', ['add', 'apps/cli/hello.txt'], { cwd: target, env: gitEnv() });
|
|
322
|
+
|
|
323
|
+
// Tell the guide to continue.
|
|
324
|
+
sendLine('');
|
|
325
|
+
|
|
326
|
+
const guideResult = await guide.waitForExit(20_000);
|
|
327
|
+
assert.ok(
|
|
328
|
+
conflictSeen,
|
|
329
|
+
`expected conflict handling markers\nstdout:\n${guideResult.stdout}\nstderr:\n${guideResult.stderr}`
|
|
330
|
+
);
|
|
331
|
+
assert.ok(
|
|
332
|
+
guideResult.combined.includes('guide complete') || guideResult.combined.includes('port complete'),
|
|
333
|
+
`expected completion output\nstdout:\n${guideResult.stdout}\nstderr:\n${guideResult.stderr}`
|
|
334
|
+
);
|
|
335
|
+
assert.equal(
|
|
336
|
+
guideResult.code,
|
|
337
|
+
0,
|
|
338
|
+
`expected guide to exit 0\nstdout:\n${guideResult.stdout}\nstderr:\n${guideResult.stderr}`
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
const content = (await readFile(join(target, 'apps', 'cli', 'hello.txt'), 'utf-8')).toString();
|
|
342
|
+
assert.equal(content, 'value=source\n');
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test('monorepo port guide quit leaves a plan; port continue resumes and completes after conflicts are resolved', async (t) => {
|
|
346
|
+
const root = await withTempRoot(t);
|
|
347
|
+
const target = join(root, 'target-mono');
|
|
348
|
+
const sourceCli = join(root, 'source-cli');
|
|
349
|
+
|
|
350
|
+
// Target monorepo stub with cli/hello.txt="value=target".
|
|
351
|
+
await mkdir(target, { recursive: true });
|
|
352
|
+
await run('git', ['init', '-q'], { cwd: target, env: gitEnv() });
|
|
353
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: target, env: gitEnv() });
|
|
354
|
+
await mkdir(join(target, 'apps', 'ui'), { recursive: true });
|
|
355
|
+
await mkdir(join(target, 'apps', 'cli'), { recursive: true });
|
|
356
|
+
await mkdir(join(target, 'apps', 'server'), { recursive: true });
|
|
357
|
+
await writeFile(join(target, 'apps', 'ui', 'package.json'), '{}\n', 'utf-8');
|
|
358
|
+
await writeFile(join(target, 'apps', 'cli', 'package.json'), '{}\n', 'utf-8');
|
|
359
|
+
await writeFile(join(target, 'apps', 'server', 'package.json'), '{}\n', 'utf-8');
|
|
360
|
+
await writeFile(join(target, 'apps', 'cli', 'hello.txt'), 'value=target\n', 'utf-8');
|
|
361
|
+
await run('git', ['add', '.'], { cwd: target, env: gitEnv() });
|
|
362
|
+
await run('git', ['commit', '-q', '-m', 'chore: init monorepo'], { cwd: target, env: gitEnv() });
|
|
363
|
+
|
|
364
|
+
// Source CLI repo: keep main at base, then create a feature branch with two commits.
|
|
365
|
+
await mkdir(sourceCli, { recursive: true });
|
|
366
|
+
await run('git', ['init', '-q'], { cwd: sourceCli, env: gitEnv() });
|
|
367
|
+
await run('git', ['checkout', '-q', '-b', 'main'], { cwd: sourceCli, env: gitEnv() });
|
|
368
|
+
await writeFile(join(sourceCli, 'package.json'), '{}\n', 'utf-8');
|
|
369
|
+
await writeFile(join(sourceCli, 'hello.txt'), 'value=base\n', 'utf-8');
|
|
370
|
+
await run('git', ['add', '.'], { cwd: sourceCli, env: gitEnv() });
|
|
371
|
+
await run('git', ['commit', '-q', '-m', 'chore: init cli'], { cwd: sourceCli, env: gitEnv() });
|
|
372
|
+
await run('git', ['checkout', '-q', '-b', 'feature'], { cwd: sourceCli, env: gitEnv() });
|
|
373
|
+
await writeFile(join(sourceCli, 'hello.txt'), 'value=source\n', 'utf-8');
|
|
374
|
+
await run('git', ['add', '.'], { cwd: sourceCli, env: gitEnv() });
|
|
375
|
+
await run('git', ['commit', '-q', '-m', 'feat: update hello'], { cwd: sourceCli, env: gitEnv() });
|
|
376
|
+
await writeFile(join(sourceCli, 'extra.txt'), 'extra\n', 'utf-8');
|
|
377
|
+
await run('git', ['add', '.'], { cwd: sourceCli, env: gitEnv() });
|
|
378
|
+
await run('git', ['commit', '-q', '-m', 'feat: add extra'], { cwd: sourceCli, env: gitEnv() });
|
|
379
|
+
|
|
380
|
+
const scriptPath = join(process.cwd(), 'scripts', 'monorepo.mjs');
|
|
381
|
+
const guide = spawnNodeWithCapture(
|
|
382
|
+
process.execPath,
|
|
383
|
+
[scriptPath, 'port', 'guide'],
|
|
384
|
+
{
|
|
385
|
+
cwd: process.cwd(),
|
|
386
|
+
env: { ...gitEnv(), HAPPIER_STACK_TEST_TTY: '1', HAPPIER_STACK_DISABLE_LLM_AUTOEXEC: '1' },
|
|
387
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
388
|
+
}
|
|
389
|
+
);
|
|
390
|
+
t.after(() => {
|
|
391
|
+
guide.kill('SIGKILL');
|
|
392
|
+
});
|
|
393
|
+
const sendLine = (line) => guide.sendLine(line);
|
|
394
|
+
|
|
395
|
+
// Feed the wizard answers step-by-step.
|
|
396
|
+
await guide.waitForText('Target monorepo path:', 5_000);
|
|
397
|
+
sendLine(target);
|
|
398
|
+
await guide.waitForText('New branch name:', 5_000);
|
|
399
|
+
sendLine('port/test-guide-quit');
|
|
400
|
+
await guide.waitForText('Use 3-way merge', 5_000);
|
|
401
|
+
sendLine('1');
|
|
402
|
+
await guide.waitForText('old happy (UI)', 5_000);
|
|
403
|
+
sendLine('');
|
|
404
|
+
await guide.waitForText('old happy-cli', 5_000);
|
|
405
|
+
sendLine(sourceCli);
|
|
406
|
+
await guide.waitForText('old happy-server', 5_000);
|
|
407
|
+
sendLine('');
|
|
408
|
+
|
|
409
|
+
// Preflight now runs before starting the port. Accept the default (guided) mode.
|
|
410
|
+
await guide.waitForText('Preflight detected conflicts', 10_000);
|
|
411
|
+
sendLine('');
|
|
412
|
+
|
|
413
|
+
// Wait for conflict prompt, then quit.
|
|
414
|
+
await Promise.race([guide.waitForText('guide: waiting for conflict resolution', 10_000), guide.waitForText('guide: conflict detected', 10_000)]);
|
|
415
|
+
await guide.waitForText('Resolve conflicts, then choose an action:', 10_000);
|
|
416
|
+
await guide.waitForText('quit guide (leave state as-is)', 10_000);
|
|
417
|
+
const menuTail = guide.getOutput().combined.split('Resolve conflicts, then choose an action:').pop() || '';
|
|
418
|
+
const quitMatch = menuTail.match(/\n\s*(\d+)\)\s*quit guide\s*\(leave state as-is\)/i);
|
|
419
|
+
if (!quitMatch?.[1]) {
|
|
420
|
+
const output = guide.getOutput();
|
|
421
|
+
throw new Error(`failed to locate quit option index\nstdout:\n${output.stdout}\nstderr:\n${output.stderr}`);
|
|
422
|
+
}
|
|
423
|
+
sendLine(quitMatch[1]);
|
|
424
|
+
const quitResult = await guide.waitForExit(10_000);
|
|
425
|
+
assert.notEqual(
|
|
426
|
+
quitResult.code,
|
|
427
|
+
0,
|
|
428
|
+
`expected guide to exit non-zero on quit\nstdout:\n${quitResult.stdout}\nstderr:\n${quitResult.stderr}`
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
// Ensure the plan exists.
|
|
432
|
+
const planRel = (await runCapture('git', ['rev-parse', '--git-path', 'happy-stacks/monorepo-port-plan.json'], { cwd: target, env: gitEnv() })).trim();
|
|
433
|
+
const planAbs = planRel.startsWith('/') ? planRel : join(target, planRel);
|
|
434
|
+
assert.equal(await readFile(planAbs, 'utf-8').then(() => true), true);
|
|
435
|
+
|
|
436
|
+
// Resolve + stage conflict.
|
|
437
|
+
await writeFile(join(target, 'apps', 'cli', 'hello.txt'), 'value=source\n', 'utf-8');
|
|
438
|
+
await run('git', ['add', 'apps/cli/hello.txt'], { cwd: target, env: gitEnv() });
|
|
439
|
+
|
|
440
|
+
// Continue should complete `git am` and then resume remaining patches from the plan (including extra.txt).
|
|
441
|
+
const contOut = await runCapture(
|
|
442
|
+
process.execPath,
|
|
443
|
+
[scriptPath, 'port', 'continue', `--target=${target}`, '--json'],
|
|
444
|
+
{ cwd: process.cwd(), env: gitEnv() }
|
|
445
|
+
);
|
|
446
|
+
const parsed = JSON.parse(contOut.trim());
|
|
447
|
+
assert.equal(parsed.ok, true);
|
|
448
|
+
assert.equal(parsed.inProgress, false);
|
|
449
|
+
|
|
450
|
+
assert.equal((await readFile(join(target, 'apps', 'cli', 'hello.txt'), 'utf-8')).toString(), 'value=source\n');
|
|
451
|
+
assert.equal((await readFile(join(target, 'apps', 'cli', 'extra.txt'), 'utf-8')).toString(), 'extra\n');
|
|
452
|
+
|
|
453
|
+
await assert.rejects(async () => await readFile(planAbs, 'utf-8'));
|
|
454
|
+
});
|