Skip to main content

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 CLIinstall guide
  • VS Code — for the lina-bridge terminal extension
  • Microphone access — grant in System Settings > Privacy & Security > Microphone
  • Accessibility permissions — required for pynput keyboard 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:

  1. Copy the extension folder to ~/.vscode/extensions/alessandro.lina-bridge-0.0.1/
  2. Reload VS Code (Cmd+Shift+P → "Reload Window")
  3. 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:

  1. Explicit path via --config / -c flag
  2. ./lina.yaml (project-level)
  3. ~/.lina/lina.yaml (global)
  4. 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 aloud
  • script -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}.md
  • rm -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

  1. Command dispatch: Lina writes commands to ~/.lina/commands/agents/{name}.txt
  2. VS Code extension: Polls the command files and types contents into the matching named terminal
  3. Log monitoring: Lina watches /tmp/{name}.log for permission prompts and reads them aloud
  4. Status files: Agents write their status to ~/.lina/status/{name}.json so Lina can track progress

Boot sequence

  1. Open VS Code with named terminals for each agent
  2. In each terminal, type the agent alias (e.g., athena) — the agent session starts with logging
  3. Run lina start — Lina begins listening and monitoring all agent logs
  4. 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

  1. Subclass the appropriate base class from plugins/base.py:

    • STTPlugin — speech-to-text (implement transcribe, is_available)
    • TTSPlugin — text-to-speech (implement speak, is_available)
    • LLMPlugin — LLM CLI integration (implement get_agent_launch_command, get_response_hook_config, parse_response, get_permission_patterns, get_mute_config, is_available)
    • IDEPlugin — IDE terminal bridge (implement send_command, setup, is_available)
    • WakeWordPlugin — wake word detection (implement start_listening, stop, pause, resume, is_available)
  2. Place your plugin at ~/.lina/plugins/<type>/<name>.py (e.g., ~/.lina/plugins/tts/elevenlabs.py)

  3. 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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lina_assistant-0.1.0.tar.gz (53.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lina_assistant-0.1.0-py3-none-any.whl (57.8 kB view details)

Uploaded Python 3

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

Hashes for lina_assistant-0.1.0.tar.gz
Algorithm Hash digest
SHA256 738a39522dea44023eba13cce199135c18a91eab8ac8be544b2f9e19de1a9298
MD5 a74e3ec3444d0ba9dbc877f0e3bd84d4
BLAKE2b-256 df99033df19c0110b5c804b2be12b45d2787423ee363218e7c8cd51fa5b71d5d

See more details on using hashes here.

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

Hashes for lina_assistant-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e78d026ae0048566accf04d45833078041ce693dfee0b33430586bcca5a3a9d3
MD5 940ac9aa46caef679a976c257abffe0b
BLAKE2b-256 6480f41d9e14197c411d0c08d97521b3f51b8a4adee4fca8675ae1e9e39fd320

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page