Voice-controlled multi-agent coding assistant with plugin architecture
Project description
Lina Assistant
A voice-controlled multi-agent coding assistant that coordinates AI agents working in parallel. Speak commands, get responses read aloud, and manage multiple Claude Code sessions hands-free.
The Agent Team
| Agent | Role | Description |
|---|---|---|
| Lina | Coordinator | Voice assistant that listens for commands, routes tasks to agents, monitors their status, and reads responses aloud |
| Athena | Feature implementation | Direct, efficient — picks up tickets and ships features |
| Isis | Feature implementation | Methodical, thorough — confirms understanding before diving in |
| Cassandra | Code quality auditor | Reviews code for quality, security, and best practices before PRs |
| Selene | Ticket reviewer | Checks implementations against Jira ticket specs, flags gaps |
Prerequisites
- macOS (currently the only supported platform)
- Python 3.9+
- sox — audio recording (
brew install sox) - Claude Code CLI — install guide
- VS Code — for the lina-bridge terminal extension
- Microphone access — grant in System Settings > Privacy & Security > Microphone
- Accessibility permissions — required for
pynputkeyboard input (System Settings > Privacy & Security > Accessibility)
Installation
One-liner — copy-paste into your terminal:
curl -fsSL https://raw.githubusercontent.com/AlessandroLiAppDev/lina-assistant/main/install.sh | bash
Or with pip directly:
pip install lina-assistant && lina setup
The setup wizard walks you through everything else (sox, shell aliases, VS Code extension, macOS permissions).
Alternative: clone for development
git clone https://github.com/AlessandroLiAppDev/lina-assistant.git
cd lina-assistant
pip install -e .
lina setup
VS Code Extension Setup
Lina communicates with agent terminals through a lightweight VS Code extension called lina-bridge. It polls ~/.lina/commands/agents/ for command files and types their contents into the matching named terminal.
To install:
- Copy the extension folder to
~/.vscode/extensions/alessandro.lina-bridge-0.0.1/ - Reload VS Code (
Cmd+Shift+P→ "Reload Window") - Create named terminals for your agents (the terminal name must match the agent name in
lina.yaml)
When Lina sends a command to an agent, it writes to ~/.lina/commands/agents/{name}.txt. The extension picks it up and delivers it to the terminal named {name}.
Configuration
Lina loads config from the first file found in this order:
- Explicit path via
--config/-cflag ./lina.yaml(project-level)~/.lina/lina.yaml(global)- Built-in defaults
Full lina.yaml reference
# Wake word — what you say to activate Lina
wake_word: "alexa" # Use "alexa" with openwakeword, or train a custom model for "lina"
language: "en"
model: "" # Model for Lina's own Claude session (e.g., "claude-sonnet-4-6")
# Plugin selection — each slot can be swapped for a different implementation
plugins:
stt: faster_whisper # Speech-to-text: "whisper" or "faster_whisper" (3x faster)
tts: edge_tts # Text-to-speech: "edge_tts" (free Microsoft Edge TTS)
llm: claude_code # LLM CLI integration: "claude_code"
ide: vscode # IDE terminal bridge: "vscode"
wake_word: openwakeword # Wake word engine: "openwakeword" (ML-based) or "whisper" (fallback)
# TTS voice settings
tts:
voice: "en-US-JennyNeural" # Edge TTS voice name
rate: "+40%" # Speech rate adjustment
pitch: "+23Hz" # Pitch adjustment
# STT model sizes (Whisper)
stt:
wake_model: "base" # Fast model for wake word detection (when using whisper wake plugin)
command_model: "medium" # Accurate model for command transcription
# Audio recording settings
audio:
silence_threshold: "3%" # Volume below this = silence (sox format)
max_command_duration: 20 # Max seconds to record a single command
conversation_timeout: 15 # Seconds to wait for follow-up in conversation mode
mic_restore_volume: 75 # Mic volume to restore after TTS playback (0-100)
# Agent definitions
agents:
- name: athena
role: "Feature implementation agent"
model: "" # Model override (e.g., "claude-opus-4-6")
compact_on_new_ticket: true # Run /compact before sending a new ticket
- name: isis
role: "Ticket handling agent"
model: ""
compact_on_new_ticket: true
- name: cassandra
role: "Code quality auditor"
model: ""
- name: selene
role: "Ticket review agent"
model: ""
Quick Start
# Start the voice listener
lina start
# Lina prints: "Lina is listening. Say 'alexa' to activate."
# 1. Say the wake word → you hear a "tink" sound
# 2. Speak your command → you hear a "pop" when it's sent
# 3. Lina transcribes your speech and delivers it to Claude Code
# 4. After the response, Lina says "mhm?" and listens for follow-ups
# 5. Stay silent to return to wake word mode
Multi-Agent Setup
Defining agents
Add agents to lina.yaml under the agents key (see configuration reference above). Each agent needs a name that matches the terminal name in VS Code.
Shell aliases
lina setup can add these automatically (Step 5). Or add them manually to ~/.zshrc:
# Universal launcher — handles both CLI commands and agent sessions
lina() {
local name="$1"
shift 2>/dev/null
if [ -z "$name" ]; then name="lina"; fi
# lina stop/status/setup/jira → Python CLI commands
if [ "$name" = "lina" ] && [ -n "$1" ]; then
command lina "$@"
return
fi
local model="claude-opus-4-6"
export AGENT_NAME="$name"
# Load persona from ~/.lina/personas/{name}.md if it exists
local persona_file="$HOME/.lina/personas/${name}.md"
local persona=""
if [ -f "$persona_file" ]; then persona=$(cat "$persona_file"); fi
tty > "/tmp/.claude-mute-${name}"
script -q "/tmp/${name}.log" env AGENT_NAME="$name" claude --model "$model" --append-system-prompt "$persona"
rm -f "/tmp/.claude-mute-${name}"
}
# Agent shortcuts
athena() { lina athena "$@"; }
isis() { lina isis "$@"; }
cassandra() { lina cassandra "$@"; }
selene() { lina selene "$@"; }
What each part does:
tty > /tmp/.claude-mute-{name}— creates a mute file so Lina doesn't read this agent's TTS output aloudscript -q /tmp/{name}.log claude ...— starts Claude Code with output logged so Lina can monitor for permission prompts--append-system-prompt "$persona"— loads the agent's persona from~/.lina/personas/{name}.mdrm -f /tmp/.claude-mute-{name}— cleans up the mute file when the session ends
Agent personas
Place markdown files in ~/.lina/personas/ to give each agent a personality:
~/.lina/personas/
├── athena.md # "You are Athena. Direct, efficient..."
├── isis.md # "You are Isis. Methodical, thorough..."
├── cassandra.md # "You are Cassandra. Sharp, critical..."
└── selene.md # "You are Selene. Strict, detail-obsessed..."
The persona content is passed to Claude Code via --append-system-prompt when launching the agent.
How agent communication works
- Command dispatch: Lina writes commands to
~/.lina/commands/agents/{name}.txt - VS Code extension: Polls the command files and types contents into the matching named terminal
- Log monitoring: Lina watches
/tmp/{name}.logfor permission prompts and reads them aloud - Status files: Agents write their status to
~/.lina/status/{name}.jsonso Lina can track progress
Boot sequence
- Open VS Code with named terminals for each agent
- In each terminal, type the agent alias (e.g.,
athena) — the agent session starts with logging - Run
lina start— Lina begins listening and monitoring all agent logs - Speak commands — Lina routes them to the appropriate agent
Agent Workflow
The full pipeline for ticket work:
Lina receives voice command
→ Analyzes Jira ticket
→ Assigns to Athena or Isis (implementation)
→ Agent implements the feature
→ Selene reviews implementation against ticket spec
→ Cassandra audits code quality and security
→ PR is created
Agents update their status in ~/.lina/status/{name}.json:
{
"agent": "athena",
"status": "working",
"task": "implementing login flow",
"timestamp": "2026-04-24T12:00:00Z"
}
Status values: working | done | error | idle
Plugin Architecture
Lina uses a plugin system with 5 slots. Each slot has an abstract base class in plugins/base.py.
Built-in plugins
| Slot | Plugin | Module |
|---|---|---|
| STT | whisper |
plugins.stt.whisper_stt |
| STT | faster_whisper |
plugins.stt.faster_whisper_stt |
| TTS | edge_tts |
plugins.tts.edge_tts_plugin |
| LLM | claude_code |
plugins.llm.claude_code |
| IDE | vscode |
plugins.ide.vscode |
| Wake Word | openwakeword |
plugins.wake_word.openwakeword_plugin |
| Wake Word | whisper |
plugins.wake_word.whisper_wake |
Swapping plugins
Set the plugin name in lina.yaml under plugins:
plugins:
stt: faster_whisper # swap to: whisper
wake_word: openwakeword # swap to: whisper
Writing custom plugins
-
Subclass the appropriate base class from
plugins/base.py:STTPlugin— speech-to-text (implementtranscribe,is_available)TTSPlugin— text-to-speech (implementspeak,is_available)LLMPlugin— LLM CLI integration (implementget_agent_launch_command,get_response_hook_config,parse_response,get_permission_patterns,get_mute_config,is_available)IDEPlugin— IDE terminal bridge (implementsend_command,setup,is_available)WakeWordPlugin— wake word detection (implementstart_listening,stop,pause,resume,is_available)
-
Place your plugin at
~/.lina/plugins/<type>/<name>.py(e.g.,~/.lina/plugins/tts/elevenlabs.py) -
Set the plugin name in
lina.yaml:plugins: tts: elevenlabs
Lina auto-discovers the plugin class by scanning the module for a subclass of the expected base class.
Jira Integration
Lina includes a Jira Cloud client for reading and managing tickets.
# First-time setup — enter your Jira URL, email, and API token
lina jira setup
# Fetch a specific ticket
lina jira KAN-41
# List your assigned tickets
lina jira mine
# Search tickets by text
lina jira search "login bug"
Credentials are stored in ~/.lina/jira.yaml (owner-only permissions, gitignored). Get an API token from Atlassian API tokens.
CLI Reference
| Command | Description |
|---|---|
lina start |
Start the voice listener (blocks until interrupted) |
lina stop |
Stop the voice listener |
lina status |
Show Lina status and all agent states |
lina setup |
Interactive setup wizard — checks deps, installs missing ones, configures shell |
lina agent list |
List configured agents |
lina agent start <name> |
Show the command to start an agent session |
lina agent stop <name> |
Clear an agent's log file |
lina jira <ticket-key> |
Fetch and display a Jira ticket |
lina jira mine |
List your assigned tickets |
lina jira search <query> |
Search tickets by text |
lina jira setup |
Configure Jira credentials |
Global flag: --config / -c — path to a specific lina.yaml file.
File Structure
~/.lina/
├── lina.yaml # Main configuration
├── jira.yaml # Jira credentials (gitignored, 0600 permissions)
├── lina.pid # PID file (created when running)
├── commands/
│ ├── main.txt # Lina's own command file
│ └── agents/
│ ├── athena.txt # Command dispatch to Athena
│ ├── isis.txt # Command dispatch to Isis
│ └── ...
├── personas/
│ ├── athena.md # Agent persona (loaded via --append-system-prompt)
│ ├── isis.md
│ └── ...
├── status/
│ ├── athena.json # Agent status file
│ ├── isis.json
│ └── ...
└── plugins/ # User-installed plugins
├── stt/
├── tts/
├── llm/
└── ide/
/tmp/
├── {name}.log # Agent session logs (created by script command)
└── .claude-mute-{name} # TTS mute files (created by agent aliases)
Dependencies
- sox — audio recording
- faster-whisper — speech recognition (CTranslate2-based, 3x faster than openai-whisper)
- edge-tts — text-to-speech
- pynput — keyboard input
- watchdog — file system monitoring
- pyyaml — configuration parsing
License
Apache 2.0 — see LICENSE
Support
- Star the repo
- Report issues
- Contribute plugins
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lina_assistant-0.1.0.tar.gz.
File metadata
- Download URL: lina_assistant-0.1.0.tar.gz
- Upload date:
- Size: 53.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
738a39522dea44023eba13cce199135c18a91eab8ac8be544b2f9e19de1a9298
|
|
| MD5 |
a74e3ec3444d0ba9dbc877f0e3bd84d4
|
|
| BLAKE2b-256 |
df99033df19c0110b5c804b2be12b45d2787423ee363218e7c8cd51fa5b71d5d
|
File details
Details for the file lina_assistant-0.1.0-py3-none-any.whl.
File metadata
- Download URL: lina_assistant-0.1.0-py3-none-any.whl
- Upload date:
- Size: 57.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e78d026ae0048566accf04d45833078041ce693dfee0b33430586bcca5a3a9d3
|
|
| MD5 |
940ac9aa46caef679a976c257abffe0b
|
|
| BLAKE2b-256 |
6480f41d9e14197c411d0c08d97521b3f51b8a4adee4fca8675ae1e9e39fd320
|