# NousResearch/hermes-agent · Mattermost slash command handling and registration improvements

## Open now

- 2 issues, 1 PR open on GitHub.
- #6296: \[Feature\]: Mattermost adapter should register COMMAND_REGISTRY as custom slash commands with autocomplete
- #27587: \[Feature\]: Mattermost: Add interactive button-based approval for dangerous commands
- PR #29373: feat(mattermost): add interactive approval buttons via send_exec_approval

These issues and pull requests address problems with Mattermost slash command parsing (leading whitespace, escaping, mobile compatibility) and add features such as registering commands with autocomplete and interactive approval buttons.

## TL;DR

- 11 member(s): 2 open issue(s), 3 closed issue(s), 1 open PR(s), 1 merged PR(s), 4 PR(s) closed without merging.
- A fix was merged to main in PR #59127. Verify it addresses your case.
- **Recommended:** read PR #59127 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `partially_resolved` · deciding PR(s): `pr-59127`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.15`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Open related issues

- #6296: \[Feature\]: Mattermost adapter should register COMMAND_REGISTRY as custom slash commands with autocomplete
- #27587: \[Feature\]: Mattermost: Add interactive button-based approval for dangerous commands

## Closed / duplicate issues

- #20649: \[Feature\]: Do \`lstrip\` user command is used to support MatterMost.
- #25184: \[Bug\]: Mattermost mobile users are unable to send commands.
- #59535: Mattermost: /approve interrupted by leading whitespace from autocomplete (incomplete fix from #46866)

## Prior & competing PRs

- **#59127: fix(mattermost): accept leading-space slash commands**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `plugins/platforms/mattermost/adapter.py`, `scripts/release.py`, `tests/gateway/test_mattermost.py`
  - fix hunks:
  - `plugins/platforms/mattermost/adapter.py`

    ```diff
    @@ -878,6 +878,8 @@ async def _handle_ws_event(self, event: Dict[str, Any]) -> None:
             # Determine message type.
             file_ids = post.get("file_ids") or []
             msg_type = MessageType.TEXT
    +        if message_text[:1].isspace() and message_text.lstrip().startswith("/"):
    +            message_text = message_text.lstrip()
             if message_text.startswith("/"):
                 msg_type = MessageType.COMMAND
     
    ```
  - `scripts/release.py`

    ```diff
    @@ -45,6 +45,7 @@
     
     # Auto-extracted from noreply emails + manual overrides
     AUTHOR_MAP = {
    +    "[redacted:email]": "l0h1nth",  # PR #32210 salvage (mattermost: accept leading-space slash commands from mobile clients; #25184)
         "[redacted:email]": "devatnull",  # PR #58704 salvage (whatsapp: native Baileys polls, clarify-as-poll, location pins, structured quoted replies, PTT/audio split, bridge_helpers extraction)
         "[redacted:email]": "tjp2021",  # PR #4097 salvage (copilot: per-turn x-initiator header so user prompts bill as premium requests; #3040)
         "[redacted:email]": "kevinrajaram",  # PR #3850 salvage (gateway: add POSIX system dirs to PATH so launchctl/systemctl resolve under UV's minimal-PATH bundled Python; #3849)
    ```
  - `tests/gateway/test_mattermost.py`

    ```diff
    @@ -6,6 +6,7 @@
     from unittest.mock import MagicMock, patch, AsyncMock
     
     from gateway.config import Platform, PlatformConfig
    +from gateway.platforms.base import MessageType
     from gateway.run import (
         _resolve_gateway_display_bool,
         _resolve_progress_thread_id,
    ```

    ```diff
    @@ -588,6 +589,55 @@ async def test_channel_type_mapping(self):
             msg_event = self.adapter.handle_message.call_args[0][0]
             assert msg_event.source.chat_type == "dm"
     
    +    @pytest.mark.asyncio
    +    async def test_leading_space_slash_command_is_command(self):
    +        """Mattermost mobile suggests leading-space slash commands."""
    +        post_data = {
    +            "id": "post_cmd",
    +            "user_id": "user_123",
    +            "channel_id": "chan_dm",
    +            "message": " /new",
    +        }
    +        event = {
    +            "event": "posted",
    +            "data": {
    +                "post": json.dumps(post_data),
    +                "channel_type": "D",
    +                "sender_name": "@bob",
    +            },
    +        }
    +
    +        await self.adapter._handle_ws_event(event)
    +        assert self.adapter.handle_message.called
    +        msg_event = self.adapter.handle_message.call_args[0][0]
    +        assert msg_event.text == "/new"
    +        assert msg_event.message_type is MessageType.COMMAND
    +        assert msg_event.get_command() == "new"
    +
    +    @pytest.mark.asyncio
    +    async def test_leading_space_normal_text_is_preserved(self):
    +        """Only command-shaped mobile messages should be normalized."""
    +        post_data = {
    +            "id": "post_text",
    +            "user_id": "user_123",
    +            "channel_id": "chan_dm",
    +            "message": " hello",
    +        }
    +        event = {
    +            "event": "posted",
    +            "data": {
    +                "post": json.dumps(post_data),
    +                "channel_type": "D",
    +                "sender_name": "@bob",
    +            },
    +        }
    +
    +        await self.adapter._handle_ws_event(event)
    +        assert self.adapter.handle_message.called
    +        msg_event = self.adapter.handle_message.call_args[0][0]
    +        assert msg_event.text == " hello"
    +        assert msg_event.message_type is MessageType.TEXT
    +
         @pytest.mark.asyncio
         async def test_thread_id_from_root_id(self):
             """Post with root_id should have thread_id set."""
    ```
- **#6298: feat(gateway/mattermost): register COMMAND_REGISTRY as custom slash commands**. closed without merge
  - maintainer direction: category `stale_abandoned`; implication `active`
- **#21070: fix(gateway): handle Mattermost escaped slash commands**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#59127`, `#32210`
- **#25187: fix(gateway): Mattermost Thread and Command Handling Fixes**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `06161c6ed8d3b878e0076f0eba244de8f3208bc8`, `1197d2bc966727194b52fb227c805793e7327a15`, `#59127`, `#20874`
- **#29373: feat(mattermost): add interactive approval buttons via send_exec_approval**. open
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `aabad7b042`, `15fad75a29`
  - files: `gateway/platforms/base.py`, `gateway/run_notifications.py`, `gateway/run_turn_runner.py`, `plugins/platforms/mattermost/adapter.py`, `plugins/platforms/mattermost/plugin.yaml`, `tests/gateway/relay/test_relay_interactive.py`, `tests/gateway/test_mattermost_approval_buttons.py`, `tests/gateway/test_update_streaming.py`, `tools/approval.py`, `website/docs/user-guide/messaging/mattermost.md`
- **#32210: fix(mattermost): accept leading-space slash commands**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#59127`, `#21070`

## Fix status

- Merged to main in PR #59127 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-07-05`.
- Subsystem: `gateway`
