1
1
Fork
You've already forked zop
0
Zop: CLI tool for AI users
  • Go 94.1%
  • JavaScript 1.6%
  • C 0.8%
  • Objective-C 0.7%
  • TypeScript 0.6%
  • Other 2.2%
mutablecc 48f2b09ceb
Some checks failed
Test / Test (ubuntu-latest) (push) Has been cancelled
Test / Test with whisper (Linux/amd64) (push) Has been cancelled
Test / Test with tts (Linux/amd64) (push) Has been cancelled
Test / Release dry run build (linux/amd64) (push) Has been cancelled
Test / Release dry run build (linux/arm64) (push) Has been cancelled
Test / Release dry run Android APK (push) Has been cancelled
Test / Capture UI screenshot (push) Has been cancelled
feature/web-ui ( #38 )
Reviewed-on: #38 
2026年05月30日 06:30:46 +02:00
.dingles Commit some fixes 2026年05月19日 23:20:23 -04:00
.fyne-fork/v2 Commit fyne fork 2026年04月26日 02:10:21 -04:00
.github merge-updates ( #37 ) 2026年04月11日 04:31:08 +02:00
cmd Commit current work on Android bug fixes 2026年04月15日 02:53:04 -04:00
internal Commit current work 2026年05月29日 22:46:50 -04:00
planning Move file 2026年05月15日 00:32:33 -04:00
playwright-tests Commit some fixes 2026年05月19日 23:20:23 -04:00
scripts Commit current work 2026年05月29日 22:46:50 -04:00
.env Commit some fixes 2026年05月19日 23:20:23 -04:00
.gitignore Commit some fixes 2026年05月19日 23:20:23 -04:00
AGENTS.md Fix cache dir 2026年05月15日 01:55:22 -04:00
DESIGN.md Commit work so far 2026年05月04日 01:01:27 -04:00
docker-compose.test.yml Commit work so far 2026年05月09日 01:22:17 -04:00
Dockerfile Attempt to fix bugs 2026年05月15日 11:20:01 -04:00
go.mod Commit web ui 2026年04月26日 03:49:29 -04:00
go.sum Commit web ui 2026年04月26日 03:49:29 -04:00
LICENSE Initial commit 2026年03月14日 16:38:56 -04:00
Makefile Commit some fixes 2026年05月19日 23:20:23 -04:00
README.md Updates 2026年04月26日 03:53:06 -04:00
TESTING.md Updates to build and testing 2026年05月11日 15:06:22 -04:00

zop

zop: A CLI tool for AI users

Overview

zop is a multi-provider AI CLI tool written in Go. It supports OpenAI, Anthropic, Google Gemini, OpenRouter, and Ollama (any OpenAI-compatible endpoint), plus voice input via Whisper in whisper-enabled builds.

Features

  • Multiple providers: OpenAI, Anthropic (Claude), Google (Gemini), OpenRouter, Ollama
  • TOML config: Define multiple named agents, providers, models, and MCP servers in ~/.config/zop/config.toml
  • Tool Calling: Models can execute tools (on by default if allow_list is populated; use --no-tools to disable)
  • Model Context Protocol (MCP): Connect to external tools via MCP servers
  • Instruction Autoloading: Automatically loads ZOP.md from the config directory as global instructions
  • Chat sessions: Persistent multi-turn conversations stored locally
  • Streaming: Real-time token streaming via --stream
  • Voice input (whisper-enabled builds): --voice flag for microphone input via Whisper
  • Voice transcription: Transcription output shown during voice input (with --verbose)
  • Voice output (tts-enabled builds): --tts flag for offline speech output via Piper/Sherpa-ONNX
  • Wake Word mode: Hands-free interactive mode using --wake-word and --stop-word

Installation

go install github.com/peterwwillis/zop/cmd/zop@latest

Or download a pre-built binary from the Releases page.

Quick Start

# Simple query (uses "default" agent from config)
zop -p "What is the capital of France?"
# Positional prompt still works
zop "What is the capital of France?"
# Use a template with @template syntax (use quotes!)
zop -p "@shell list files in current directory"
# Use template alias
zop -p "@sh list files in current directory"
# List files shorthand
zop -p "@sh list files"
# Pipe from stdin
echo "Explain recursion" | zop
# Use a specific agent
zop --agent claude "Summarise this text"
# Multi-turn chat session
zop --chat my-chat "Start a conversation"
zop --chat my-chat "Follow up question"
# Interactive chat session
zop --interactive --chat my-chat
# Interactive mode with automatic session resume
zop --interactive
# Stream the response
zop --stream "Write a haiku about Go"
# Disable tool calling support (enabled by default if allow_list is populated)
zop --no-tools "How are you?"
# Voice input (whisper-enabled build)
zop --voice
# Voice input with VAD debug diagnostics
zop --voice --debug
# Voice input with manual send (disable silence auto-stop)
zop --voice --voice-manual
# Voice output (tts-enabled build)
zop --tts "Hello world"
# Interactive mode with both voice input and output
zop -iVt
# Customize TTS speed and turn safety delay
zop -iVt --tts-speed 1.2 --tts-delay 2000
# Wake Word mode (starts in "sleeping" state)
zop -iVt --wake-word "hey zop" --stop-word "goodbye"

Whisper's native initialization logs are suppressed by default and are shown when --debug is enabled.

In interactive mode, zop now manages sessions automatically when --chat is not provided: it creates a unique auto-session on first run and resumes that same auto-session on the next interactive run. User-created sessions (via --chat) remain separate and are never auto-resumed.

When an interactive conversation exceeds provider context limits, zop automatically starts a new session and retries the turn.

Configuration

On first run, zop writes the default config to ~/.config/zop/config.toml. You can also copy the built-in default config as a starting point:

mkdir -p ~/.config/zop
cp internal/config/configs/default.toml ~/.config/zop/config.toml

Then set your API key environment variables:

export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
export GOOGLE_API_KEY="..."
export OPENROUTER_API_KEY="..."

Config Structure

# Define named agents (provider + model pairings)
[agents.default]
provider = "openai"
model = "gpt4o"
system_prompt = "You are a helpful assistant."
disable_tools = false
[agents.claude]
provider = "anthropic"
model = "claude-sonnet"
# Provider connection details
# The "engine" field specifies the backend: "openai", "anthropic", "google".
# If omitted, it defaults to the provider name itself.
# OpenAI-compatible backends (OpenAI, OpenRouter, Ollama, Azure, etc.) use "openai".
[providers.openai]
api_key_env = "OPENAI_API_KEY"
# engine = "openai" # default if not specified
[providers.ollama]
engine = "openai"
base_url = "http://localhost:11434/v1" # no API key required
[providers.azure]
engine = "openai"
base_url = "https://my-resource.openai.azure.com"
api_key_env = "AZURE_OPENAI_KEY"
api_version = "2024年02月01日"
# MCP Servers (optional)
[mcp_servers.sqlite]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-sqlite", "--db", "zop.db"]
# Model hyperparameters
[models.gpt4o]
model_id = "gpt-4o"
max_tokens = 4096
temperature = 1.0
top_p = 1.0
system_prompt = "You are a helpful assistant."
# TTS settings
[tts]
speed = 1.0
safety_delay_ms = 1000

Multiple Providers

You can configure unlimited providers with different backends, URLs, and API keys:

# Custom OpenAI-compatible endpoint
[providers.my-ollama]
engine = "openai"
base_url = "http://192.168.1.100:11434/v1"
[providers.lm-studio]
engine = "openai"
base_url = "http://localhost:1234/v1"
[providers.azure-openai]
engine = "openai"
base_url = "https://my-resource.openai.azure.com"
api_key_env = "AZURE_OPENAI_KEY"
api_version = "2024年02月01日"
[providers.another-openrouter]
engine = "openai"
base_url = "https://openrouter.ai/api/v1"
api_key_env = "OPENROUTER_API_KEY"
# Then use them in agents:
[agents.local]
provider = "my-ollama"
model = "llama3"

The engine field supports:

  • openai - OpenAI, OpenRouter, Ollama, Azure, LM Studio, and any OpenAI-compatible API
  • anthropic - Anthropic Claude
  • google - Google Gemini

Prompt Templates

zop supports Go text templates for system and user prompts. Templates can be defined inline, loaded from files, or referenced by name.

Template Data Available:

Variable Description
.Input The user's input text
.Config Full config object
.Agent Agent configuration
.Model Model configuration
.Env Map of environment variables

Template Functions:

Function Description
now Current time
date Current date (2006年01月02日)
time Current time (15:04:05)
upper Convert to uppercase
lower Convert to lowercase
trim Trim whitespace
indent(n, s) Indent text by n spaces

Configuration Examples:

# Inline templates
[agents.default]
system_prompt = "You are a helpful assistant. Today is {{date}}."
prompt = "User said: {{.Input}}"
# Load from file
[agents.expert]
system_prompt_file = "prompts/expert.txt"
prompt_file = "prompts/user_wrapper.txt"
# Use named template
[agents.summarizer]
prompt_template = "summarizer"
# Named templates (reusable)
[templates.expert]
system_prompt = "You are an expert in {{.Model.ModelID}}."
prompt = "Please explain: {{.Input}}"
[templates.summarizer]
system_prompt = "Summarize the following text concisely."
prompt = "Summarize:\n\n{{.Input}}"
aliases = ["sum"]
# Template with aliases
[templates.my_template]
system_prompt = "You are a helpful assistant."
prompt = "Help with: {{.Input}}"
aliases = ["mt", "help"]

Using Templates via CLI:

You can use templates directly from the command line with the @ prefix:

zop -p @shell "list files" # Use the 'shell' template
zop -p @sh "list files" # Use alias 'sh' for 'shell'
zop -p @sum "this text" # Use alias 'sum' for 'summarizer'

If the template name is not found, the prompt is passed through as-is.

Template Resolution Order:

  • System prompt: CLI -S flag → Agent system_prompt → Agent system_prompt_file → Agent system_prompt_template → Model system_prompt → Model system_prompt_file → Model system_prompt_template
  • User prompt: Agent prompt → Agent prompt_file → Agent prompt_template

See internal/config/configs/default.toml for the full set of built-in agents, providers, and models.

Config Commands

zop config list
zop config get agents.default
zop config get models.gpt4o.max_tokens
zop config set models.gpt4o.max_tokens 2048
zop config unset models.gpt4o.system_prompt
zop config remove agents.temp
zop config edit

Per-Agent Streaming

Each agent can override the global streaming setting:

[agents.default]
stream = false # disable streaming for this agent

Environment Variables for Debugging

Variable Description
ZOP_DEBUG_VAD=1 Enable VAD debug diagnostics (whisper input)
ZOP_DEBUG_TTS=1 Enable TTS debug diagnostics (voice output)
ZOP_WHISPER_MODEL Override whisper model path
ZOP_TTS_MODEL Override TTS model directory

These are set automatically when using --debug (-d).

Chat Sessions

zop chat list # list all sessions
zop chat show my-chat # show messages in a session
zop chat delete my-chat # delete a session

Tool Calling & MCP

zop supports tool calling, allowing models to interact with the external world.

Built-in Tools

  • run_command: Execute a shell command. The model can request to run commands to perform tasks or gather information. Note: Commands are executed automatically by the CLI.

Model Context Protocol (MCP)

zop supports the Model Context Protocol (MCP) for connecting to external tool servers.

To use MCP, add mcp_servers to your config.toml:

[mcp_servers.sqlite]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-sqlite", "--db", "zop.db"]
[mcp_servers.everything]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-everything"]
[mcp_servers.remote]
url = "http://localhost:8080/mcp/sse"

Tools provided by these servers will be automatically registered and made available to models that support tool calling (OpenAI, Anthropic, Google Gemini) in both the CLI and Mobile UI.

Tool Call Security Policies

Tool calling is enabled by default whenever you have an allow_list populated in your configuration. Models will only see tool definitions that they are permitted to use.

Disabling Tool Calling

If you wish to prevent models from using tools entirely:

  • CLI Flag: Use --no-tools (or -T) to disable tools for a single command.
  • Configuration: Set disable_tools = true globally or per-agent in your config.toml.

zop provides a flexible security policy system for tool calls, allowing you to control which commands the run_command tool is allowed to execute. Policies can be defined globally or overridden per-agent.

A policy consists of an allow_list, a deny_list, and tag-based filtering. By default, no tools are allowed to run. You must populate the allow_list to grant permission for specific tools or commands.

Configuration

You can manage policies using the config CLI command or by editing config.toml:

# Allow a specific shell command
zop config set tool_policy.my-allow-rule.exact '["ls", "-la"]'
# Allow an MCP tool by name
zop config set tool_policy.mcp-rule.tool "everything.list_files"

Add tool_policy to your config.toml:

[tool_policy]
# Global tags filtering
deny_tags = ["dangerous", "network"]
allow_tags = ["safe"]
# Allow specific commands and MCP tools
allow_list = [
 # Shell command (run_command)
 { tool = "run_command", exact = ["ls", "-la"], tags = ["safe", "fs"] },
 
 # MCP Tool by name
 { tool = "sqlite.query", tags = ["safe"] },
 
 # MCP Tool with argument filtering (Regex on the JSON arguments string)
 { tool = "everything.read_file", regex = "notes.txt" }
]
# Deny specific tools
deny_list = [
 { tool = "everything.delete_file" },
 { tool = "run_command", regex = ".*;.*" }
]
# Per-agent overrides
[agents.restricted]
provider = "openai"
model = "gpt4o"
[agents.restricted.tool_policy]
allow_list = [{ tool = "run_command", exact = ["ls"] }]

Entry Types

  • tool: The name of the tool (e.g., run_command, everything.list_files). If omitted, run_command is assumed.
  • exact: (For run_command) An array of strings representing the program and its arguments.
  • regex: A regular expression that must match the command string (for run_command) or the JSON arguments string (for other tools).
  • regex_array: (For run_command) An array of regular expressions matching parts of the command.
  • tags: A list of labels associated with the entry.

Building from Source

Prerequisites

  • GNU Make
  • curl and tar / unzip (for Go toolchain download)
  • cmake (for whisper-enabled builds)
  • Docker (for Android APK builds via fyne-cross)

1 — Install Go

If Go is not already installed, the Makefile can download and install the version declared in go.mod automatically:

make setup-go

The toolchain is installed to ~/.local/go/<version>/ and is activated automatically for all subsequent make targets — no shell changes required.

To install a specific version or to a custom path:

make setup-go GO_VERSION=1.23.0
make setup-go GO_INSTALL_DIR=/opt/go/1.24.0

2 — Build

The default build includes Voice support (fetches and compiles whisper.cpp and sherpa-onnx automatically):

make deps # download Go module dependencies
make build # build all packages with -tags "whisper tts"

Or run both in one shot:

make deps build

To build without voice support:

make build BUILD_TAGS="" CGO_ENABLED=0

3 — Test & Vet

make test # with voice (default)
make test TEST_ARGS="-race -coverprofile=out.cov" # with race detector + coverage
make vet # go vet (with voice by default)
make test BUILD_TAGS="" CGO_ENABLED=0 # without voice

4 — Release binary

Build a standalone binary for the current host:

make build-bin VERSION=v1.2.3

Cross-compile (whisper-enabled requires native CGO, so cross-compilation only works cleanly for the no-whisper variant):

# Whisper-enabled — must run on the target platform
make build-bin GOOS=linux GOARCH=arm64 VERSION=v1.2.3
# Fully static Linux build (recommended for Linux releases)
make build-static
# No-voice — cross-compilation works anywhere
make build-bin GOOS=linux GOARCH=amd64 BUILD_TAGS="" CGO_ENABLED=0 BINARY_SUFFIX=-novoice VERSION=v1.2.3

The output binary is named zop-<os>-<arch>[<suffix>][.exe] by default; override with BINARY=<name>.

5 — Android APK

Requires Docker (used internally by fyne-cross):

go install github.com/fyne-io/fyne-cross@latest
make android-apk

Output: zop-android-arm64.apk

Makefile quick reference

Target Description
make setup-go Download & install Go from go.mod
make deps go mod download
make build Build all packages (voice enabled by default)

| make test | Run tests (voice enabled by default) | | make vet | Run go vet | | make build-bin | Build release binary for current platform | | make build-static | Build a fully static Linux binary | | make whisper-fetch | Clone & compile whisper.cpp | | make whisper-clean | Remove whisper.cpp build tree | | make tts-fetch | Clone & compile sherpa-onnx | | make tts-clean | Remove sherpa-onnx build tree | | make android-apk | Build Android APK via fyne-cross | | make setup-go-clean | Remove the installed Go toolchain |

All variables (GO_VERSION, BUILD_TAGS, CGO_ENABLED, GOOS, GOARCH, VERSION, ...) can be overridden on the command line.

Building with Voice Support

Voice support (input and output) is enabled by default when building with make. To build manually with raw go:

make whisper-fetch # clone + compile whisper.cpp
make tts-fetch # clone + compile sherpa-onnx
CGO_ENABLED=1 go build -tags "whisper tts" -o zop ./cmd/zop

Voice Models

  • Input (Whisper): Set ZOP_WHISPER_MODEL to override the model path (default: ~/.local/share/zop/whisper/ggml-base.en.bin).
  • Output (TTS): Set ZOP_TTS_MODEL to override the base model directory (default: ~/.local/share/zop/tts/).

To build a smaller binary without voice, omit the tags, or grab release artifacts suffixed with -novoice.

Mobile Roadmap

See ANDROID_FYNE_PLAN.md for the design and implementation plan for the Android (Fyne) port, including the development checklist and build workflow.

Android App (Fyne)

Download the zop-android-arm64.apk asset from the latest GitHub Release, or build it locally:

go install github.com/fyne-io/fyne-cross@latest
make android-apk

Install on a physical Android device (e.g., Samsung Galaxy S10e)

  1. Enable Developer Options on the device and turn on USB debugging.
  2. Install the Android Platform Tools (adb) on your workstation.
  3. Connect the phone over USB and approve the debugging prompt.
  4. Verify the device is visible:
    adb devices
    
  5. Install (or update) the APK:
    adb install -r /path/to/zop-android-arm64.apk
    

To uninstall later:

adb uninstall com.zop.app

Web UI

zop includes a built-in ChatGPT-like web interface accessible from any browser.

Starting the Web UI

# Default: http://localhost:8080
zop web
# Custom address
zop web --addr :3000
zop web --addr 127.0.0.1:8080

Default Login

On first start, the Web UI automatically creates a default user:

Field Value
Username admin
Password admin

Important: Change the default password or create new users via the UserStore JSON file at ~/.config/zop/web_users.json in production deployments.

Features

  • Responsive chat interface — Sidebar with session/folder management + main chat area
  • Dark / Light mode — Toggle between themes (respects prefers-color-scheme by default)
  • Multi-user support — Each user gets an isolated controller session; users are stored in ~/.config/zop/web_users.json
  • Session folders — Organize chat sessions into named folders via the sidebar
  • Real-time streaming — Responses stream in via Server-Sent Events (SSE)
  • Agent & model selection — Switch agents and browse available models from the header
  • Tool toggle — Enable/disable tool calling from the settings modal or tools dropdown
  • Speech-to-text — Click the microphone button to dictate via the Web Speech API (browser-dependent)
  • Read aloud — Each assistant message has a "Read aloud" button that triggers server-side TTS (when built with the tts tag)

Session Management in the Web UI

Sessions are shared with the CLI — any session created via zop --chat <name> is visible in the web UI, and vice versa. Sessions can be organized into folders:

# Create a session in a folder from the CLI
zop --chat work/standup "What did the team accomplish yesterday?"

Folders are stored in ~/.config/zop/web_folders.json.

API Endpoints

The Web UI exposes a JSON/SSE API under /api/:

Endpoint Description
POST /api/login Authenticate (returns session cookie)
POST /api/logout Clear session
GET /api/config List agents and active agent
GET /api/models List available models from the current provider
GET /api/chat/messages Get current session messages
POST /api/chat/send Send a prompt (returns SSE stream)
POST /api/chat/clear Clear current session
GET /api/sessions List sessions grouped by folder
POST /api/sessions/{name}/switch Switch to a session
GET /api/settings Get settings (tools, TTS config)
POST /api/tools/toggle Enable/disable tool calling
POST /api/tts Queue text for text-to-speech

Development

make deps build test vet

See Building from Source for the full workflow.

License

MIT – see LICENSE.