Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/TestingCI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Rust

on:
push:
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1

jobs:
linux-ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: cargo build --release --verbose
- name: Run tests
run: cargo test --release --verbose

macos-homebrew:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: sfackler/actions/rustup@master
- run: echo "version=$(rustc --version)" >> $GITHUB_OUTPUT
id: rust-version
- uses: actions/cache@v4
with:
path: ~/.cargo/registry/index
key: index-${{ runner.os }}-${{ github.run_number }}
restore-keys: |
index-${{ runner.os }}-
- run: cargo generate-lockfile
- uses: actions/cache@v4
with:
path: ~/.cargo/registry/cache
key: registry-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }}
- name: Fetch
run: cargo fetch
- name: Build
run: cargo build --release --verbose
- name: Run tests
run: cargo test --release --verbose
26 changes: 26 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ regex = "1"
rustyline = "14"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = { package = "serde_yml", version = "0.0.12" }
sha2 = "0.10"
toml = "0.8"
ureq = { version = "2", features = ["json"] }
Expand Down
115 changes: 104 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ An open source, local agentic butler for software development. `yo` orchestrates
- **Built-in tools** - Read, Write, Edit, Grep, Glob, Bash
- **MCP integration** - Connect external tool servers via Model Context Protocol
- **Subagents** - Delegate tasks to specialized agents with restricted tools
- **Skill Packs** - Reusable instruction sets with tool restrictions (Claude Code compatible)
- **Model Routing** - Automatic model selection based on task type
- **Permission system** - Granular allow/ask/deny rules for tool access
- **Skill routing** - Map named skills to specific model@backend targets
- **Session transcripts** - JSONL audit logs of all interactions
- **Context management** - Automatic compaction when conversation grows large

Expand Down Expand Up @@ -52,7 +53,7 @@ yo -p "refactor main.rs" --yes
| `--mode` | Permission mode: default, acceptEdits, bypassPermissions |
| `--max-turns` | Max agent iterations per turn (default: 12) |
| `--trace` | Enable detailed tracing |
| `--list-targets` | Show configured backends and skills |
| `--list-targets` | Show configured backends and default target |

## REPL Commands

Expand All @@ -64,15 +65,17 @@ yo -p "refactor main.rs" --yes
| `/session` | Show session ID and transcript path |
| `/context` | Show context usage stats |
| `/backends` | List configured backends |
| `/skills` | List available skills |
| `/skill [name]` | Get or set current skill |
| `/target [model@backend]` | Override current target |
| `/target [model@backend]` | Show or set current target |
| `/mode [name]` | Get or set permission mode |
| `/permissions` | Show permission rules |
| `/permissions add [allow\|ask\|deny] "pattern"` | Add rule |
| `/trace` | Toggle tracing |
| `/agents` | List available subagents |
| `/task <agent> <prompt>` | Run a subagent with the given prompt |
| `/skillpacks` | List available skill packs |
| `/skillpack use <name>` | Activate a skill pack |
| `/skillpack drop <name>` | Deactivate a skill pack |
| `/skillpack active` | List active skill packs |
| `/mcp list` | List MCP servers |
| `/mcp connect <name>` | Connect to MCP server |
| `/mcp disconnect <name>` | Disconnect MCP server |
Expand All @@ -94,9 +97,7 @@ Configuration hierarchy (highest to lowest priority):
base_url = "https://api.venice.ai/api/v1"
api_key_env = "VENICE_API_KEY"

[skills]
default = "qwen3-235b-a22b-instruct-2507@venice"
fast = "gpt-4o-mini@chatgpt"
default_target = "qwen3-235b-a22b-instruct-2507@venice"

[permissions]
mode = "default"
Expand All @@ -116,6 +117,11 @@ auto_compact_enabled = true
command = "/path/to/mcp-calc"
args = []
auto_start = false

[model_routing.routes]
planning = "qwen3-235b-a22b-instruct-2507@venice"
coding = "claude-3-5-sonnet-latest@claude"
exploration = "gpt-4o-mini@chatgpt"
```

See `example-yo.toml` for complete reference.
Expand Down Expand Up @@ -162,8 +168,7 @@ You are Scout, a read-only exploration agent.
Use Glob to find files, Grep to search, Read to examine.
"""

# Optional: override skill or target
# skill = "fast"
# Optional: override target for this agent
# target = "gpt-4o-mini@chatgpt"
```

Expand Down Expand Up @@ -200,6 +205,87 @@ The main agent can delegate using the `Task` tool:
- Tool access is restricted to `allowed_tools` list
- Subagent activity is logged to transcripts

## Skill Packs

Skill packs are reusable instruction sets that guide the agent for specific tasks. They use the Claude Code compatible SKILL.md format with YAML frontmatter.

### SKILL.md Format

Skill packs are stored in `.yo/skills/<name>/SKILL.md` or `~/.yo/skills/<name>/SKILL.md`:

```markdown
---
name: safe-file-reader
description: Read files without making changes
allowed-tools: Read, Grep, Glob
---

You are in safe-file-reader mode. Only inspect files; do not modify anything.
Use Glob to find files, Grep to search content, Read to examine.
```

### Frontmatter Fields

| Field | Required | Description |
|-------|----------|-------------|
| `name` | Yes | Lowercase letters, numbers, hyphens (max 64 chars) |
| `description` | Yes | Brief description (max 1024 chars) |
| `allowed-tools` | No | Restrict to specific tools (CSV or YAML list) |

### Using Skill Packs

**Via REPL:**
```
/skillpacks # List available skill packs
/skillpack use reader # Activate a skill pack
/skillpack active # Show active skill packs
/skillpack drop reader # Deactivate
```

