cline-rb
Ruby bindings for the Cline AI assistant ecosystem β programmatically control the CLI, manage configurations, skills, sessions, tasks, models, logs, and secrets.
cline-rb is a Ruby gem that gives you programmatic access to the Cline AI assistant ecosystem. π―
Cline is an AI coding assistant that lives in your terminal. This library wraps its internals so you can:
- π€ Launch & control the Cline CLI from Ruby β run tasks, authenticate providers, and handle interactive sessions via PTY.
- π Read & write configuration β access global (
~/.cline), project (.cline), and VSCode extension data with a clean Ruby API. - π§ Manage skills, tasks & sessions β list, enable/disable skills, monitor real-time task/session messages, and track token usage & costs.
- π Handle secrets safely β store and retrieve API keys for dozens of AI providers (OpenAI, Anthropic, Gemini, DeepSeek, Groqβ¦).
- π Parse logs & global state β read structured log entries, inspect auto-approval rules, browser settings, feature flags, and MCP configurations.
- β±οΈ Watch for file changes β monitor config files, logs, and data files for live updates.
Designed as a Ruby library (not a standalone CLI), cline-rb lets you build automation, testing tooling, custom dashboards, or integration scripts on top of the Cline ecosystem with minimal effort. β¨
Table of contents
- Quick start
- Prerequisites
- Installation
- Configuration (optional)
- Reading Cline data
- Running a Cline task
- Running a task with a specific model
- Monitoring messages in real time
- Handling interactive sessions (questions from the assistant)
- Authentication
- Reading logs
- Interrupting a running task
- Requirements
- Features
- Public API
-
Clineβ top-level moduleCline.configureCline.configCline::VERSION
-
Cline::Configurationβ gem configuration -
Cline::Cliβ CLI wrapperCli#initialize(stdout_echo: false, **kwargs)Cli#task(prompt, on_message: nil, on_question: nil, monitoring_interval_secs: 1, **kwargs)Cli#auth(**kwargs)Cli#interrupt-
Cli#cline_pid/Cli#session
-
Cline::Configβ Cline configuration directoryConfig.mainConfig.globalConfig.projectConfig#skills(create: false)Config#data(create: false)Config#cli(**kwargs)Config#refresh!
-
Cline::OverlayHashβ layered hash -
Cline::Dataβ Cline data directoryData.vscode
-
Cline::Skillβ individual skill -
Cline::Skillsβ collection of skills -
Cline::Sessionβ a Cline session -
Cline::SessionDataβ session metadata JSON -
Cline::SessionMessageβ individual session message -
Cline::SessionMessagesβ session messages file -
Cline::Taskβ a Cline task -
Cline::TaskMessageβ individual task message -
Cline::Logsβ log file -
Cline::Logβ log entry schema -
Cline::Secretsβ API keys -
Cline::Modelβ cached model info -
Cline::GlobalStateβ global Cline state -
Cline::GlobalSettingsβ global settings -
Cline::McpSettingsβ MCP server settings -
Cline::Providersβ provider configurations -
Cline::Workspaceβ workspace data -
Cline::Usageβ token usage statistics -
Cline::Schemaβ base JSON schema class -
Cline::FileContentβ skill file content -
Cline::Utils::FileMonitorβ file change monitor
-
- Documentation
- How it works
- Architecture overview
- 1οΈβ£ Auto-loading with Zeitwerk β‘
- 2οΈβ£ Schema & Serialization (the core pattern) π
- 3οΈβ£ Config β the entry point πͺ
- 4οΈβ£ CLI interaction β PTY-based process control π€
- 5οΈβ£ File monitoring β polling-based change detection π
- 6οΈβ£ OverlayHash β merging global + project config π
- 7οΈβ£ Skill management π
- 8οΈβ£ Secret handling π
- Data flow (end-to-end) π―
- Development
- Prerequisites
- Clone the repository
- Install dependencies
- Project structure
- Running tests
- Using the Cline CLI stub in other project's test cases
- Code linting
- Code coverage
- Building the gem
- Generating YARD documentation
- Common development tasks
- Contributing
- π Issues
- π΄ Fork & Pull Request workflow
- β Pull Request guidelines
- π CI & Quality gates
- π License
- π¬ Questions?
- License
Quick start
Prerequisites
- Ruby >= 3.1
- Cline CLI installed and configured on your system (see cline.bot)
Installation
Add the gem to your project's Gemfile:
$ bundle add cline-rbOr install it globally:
$ gem install cline-rbThen require the library:
require 'cline'Configuration (optional)
Enable debug logging to see CLI interactions:
Cline.configure do |config|
config.debug = true
endReading Cline data
Access the global configuration (~/.cline), project config (.cline), or the merged main config:
# Main config (global + project merged)
config = Cline::Config.main
# Global config only
config = Cline::Config.global
# Project config only
config = Cline::Config.project
# List available skills
config.skills&.each do |name, skill|
puts "#{name}: #{skill.enabled? ? 'enabled' : 'disabled'}"
end
# List recent sessions
config.data.sessions.each do |id, session|
puts "[#{session.status}] #{session.model} - #{session.session_id}"
puts " Tokens: #{session.data.metadata.usage&.input_tokens} in / #{session.data.metadata.usage&.output_tokens} out" if session.data&.metadata&.usage
end
# List tasks
config.data.tasks.each do |id, task|
puts "Task #{id}: #{task.messages&.first&.text&.slice(0..80)}"
end
# Read stored API keys (returned as SecretString, redacted on inspect)
secrets = config.data.secrets
puts secrets.open_ai_api_key # => "sk-...********" (redacted)
# Read cached model information
models = config.data.cline_models
puts models.keys # e.g. ["gpt-4o", "claude-sonnet-4-6", ...]Running a Cline task
Use Cline::Cli to launch the Cline CLI, send a prompt, and collect the result:
cli = Cline::Cli.new(stdout_echo: true)
result = cli.task('Explain what Ruby symbols are in one sentence.')
puts result[:stdout] # Full CLI output
puts result[:message] # Last assistant message (SessionMessage object)
puts result[:status] # e.g. "completed"
puts result[:error] # Error log entry if status is "failed"Running a task with a specific model
result = cli.task(
'Refactor this code snippet: puts "hello"',
model: 'claude-sonnet-4-6',
provider: 'anthropic'
)Monitoring messages in real time
result = cli.task(
'Write a Ruby script that reads a CSV file.',
on_message: proc do |message, last, previous_version|
puts "[#{message.say}] #{message.to_human(limit: 120)}"
end
)Handling interactive sessions (questions from the assistant)
result = cli.task(
'Create a new Rails API project',
on_question: proc do |question|
puts "Assistant asks: #{question.question}"
'Use the default settings'
end
)Authentication
Set up a provider's API key:
cli = Cline::Cli.new
cli.auth(provider: 'openai-native', apikey: 'sk-...')Reading logs
logs = config.data.logs
logs.logs.each do |entry|
puts "[#{entry.time}] #{entry.msg}" if entry.is_a?(Cline::Log)
end
# Monitor logs in real time
logs.monitor(on_log: ->(log, _last) {
puts log.msg if log.is_a?(Cline::Log)
}) do
sleep 5
endInterrupting a running task
cli.interrupt # kills the running Cline process treeRequirements
- Ruby >= 3.1
-
Cline CLI installed and available on your
PATH(see cline.bot for installation instructions) - Operating System: Linux, macOS, or Windows (the gem includes platform-specific dependencies for each)
-
Bundler (recommended) β to manage the gem dependency in your project via
bundle add cline-rb
Features
cline-rb is a Ruby library that wraps the Cline AI assistant ecosystem, providing programmatic access to its CLI, configuration, data, and runtime β all through a clean Ruby API. Key capabilities include:
- π€ CLI control β Launch and drive the Cline CLI from Ruby via PTY: run tasks with custom models/providers, authenticate providers, handle interactive questions, and interrupt running sessions.
- π Configuration access β Read and write Cline config from multiple sources (global
~/.cline, project.cline, or custom paths). Merge global + project layers into a unified view. - π Secrets management β Safely store and retrieve API keys for 30+ AI providers (OpenAI, Anthropic, Gemini, DeepSeek, Groq, Mistral, Together, etc.) using
SecretStringfor secure in-memory handling. - π§ Skills management β List, read, enable, and disable skills; inspect their YAML front matter and file contents.
- π Sessions & tasks β Enumerate sessions and tasks from the data directory, read their structured messages, extract token usage, model info, and costs.
- β±οΈ Real-time monitoring β Subscribe to live updates on session messages, task messages, and log entries via file-change polling with callback-driven notifications.
- π Log parsing β Read, parse, and append structured JSON log entries (error details, telemetry events, API call errors, etc.) with support for real-time log monitoring.
- βοΈ Global state inspection β Access auto-approval rules, browser/viewport settings, feature flags, model configurations, API provider settings, workspace roots, and toggle states.
- π MCP settings β Read and write Model Context Protocol server configurations (type, URL, timeout, auto-approve tools).
- π§© Provider config β Read provider entries with API keys, model IDs, reasoning settings, and token sources.
- πΎ Cached model info β Access cached model metadata (context windows, pricing, image/cache support, thinking config).
- π Workspace settings β Read per-workspace toggle states for skills, rules, and workflows.
- π Configuration merging β
OverlayHashprovides a layered Hash interface combining global and project configs. - π¦ VSCode support β Access the VSCode extension's Cline data directory directly.
- π οΈ File change monitoring β A background thread polls files at configurable intervals and fires callbacks on change β used across logs, sessions, tasks, and Cline data.
- ποΈ Schema-driven JSON serialization β Domain objects (via
Shale) handle automatic camelCaseβsnake_case mapping, preserve extra attributes, and support round-trip JSON serialization. - π» Cross-platform β OS-agnostic helpers for Linux and Windows (MinGW) covering executable paths, home directories, app data, and process tree management.
- π Debug support β Configurable debug mode enables verbose PTY output logging, temporary directory snapshots, and ANSI-sanitized logs.
Public API
cline-rb is a Ruby library (no CLI executables). All public entry points are Ruby classes and methods under the Cline module.
Cline β top-level module
Cline.configure
Configure the cline-rb gem behavior.
Cline.configure do |config|
config.debug = true
config.temp_dir_root = '/tmp/my_debug'
endCline.config
Get the current gem configuration (a Cline::Configuration instance).
Cline.config.debug #=> true or falseCline::VERSION
Gem version constant.
Cline::VERSION #=> "0.1.0"
Cline::Configuration β gem configuration
| Attribute | Type | Description |
|---|---|---|
debug |
Boolean |
Debug mode (defaults to ENV['CLINE_DEBUG'] == '1') |
temp_dir_root |
String |
Debug temp directory root (default: .cline-rb/tmp) |
Cline::Cli β CLI wrapper
Wraps the Cline CLI process via PTY. Create an instance and use it to run tasks, authenticate providers, or interrupt running commands.
cli = Cline::Cli.new(stdout_echo: true)Cli#initialize(stdout_echo: false, **kwargs)
Constructor. kwargs can include global options like verbose, cwd, or config.
cli = Cline::Cli.new(stdout_echo: true, cwd: '/my/project')Cli#task(prompt, on_message: nil, on_question: nil, monitoring_interval_secs: 1, **kwargs)
Start a task by sending a prompt to the Cline CLI. Returns { stdout:, message:, status:, error: }.
result = cli.task('Explain Ruby symbols in one sentence.', model: 'claude-sonnet-4-6')
puts result[:stdout] # Full CLI output
puts result[:message] # Last assistant SessionMessage
puts result[:status] # e.g. "completed"Cli#auth(**kwargs)
Authenticate a provider with the CLI.
cli.auth(provider: 'openai-native', apikey: 'sk-...')Cli#interrupt
Kill the currently running Cline process tree.
cli.interrupt
Cli#cline_pid / Cli#session
Access the current Cline process PID and the last session handled.
Cline::Config β Cline configuration directory
Access global (~/.cline), project (.cline), or merged configuration.
Config.main
Merged global + project config.
config = Cline::Config.mainConfig.global
Global config only (~/.cline).
global = Cline::Config.globalConfig.project
Project config only (.cline).
project = Cline::Config.projectConfig#skills(create: false)
Get skills (returns an OverlayHash combining global + project skills).
config.skills&.each { |name, skill| puts name }Config#data(create: false)
Get the Data object for this config.
config.data.secrets #=> Cline::Secrets
config.data.logs #=> Cline::Logs
config.data.sessions #=> Cline::SessionsConfig#cli(**kwargs)
Create a Cli instance attached to this config.
config.cli(stdout_echo: true).task('Hello')Config#refresh!
Clear cached objects to reload from disk.
Cline::OverlayHash β layered hash
Merges several Hash-like objects, with priority order for reads and writes to the first layer only.
merged = Cline::OverlayHash.new(global_skills, project_skills)
merged['my_skill'] # retrieves from global first, falls back to projectSupports each, [], key?, keys, values, size, empty?, to_h, ==.
Cline::Data β Cline data directory
Wraps the content of ~/.cline/data. Provides accessors returning domain objects.
data = Cline::Config.global.data| Method | Returns |
|---|---|
#cline_models |
Models |
#global_settings |
GlobalSettings |
#global_state |
GlobalState |
#logs |
Logs |
#mcp_settings |
McpSettings |
#providers |
Providers |
#secrets |
Secrets |
#sessions |
Sessions |
#tasks |
Tasks |
#workspaces |
Workspaces |
Data.vscode
Get the VSCode Cline extension data directory.
vscode_data = Cline::Data.vscode
Cline::Skill β individual skill
Represents a skill directory.
skill = config.skills['my-skill']
skill.name #=> "my-skill"
skill.enabled? #=> true
skill.enable # Enable the skill
skill.disable # Disable the skill
skill.files #=> { "SKILL.md" => #<FileContent>, ... }
skill.save # Persist changes to disk
skill.yaml_front_matter #=> { name: "...", ... }
Cline::Skills β collection of skills
Enumerable + Hash-like interface over skill directories.
skills = config.skills
skills.keys.each { |name| puts name }
skills.new('my-new-skill') # Create a new skill directory
Cline::Session β a Cline session
session = config.data.sessions['session-id']
session.status #=> "completed"
session.model #=> "deepseek/deepseek-v4-flash"
session.data #=> Cline::SessionData (metadata, usage, costs)
session.messages #=> Cline::SessionMessages
session.monitor_messages(on_message: ->(msg, last, prev) { puts msg.to_human }) do
sleep 5
endThe Session delegates SessionData attributes directly: session_id, status, model, provider, started_at, ended_at, etc.
Cline::SessionData β session metadata JSON
Contains Metadata (with Usage, Checkpoint, CheckpointEntry), version, session_id, source, pid, timestamps, status, model, provider, costs, etc.
session.data.metadata.usage.input_tokens #=> 1234
session.data.metadata.usage.total_cost #=> 0.0023
Cline::SessionMessage β individual session message
msg = session.messages.first
msg.role #=> "user" or "assistant"
msg.content #=> Array of MessageContent blocks
msg.timestamp #=> Time object
msg.usage #=> Usage struct (cost, tokens)
msg.to_human #=> human-friendly string (limited to 128 chars by default)Contains nested schemas: ModelInfo, Metrics, MessageContent, ToolUseInput.
Cline::SessionMessages β session messages file
Contains version, updated_at, agent, session_id, messages (array of SessionMessage), system_prompt.
Cline::Task β a Cline task
task = config.data.tasks['task-id']
task.messages #=> Cline::TaskMessages
task.monitor_messages(on_message: ->(msg, last, prev) { puts msg.to_human }) do
sleep 5
end
Cline::TaskMessage β individual task message
msg = task.messages.first
msg.ts #=> Integer timestamp
msg.type #=> "say" or "ask"
msg.say #=> "text", "api_req_started", "tool", etc.
msg.text #=> Raw text content
msg.timestamp #=> Time object
msg.usage #=> Usage struct (for api_req_started messages)
msg.to_human #=> Human-friendly description
Cline::Logs β log file
logs = config.data.logs
logs.logs.each { |entry| puts "[#{entry.time}] #{entry.msg}" if entry.is_a?(Cline::Log) }
# Add a log entry
logs << Cline::Log.new(level: 30, time: Time.now.iso8601, msg: 'Hello', pid: 1234, hostname: 'myhost', name: 'myapp', component: 'main')
logs.save
# Monitor logs in real-time
logs.monitor(on_log: ->(log, _last) { puts log.msg }) { sleep 5 }
Cline::Log β log entry schema
Attributes: level, time, pid, hostname, name, component, msg, interactive, has_prompt, cwd, reason, backend_mode, force_local_backend, telemetry_sink, event, properties (Properties), severity, provider_id, err (Error).
Nested schemas: ErrorCause, ApiError, Error, Properties (with ulid, api_provider, agent_id, team info, model info, platform info, etc.).
Cline::Secrets β API keys
Access stored API keys for 30+ AI providers. All values are SecretString (redacted on inspect).
secrets = config.data.secrets
secrets.open_ai_api_key #=> #<SecretString ...>
secrets.anthropic_api_key
secrets.gemini_api_key
secrets.deep_seek_api_key
# ... and many more
Cline::Model β cached model info
model = config.data.cline_models['claude-sonnet-4-6']
model.name #=> "Claude Sonnet 4.6"
model.max_tokens #=> 8192
model.context_window #=> 200000
model.input_price #=> 3.0
model.output_price #=> 15.0
model.supports_images #=> true
model.supports_prompt_cache #=> true
model.thinking_config #=> Cline::Model::ThinkingConfig (with max_budget)
Cline::GlobalState β global Cline state
Access via config.data.global_state. Includes the following modules:
| Module | Provides |
|---|---|
AutoApproval |
auto_approval_settings (AutoApprovalSettings with actions toggles), yolo_mode_toggled
|
Browser |
browser_settings (BrowserSettings with viewport, remote browser, chrome path) |
Workspace |
workspace_roots (array of WorkspaceRoot), primary_root_index, multi_root_enabled
|
Features |
focus_chain_settings, cline_web_tools_enabled, double_check_completion_enabled, subagents_enabled, etc. |
ApiProviders |
open_ai_headers, anthropic_base_url, request_timeout_ms, azure_api_version, etc. |
General |
welcome_view_completed, custom_prompt, cline_version, telemetry_setting, is_new_user, etc. |
Toggles |
remote_rules_toggles, global_skills_toggles, global_workflow_toggles, etc. |
Models |
Mode-specific model configs (act_mode_cline_model_id, plan_mode_cline_model_id, etc.) |
Cline::GlobalSettings β global settings
settings = config.data.global_settings
settings.auto_update_enabled #=> true/false
settings.telemetry_opt_out #=> true/false
settings.disabled_tools #=> ["tool1", "tool2"]
Cline::McpSettings β MCP server settings
mcp = config.data.mcp_settings
mcp.mcp_servers['my-server'].type #=> "sse" or "stdio"
mcp.mcp_servers['my-server'].url #=> "https://..."
mcp.mcp_servers['my-server'].timeout #=> 60
mcp.mcp_servers['my-server'].auto_approve #=> ["tool1", "tool2"]
mcp.mcp_servers['my-server'].disabled #=> false
Cline::Providers β provider configurations
providers = config.data.providers
providers.version #=> 1
providers.last_used_provider #=> "anthropic"
providers.providers['anthropic'].settings.api_key #=> SecretString
providers.providers['anthropic'].settings.model #=> "claude-sonnet-4-6"
providers.providers['anthropic'].updated_at #=> ISO 8601 timestamp
providers.providers['anthropic'].token_source #=> "manual"
Cline::Workspace β workspace data
workspace = config.data.workspaces['workspace-id']
workspace.settings.local_skills_toggles #=> { "skill1" => true }
workspace.settings.workflow_toggles #=> { "wf1" => false }
Cline::Usage β token usage statistics
A Struct with cost, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, cline_model.
usage = msg.usage
usage.context_tokens #=> input + output + cache reads + cache writes
usage.context_tokens_limit #=> model's context_window
Cline::Schema β base JSON schema class
Base class for all domain objects. Provides JSON serialization with automatic camelCaseβsnake_case mapping.
class MyConfig < Cline::Schema
attribute :my_field, :string
end
obj = MyConfig.of_hash({ "myField" => "value" })
obj.to_cline_json #=> '{"myField":"value"}'
obj.to_hash #=> { my_field: "value", extra_attributes: nil }
Cline::FileContent β skill file content
file = skill.files['SKILL.md']
file.content #=> The raw file content
Cline::Utils::FileMonitor β file change monitor
Polls a file for changes on a background thread.
monitor = Cline::Utils::FileMonitor.new(
'path/to/file.log',
on_change: ->(mtime) { puts "File changed at #{mtime}" },
monitoring_interval_secs: 2
)
monitor.start
# ... later ...
monitor.stopDocumentation
- README.md β Main project overview, installation, development and contributing instructions.
-
RubyDoc.info β Auto-generated API documentation for the
cline-rbgem, with detailed class and method references from YARD doc comments. -
YARD API Documentation β Run
bundle exec yard doclocally to generate HTML docs in thedoc/directory. The project enforces 100% documented code via CI. - CHANGELOG.md β Release history, auto-generated by semantic-release.
- TODO.md β Project roadmap and pending development tasks.
- LICENSE β BSD 3-Clause License.
- GitHub Repository β Source code, issue tracker, and pull requests.
- Continuous Integration β CI workflow definition running tests, linting, and release automation.
- Documentation Specs β RSpec tests verifying YARD documentation generation and 100% doc coverage.
How it works
Architecture overview
cline-rb is organized in three layers that mirror the Cline filesystem layout:
graph TD
subgraph "1οΈβ£ Configuration Layer"
Config[Config] -->|reads| HD[~/.cline - Global]
Config -->|reads| PD[.cline - Project]
Config -->|merges| OH[OverlayHash]
end
subgraph "2οΈβ£ Data Layer"
Data[Data] -->|JSON files| GS[GlobalSettings]
Data -->|JSON files| GV[GlobalState]
Data -->|JSON files| ST[Secrets]
Data -->|JSON files| PS[Providers]
Data -->|JSON files| MS[McpSettings]
Data -->|directories| SS[Sessions]
Data -->|directories| TS[Tasks]
Data -->|directories| WS[Workspaces]
Data -->|text file| LG[Logs]
Data -->|JSON| MM[ClineModels]
end
subgraph "3οΈβ£ CLI Layer"
Cli[Cli] -->|PTY.spawn| CMD[Cline CLI Process]
Cli -->|parses| STDOUT[stdout]
Cli -->|monitors| MMON[Message Monitor]
end
Config --> Data
Data --> Cli
1οΈβ£ Auto-loading with Zeitwerk β‘
The gem uses Zeitwerk (lib/cline.rb line 3) for convention-over-configuration code loading. All classes under lib/cline/ are auto-discovered and loaded on demand β no explicit require statements needed beyond require 'cline'.
2οΈβ£ Schema & Serialization (the core pattern) π
Every domain object (settings, providers, sessions, tasks...) extends Cline::Schema < Shale::Mapper (schema.rb). This provides:
-
Declarative attributes β
attribute :my_field, :stringmirrors the JSON schema. -
Automatic camelCaseβsnake_case mapping β Cline stores
autoUpdateEnabled, Ruby getsauto_update_enabled. -
Extra attribute preservation β unknown JSON keys are stored in
extra_attributesand re-serialized back, maintaining round-trip fidelity.
3 mixin modules handle file persistence β classes include them to "come alive" from disk:
| Mixin | Purpose |
|---|---|
Serializable::Dir |
Opens from a directory (config, sessions, tasks, skills) |
Serializable::File |
Opens from a single file (logs) |
Serializable::ClineData |
Opens from a JSON file relative to a base directory (settings, secrets, providers) |
3οΈβ£ Config β the entry point πͺ
Cline::Config (config.rb) is the main access point. It provides 3 static factories:
-
Config.globalβ reads~/.cline -
Config.projectβ reads.cline(current directory) -
Config.mainβ merges global + project viaOverlayHash
Data (data.rb) lazily loads sub-objects (sessions, tasks, secrets...) with per-attribute caching (@variable ||= ...).
4οΈβ£ CLI interaction β PTY-based process control π€
Cline::Cli (cli.rb) wraps the Cline CLI binary using Ruby's PTY.spawn:
- ποΈ Builds command-line arguments from a declarative
COMMANDShash (global options + task/auth options). - π Real-time monitoring β streams stdout line-by-line via
reader.each_line, callingon_message/on_stdoutcallbacks. - π Interrupt support β kills the entire Cline process tree recursively (using
Sys::ProcTable). - π§ͺ Harvests results β parses the last JSON output message, detects completion/failure/interactive-question states.
- π Debug mode β when
Cline.config.debug == true, all CLI calls are logged with redacted output.
5οΈβ£ File monitoring β polling-based change detection π
Utils::FileMonitor (file_monitor.rb) polls a file's mtime in a background Thread.new at configurable intervals. This is used by:
-
Task#monitor_messagesβ detects new/updated task messages inui_messages.json -
Session#monitor_messagesβ detects new/updated session messages in*.messages.json -
Logs#monitorβ watchescline.logfor new log entries
Thread-safe detection compares the current message state (keyed by timestamp) to detect updates vs. new messages.
6οΈβ£ OverlayHash β merging global + project config π
OverlayHash (overlay_hash.rb) implements a layered hash pattern:
- Reads β iterates layers in priority order, returns first match.
- Writes β always go to the top (global) layer.
-
Used by β
Config#skillsmerges global + project skills transparently.
7οΈβ£ Skill management π
Skills (skill.rb) are directory-based, containing a SKILL.md file with YAML front matter. The gem:
- Parses YAML front matter using
front_matter_parser. - Detects
disabled: trueto determine enabled state. - Provides
enable/disablemethods that rewrite theSKILL.mdfront matter in-place. - Caches file discovery in an
@fileshash, lazily loaded.
8οΈβ£ Secret handling π
SecretString (secret_string.rb) wraps the secret_string gem (not Shale's native type) by providing custom cast, as_hash, and of_hash methods, so API keys are:
-
Redacted on inspect β
"sk-...********" - Stored securely β using secret_string's safe memory handling
Data flow (end-to-end) π―
sequenceDiagram
participant U as User Code
participant C as Config
participant D as Data
participant CLI as Cli
participant PTY as Cline Process
U->>C: Config.main
C->>D: Lazily loads data/*
U->>D: .sessions / .tasks / .secrets
D-->>U: Schema objects (JSON β Ruby)
U->>CLI: .task(prompt, on_message:)
CLI->>PTY: PTY.spawn(cmd, args...)
PTY-->>CLI: stdout lines (streaming)
CLI-->>U: on_message callbacks (real-time)
Note over CLI,PTY: On completion:
CLI->>CLI: Parse last JSON message
CLI-->>U: { stdout:, message:, status: }
U->>CLI: .interrupt
CLI->>PTY: Kill process tree (recursive)
Development
Prerequisites
- Ruby >= 3.1 (the CI runs against Ruby 3.4)
-
Bundler (
gem install bundler) -
Cline CLI installed and available on your
PATH(needed for integration tests β see cline.bot) -
Node.js (needed for
node-ptyon Windows β see.gitignore)
Clone the repository
git clone https://github.com/Muriel-Salvan/cline-rb.git
cd cline-rbInstall dependencies
bundle installFor local development you can create a Gemfile.local file (already provided in the project) that adds debug and ruby-lsp gems on top of the main dependencies. Bundler will pick it up automatically if present.
Note: This is a Ruby gem, so
Gemfile.lockis in.gitignoreand not committed.
Project structure
cline-rb/
βββ lib/ # Library source code (autoloaded via Zeitwerk)
β βββ cline.rb # Entry point β requires Zeitwerk loader
β βββ cline/ # Core domain modules
β β βββ version.rb # Gem version (Cline::VERSION)
β β βββ cli.rb # CLI interaction (task, auth, interrupt)
β β βββ config.rb # Configuration reader
β β βββ data.rb # Cline data directory
β β βββ schema.rb # Base JSON schema / serialization
β β βββ global_state/ # Global state sub-models (browser, toggles, featuresβ¦)
β β βββ serializable/ # File/directory serialization mixins
β β βββ utils/ # Utilities (file monitor, enumerable dir objects)
βββ spec/ # RSpec test suite
β βββ spec_helper.rb # Test bootstrap (SimpleCov, Zeitwerk loader, RSpec config)
β βββ cline_test/ # Test support (helpers, CLI stub, mock configs)
β βββ scenarios/ # High-level scenario specs (packaging, code quality, docs)
βββ tools/ # Compatibility check scripts
βββ .rspec # RSpec CLI options
βββ .rubocop.yml # RuboCop configuration
βββ .releaserc # semantic-release configuration
βββ cline-rb.gemspec # Gem specification
βββ Gemfile # Main Gemfile
βββ Gemfile.local # Local development Gemfile (adds debug, ruby-lsp)
Running tests
The project uses RSpec (~> 3.13) with SimpleCov enforcing 98% minimum code coverage.
# Run the full test suite
bundle exec rspec
# Run with documentation format (also used in CI)
bundle exec rspec --format documentation
# Run a specific spec file
bundle exec rspec spec/scenarios/packaging_spec.rb
# Run specs matching a tag
bundle exec rspec --tag focus
# Test debug mode β prints verbose test debug output
TEST_DEBUG=1 bundle exec rspecThe .rspec file configures --color and --require spec_helper by default.
Using the Cline CLI stub in other project's test cases
The cline-rb gem ships with a CliStub class that external projects can use to mock the Cline CLI in their own unit tests. It intercepts PTY.spawn calls and replaces them with a lightweight stub that simulates Cline CLI behaviour (stdout, sessions, tasks, logs, exit codes, etc.).
Example usage:
require "#{Gem.loaded_specs['cline-rb'].full_gem_path}/spec/cline_test/cli_stub"
it 'tests something in my project' do
# Setup the Cline CLI stub
cli_stub = ClineTest::CliStub.new
cli_stub.mock_commands(
{
log: {},
session: {
messages: [
{
ts: 100,
role: 'assistant',
content: [{ type: 'text', text: 'Assistant Output' }]
}
]
}
}
)
# Run the code that would have executed the Cline CLI
result = Cline::Cli.new(config: '.test-config').task 'A nice user prompt'
expect(result[:message].content.first.text).to eq 'Assistant Output'
# Use the stub to check what was called
expect(cli_stub.issued_commands.first[:command]).to eq ['--config', '.test-config', 'A nice user prompt']
endFor full API documentation of the CliStub class, see the ClineTest::CliStub YARD documentation.
Code linting
The project uses RuboCop (~> 1.86) with rubocop-rspec and rubocop-yard plugins.
# Run RuboCop linter
bundle exec rubocop
# Auto-correct fixable offenses
bundle exec rubocop -a
# Auto-correct all fixable offenses (including unsafe)
bundle exec rubocop -AThe custom RuboCop configuration (.rubocop.yml) relaxes several metrics (line length: 160, method length: 110, ABC size: 140, etc.) to match the project's coding style.
Code coverage
SimpleCov runs automatically with every test execution. After running the suite, open the report:
# Coverage report is generated at coverage/index.html
open coverage/index.html # macOS
start coverage/index.html # Windows
xdg-open coverage/index.html # LinuxBuilding the gem
Build the gem locally to verify packaging:
gem build cline-rb.gemspecThis produces cline-rb-<VERSION>.gem in the current directory. The version is defined in lib/cline/version.rb (currently 0.1.0).
Generating YARD documentation
The project uses YARD for API documentation. The test suite enforces 100% documented code.
# Generate docs
bundle exec yard doc
# Generate docs with strict mode (fails on warnings)
bundle exec yard doc --fail-on-warning
# Check documentation coverage
bundle exec yard stats --list-undocGenerated docs are output to the doc/ directory (gitignored).
Common development tasks
Adding a new runtime dependency:
Add the dependency to cline-rb.gemspec using spec.add_dependency, then run bundle install.
Adding development / test dependencies:
Add them to Gemfile (shared across the team) or Gemfile.local (local only), then run bundle install.
Running compatibility checks:
The tools/ directory contains scripts that verify cline-rb can read and write real Cline configuration data:
bundle exec ruby tools/compatibility_check_settings
bundle exec ruby tools/compatibility_check_tasks
bundle exec ruby tools/compatibility_check_workspacesPublishing a new release:
Releases are automated via semantic-release (configured in .releaserc). The CI pipeline (.github/workflows/continuous_integration.yml) handles version bumps, changelog generation, and gem publishing to Rubygems.org when commits are pushed to main.
To manually publish a pre-release version:
# 1. Update the version in lib/cline/version.rb
# 2. Build the gem
gem build cline-rb.gemspec
# 3. Push to Rubygems.org
gem push cline-rb-<VERSION>.gemNote: Rubygems.org requires MFA to be enabled for gem owners (see
spec.metadata['rubygems_mfa_required'] = 'true'in the gemspec).
Contributing
We welcome contributions to cline-rb! π Whether it's a bug report, a feature request, a documentation improvement, or a pull request, your help is appreciated.
π Issues
- π Bug reports β Open an issue on the GitHub issue tracker. Please include:
- A clear and descriptive title.
- Steps to reproduce the problem.
- The Ruby version, OS, and Cline CLI version you're using.
- Any relevant error output or stack traces.
- β¨ Feature requests β Describe what you'd like to see added, why it's useful, and (if applicable) how you envision the API.
- Before opening a new issue, please search the existing issues to avoid duplicates.
π΄ Fork & Pull Request workflow
- Fork the repository on GitHub.
-
Create a feature branch from
main:git checkout -b my-feature-branch. - Make your changes β follow the existing code style and conventions (RuboCop enforces them).
- Write or update tests β all changes should be covered by RSpec examples, and the minimum code coverage is 98% (enforced by SimpleCov).
-
Run the full test suite before committing:
Run a single spec file to iterate faster:
bundle exec rspecRun a specific example by line number:bundle exec rspec spec/my_spec.rbbundle exec rspec spec/my_spec.rb:42 -
Run the linter to ensure code quality:
The CI also runs RuboCop and enforces no offenses detected (check
bundle exec rubocopspec/scenarios/code_quality_spec.rb). -
Document your code β this project enforces 100% documented code via YARD (see
spec/scenarios/documentation_spec.rb). Run locally to verify:bundle exec yard doc --fail-on-warning bundle exec yard stats --list-undoc --fail-on-warning
- Commit your changes using clear, descriptive commit messages.
-
Push to your fork and open a Pull Request against the
mainbranch of the upstream repository.
β Pull Request guidelines
- Reference any related issue(s) in the PR description (e.g. "Fixes #123" or "Closes #456").
- Keep PRs focused β one feature or fix per PR is ideal.
- Ensure all checks pass on GitHub (when CI is configured): tests, linting, documentation coverage, and gem packaging.
- The project uses semantic-release for versioning. Commit messages should follow conventional commit format for automatic changelog generation.
π CI & Quality gates
The repository enforces the following checks (see spec/scenarios/):
| Check | Requirement |
|---|---|
| β Tests | All RSpec examples pass (run via bundle exec rspec) |
| π§Ή RuboCop | Zero offenses (rubocop must pass) |
| π YARD docs | 100% documented code |
| π¦ Gem packaging | The gem must build successfully (gem build cline-rb.gemspec) |
| π Code coverage | Minimum 98% coverage (SimpleCov) |
π License
By contributing, you agree that your contributions will be licensed under the BSD 3-Clause License.
π¬ Questions?
If you have any questions or need help, feel free to open a discussion or reach out to the maintainer.
Thank you for taking the time to contribute! π
License
The gem is available as open source under the terms of the BSD 3-Clause License.
Copyright (c) 2026, Muriel Salvan. All rights reserved.
See the LICENSE file for the full license text.