Skip to main content

A Python agent harness with configurable tools and guardrails - one who yokes agents together

Project description

Yoker

PyPI Python uv CI Coverage License Agentic

A Python agent harness with configurable tools, guardrails, and multi-provider LLM backend integration.

Installation

pip install yoker

Quick Start

Run Yoker interactively (default):

python -m yoker

Or with an agent definition:

python -m yoker --agents-definition examples/agents/researcher.md

Example session:

Yoker Session

Usage Modes

Yoker supports three ways to run: interactive CLI, batch/non-interactive, and as a library.

Interactive Mode

Interactive mode is the default. It provides a rich terminal UI with multiline input, command history, streaming output, and tool call display.

python -m yoker

# With an agent definition
python -m yoker --agents-definition examples/agents/researcher.md

# Hide tool calls and statistics
python -m yoker --ui-mode interactive

See Interactive Input and Slash Commands for the available keyboard shortcuts and commands.

Batch Mode

Batch mode reads input from stdin and writes response content to stdout. Thinking, tool calls, errors, and statistics are written to stderr. This makes Yoker usable in pipelines and scripts.

# Single prompt
python -m yoker --ui-mode batch
Hello, how can you help me?
^D

# Pipe input
printf "Hello\nWhat is 2+2?\n" | python -m yoker --ui-mode batch

# Show thinking and tool calls on stderr
printf "Hello\n" | python -m yoker --ui-mode batch --ui-show-thinking --ui-show-tool-calls

Batch mode options:

Flag Effect
--ui-mode batch Enable batch mode
--ui-show-thinking Print thinking/trace output to stderr
--ui-show-tool-calls Print tool call information to stderr
--ui-show-stats Print turn statistics to stderr

Library Usage

Yoker is designed to be embedded as a library. The Agent class emits events; your application implements a UIHandler (or uses the built-in handlers) and wires events through UIBridge.

import asyncio
from yoker import Agent
from yoker.config import get_yoker_config
from yoker.ui import BatchUIHandler, UIBridge

async def main():
  config = get_yoker_config(cli=False)
  agent = Agent(config=config)

  ui = BatchUIHandler(show_thinking=True, show_tool_calls=True)
  bridge = UIBridge(ui)
  agent.add_event_handler(bridge)

  await ui.start(agent)
  try:
    response = await agent.process("Hello, how can you help me?")
    print(response)
  finally:
    await ui.shutdown("complete")

asyncio.run(main())

See the examples/ directory for more complete examples:

  • examples/batch_mode.py - Batch mode with predefined messages
  • examples/library_usage.py - Using Yoker as a library without the CLI
  • examples/custom_handler.py - Implementing a custom UIHandler
  • examples/research_workflow.py - Running a researcher agent programmatically

Plugins

Yoker can load tools, skills, and agents from external Python packages via the plugin system.

Quick Start

Plugins are disabled by default for security. Enable them in your configuration:

# Create yoker.toml
cat > yoker.toml << EOF
[plugins]
enabled = true
EOF

# Secure the configuration file
chmod 600 yoker.toml

Load plugins with --with:

# Using uvx (recommended)
uvx --with pkgq yoker --with pkgq

# Or install first
pip install pkgq
python -m yoker --with pkgq

Security Workflow

When you load a plugin for the first time, Yoker shows a confirmation dialog with the plugin's components (tools, skills, agents). Review them carefully—plugins can execute arbitrary code.

After accepting, Yoker displays instructions to trust the plugin permanently:

[plugins.trusted]
pkgq = true

Using Plugin Components

Verify loaded components with:

> /skills     # List all skills (including plugin skills)
> /tools      # List all tools (including plugin tools)
> /pkgq:create  # Invoke a plugin skill directly

Available Plugins

  • pkgq - Package documentation tools (PyPI: pip install pkgq)
    • pkgq:find tool - Find Python package documentation
    • pkgq:create skill - Generate PACKAGE.md for a project
    • pkgq:update skill - Update documentation for new versions

Developing Plugins

See examples/plugins/demo/README.md for a complete plugin development guide.

For comprehensive plugin documentation including security best practices, configuration reference, and troubleshooting, see docs/plugins.md.

Why Yoker?

Yoker fills a unique gap in the coding agent ecosystem: a library-first, transparent agent harness designed for developers who want full control, visibility, and simplicity.

Key Differentiators:

  • Library-first - Embed in your applications, not locked into a CLI
  • LLM-neutral - Choose your provider, your model, your cost model
  • No hidden manipulation - All prompts visible, editable, configurable
  • Static permissions - Deterministic boundaries, not runtime prompts
  • Full transparency - Event-driven, everything inspectable

