diff --git a/hermes_cli/main.py b/hermes_cli/main.py index e70cd2520..abd6bd0f0 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -2917,10 +2917,15 @@ def cmd_update(args): print(f" ℹ️ {len(missing_config)} new config option(s) available") print() - if sys.stdin.isatty(): - response = input("Would you like to configure them now? [Y/n]: ").strip().lower() - else: + if not (sys.stdin.isatty() and sys.stdout.isatty()): + print(" ℹ Non-interactive session — skipping config migration prompt.") + print(" Run 'hermes config migrate' later to apply any new config/env options.") response = "n" + else: + try: + response = input("Would you like to configure them now? [Y/n]: ").strip().lower() + except EOFError: + response = "n" if response in ('', 'y', 'yes'): print() diff --git a/tests/hermes_cli/test_cmd_update.py b/tests/hermes_cli/test_cmd_update.py index 0ccb7af81..9ffa809a5 100644 --- a/tests/hermes_cli/test_cmd_update.py +++ b/tests/hermes_cli/test_cmd_update.py @@ -105,3 +105,24 @@ class TestCmdUpdateBranchFallback: commands = [" ".join(str(a) for a in c.args[0]) for c in mock_run.call_args_list] pull_cmds = [c for c in commands if "pull" in c] assert len(pull_cmds) == 0 + + def test_update_non_interactive_skips_migration_prompt(self, mock_args, capsys): + """When stdin/stdout aren't TTYs, config migration prompt is skipped.""" + with patch("shutil.which", return_value=None), patch( + "subprocess.run" + ) as mock_run, patch("builtins.input") as mock_input, patch( + "hermes_cli.config.get_missing_env_vars", return_value=["MISSING_KEY"] + ), patch("hermes_cli.config.get_missing_config_fields", return_value=[]), patch( + "hermes_cli.config.check_config_version", return_value=(1, 2) + ), patch("hermes_cli.main.sys") as mock_sys: + mock_sys.stdin.isatty.return_value = False + mock_sys.stdout.isatty.return_value = False + mock_run.side_effect = _make_run_side_effect( + branch="main", verify_ok=True, commit_count="1" + ) + + cmd_update(mock_args) + + mock_input.assert_not_called() + captured = capsys.readouterr() + assert "Non-interactive session" in captured.out