Rubyn Code
AI Code Assistant for Ruby & Rails — Open Source
Refactor controllers, generate idiomatic RSpec, catch N+1 queries, review code for anti-patterns, and build entire features — all context-aware with your schema, routes, and specs. Powered by Claude Opus 4.6, running on your existing Claude subscription.
Rubyn is going open source. The original Rubyn gem provided AI-assisted refactoring, spec generation, and code review through the Rubyn API. Rubyn Code is the next evolution — a complete agentic coding assistant that runs locally, thinks for itself, and learns from every session. No API keys. No separate billing. Just
gem install rubyn-codeand go.
Table of Contents
- Why Rubyn?
- Install
- Quick Start
- What Can Rubyn Do?
- VS Code Extension
- 29 Built-in Tools
- MCP — External Tool Servers
- Codebase Indexing
- 112 Best Practice Skills
- Skill Packs — Registry-Backed Extensions
- Context Architecture
- RUBYN.md — Project Instructions
- PR Review
- Megaplan — Phased Planning
- Sub-Agents & Teams
- GOLEM — Autonomous Daemon
- Continuous Learning
- Streaming Output
- Search Providers
- User Hooks
- CLI Reference
- Authentication
- Architecture
- Configuration
- Security
- Diagnostics
- Development
- From Rubyn to Rubyn Code
- Contributing
- License
Why Rubyn?
- Rails-native — understands service object extraction, RSpec conventions, ActiveRecord patterns, and Hotwire
- Context-aware — automatically incorporates schema, routes, specs, factories, and models
- Best practices built in — ships with 112 curated Ruby and Rails guidelines that load on demand, plus registry-backed skill packs that autoload as you need them
- Plans big work in phases —
/megaplanruns a read-only interview, then breaks rewrites and migrations into vertical-slice phases that ship one at a time - Agentic — doesn't just answer questions. Reads files, writes code, runs specs, commits, reviews PRs, spawns sub-agents, and remembers what it learns
- IDE-ready — works in the terminal and inside VS Code with full bidirectional communication
- Extensible — connect external tool servers via MCP, add custom skills, or wire up your own providers
Install
Requires Ruby 4.0.2+. Install with your latest Ruby, then pin it so it works in every project:
# Install the gem
gem install rubyn-code
# Pin to this Ruby — bypasses rbenv/rvm version switching
rubyn-code --setup
That's it. rubyn-code now works in any project regardless of .ruby-version.
Using rbenv?
If you manage multiple Rubies with rbenv, install on your latest (run `rbenv versions` to list what you have): ```bash RBENV_VERSION=Using rvm?
```bash rvm useFrom source
```bash git clone https://github.com/MatthewSuttles/rubyn-code.git cd rubyn-code bundle install bundle exec ruby -Ilib exe/rubyn-code ```Authentication: Rubyn Code reads your Claude Code OAuth token from the macOS Keychain automatically. Just make sure you've logged into Claude Code once (claude in your terminal). Also supports ANTHROPIC_API_KEY env var. See Authentication for OpenAI and other providers.
Quick Start
# Interactive REPL
rubyn-code
# YOLO mode — no tool approval prompts
rubyn-code --yolo
# Single prompt
rubyn-code -p "Refactor app/controllers/orders_controller.rb into service objects"
# VS Code IDE mode (used by the extension)
rubyn-code --ide
What Can Rubyn Do?
Refactor code
rubyn > This orders controller is 300 lines. Break it up.
> read_file: path=app/controllers/orders_controller.rb
> read_file: path=app/models/order.rb
> read_file: path=config/routes.rb
> write_file: path=app/services/orders/create_service.rb
> write_file: path=app/services/orders/cancel_service.rb
> edit_file: path=app/controllers/orders_controller.rb
Done. Extracted CreateService and CancelService. Controller is down to 45 lines.
Generate specs
rubyn > Write specs for the new service objects
> read_file: path=app/services/orders/create_service.rb
> read_file: path=spec/factories/orders.rb
> write_file: path=spec/services/orders/create_service_spec.rb
> run_specs: path=spec/services/orders/
4 examples, 0 failures. All green.
Review code
rubyn > /review
> review_pr: base_branch=main
[warning] app/models/user.rb:15 — N+1 query detected
[critical] app/controllers/admin_controller.rb:8 — SQL injection risk
[suggestion] app/services/create_order.rb:22 — Method too long, extract private methods
Explore codebases
rubyn > I'm new to this project. Give me the lay of the land.
Spawning explore agent...
[⠹] Agent exploring the codebase... (23 tools)
Agent finished (23 tool calls).
This is a Rails 7.1 e-commerce app with...
VS Code Extension
Rubyn Code includes a VS Code extension that provides a full IDE experience with bidirectional JSON-RPC communication. The extension runs Rubyn as a subprocess and connects over stdin/stdout.
Capabilities:
- Chat panel with streaming responses and syntax-highlighted code blocks
- Inline diffs — review and accept generated code changes directly in the editor
- Tool approval prompts in the IDE (or skip them in YOLO mode)
- Full session management — resume, list, fork, and reset conversations
- Structured code review feedback with severity ratings
- IDE config get/set for persistent settings
- All 29 tools available, including MCP tools
Permission modes:
| Mode | Behavior |
|---|---|
default |
Per-tool approval required |
bypass |
YOLO — skip all approval prompts |
The extension communicates over 19 RPC methods: initialize, prompt, cancel, review, approveToolUse, acceptEdit, session/*, config/*, models/list, plan/propose, plan/interview/* (chat-resident megaplan), recover_ci, and shutdown.
29 Built-in Tools
| Category | Tools |
|---|---|
| File I/O | read_file, write_file, edit_file |
| Search | glob, grep |
| Execution | bash (sandboxed, dangerous commands blocked) |
| Web | web_search, web_fetch |
| Git | git_status, git_diff, git_log, git_commit |
| Rails | rails_generate, db_migrate, run_specs, bundle_install, bundle_add |
| Review | review_pr (diff-based best practice code review) |
| Agents | spawn_agent, spawn_teammate, background_run |
| Context | compact, load_skill, task |
| Memory | memory_search, memory_write |
| Teams | send_message, read_inbox |
| Interactive | ask_user (ask clarifying questions mid-task) |
MCP — External Tool Servers
Connect external tool servers via the Model Context Protocol. MCP tools are dynamically discovered and registered as native Rubyn tools, available in the REPL, IDE, and daemon.
Configuration
Create .rubyn-code/mcp.json in your project or ~/.rubyn-code/mcp.json globally:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
},
"my-api": {
"url": "http://localhost:3001/mcp",
"timeout": 30
}
}
}
- Stdio transport — specify
commandandargsto run a subprocess - SSE transport — specify
urlfor HTTP-based servers - Environment variables are interpolated with
${VAR}syntax
MCP tools appear in the tool palette prefixed with mcp_ and require confirmation before execution. Run /doctor to verify server connectivity.
Rubyn ships with three example MCP servers: database explorer, RubyGems lookup, and Rails routes. See /mcp for documentation.
Codebase Indexing
Rubyn builds a structural index of your codebase on first session and incrementally updates it as files change. The index powers smarter context injection, skill suggestions, and impact analysis.
What it tracks:
- Classes, modules, methods, callbacks, scopes, validations, associations
- Relationships between files (associations, test coverage, caller/callee)
- Rails patterns:
has_many,belongs_to,before_action,validates, etc. - File classification: model, controller, service, concern, spec
How it's used:
- Injects a compact structural summary into the system prompt
- Feeds the dynamic tool schema for smarter tool selection
- Powers
impact_analysis(file)to find affected tests and dependents - Suggests relevant skills based on the code you're working with
Stored at .rubyn-code/codebase_index.json. The /doctor command flags stale indexes (>24 hours).
112 Best Practice Skills
Rubyn ships with curated best practice documents that load on demand. Only skill names are in memory — full content loads when Rubyn needs it.
| Category | Topics |
|---|---|
| Ruby | Collections, error handling, metaprogramming, concurrency, pattern matching |
| Rails | Controllers, models, views, migrations, ActiveRecord, Hotwire, caching, security |
| RSpec | Matchers, factories, request specs, shared examples, performance |
| Minitest | Assertions, system tests, fixtures, mocking |
| Design Patterns | Observer, strategy, decorator, builder, factory, adapter, and more |
| SOLID | All five principles with Ruby examples |
| Refactoring | Extract method/class, replace conditional, code smells, command-query separation |
| Code Quality | Naming, YAGNI, value objects, null object, technical debt |
| Gems | Sidekiq, Devise, FactoryBot, Pundit, Faraday, Stripe, RuboCop, dry-rb |
| Sinatra | Application structure, middleware, testing |
Skill search & filter
rubyn > /skill search factory # search by name, description, or tags
rubyn > /skill list rails # filter by category
rubyn > /skill list # show all categories
rubyn > /skill load rspec_matchers # inject a skill into context
Results are relevance-ranked: name matches score highest, then description, then tags.
Custom skills
Override or extend with your own:
# Project-specific
mkdir -p .rubyn-code/skills
echo "# Always use Grape for APIs" > .rubyn-code/skills/api_conventions.md
# Global
mkdir -p ~/.rubyn-code/skills
echo "# Use double quotes for strings" > ~/.rubyn-code/skills/my_style.md
Skill Packs — Registry-Backed Extensions
Beyond the 112 built-in skills, Rubyn can pull additional skill packs from the rubyn.ai registry. Packs are bundles of related skills published by the community or by Rubyn itself.
rubyn > /skills # list installed packs and browse the registry
rubyn > /install-skills sidekiq # install a pack by name
rubyn > /install-skills graphql viewcomponent # install multiple at once
rubyn > /remove-skills sidekiq # uninstall
Installed packs live at ~/.rubyn-code/skill-packs/<pack-name>/ and load alongside the built-in catalog.
Auto-suggest from your Gemfile
On session start, Rubyn parses your Gemfile and quietly suggests matching packs the first time it sees a gem (e.g. detects sidekiq → suggests the sidekiq pack). Suggestions are recorded in .rubyn-code/suggested.json so you only see each one once.
Trigger-based autoload
If your message mentions a topic that matches an uninstalled pack's name or tags, Rubyn fetches the pack from the registry on the fly, installs it, and feeds the relevant skills into the same turn. Registry failures are silent — the conversation continues as if the autoload weren't there.
Point at a custom registry with RUBYN_REGISTRY_URL=https://your-registry.example.com.
Context Architecture
Rubyn automatically loads relevant context based on what you're working on:
- Controllers → includes models, routes, request specs, services
- Models → includes schema, associations, specs, factories
- Service objects → includes referenced models and their specs
- Any file → checks for
RUBYN.md,CLAUDE.md, orAGENT.mdinstructions
The codebase index enhances this with structural awareness — Rubyn knows which files depend on each other before it reads them.
RUBYN.md — Project Instructions
Drop a RUBYN.md in your project root and Rubyn follows your conventions:
# My Project
- Always use RSpec, never Minitest
- Use FactoryBot for test data
- Service objects go in app/services/ with a .call interface
- API endpoints use Grape, not Rails controllers
- Run rubocop before committing
Also reads CLAUDE.md and AGENT.md — no migration needed from other tools.
| Location | Scope |
|---|---|
~/.rubyn-code/RUBYN.md |
Global — all projects |
| Parent directories | Monorepo — shared conventions |
./RUBYN.md |
Project root |
./subdir/RUBYN.md |
Subfolder-specific |
PR Review
Review your branch against best practices before opening a PR:
rubyn > /review # vs main
rubyn > /review develop # vs develop
rubyn > /review main security # security focus only
Focus areas: all, security, performance, style, testing
Severity ratings: [critical] [warning] [suggestion] [nitpick]
Megaplan — Phased Planning
For work too big for a single PR — rewrites, migrations, multi-feature initiatives — Rubyn ships a planning workflow that breaks the feature into vertical-slice phases before any code gets written.
rubyn > /megaplan extract billing into its own service
Megaplan mode — interviewer with read-only tools
Decisions so far: (none yet)
Q1. What triggers the extraction now — a scaling issue, a team boundary,
or a compliance constraint?
1. Scaling (recommended — billing is the hottest table)
2. Team boundary
3. Compliance
What happens when you run /megaplan:
- Loads the megaplan skill into context.
- Flips the agent into plan mode — only read-only tools (file reads, search, git status) are available. No edits, no shell mutations.
- Conducts a one-question-at-a-time interview to lock down scope, constraints, and risk before proposing phases.
- Outputs a numbered phase breakdown, each phase shippable on its own with the trunk staying green.
Trigger phrases like "megaplan", "mega plan", "plan phases", or "phase this out" in normal conversation will surface the skill via trigger-based autoload too.
In the VS Code extension the same workflow runs as a chat-resident interview with structured question cards instead of free-text Q&A. Same skill driving both surfaces.
Sub-Agents & Teams
Sub-Agents (disposable)
rubyn > Explore the app/services directory and summarize the patterns
Spawning explore agent...
[⠹] Dispatching the intern... (18 tools)
Agent finished (18 tool calls).
Two types: explore (read-only) and worker (full write access).
Teams (persistent)
rubyn > /spawn alice tester
Spawned teammate alice as tester
rubyn > Send alice a message to write specs for the User model
Teammates run in background threads with their own agent loop and mailbox.
GOLEM — Autonomous Daemon
GOLEM is an always-on autonomous agent that claims tasks from a queue and works through them independently. It runs a full agent loop per task with access to all tools, MCP servers, and memory.
rubyn-code daemon \
--name golem-1 \
--role "Backend Engineer" \
--max-runs 100 \
--max-cost 10.0 \
--poll-interval 5 \
--idle-timeout 60
Lifecycle: spawned → working ⇄ idle → shutting_down → stopped
Safety limits:
| Guard | Description |
|---|---|
--max-runs |
Auto-shutdown after N completed tasks |
--max-cost |
Stop when cumulative USD spend exceeds limit |
| Retry backoff | 3 retries per task before marking failed |
| Audit trail | Full conversation saved per task via session persistence |
| Cost tracking | Accurate per-task spend via the observability layer |
Continuous Learning
Rubyn gets smarter with every session:
- During conversation — saves preferences and patterns to memory
- On session end — extracts reusable "instincts" with confidence scores
- On next startup — injects top instincts into the system prompt
- Over time — reinforced instincts strengthen, unused ones decay and get pruned
Streaming Output
Real-time streaming with live syntax highlighting via Rouge/Monokai. Code blocks are buffered and highlighted when complete. No waiting for full responses.
Search Providers
Auto-detects the best available provider:
| Provider | Env Variable | Free Tier |
|---|---|---|
| DuckDuckGo | (none) | Unlimited |
| Tavily | TAVILY_API_KEY |
1,000/mo |
| Brave | BRAVE_API_KEY |
2,000/mo |
| SerpAPI | SERPAPI_API_KEY |
100/mo |
GOOGLE_SEARCH_API_KEY + GOOGLE_SEARCH_CX |
100/day |
User Hooks
Customize behavior via .rubyn-code/hooks.yml:
pre_tool_use:
- tool: bash
match: "rm -rf"
action: deny
reason: "Destructive delete blocked"
- tool: write_file
path: "db/migrate/**"
action: deny
reason: "Use rails generate migration"
post_tool_use:
- tool: write_file
action: log
CLI Reference
rubyn-code # Interactive REPL
rubyn-code --yolo # Auto-approve all tools
rubyn-code -p "prompt" # Single prompt, exit when done
rubyn-code --ide # IDE server mode (JSON-RPC over stdin/stdout)
rubyn-code --resume [ID] # Resume previous session
rubyn-code --setup # Pin to this Ruby (run once after install)
rubyn-code --debug # Enable debug output
rubyn-code --auth # Authenticate with Claude
rubyn-code --version # Show version
rubyn-code --help # Show help
rubyn-code daemon [OPTIONS] # Run GOLEM autonomous daemon
Slash Commands
| Command | Purpose |
|---|---|
/help |
Show help |
/quit |
Exit (saves session + extracts learnings) |
/new |
Save session and start a fresh conversation |
/review [base] |
PR review against best practices |
/spawn name role |
Spawn a persistent teammate |
/compact |
Compress conversation context |
/cost |
Show token usage and costs |
/tasks |
List all tasks |
/budget [amt] |
Show or set session budget |
/skill [name] |
Load, search, or list available skills |
/resume [id] |
Resume or list sessions |
/provider |
Add or list providers |
/model |
Show/switch model and provider |
/doctor |
Run environment health checks |
/mcp |
MCP server documentation and status |
Authentication
Anthropic (default)
| Priority | Source | Setup |
|---|---|---|
| 1 | macOS Keychain | Log into Claude Code once: claude |
| 2 | Token file | ~/.rubyn-code/tokens.yml |
| 3 | Environment | export ANTHROPIC_API_KEY=sk-ant-... |
Works with Claude Pro, Max, Team, and Enterprise. Default model: Claude Opus 4.6.
OpenAI
export OPENAI_API_KEY=sk-...
Available models: gpt-5.4, gpt-5.4-mini, gpt-5.4-nano, gpt-4o, gpt-4o-mini, o3, o4-mini
Other Providers (Groq, Together, Ollama, etc.)
Add a provider and its API key in one command:
/provider add groq https://api.groq.com/openai/v1 --key gsk-xxx --models llama-3.3-70b
# For Anthropic-format proxies (e.g., Bedrock, custom gateways)
/provider add my-proxy https://proxy.example.com/v1 --format anthropic --key sk-xxx --models claude-sonnet-4-6
# Update a key later
/provider set-key groq gsk-new-key
# List configured providers
/provider list
API keys are encrypted at rest using AES-256-GCM. The encryption key is derived from your machine identity (username, hostname, home directory) via PBKDF2, so keys are only decryptable on the same machine by the same user. Rubyn decrypts them automatically at runtime and re-encrypts on save — no manual steps required.
Keys stored via environment variables (GROQ_API_KEY, TOGETHER_API_KEY, etc.) also work
as a fallback if you prefer that approach.
Or add directly to ~/.rubyn-code/config.yml:
providers:
groq:
base_url: https://api.groq.com/openai/v1
env_key: GROQ_API_KEY
models:
top: llama-3.3-70b
my-proxy:
api_format: anthropic # 'openai' (default) or 'anthropic'
base_url: https://proxy.example.com/v1
env_key: PROXY_API_KEY
models:
top: claude-sonnet-4-6
Then switch with /model groq:llama-3.3-70b.
Local providers (Ollama, LM Studio) running on localhost/127.0.0.1 don't require an API key.
Architecture
16-layer agentic architecture:
┌──────────────────────────────────────────────────────────────┐
│ Layer 16: Continuous Learning (pattern extraction + decay) │
│ Layer 15: MCP (external tool servers via protocol) │
│ Layer 14: Hooks & Events (user-configurable pre/post hooks) │
│ Layer 13: Observability (cost tracking, budget enforcement) │
│ Layer 12: Memory (persistent knowledge across sessions) │
│ Layer 11: Autonomous Operation (GOLEM daemon, task claiming) │
│ Layer 10: Protocols (shutdown handshake, plan approval) │
│ Layer 9: Teams (persistent teammates, mailbox messaging) │
│ Layer 8: Background Execution (async tasks, notifications) │
│ Layer 7: Task System (persistent DAG with dependencies) │
│ Layer 6: Sub-Agents (explore + worker, isolated contexts) │
│ Layer 5: Skills (112 best practice docs, on-demand loading) │
│ Layer 4: Context Management (3-layer compression pipeline) │
│ Layer 3: Permissions (tiered access + deny lists + hooks) │
│ Layer 2: Tool System (29 tools, dispatch map registry) │
│ Layer 1: THE AGENT LOOP (while tool_use → execute → repeat) │
└──────────────────────────────────────────────────────────────┘
Configuration
The provider and model keys at the top set the default provider and model used at startup.
These must match a provider defined in the providers section (or a built-in like anthropic/openai).
# ~/.rubyn-code/config.yml (global)
provider: anthropic # default provider on startup
model: claude-opus-4-6 # default model on startup
permission_mode: allow_read
session_budget: 5.00
daily_budget: 10.00
# .rubyn-code/config.yml (project — overrides global)
provider: minimax # this project uses MiniMax by default
model: MiniMax-M2.7-highspeed
permission_mode: autonomous
Multi-Provider Model Routing
Rubyn can automatically route tasks to different AI models based on complexity. Simple tasks (file search, git ops) use cheap, fast models. Complex tasks (architecture, security review) use the most capable model. Configure per-provider model tiers in config.yml:
# ~/.rubyn-code/config.yml
provider: anthropic
model: claude-opus-4-6
providers:
anthropic:
env_key: ANTHROPIC_API_KEY
models:
cheap: claude-haiku-4-5 # file search, git ops, formatting
mid: claude-sonnet-4-6 # code gen, specs, refactors, reviews
top: claude-opus-4-6 # architecture, security, complex work
openai:
env_key: OPENAI_API_KEY
models:
cheap: gpt-5.4-nano # lightweight tasks
mid: gpt-5.4-mini # regular coding
top: gpt-5.4 # complex reasoning
groq:
base_url: https://api.groq.com/openai/v1
env_key: GROQ_API_KEY
models:
cheap: llama-3-8b
mid: llama-3-70b
pricing:
llama-3-8b: [0.05, 0.08] # [input_rate, output_rate] per million tokens
llama-3-70b: [0.59, 0.79]
ollama:
base_url: http://localhost:11434/v1
models:
cheap: llama3
mid: llama3
top: llama3
How it works: When you ask Rubyn to do something, the Model Router detects the task type and picks the right tier. If you've configured model tiers for a provider, those are used first. Otherwise it falls back to the built-in defaults (Anthropic for all tiers).
| Tier | Task types | Default model |
|---|---|---|
| cheap | File search, git ops, formatting, summaries | claude-haiku-4-5 |
| mid | Code generation, specs, refactors, code review, bug fixes | claude-sonnet-4-6 |
| top | Architecture, security review, complex refactors, planning | claude-opus-4-6 |
You can also set custom pricing per model so /cost reports accurate spending for third-party providers.
Security
Credential Storage
All provider API keys are encrypted at rest using AES-256-GCM (authenticated encryption). Keys are never stored as plaintext on disk.
| Layer | Detail |
|---|---|
| Cipher | AES-256-GCM (authenticated — detects tampering) |
| Key derivation | PBKDF2-HMAC-SHA256, 100,000 iterations |
| Machine binding | Key derived from username + hostname + home directory |
| Salt | Random 32-byte salt, generated once, stored in ~/.rubyn-code/.encryption_salt |
| File permissions | tokens.yml and .encryption_salt are 0600 (owner read/write only) |
This means:
- Keys copied to another machine or user account cannot be decrypted
- The encryption key is never stored — it is derived at runtime
- Plaintext keys from older versions are automatically encrypted on first read
File Permissions
| File | Permissions | Contents |
|---|---|---|
~/.rubyn-code/ |
0700 |
Home directory |
~/.rubyn-code/tokens.yml |
0600 |
Encrypted API keys, OAuth tokens |
~/.rubyn-code/.encryption_salt |
0600 |
PBKDF2 salt (not secret alone, but protected) |
~/.rubyn-code/config.yml |
0600 |
Provider config (no secrets) |
Diagnostics
Run /doctor to check your environment:
rubyn > /doctor
✓ Ruby version 4.0.2
✓ Bundler installed
✓ Database 12 migrations applied
✓ Authentication valid (keychain)
✓ Skills 112 available
✓ Project detected Rails 7.1
✓ MCP servers 2 connected
✓ Codebase index fresh (2 hours ago)
✓ Skill catalog 112 skills, 0 malformed
Checks Ruby version, bundler, database state, authentication, skills, project type, MCP server connectivity, codebase index freshness, and skill catalog integrity.
Development
Requires Ruby 4.0.2+.
git clone https://github.com/MatthewSuttles/rubyn-code.git
cd rubyn-code
bundle install
bundle exec rspec
Quick rebuild from source:
bin/dev-install
From Rubyn to Rubyn Code
If you used the original Rubyn gem, here's what changed:
| Rubyn (original) | Rubyn Code (open source) |
|---|---|
| Rubyn API required | Runs locally, no external API |
| API key billing | Uses your Claude subscription |
| Refactor, spec, review commands | Full agentic assistant — reads, writes, thinks, learns |
| Static best practices | 112 on-demand skills + custom overrides |
| Single-turn commands | Multi-turn sessions with memory and context |
| Closed source | MIT open source |
Contributing
PRs welcome. If your team has conventions that should be a skill document, contribute it. If you need a tool we don't have, the tool system is a base class and a registry — add yours.
# Add a new tool
lib/rubyn_code/tools/your_tool.rb # extend Base, register with Registry
# Add a new skill
skills/your_category/your_skill.md # markdown with optional YAML frontmatter
License
MIT License — see LICENSE for details.