See docs/rationale.md for the full rationale and comparison with other solutions.

Features

Current Features:

  • Chat loop - Interactive conversation with any configured provider
  • Multi-provider backends - Ollama (native SDK), OpenAI, Anthropic, Google Gemini, and 100+ providers via LiteLLM
  • Bootstrap wizard - Interactive first-run setup that writes ~/.yoker.toml for you
  • Tool calling - Structured tool execution with parameters
  • read tool - Read file contents with guardrails
  • list tool - Directory listing with pattern filtering
  • write tool - Write files with overwrite protection
  • update tool - Edit existing files with replace, insert, and delete operations
  • search tool - Search file contents with regex or filenames with glob
  • existence tool - Check if files or folders exist with security hardening
  • mkdir tool - Create directories with recursive parent creation and depth limits
  • git tool - Git operations (status, log, diff, branch, show) with permission-controlled commit/push
  • websearch tool - Web search with SSRF protection, domain filtering, and rate limiting
  • webfetch tool - Fetch web content with SSRF protection, URL validation, and size limits
  • agent tool - Spawn subagents with isolated context and recursion limits
  • skill tool - Invoke skills dynamically by name with full content loading
  • Slash commands - Built-in commands: /help, /think on|off|silent, /skills, /context, /tools, /agents
  • Thinking mode - LLM reasoning trace with gray output (on/off/silent)
  • Streaming - Real-time token streaming from any provider
  • Configuration - TOML-based configuration system via Clevis
  • Agent definitions - Load agents from Markdown files with YAML frontmatter
  • Package plugins - Load tools, skills, and agents from Python packages with --with
  • Multiline input - Esc+Enter for newlines, Enter to submit
  • Rich output - Styled terminal output with Rich
  • Event-driven architecture - Library-first design with event emission
  • Context persistence - Session resumption with JSONL storage
  • Event logging - Full session replay capability
  • Demo scripts - Generate documentation screenshots from Markdown scripts
  • Schema-driven guardrails - Tool parameters are annotated with yoker.tools.annotations markers (Path, Url, Query, Text); the harness strips the metadata before sending schemas to the model and dispatches the matching guardrail at execution time
  • Permissions - Static TOML-based access control
  • Secure API key handling - Masked input during bootstrap, config files written with chmod 600

Planned Features:

  • Multi-agent orchestration - Run coordinated agent teams
  • Keyring integration - Store API keys in the OS keychain instead of config files (TODO S.1)
  • Tool timing metrics - Performance tracking
  • Token usage tracking - Cost monitoring
  • Tool result caching - Reduce redundant calls
  • Parallel tool execution - Concurrent read operations

Interactive Input

The interactive session supports:

  • Multiline input: Press Esc+Enter to add newlines, Enter to submit
  • Command history: Up/Down arrows navigate previous messages
  • History search: Ctrl+R to search through history
  • Keyboard navigation: Arrow keys, Ctrl+A/E for cursor positioning
  • Text selection: Click and drag to select output, copy with Ctrl+Shift+C or Cmd+C

Slash Commands

Command Description
/help Show available commands
/think on|off Enable/disable LLM thinking trace
/skills List available skills
/context Show current conversation context
/tools List available tools and their availability
/agents Show loaded and available agents

Thinking Mode

When thinking is enabled, the LLM shows its reasoning process:

[Thinking]
Let me analyze this step by step...
First, I need to understand the file structure...

[Response]
Based on my analysis, here's what I found...

Demo Session Script

Generate terminal screenshots for documentation from Markdown script files:

# Run default demo script (demos/session.md)
python scripts/demo_session.py

# Run a specific demo script
python scripts/demo_session.py --script demos/list-tool.md

# Run all demo scripts in a directory
python scripts/demo_session.py --scripts-dir demos/

# Real LLM + log conversation for replay
python scripts/demo_session.py --script demos/session.md --log

# Replay from log (no LLM calls)
python scripts/demo_session.py --script demos/session.md --replay

# With an agent definition
python scripts/demo_session.py --script demos/session.md --agent examples/agents/markdown.md

Configuration

Yoker auto-discovers configuration files:

  1. ./yoker.toml (current directory)
  2. ~/.yoker.toml (user home directory)
  3. Built-in defaults
# Zero-configuration startup - uses auto-discovered config
python -m yoker

Or create a yoker.toml file for explicit configuration. Yoker supports multiple providers — Ollama (native SDK), and OpenAI, Anthropic, Google Gemini, plus any LiteLLM-supported provider (e.g. Groq, Cohere, Azure, Mistral) via the LiteLLM backend:

[harness]
name = "my-yoke"

