Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/bot/orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,12 @@ async def get_bot_commands(self) -> list: # type: ignore[type-arg]
BotCommand("verbose", "Set output verbosity (0/1/2)"),
BotCommand("repo", "List repos / switch workspace"),
BotCommand("restart", "Restart the bot"),
# Vault commands (forwarded to Claude via _handle_unknown_command)
BotCommand("today", "Morning briefing and daily plan"),
BotCommand("closeday", "End of day review and reflection"),
BotCommand("capture", "Quick capture a thought"),
BotCommand("schedule", "Plan your week"),
BotCommand("context", "Load context for a topic"),
]
if self.settings.enable_project_threads:
commands.append(BotCommand("sync_threads", "Sync project topics"))
Expand Down
40 changes: 37 additions & 3 deletions tests/unit/test_orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,16 @@ def test_agentic_registers_text_document_photo_handlers(agentic_settings, deps):


async def test_agentic_bot_commands(agentic_settings, deps):
"""Agentic mode returns 6 bot commands."""
"""Agentic mode returns 11 bot commands (6 core + 5 vault)."""
orchestrator = MessageOrchestrator(agentic_settings, deps)
commands = await orchestrator.get_bot_commands()

assert len(commands) == 6
assert len(commands) == 11
cmd_names = [c.command for c in commands]
assert cmd_names == ["start", "new", "status", "verbose", "repo", "restart"]
assert cmd_names == [
"start", "new", "status", "verbose", "repo", "restart",
"today", "closeday", "capture", "schedule", "context",
]


async def test_classic_bot_commands(classic_settings, deps):
Expand Down Expand Up @@ -990,3 +993,34 @@ async def test_bot_suffixed_command_not_forwarded(agentic_settings, deps):
) as mock_claude:
await orchestrator._handle_unknown_command(update, context)
mock_claude.assert_not_called()


# --- Vault commands menu tests ---

VAULT_COMMANDS = {"today", "closeday", "capture", "schedule", "context"}


class TestVaultCommandsMenu:
"""Vault commands appear in the bot menu but pass through to Claude."""

async def test_vault_commands_in_bot_command_list(self, agentic_settings, deps):
"""Vault commands appear in the Telegram command menu."""
orchestrator = MessageOrchestrator(agentic_settings, deps)
commands = await orchestrator.get_bot_commands()
cmd_names = {c.command for c in commands}
assert VAULT_COMMANDS.issubset(cmd_names), (
f"Missing vault commands: {VAULT_COMMANDS - cmd_names}"
)

async def test_vault_commands_not_in_known_commands(self, agentic_settings, deps):
"""Vault commands are NOT in _known_commands so they forward to Claude."""
orchestrator = MessageOrchestrator(agentic_settings, deps)
# register_handlers populates _known_commands
app = MagicMock()
app.add_handler = MagicMock()
orchestrator.register_handlers(app)

for cmd in VAULT_COMMANDS:
assert cmd not in orchestrator._known_commands, (
f"/{cmd} should NOT be a known command — it must pass through to Claude"
)