Control

Commands & shell

Every input surface — the terminal UI, the HUD, and the Telegram bot — understands the same prefix grammar. The first character of your message decides how it's handled, before the router ever runs.

The prefix grammar

FRIDAY short-circuits four input shapes in _maybe_handle_input_prefix() before the turn orchestrator. This keeps commands deterministic and the LLM out of the hot path.

four prefixes
(no prefix)   set brightness to 60        → deterministic router (voice or text)
/            /deep RISC-V vs ARM         → slash dispatcher (direct to capability)
!            !sudo systemctl restart x   → interactive PTY shell
>            > my-password               → stdin to the running shell command

Slash commands

A /command is dispatched directly by core/slash_commands.py — no intent recognition, no planner. On Telegram these populate the native /-autocomplete menu (pushed via setMyCommands), so the registry is the single source of truth.

commandwhat it does
/newReset the conversation and start a new session
/clearAlias for /new
/web <query>Web search — returns result links
/quick <question>Instant web-backed answer in chat, nothing saved
/fast <topic>Fast research — ~2-minute latest-info summary
/deep <topic>Deep research — heavy multi-source briefing, saved to disk
/research <topic>Hand off to the research agent
/fetch <url>Fetch a URL as plain text
/crawl <url> [what to look for]Crawl a URL with instructions
/screenshotTake a full-screen screenshot
/voice on|off|statusToggle voice mode
/lockLock the computer screen (OS session lock)
/unlockHow to unlock the screen / shell PIN gate
/helpList every slash command
Pure-state slashes always work
/new, /help, /lock, and /unlock run even when the screen is locked, because they have to. Slash commands that hit a capability are still gated by the executor's lock + consent checks.

The ! interactive shell

Prefix any line with ! to run it as a real shell command. On Linux/macOS it runs under a pseudo-terminal (PTY), so commands that need a TTY — sudo password prompts, ssh, read, passwd — actually work.

  • Bash by default — uses /bin/bash (not dash), so source, [[ ]], arrays, and process substitution work.
  • venv-aware — if the project has a .venv/, it's on PATH automatically, so !python script.py uses the project interpreter.
  • Bounded — a 5-minute wall-clock cap per command and an output cap prevent runaway buffering.
  • Windows — PTYs are POSIX-only; Windows degrades to a non-interactive run and rejects > follow-ups with a clear message.

Interactive follow-ups with >

When a command is waiting on input, FRIDAY tells you and keeps the process alive. Reply with a >-prefixed line to pipe it to the command's stdin:

interactive shell session
!sudo apt install brightnessctl
  [sudo] password for you:        ← FRIDAY: "Awaiting password — reply with > …"
> ••••••••                        ← piped to stdin (not echoed)
  Do you want to continue? [Y/n]
> y                               ← piped to the apt prompt
  ✓ done · [exit 0]
A stray reply cancels — it never leaks to chat
While a shell session is alive, any message that does not start with > cancels the running command instead of being interpreted by the LLM. That's the safety rule that stops a casual "yes" from being read as a sudo password or a chat message.

Security gating

The shell is gated by the screen lock — while FRIDAY's PIN gate is locked, ! is refused with "Shell access is locked. Run /unlock <pin> first." This matters most for the remote case: the same shell is reachable from Telegram, so the lock is what keeps a remote session from running arbitrary commands. See Telegram & remote.