[logging]
level = "INFO"

[backend]
provider = "ollama"  # or "openai", "anthropic", "gemini", or any litellm provider

[backend.ollama]
base_url = "http://localhost:11434"
api_key = ""  # Optional API key for authenticated Ollama endpoints
model = "qwen3.5:cloud"

[agents]
definition = "./agents/researcher.md"  # Optional: agent definition file

[tools.read]
enabled = true
allowed_extensions = [".txt", ".md", ".py"]

Example for OpenAI:

[backend]
provider = "openai"

[backend.openai]
api_key = "${OPENAI_API_KEY}"  # Interpolated from environment variables
model = "gpt-4o-mini"

The bootstrap wizard (run python -m yoker with no config present) writes ~/.yoker.toml for you interactively, including masked API key entry and chmod 600 file permissions. See Model Catalog for the curated model lists per provider.

See examples/yoker.toml for the full configuration reference.

Architecture

Yoker uses an event-driven architecture for library-first design. The Agent emits events; the UI layer receives them through UIBridge and decides how to present them.

Architecture Diagram

Agent layer (yoker.agent): Configuration, context management, tool execution, and event emission. It has no terminal or presentation logic.

Backend layer (yoker.backends): Provider-neutral streaming chat backend. OllamaBackend uses the native Ollama SDK; LitellmBackend unifies OpenAI, Anthropic, Gemini, and 100+ LiteLLM-supported providers. The ModelBackend Protocol normalizes streaming into provider-agnostic ChatChunk events.

UI layer (yoker.ui): Implements the UIHandler protocol. Built-in implementations:

  • InteractiveUIHandler - Rich terminal UI with streaming output
  • BatchUIHandler - stdin/stdout/stderr for scripts and pipelines

Bridge (yoker.ui.UIBridge): Converts agent events into UIHandler method calls so the agent stays independent of presentation details.

Event Types: Turn (start/end), Thinking (start/chunk/end), Content (start/chunk/end), Tool (call/result/content), Command

Providers

Yoker supports multiple LLM providers through a dual backend architecture:

Provider Backend API key required Free tier
Ollama Native Ollama SDK No (app path) or yes (API key) Yes
OpenAI LiteLLM Yes No
Anthropic LiteLLM Yes No
Google Gemini LiteLLM Yes Yes (limited)
Any LiteLLM provider LiteLLM Varies Varies

The bootstrap wizard offers curated model lists for each provider. See Model Catalog for the full list, or run python -m yoker with no config to launch the wizard.

Security: API keys are collected via masked input during bootstrap, and config files are written with chmod 600 permissions. API keys can also be injected from environment variables using Clevis interpolation (${OPENAI_API_KEY}). Keyring integration for OS keychain storage is planned (TODO S.1).

Documentation

Development

git clone https://github.com/christophevg/yoker.git
cd yoker
make env-dev  # Create virtual environment and install dependencies

make test     # Run tests with coverage
make check    # Type checking + linting
make docs     # Build documentation

Requires Python 3.10+. Uses uv for dependency management. See CLAUDE.md for project conventions.

Contributing

Contributions welcome! Please read CLAUDE.md for project conventions and development guidelines.

Changelog

See GitHub Releases for version history.

License

MIT License - see LICENSE for details.


Name: "yoker" - One who yokes. The agent noun from "yoke" (PIE yeug- meaning "to join"). Pairs with "clitic" (both are joining tools). See docs/NAME.md for full etymology.

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

yoker-0.6.0.tar.gz (8.4 MB view details)

Uploaded Source

Built Distribution

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

yoker-0.6.0-py3-none-any.whl (185.2 kB view details)

Uploaded Python 3

File details

Details for the file yoker-0.6.0.tar.gz.

File metadata

  • Download URL: yoker-0.6.0.tar.gz
  • Upload date:
  • Size: 8.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for yoker-0.6.0.tar.gz
Algorithm Hash digest
SHA256 328b188db62cbb3a894c0e537fc55c2efda81c48d886ecffc30c99ec23643c5f
MD5 7e93019e2401189a1ef61ef17c2c8a3a
BLAKE2b-256 f3eac67721b07613a2a8303396b27774a04a951ae989e8c7a1bd7d2087aa233b

See more details on using hashes here.

File details

Details for the file yoker-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: yoker-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 185.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for yoker-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 45069cc6e508af98be5bdbc8aafc68536b83b88bd5c507d244e995c0a80344b8
MD5 4f64dd4b8e40e2b2881e452b9d53d4c0
BLAKE2b-256 ddd426a2d16830608658377262809deb6d495b0addf89b51e28d2a1082fb3db8

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