# How FluidVoice Command Mode Executes Terminal Commands from Voice Input

> Learn how FluidVoice Command Mode executes terminal commands with voice input. It transcribes speech, uses an LLM to validate and execute, and verifies zsh subprocess commands.

- Repository: [ALTIC/FluidVoice](https://github.com/altic-dev/FluidVoice)
- Tags: how-to-guide
- Published: 2026-07-09

---

**FluidVoice's Command Mode translates spoken requests into terminal commands by routing transcribed voice through an LLM agent that validates, executes, and verifies shell commands via a zsh subprocess.**

FluidVoice is an open-source macOS application that bridges voice input and terminal automation. The **Command Mode** feature acts as an AI agent that listens to natural language, converts speech into executable shell commands, and manages the entire execution lifecycle with safety guardrails. According to the altic-dev/FluidVoice source code, this pipeline relies on a tight integration between SwiftUI views, a `@MainActor`-isolated service layer, and a raw zsh process spawner.

## Voice Capture and Command Initiation

Command Mode begins in the UI layer when the user finishes speaking. The `CommandModeView` forwards the transcribed text to `CommandModeService.processUserCommand(_:)`, which appends the message to the conversation history and persists the chat.

```swift
// In CommandModeView – when the user finishes speaking
Task {
    await commandModeService.processUserCommand(transcribedText)
}

```

This entry point, located in [`Sources/Fluid/Services/CommandModeService.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/CommandModeService.swift) (lines 307–324), marks the transition from voice input to the LLM-driven execution pipeline.

## LLM Orchestration and Tool Calling

### The System Prompt and execute_terminal_command

Inside `processNextTurn()`, the service constructs an OpenAI-style chat payload combining a system prompt with the conversation history. The system prompt (lines 376–419 in [`CommandModeService.swift`](https://github.com/altic-dev/FluidVoice/blob/main/CommandModeService.swift)) instructs the model to use the **function calling** tool named `execute_terminal_command` and to follow a strict "check → execute → verify" workflow.

```swift
// Conceptual representation of the system prompt logic
// Defined in callLLM() around lines 376-419
let systemPrompt = """
You are a terminal assistant. Use the execute_terminal_command tool.
Always follow: checking → executing → verifying.
"""

```

### Parsing the Tool Call Response

When the LLM decides a command is necessary, `callLLM()` returns an `LLMResponse` containing a `toolCall` field. This structure includes the command string, an optional working directory, and a **purpose** field with values like `checking`, `executing`, or `verifying`. The service maps this to a `stepType` via `determineStepType(for:purpose:)` (lines 850–891).

```json
{
  "name": "execute_terminal_command",
  "arguments": {
    "command": "ls -la ~/Downloads",
    "workingDirectory": "/Users/joe",
    "purpose": "checking"
  }
}

```

## Safety Guardrails for Destructive Commands

Before execution, the service validates the command through `isDestructiveCommand(_:)` (lines 594–633). This guard detects dangerous keywords like `rm` or `sudo` and triggers a pending-command state that requires explicit user confirmation. This prevents accidental data loss or privilege escalation directly from voice input.

## Terminal Execution via Zsh

Once cleared (or if non-destructive), `executeCommand(_:workingDirectory:callId:purpose:)` (lines 636–658) delegates to `TerminalService.execute(command:workingDirectory:)`. This spawns a `/bin/zsh` process on a background thread, captures stdout and stderr, enforces a timeout, and returns a `CommandResult` struct.

```swift
// Inside CommandModeService
await self.executeCommand(tc.command,
                         workingDirectory: tc.workingDirectory,
                         callId: tc.id,
                         purpose: tc.purpose)

// Inside TerminalService (lines 66-84)
let result = await terminalService.execute(
    command: "ls -la ~/Downloads",
    workingDirectory: "/Users/joe"
)

```

The entire service layer runs on the **main actor** (`@MainActor`) to keep UI state synchronized, while the actual shell execution remains non-blocking.

## Feedback Loop and Verification

After execution, the service wraps the raw result in `EnhancedCommandResult` and converts it to JSON via `toJSON()` (lines 668–686). This structured data includes the exit code, output, error stream, and execution time.

```json
{
  "success": true,
  "command": "ls -la ~/Downloads",
  "output": "total 0\n-rw-r--r--  1 joe  staff  0 Sep  9 12:34 file.txt",
  "error": null,
  "exitCode": 0,
  "executionTimeMs": 12,
  "purpose": "checking"
}

```

The JSON is appended to the conversation history as a tool message, and `processNextTurn()` is invoked again (line 662). This creates a **verification loop**, allowing the LLM to inspect the result and determine if further steps are required. The cycle terminates when the LLM produces a final text response without additional tool calls.

## Summary

- **CommandModeService.swift** (lines 307–324) initiates the flow by receiving transcribed voice and maintaining conversation state.
- **LLM tool calling** uses the `execute_terminal_command` function with a "check-execute-verify" workflow defined in the system prompt (lines 376–419).
- **Safety checks** via `isDestructiveCommand` (lines 594–633) require user confirmation for destructive operations like `rm` or `sudo`.
- **TerminalService.swift** (lines 66–84) executes raw `/bin/zsh` commands asynchronously with timeout enforcement.
- **Verification loops** continue until the LLM confirms task completion, using `EnhancedCommandResult` JSON (lines 668–686) to feed terminal output back into the conversation.

## Frequently Asked Questions

### Does FluidVoice Command Mode work on Linux or Windows?

No. According to the source code in [`TerminalService.swift`](https://github.com/altic-dev/FluidVoice/blob/main/TerminalService.swift), the application explicitly spawns `/bin/zsh` processes, which ties the execution layer to macOS. The path assumptions and shell-specific behavior are not portable to other operating systems.

### What happens if a voice command is ambiguous?

The LLM can request clarification by returning a text response instead of a tool call, or it can execute a `purpose: "checking"` command first to inspect the environment before proceeding with the main operation. This multi-step reasoning is enforced by the system prompt instructions in `callLLM()`.

### How does FluidVoice prevent dangerous commands from running automatically?

The `isDestructiveCommand(_:)` guard (lines 594–633 in [`CommandModeService.swift`](https://github.com/altic-dev/FluidVoice/blob/main/CommandModeService.swift)) scans command strings for destructive patterns like `rm`, `sudo`, or `>` redirects. When detected, the service enters a pending state and surfaces a confirmation dialog to the user through the `@MainActor`-bound UI layer before proceeding with execution.

### Can I customize the LLM system prompt for Command Mode?

Yes. The system prompt defining the "check → execute → verify" workflow and the `execute_terminal_command` tool specification is defined in `callLLM()` around lines 376–419 of [`CommandModeService.swift`](https://github.com/altic-dev/FluidVoice/blob/main/CommandModeService.swift). Modifying this string allows you to adjust the agent's behavior, though recompilation is required.