feat(slack): add --no-assistant flag to manifest generation

By default `hermes slack manifest` opts the app into Slack's AI Assistant
container (assistant_view feature + assistant:write scope +
assistant_thread_* events). Slack then renders DMs as the right-hand
Assistant split-pane, where every exchange is a thread and bare slash
commands (/help, /new, ...) are not delivered as normal command events —
they only work when the bot is @mentioned. There was no way to opt out
short of hand-editing the generated JSON.

Add --no-assistant to emit a flat-DM manifest that omits those three
pieces, so DMs render as a normal chat and slash commands dispatch
inline. The regular messaging surface (Messages tab, slash commands,
Socket Mode, channel + DM scopes/events) is preserved in both modes.

Default behaviour is unchanged (assistant mode still on).

Tests: cover both manifest modes and the argparse wiring.
This commit is contained in:
Victor Kyriazakos 2026-06-23 18:30:32 +03:00 committed by Teknium
parent 70d28b62fb
commit da80ac0042
3 changed files with 137 additions and 41 deletions

View file

@ -1,6 +1,30 @@
"""Tests for Slack CLI helpers."""
import argparse
from hermes_cli.slack_cli import _build_full_manifest
from hermes_cli.subcommands.slack import build_slack_parser
def _parse_slack_args(argv):
"""Build the real `hermes slack` parser and parse argv against it."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command")
build_slack_parser(subparsers, cmd_slack=lambda _args: 0)
return parser.parse_args(argv)
class TestSlackManifestArgparse:
"""The `--no-assistant` flag wires through argparse to `no_assistant`."""
def test_no_assistant_flag_defaults_false(self):
args = _parse_slack_args(["slack", "manifest"])
assert getattr(args, "no_assistant", False) is False
def test_no_assistant_flag_sets_true(self):
args = _parse_slack_args(["slack", "manifest", "--no-assistant"])
assert args.no_assistant is True
class TestSlackFullManifest:
@ -28,3 +52,35 @@ class TestSlackFullManifest:
assert "assistant:write" in manifest["oauth_config"]["scopes"]["bot"]
bot_events = manifest["settings"]["event_subscriptions"]["bot_events"]
assert "assistant_thread_started" in bot_events
def test_no_assistant_omits_assistant_pieces(self):
manifest = _build_full_manifest(
"Hermes", "Your Hermes agent on Slack", include_assistant=False
)
# assistant_view feature is gone -> Slack renders a flat DM, not the
# Assistant thread pane (where bare slash commands don't dispatch).
assert "assistant_view" not in manifest["features"]
assert "assistant:write" not in manifest["oauth_config"]["scopes"]["bot"]
bot_events = manifest["settings"]["event_subscriptions"]["bot_events"]
assert "assistant_thread_started" not in bot_events
assert "assistant_thread_context_changed" not in bot_events
def test_no_assistant_preserves_core_surface(self):
"""Dropping assistant mode must NOT strip the regular messaging surface."""
manifest = _build_full_manifest(
"Hermes", "Your Hermes agent on Slack", include_assistant=False
)
# Flat DM still needs the Messages tab writable.
assert manifest["features"]["app_home"]["messages_tab_enabled"] is True
# Slash commands and Socket Mode are independent of assistant mode.
assert manifest["features"]["slash_commands"]
assert manifest["settings"]["socket_mode_enabled"] is True
# Channel + DM scopes/events survive so the bot still works everywhere.
bot_scopes = manifest["oauth_config"]["scopes"]["bot"]
for scope in ("commands", "channels:history", "groups:read", "im:history"):
assert scope in bot_scopes
bot_events = manifest["settings"]["event_subscriptions"]["bot_events"]
for event in ("message.im", "message.channels", "message.groups", "app_mention"):
assert event in bot_events