**Via LLM (ActivateSkill tool):**
The agent can activate skills using the `ActivateSkill` tool, or by mentioning `$skill-name` in conversation.

### Tool Restrictions

When multiple skills are active, their `allowed-tools` are intersected. Only tools allowed by all active skills can be used.

## Model Routing

Model routing automatically selects the best model for each subagent based on task type. Different models excel at different tasks—planning, coding, exploration, etc.

### Route Categories

| Category | Keywords | Default Target |
|----------|----------|----------------|
| `planning` | plan, architect, design | qwen3-235b@venice |
| `coding` | patch, edit, code, implement | claude-3-5-sonnet@claude |
| `exploration` | scout, explore, find, search | gpt-4o-mini@chatgpt |
| `testing` | test, verify, check | gpt-4o-mini@chatgpt |
| `documentation` | doc, readme, comment | gpt-4o-mini@chatgpt |
| `fast` | (explicit) | gpt-4o-mini@chatgpt |
| `default` | (fallback) | gpt-4o-mini@chatgpt |

### How It Works

1. Subagent name/description is analyzed for keywords
2. Category is inferred from keywords
3. Target is resolved: explicit spec > config route > hardcoded default
4. Subagent runs on the selected model

### Configuration

Override defaults in config:

```toml
[model_routing.routes]
planning = "qwen3-235b-a22b-instruct-2507@venice"
coding = "claude-3-5-sonnet-latest@claude"
exploration = "gpt-4o-mini@chatgpt"
testing = "gpt-4o-mini@chatgpt"
```

Explicit `target` in agent specs always takes priority over routing.

## Architecture

```
Expand Down Expand Up @@ -256,7 +342,13 @@ User Input
| `tools/glob.rs` | File pattern matching |
| `tools/task.rs` | Subagent delegation tool |
| `tools/mcp_dispatch.rs` | Route MCP tool calls |
| `tools/activate_skill.rs` | Skill pack activation tool |
| `subagent.rs` | Subagent runtime, tool filtering, mode clamping |
| `skillpacks/mod.rs` | Skill pack module exports |
| `skillpacks/parser.rs` | SKILL.md file parser |
| `skillpacks/index.rs` | Skill pack discovery and indexing |
| `skillpacks/activation.rs` | Active skill lifecycle |
| `model_routing.rs` | Task-based model selection |
| `mcp/client.rs` | MCP JSON-RPC client |
| `mcp/manager.rs` | MCP server lifecycle |
| `mcp/transport.rs` | Stdio transport layer |
Expand All @@ -265,7 +357,7 @@ User Input

1. User input received (REPL or one-shot)
2. Agent adds message to conversation
3. Agent resolves skill → target (model@backend)
3. Agent resolves target (model@backend)
4. Agent collects tool schemas (built-in + MCP)
5. LLM request sent with messages + tools
6. Response parsed for text and tool calls
Expand All @@ -281,5 +373,6 @@ Sessions logged to `.yo/sessions/<uuid>.jsonl` with events:
- Tool calls and results
- Permission decisions
- Subagent lifecycle (start, end, tool calls)
- Skill pack lifecycle (index built, activate, deactivate, parse errors)
- MCP server lifecycle
- Errors and metadata
42 changes: 31 additions & 11 deletions example-yo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,12 @@ base_url = "http://localhost:11434/v1"
# Ollama typically doesn't require an API key

# =============================================================================
# SKILLS
# DEFAULT TARGET
# =============================================================================
# Skills map task categories to targets. A target is "model@backend".
#
# The "default" skill is used when no specific skill is selected.
# Switch skills in REPL with: /skill <name>
# The default target to use when no --target flag is provided.
# Format: model@backend

[skills]
default = "qwen3-235b-a22b-instruct-2507@venice"
# planning = "gpt-4@chatgpt"
# coding = "claude-3-5-sonnet-latest@claude"
# fast = "llama3.2@ollama"
default_target = "qwen3-235b-a22b-instruct-2507@venice"

# =============================================================================
# PERMISSIONS
Expand Down Expand Up @@ -146,6 +140,33 @@ auto_compact_enabled = true
# Number of recent turns to keep after compaction
keep_last_turns = 10

# =============================================================================
# MODEL ROUTING
# =============================================================================
# Configure automatic model selection based on task type.
# Subagents are automatically routed to different models based on their
# name/description. Override defaults here.
#
# Route categories:
# planning - Architecture and design tasks (keywords: plan, architect, design)
# coding - Code generation and refactoring (keywords: patch, edit, code, implement)
# exploration - Finding files, understanding codebase (keywords: scout, explore, find)
# testing - Running tests, verification (keywords: test, verify, check)
# documentation - Writing docs, READMEs (keywords: doc, readme, comment)
# fast - Quick operations that need low latency
# default - Fallback for unmatched categories
#
# Format: category = "model@backend"

# [model_routing.routes]
# planning = "qwen3-235b-a22b-instruct-2507@venice"
# coding = "claude-3-5-sonnet-latest@claude"
# exploration = "gpt-4o-mini@chatgpt"
# testing = "gpt-4o-mini@chatgpt"
# documentation = "gpt-4o-mini@chatgpt"
# fast = "gpt-4o-mini@chatgpt"
# default = "gpt-4o-mini@chatgpt"

# =============================================================================
# MCP (Model Context Protocol) SERVERS
# =============================================================================
Expand Down Expand Up @@ -209,7 +230,6 @@ keep_last_turns = 10
# permission_mode - "default", "acceptEdits", or "bypassPermissions"
# max_turns - Maximum iterations (default: 8)
# system_prompt - Custom system prompt
# skill - Optional skill to use for LLM target
# target - Optional explicit target (model@backend)
#
# Use agents in REPL with:
Expand Down
Loading