From a118b94a856ef80301cb26d16be6d08c0104e0db Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Fri, 12 Jun 2026 12:58:36 -0700 Subject: [PATCH] fix(dashboard): skill installs from the dashboard silently auto-cancel (#45150) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dashboard's /api/skills/hub/install (and the new-profile hub_skills path) spawned `hermes skills install ` with stdin=DEVNULL but without --yes. do_install()'s 'Confirm [y/N]' prompt hit EOF, defaulted to 'n', and printed 'Installation cancelled.' into a background log the user never sees — every dashboard install no-opped. Pass --yes on both spawn sites, matching the uninstall endpoint which already passed --yes. The dashboard install button is the explicit user consent, same as the TUI/slash-command skip_confirm rationale. Repro: spawned the exact argv with stdin=DEVNULL against a temp HERMES_HOME — without --yes it cancels, with --yes the skill installs. --- hermes_cli/web_server.py | 5 +++-- tests/hermes_cli/test_web_server.py | 7 ++++++- tests/hermes_cli/test_web_server_skills_profiles.py | 7 +++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/hermes_cli/web_server.py b/hermes_cli/web_server.py index 31e28cc2996..32a8fc67a50 100644 --- a/hermes_cli/web_server.py +++ b/hermes_cli/web_server.py @@ -8105,7 +8105,8 @@ async def install_skill_hub(body: SkillInstallRequest, profile: Optional[str] = raise HTTPException(status_code=400, detail="identifier is required") try: proc = _spawn_hermes_action( - _profile_cli_args(body.profile or profile) + ["skills", "install", identifier], + _profile_cli_args(body.profile or profile) + + ["skills", "install", identifier, "--yes"], "skills-install", ) except HTTPException: @@ -8843,7 +8844,7 @@ async def create_profile_endpoint(body: ProfileCreate): continue try: proc = _spawn_hermes_action( - ["-p", body.name, "skills", "install", ident], + ["-p", body.name, "skills", "install", ident, "--yes"], "skills-install", ) hub_installs.append({"identifier": ident, "pid": proc.pid}) diff --git a/tests/hermes_cli/test_web_server.py b/tests/hermes_cli/test_web_server.py index 28b6ee3b019..4782176caf4 100644 --- a/tests/hermes_cli/test_web_server.py +++ b/tests/hermes_cli/test_web_server.py @@ -2690,7 +2690,12 @@ class TestNewEndpoints: assert data["hub_installs"] == [{"identifier": "someuser/some-skill", "pid": 4321}] # Hub install was scoped to the new profile. - assert spawned == [(["-p", "builder", "skills", "install", "someuser/some-skill"], "skills-install")] + assert spawned == [ + ( + ["-p", "builder", "skills", "install", "someuser/some-skill", "--yes"], + "skills-install", + ) + ] # Verify the writes landed in the NEW profile's config, not the root. prof_dir = get_hermes_home() / "profiles" / "builder" diff --git a/tests/hermes_cli/test_web_server_skills_profiles.py b/tests/hermes_cli/test_web_server_skills_profiles.py index 9a131bbb246..76325d628f2 100644 --- a/tests/hermes_cli/test_web_server_skills_profiles.py +++ b/tests/hermes_cli/test_web_server_skills_profiles.py @@ -178,7 +178,10 @@ class TestProfileScopedHubActions: ) assert resp.status_code == 200 assert calls == [ - (["-p", "worker_alpha", "skills", "install", "official/demo"], "skills-install") + ( + ["-p", "worker_alpha", "skills", "install", "official/demo", "--yes"], + "skills-install", + ) ] def test_hub_install_without_profile_keeps_legacy_argv( @@ -200,7 +203,7 @@ class TestProfileScopedHubActions: "/api/skills/hub/install", json={"identifier": "official/demo"} ) assert resp.status_code == 200 - assert calls == [["skills", "install", "official/demo"]] + assert calls == [["skills", "install", "official/demo", "--yes"]] def test_hub_install_unknown_profile_404(self, client, isolated_profiles): resp = client.post(