cline-rb 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +139 -0
  3. data/README.md +1216 -0
  4. data/TODO.md +2 -0
  5. data/lib/cline/cli.rb +373 -0
  6. data/lib/cline/config.rb +100 -0
  7. data/lib/cline/configuration.rb +23 -0
  8. data/lib/cline/data.rb +119 -0
  9. data/lib/cline/file_content.rb +33 -0
  10. data/lib/cline/global_settings.rb +17 -0
  11. data/lib/cline/global_state/api_providers.rb +48 -0
  12. data/lib/cline/global_state/auto_approval.rb +73 -0
  13. data/lib/cline/global_state/browser.rb +52 -0
  14. data/lib/cline/global_state/features.rb +56 -0
  15. data/lib/cline/global_state/general.rb +77 -0
  16. data/lib/cline/global_state/models.rb +127 -0
  17. data/lib/cline/global_state/toggles.rb +33 -0
  18. data/lib/cline/global_state/workspace.rb +41 -0
  19. data/lib/cline/global_state.rb +16 -0
  20. data/lib/cline/log.rb +288 -0
  21. data/lib/cline/logs.rb +136 -0
  22. data/lib/cline/mcp_settings.rb +30 -0
  23. data/lib/cline/model.rb +47 -0
  24. data/lib/cline/models.rb +11 -0
  25. data/lib/cline/overlay_hash.rb +125 -0
  26. data/lib/cline/providers.rb +59 -0
  27. data/lib/cline/schema.rb +144 -0
  28. data/lib/cline/secret_string.rb +83 -0
  29. data/lib/cline/secrets.rb +119 -0
  30. data/lib/cline/serializable/cline_data.rb +131 -0
  31. data/lib/cline/serializable/dir.rb +81 -0
  32. data/lib/cline/serializable/file.rb +106 -0
  33. data/lib/cline/session.rb +87 -0
  34. data/lib/cline/session_data.rb +154 -0
  35. data/lib/cline/session_message.rb +178 -0
  36. data/lib/cline/session_messages.rb +61 -0
  37. data/lib/cline/sessions.rb +30 -0
  38. data/lib/cline/skill.rb +148 -0
  39. data/lib/cline/skills.rb +8 -0
  40. data/lib/cline/task.rb +75 -0
  41. data/lib/cline/task_message.rb +247 -0
  42. data/lib/cline/task_messages.rb +11 -0
  43. data/lib/cline/tasks.rb +30 -0
  44. data/lib/cline/usage.rb +37 -0
  45. data/lib/cline/utils/enumerable_dir_objects.rb +103 -0
  46. data/lib/cline/utils/file.rb +71 -0
  47. data/lib/cline/utils/file_monitor.rb +56 -0
  48. data/lib/cline/utils/logger.rb +37 -0
  49. data/lib/cline/utils/os/linux.rb +43 -0
  50. data/lib/cline/utils/os/mingw32.rb +46 -0
  51. data/lib/cline/utils/os.rb +31 -0
  52. data/lib/cline/utils/schema.rb +290 -0
  53. data/lib/cline/version.rb +6 -0
  54. data/lib/cline/workspace.rb +25 -0
  55. data/lib/cline/workspace_settings.rb +29 -0
  56. data/lib/cline/workspaces.rb +8 -0
  57. data/lib/cline.rb +22 -0
  58. metadata +249 -0
data/README.md ADDED
@@ -0,0 +1,1216 @@
1
+ <div align="center">
2
+
3
+ # cline-rb
4
+
5
+ Ruby bindings for the [Cline](https://cline.bot/) AI assistant ecosystem β€” programmatically control the CLI, manage configurations, skills, sessions, tasks, models, logs, and secrets.
6
+
7
+ [![Build](https://github.com/Muriel-Salvan/cline-rb/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/Muriel-Salvan/cline-rb/actions/workflows/continuous_integration.yml)
8
+ [![Test Coverage](https://img.shields.io/codecov/c/gh/Muriel-Salvan/cline-rb)](https://codecov.io/gh/Muriel-Salvan/cline-rb)
9
+ [![GitHub stars](https://img.shields.io/github/stars/Muriel-Salvan/cline-rb)](https://github.com/Muriel-Salvan/cline-rb/stargazers)
10
+ [![License](https://img.shields.io/github/license/Muriel-Salvan/cline-rb)](LICENSE)
11
+ [![Gem Version](https://badge.fury.io/rb/cline-rb.svg)](https://badge.fury.io/rb/cline-rb)
12
+
13
+ </div>
14
+
15
+ **cline-rb** is a Ruby gem that gives you programmatic access to the [Cline](https://cline.bot/) AI assistant ecosystem. 🎯
16
+
17
+ Cline is an AI coding assistant that lives in your terminal. This library wraps its internals so you can:
18
+
19
+ * πŸ€– **Launch & control** the Cline CLI from Ruby β€” run tasks, authenticate providers, and handle interactive sessions via PTY.
20
+ * πŸ“‚ **Read & write configuration** β€” access global (`~/.cline`), project (`.cline`), and VSCode extension data with a clean Ruby API.
21
+ * 🧠 **Manage skills, tasks & sessions** β€” list, enable/disable skills, monitor real-time task/session messages, and track token usage & costs.
22
+ * πŸ” **Handle secrets safely** β€” store and retrieve API keys for dozens of AI providers (OpenAI, Anthropic, Gemini, DeepSeek, Groq…).
23
+ * πŸ“‹ **Parse logs & global state** β€” read structured log entries, inspect auto-approval rules, browser settings, feature flags, and MCP configurations.
24
+ * ⏱️ **Watch for file changes** β€” monitor config files, logs, and data files for live updates.
25
+
26
+ 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. ✨
27
+
28
+ ## Table of contents
29
+
30
+ - [Quick start](#quick-start)
31
+ - [Prerequisites](#prerequisites)
32
+ - [Installation](#installation)
33
+ - [Configuration (optional)](#configuration-optional)
34
+ - [Reading Cline data](#reading-cline-data)
35
+ - [Running a Cline task](#running-a-cline-task)
36
+ - [Running a task with a specific model](#running-a-task-with-a-specific-model)
37
+ - [Monitoring messages in real time](#monitoring-messages-in-real-time)
38
+ - [Handling interactive sessions (questions from the assistant)](#handling-interactive-sessions-questions-from-the-assistant)
39
+ - [Authentication](#authentication)
40
+ - [Reading logs](#reading-logs)
41
+ - [Interrupting a running task](#interrupting-a-running-task)
42
+ - [Requirements](#requirements)
43
+ - [Features](#features)
44
+ - [Public API](#public-api)
45
+ - [`Cline` β€” top-level module](#cline--top-level-module)
46
+ - [`Cline.configure`](#clineconfigure)
47
+ - [`Cline.config`](#clineconfig)
48
+ - [`Cline::VERSION`](#clineversion)
49
+ - [`Cline::Configuration` β€” gem configuration](#clineconfiguration--gem-configuration)
50
+ - [`Cline::Cli` β€” CLI wrapper](#clinecli--cli-wrapper)
51
+ - [`Cli#initialize(stdout_echo: false, **kwargs)`](#cliinitializestdout_echo-false-kwargs)
52
+ - [`Cli#task(prompt, on_message: nil, on_question: nil, monitoring_interval_secs: 1, **kwargs)`](#clitaskprompt-on_message-nil-on_question-nil-monitoring_interval_secs-1-kwargs)
53
+ - [`Cli#auth(**kwargs)`](#cliauthkwargs)
54
+ - [`Cli#interrupt`](#cliinterrupt)
55
+ - [`Cli#cline_pid` / `Cli#session`](#clicline_pid--clisession)
56
+ - [`Cline::Config` β€” Cline configuration directory](#clineconfig--cline-configuration-directory)
57
+ - [`Config.main`](#configmain)
58
+ - [`Config.global`](#configglobal)
59
+ - [`Config.project`](#configproject)
60
+ - [`Config#skills(create: false)`](#configskillscreate-false)
61
+ - [`Config#data(create: false)`](#configdatacreate-false)
62
+ - [`Config#cli(**kwargs)`](#configclikwargs)
63
+ - [`Config#refresh!`](#configrefresh)
64
+ - [`Cline::OverlayHash` β€” layered hash](#clineoverlayhash--layered-hash)
65
+ - [`Cline::Data` β€” Cline data directory](#clinedata--cline-data-directory)
66
+ - [`Data.vscode`](#datavscode)
67
+ - [`Cline::Skill` β€” individual skill](#clineskill--individual-skill)
68
+ - [`Cline::Skills` β€” collection of skills](#clineskills--collection-of-skills)
69
+ - [`Cline::Session` β€” a Cline session](#clinesession--a-cline-session)
70
+ - [`Cline::SessionData` β€” session metadata JSON](#clinesessiondata--session-metadata-json)
71
+ - [`Cline::SessionMessage` β€” individual session message](#clinesessionmessage--individual-session-message)
72
+ - [`Cline::SessionMessages` β€” session messages file](#clinesessionmessages--session-messages-file)
73
+ - [`Cline::Task` β€” a Cline task](#clinetask--a-cline-task)
74
+ - [`Cline::TaskMessage` β€” individual task message](#clinetaskmessage--individual-task-message)
75
+ - [`Cline::Logs` β€” log file](#clinelogs--log-file)
76
+ - [`Cline::Log` β€” log entry schema](#clinelog--log-entry-schema)
77
+ - [`Cline::Secrets` β€” API keys](#clinesecrets--api-keys)
78
+ - [`Cline::Model` β€” cached model info](#clinemodel--cached-model-info)
79
+ - [`Cline::GlobalState` β€” global Cline state](#clineglobalstate--global-cline-state)
80
+ - [`Cline::GlobalSettings` β€” global settings](#clineglobalsettings--global-settings)
81
+ - [`Cline::McpSettings` β€” MCP server settings](#clinemcpsettings--mcp-server-settings)
82
+ - [`Cline::Providers` β€” provider configurations](#clineproviders--provider-configurations)
83
+ - [`Cline::Workspace` β€” workspace data](#clineworkspace--workspace-data)
84
+ - [`Cline::Usage` β€” token usage statistics](#clineusage--token-usage-statistics)
85
+ - [`Cline::Schema` β€” base JSON schema class](#clineschema--base-json-schema-class)
86
+ - [`Cline::FileContent` β€” skill file content](#clinefilecontent--skill-file-content)
87
+ - [`Cline::Utils::FileMonitor` β€” file change monitor](#clineutilsfilemonitor--file-change-monitor)
88
+ - [Documentation](#documentation)
89
+ - [How it works](#how-it-works)
90
+ - [Architecture overview](#architecture-overview)
91
+ - [1️⃣ Auto-loading with Zeitwerk ⚑](#-auto-loading-with-zeitwerk-)
92
+ - [2️⃣ Schema & Serialization (the core pattern) πŸ”Œ](#-schema--serialization-the-core-pattern-)
93
+ - [3️⃣ Config β€” the entry point πŸšͺ](#-config--the-entry-point-)
94
+ - [4️⃣ CLI interaction β€” PTY-based process control πŸ€–](#-cli-interaction--pty-based-process-control-)
95
+ - [5️⃣ File monitoring β€” polling-based change detection πŸ‘€](#-file-monitoring--polling-based-change-detection-)
96
+ - [6️⃣ OverlayHash β€” merging global + project config πŸ”—](#-overlayhash--merging-global--project-config-)
97
+ - [7️⃣ Skill management πŸ“](#-skill-management-)
98
+ - [8️⃣ Secret handling πŸ”](#-secret-handling-)
99
+ - [Data flow (end-to-end) 🎯](#data-flow-end-to-end-)
100
+ - [Development](#development)
101
+ - [Prerequisites](#prerequisites-1)
102
+ - [Clone the repository](#clone-the-repository)
103
+ - [Install dependencies](#install-dependencies)
104
+ - [Project structure](#project-structure)
105
+ - [Running tests](#running-tests)
106
+ - [Code linting](#code-linting)
107
+ - [Code coverage](#code-coverage)
108
+ - [Building the gem](#building-the-gem)
109
+ - [Generating YARD documentation](#generating-yard-documentation)
110
+ - [Common development tasks](#common-development-tasks)
111
+ - [Contributing](#contributing)
112
+ - [πŸ“ Issues](#-issues)
113
+ - [🍴 Fork & Pull Request workflow](#-fork--pull-request-workflow)
114
+ - [βœ… Pull Request guidelines](#-pull-request-guidelines)
115
+ - [πŸ” CI & Quality gates](#-ci--quality-gates)
116
+ - [πŸ“„ License](#-license)
117
+ - [πŸ’¬ Questions?](#-questions)
118
+ - [License](#license)
119
+
120
+ ## Quick start
121
+
122
+ ### Prerequisites
123
+
124
+ - **Ruby >= 3.1**
125
+ - **Cline CLI** installed and configured on your system (see [cline.bot](https://cline.bot/))
126
+
127
+ ### Installation
128
+
129
+ Add the gem to your project's Gemfile:
130
+
131
+ ```bash
132
+ $ bundle add cline-rb
133
+ ```
134
+
135
+ Or install it globally:
136
+
137
+ ```bash
138
+ $ gem install cline-rb
139
+ ```
140
+
141
+ Then require the library:
142
+
143
+ ```ruby
144
+ require 'cline'
145
+ ```
146
+
147
+ ### Configuration (optional)
148
+
149
+ Enable debug logging to see CLI interactions:
150
+
151
+ ```ruby
152
+ Cline.configure do |config|
153
+ config.debug = true
154
+ end
155
+ ```
156
+
157
+ ### Reading Cline data
158
+
159
+ Access the global configuration (`~/.cline`), project config (`.cline`), or the merged main config:
160
+
161
+ ```ruby
162
+ # Main config (global + project merged)
163
+ config = Cline::Config.main
164
+
165
+ # Global config only
166
+ config = Cline::Config.global
167
+
168
+ # Project config only
169
+ config = Cline::Config.project
170
+
171
+ # List available skills
172
+ config.skills&.each do |name, skill|
173
+ puts "#{name}: #{skill.enabled? ? 'enabled' : 'disabled'}"
174
+ end
175
+
176
+ # List recent sessions
177
+ config.data.sessions.each do |id, session|
178
+ puts "[#{session.status}] #{session.model} - #{session.session_id}"
179
+ puts " Tokens: #{session.data.metadata.usage&.input_tokens} in / #{session.data.metadata.usage&.output_tokens} out" if session.data&.metadata&.usage
180
+ end
181
+
182
+ # List tasks
183
+ config.data.tasks.each do |id, task|
184
+ puts "Task #{id}: #{task.messages&.first&.text&.slice(0..80)}"
185
+ end
186
+
187
+ # Read stored API keys (returned as SecretString, redacted on inspect)
188
+ secrets = config.data.secrets
189
+ puts secrets.open_ai_api_key # => "sk-...********" (redacted)
190
+
191
+ # Read cached model information
192
+ models = config.data.cline_models
193
+ puts models.keys # e.g. ["gpt-4o", "claude-sonnet-4-6", ...]
194
+ ```
195
+
196
+ ### Running a Cline task
197
+
198
+ Use `Cline::Cli` to launch the Cline CLI, send a prompt, and collect the result:
199
+
200
+ ```ruby
201
+ cli = Cline::Cli.new(stdout_echo: true)
202
+
203
+ result = cli.task('Explain what Ruby symbols are in one sentence.')
204
+
205
+ puts result[:stdout] # Full CLI output
206
+ puts result[:message] # Last assistant message (SessionMessage object)
207
+ puts result[:status] # e.g. "completed"
208
+ puts result[:error] # Error log entry if status is "failed"
209
+ ```
210
+
211
+ #### Running a task with a specific model
212
+
213
+ ```ruby
214
+ result = cli.task(
215
+ 'Refactor this code snippet: puts "hello"',
216
+ model: 'claude-sonnet-4-6',
217
+ provider: 'anthropic'
218
+ )
219
+ ```
220
+
221
+ #### Monitoring messages in real time
222
+
223
+ ```ruby
224
+ result = cli.task(
225
+ 'Write a Ruby script that reads a CSV file.',
226
+ on_message: proc do |message, last, previous_version|
227
+ puts "[#{message.say}] #{message.to_human(limit: 120)}"
228
+ end
229
+ )
230
+ ```
231
+
232
+ #### Handling interactive sessions (questions from the assistant)
233
+
234
+ ```ruby
235
+ result = cli.task(
236
+ 'Create a new Rails API project',
237
+ on_question: proc do |question|
238
+ puts "Assistant asks: #{question.question}"
239
+ 'Use the default settings'
240
+ end
241
+ )
242
+ ```
243
+
244
+ ### Authentication
245
+
246
+ Set up a provider's API key:
247
+
248
+ ```ruby
249
+ cli = Cline::Cli.new
250
+ cli.auth(provider: 'openai-native', apikey: 'sk-...')
251
+ ```
252
+
253
+ ### Reading logs
254
+
255
+ ```ruby
256
+ logs = config.data.logs
257
+ logs.logs.each do |entry|
258
+ puts "[#{entry.time}] #{entry.msg}" if entry.is_a?(Cline::Log)
259
+ end
260
+
261
+ # Monitor logs in real time
262
+ logs.monitor(on_log: ->(log, _last) {
263
+ puts log.msg if log.is_a?(Cline::Log)
264
+ }) do
265
+ sleep 5
266
+ end
267
+ ```
268
+
269
+ ### Interrupting a running task
270
+
271
+ ```ruby
272
+ cli.interrupt # kills the running Cline process tree
273
+ ```
274
+
275
+ ## Requirements
276
+
277
+ - **Ruby** >= 3.1
278
+ - **Cline CLI** installed and available on your `PATH` (see [cline.bot](https://cline.bot/) for installation instructions)
279
+ - **Operating System**: Linux, macOS, or Windows (the gem includes platform-specific dependencies for each)
280
+ - **Bundler** (recommended) β€” to manage the gem dependency in your project via `bundle add cline-rb`
281
+
282
+ ## Features
283
+
284
+ **cline-rb** is a Ruby library that wraps the [Cline](https://cline.bot/) AI assistant ecosystem, providing programmatic access to its CLI, configuration, data, and runtime β€” all through a clean Ruby API. Key capabilities include:
285
+
286
+ * πŸ€– **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.
287
+ * πŸ“‚ **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.
288
+ * πŸ” **Secrets management** β€” Safely store and retrieve API keys for 30+ AI providers (OpenAI, Anthropic, Gemini, DeepSeek, Groq, Mistral, Together, etc.) using `SecretString` for secure in-memory handling.
289
+ * 🧠 **Skills management** β€” List, read, enable, and disable skills; inspect their YAML front matter and file contents.
290
+ * πŸ“‹ **Sessions & tasks** β€” Enumerate sessions and tasks from the data directory, read their structured messages, extract token usage, model info, and costs.
291
+ * ⏱️ **Real-time monitoring** β€” Subscribe to live updates on session messages, task messages, and log entries via file-change polling with callback-driven notifications.
292
+ * πŸ“ **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.
293
+ * βš™οΈ **Global state inspection** β€” Access auto-approval rules, browser/viewport settings, feature flags, model configurations, API provider settings, workspace roots, and toggle states.
294
+ * πŸ”— **MCP settings** β€” Read and write Model Context Protocol server configurations (type, URL, timeout, auto-approve tools).
295
+ * 🧩 **Provider config** β€” Read provider entries with API keys, model IDs, reasoning settings, and token sources.
296
+ * πŸ’Ύ **Cached model info** β€” Access cached model metadata (context windows, pricing, image/cache support, thinking config).
297
+ * πŸ“ **Workspace settings** β€” Read per-workspace toggle states for skills, rules, and workflows.
298
+ * πŸ”„ **Configuration merging** β€” `OverlayHash` provides a layered Hash interface combining global and project configs.
299
+ * πŸ“¦ **VSCode support** β€” Access the VSCode extension's Cline data directory directly.
300
+ * πŸ› οΈ **File change monitoring** β€” A background thread polls files at configurable intervals and fires callbacks on change β€” used across logs, sessions, tasks, and Cline data.
301
+ * πŸ—οΈ **Schema-driven JSON serialization** β€” Domain objects (via `Shale`) handle automatic camelCase↔snake_case mapping, preserve extra attributes, and support round-trip JSON serialization.
302
+ * πŸ’» **Cross-platform** β€” OS-agnostic helpers for Linux and Windows (MinGW) covering executable paths, home directories, app data, and process tree management.
303
+ * 🐞 **Debug support** β€” Configurable debug mode enables verbose PTY output logging, temporary directory snapshots, and ANSI-sanitized logs.
304
+
305
+ ## Public API
306
+
307
+ **cline-rb** is a Ruby library (no CLI executables). All public entry points are Ruby classes and methods under the `Cline` module.
308
+
309
+ ---
310
+
311
+ ### `Cline` β€” top-level module
312
+
313
+ #### `Cline.configure`
314
+
315
+ Configure the cline-rb gem behavior.
316
+
317
+ ```ruby
318
+ Cline.configure do |config|
319
+ config.debug = true
320
+ config.temp_dir_root = '/tmp/my_debug'
321
+ end
322
+ ```
323
+
324
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline.rb#L13-L15)
325
+
326
+ #### `Cline.config`
327
+
328
+ Get the current gem configuration (a `Cline::Configuration` instance).
329
+
330
+ ```ruby
331
+ Cline.config.debug #=> true or false
332
+ ```
333
+
334
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline.rb#L18-L20)
335
+
336
+ #### `Cline::VERSION`
337
+
338
+ Gem version constant.
339
+
340
+ ```ruby
341
+ Cline::VERSION #=> "0.1.0"
342
+ ```
343
+
344
+ ---
345
+
346
+ ### `Cline::Configuration` β€” gem configuration
347
+
348
+ | Attribute | Type | Description |
349
+ |---|---|---|
350
+ | `debug` | `Boolean` | Debug mode (defaults to `ENV['CLINE_DEBUG'] == '1'`) |
351
+ | `temp_dir_root` | `String` | Debug temp directory root (default: `.cline-rb/tmp`) |
352
+
353
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/configuration.rb)
354
+
355
+ ---
356
+
357
+ ### `Cline::Cli` β€” CLI wrapper
358
+
359
+ Wraps the Cline CLI process via PTY. Create an instance and use it to run tasks, authenticate providers, or interrupt running commands.
360
+
361
+ ```ruby
362
+ cli = Cline::Cli.new(stdout_echo: true)
363
+ ```
364
+
365
+ #### `Cli#initialize(stdout_echo: false, **kwargs)`
366
+
367
+ Constructor. `kwargs` can include global options like `verbose`, `cwd`, or `config`.
368
+
369
+ ```ruby
370
+ cli = Cline::Cli.new(stdout_echo: true, cwd: '/my/project')
371
+ ```
372
+
373
+ #### `Cli#task(prompt, on_message: nil, on_question: nil, monitoring_interval_secs: 1, **kwargs)`
374
+
375
+ Start a task by sending a prompt to the Cline CLI. Returns `{ stdout:, message:, status:, error: }`.
376
+
377
+ ```ruby
378
+ result = cli.task('Explain Ruby symbols in one sentence.', model: 'claude-sonnet-4-6')
379
+ puts result[:stdout] # Full CLI output
380
+ puts result[:message] # Last assistant SessionMessage
381
+ puts result[:status] # e.g. "completed"
382
+ ```
383
+
384
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/cli.rb#L129-L237)
385
+
386
+ #### `Cli#auth(**kwargs)`
387
+
388
+ Authenticate a provider with the CLI.
389
+
390
+ ```ruby
391
+ cli.auth(provider: 'openai-native', apikey: 'sk-...')
392
+ ```
393
+
394
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/cli.rb#L108-L110)
395
+
396
+ #### `Cli#interrupt`
397
+
398
+ Kill the currently running Cline process tree.
399
+
400
+ ```ruby
401
+ cli.interrupt
402
+ ```
403
+
404
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/cli.rb#L246-L259)
405
+
406
+ #### `Cli#cline_pid` / `Cli#session`
407
+
408
+ Access the current Cline process PID and the last session handled.
409
+
410
+ ---
411
+
412
+ ### `Cline::Config` β€” Cline configuration directory
413
+
414
+ Access global (`~/.cline`), project (`.cline`), or merged configuration.
415
+
416
+ #### `Config.main`
417
+
418
+ Merged global + project config.
419
+
420
+ ```ruby
421
+ config = Cline::Config.main
422
+ ```
423
+
424
+ #### `Config.global`
425
+
426
+ Global config only (`~/.cline`).
427
+
428
+ ```ruby
429
+ global = Cline::Config.global
430
+ ```
431
+
432
+ #### `Config.project`
433
+
434
+ Project config only (`.cline`).
435
+
436
+ ```ruby
437
+ project = Cline::Config.project
438
+ ```
439
+
440
+ #### `Config#skills(create: false)`
441
+
442
+ Get skills (returns an `OverlayHash` combining global + project skills).
443
+
444
+ ```ruby
445
+ config.skills&.each { |name, skill| puts name }
446
+ ```
447
+
448
+ #### `Config#data(create: false)`
449
+
450
+ Get the `Data` object for this config.
451
+
452
+ ```ruby
453
+ config.data.secrets #=> Cline::Secrets
454
+ config.data.logs #=> Cline::Logs
455
+ config.data.sessions #=> Cline::Sessions
456
+ ```
457
+
458
+ #### `Config#cli(**kwargs)`
459
+
460
+ Create a `Cli` instance attached to this config.
461
+
462
+ ```ruby
463
+ config.cli(stdout_echo: true).task('Hello')
464
+ ```
465
+
466
+ #### `Config#refresh!`
467
+
468
+ Clear cached objects to reload from disk.
469
+
470
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/config.rb)
471
+
472
+ ---
473
+
474
+ ### `Cline::OverlayHash` β€” layered hash
475
+
476
+ Merges several Hash-like objects, with priority order for reads and writes to the first layer only.
477
+
478
+ ```ruby
479
+ merged = Cline::OverlayHash.new(global_skills, project_skills)
480
+ merged['my_skill'] # retrieves from global first, falls back to project
481
+ ```
482
+
483
+ Supports `each`, `[]`, `key?`, `keys`, `values`, `size`, `empty?`, `to_h`, `==`.
484
+
485
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/overlay_hash.rb)
486
+
487
+ ---
488
+
489
+ ### `Cline::Data` β€” Cline data directory
490
+
491
+ Wraps the content of `~/.cline/data`. Provides accessors returning domain objects.
492
+
493
+ ```ruby
494
+ data = Cline::Config.global.data
495
+ ```
496
+
497
+ | Method | Returns |
498
+ |---|---|
499
+ | `#cline_models` | `Models` |
500
+ | `#global_settings` | `GlobalSettings` |
501
+ | `#global_state` | `GlobalState` |
502
+ | `#logs` | `Logs` |
503
+ | `#mcp_settings` | `McpSettings` |
504
+ | `#providers` | `Providers` |
505
+ | `#secrets` | `Secrets` |
506
+ | `#sessions` | `Sessions` |
507
+ | `#tasks` | `Tasks` |
508
+ | `#workspaces` | `Workspaces` |
509
+
510
+ #### `Data.vscode`
511
+
512
+ Get the VSCode Cline extension data directory.
513
+
514
+ ```ruby
515
+ vscode_data = Cline::Data.vscode
516
+ ```
517
+
518
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/data.rb)
519
+
520
+ ---
521
+
522
+ ### `Cline::Skill` β€” individual skill
523
+
524
+ Represents a skill directory.
525
+
526
+ ```ruby
527
+ skill = config.skills['my-skill']
528
+ skill.name #=> "my-skill"
529
+ skill.enabled? #=> true
530
+ skill.enable # Enable the skill
531
+ skill.disable # Disable the skill
532
+ skill.files #=> { "SKILL.md" => #<FileContent>, ... }
533
+ skill.save # Persist changes to disk
534
+ skill.yaml_front_matter #=> { name: "...", ... }
535
+ ```
536
+
537
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/skill.rb)
538
+
539
+ ---
540
+
541
+ ### `Cline::Skills` β€” collection of skills
542
+
543
+ Enumerable + Hash-like interface over skill directories.
544
+
545
+ ```ruby
546
+ skills = config.skills
547
+ skills.keys.each { |name| puts name }
548
+ skills.new('my-new-skill') # Create a new skill directory
549
+ ```
550
+
551
+ ---
552
+
553
+ ### `Cline::Session` β€” a Cline session
554
+
555
+ ```ruby
556
+ session = config.data.sessions['session-id']
557
+ session.status #=> "completed"
558
+ session.model #=> "deepseek/deepseek-v4-flash"
559
+ session.data #=> Cline::SessionData (metadata, usage, costs)
560
+ session.messages #=> Cline::SessionMessages
561
+ session.monitor_messages(on_message: ->(msg, last, prev) { puts msg.to_human }) do
562
+ sleep 5
563
+ end
564
+ ```
565
+
566
+ The `Session` delegates `SessionData` attributes directly: `session_id`, `status`, `model`, `provider`, `started_at`, `ended_at`, etc.
567
+
568
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/session.rb)
569
+
570
+ ---
571
+
572
+ ### `Cline::SessionData` β€” session metadata JSON
573
+
574
+ Contains `Metadata` (with `Usage`, `Checkpoint`, `CheckpointEntry`), version, session_id, source, pid, timestamps, status, model, provider, costs, etc.
575
+
576
+ ```ruby
577
+ session.data.metadata.usage.input_tokens #=> 1234
578
+ session.data.metadata.usage.total_cost #=> 0.0023
579
+ ```
580
+
581
+ ---
582
+
583
+ ### `Cline::SessionMessage` β€” individual session message
584
+
585
+ ```ruby
586
+ msg = session.messages.first
587
+ msg.role #=> "user" or "assistant"
588
+ msg.content #=> Array of MessageContent blocks
589
+ msg.timestamp #=> Time object
590
+ msg.usage #=> Usage struct (cost, tokens)
591
+ msg.to_human #=> human-friendly string (limited to 128 chars by default)
592
+ ```
593
+
594
+ Contains nested schemas: `ModelInfo`, `Metrics`, `MessageContent`, `ToolUseInput`.
595
+
596
+ ---
597
+
598
+ ### `Cline::SessionMessages` β€” session messages file
599
+
600
+ Contains `version`, `updated_at`, `agent`, `session_id`, `messages` (array of `SessionMessage`), `system_prompt`.
601
+
602
+ ---
603
+
604
+ ### `Cline::Task` β€” a Cline task
605
+
606
+ ```ruby
607
+ task = config.data.tasks['task-id']
608
+ task.messages #=> Cline::TaskMessages
609
+ task.monitor_messages(on_message: ->(msg, last, prev) { puts msg.to_human }) do
610
+ sleep 5
611
+ end
612
+ ```
613
+
614
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/task.rb)
615
+
616
+ ---
617
+
618
+ ### `Cline::TaskMessage` β€” individual task message
619
+
620
+ ```ruby
621
+ msg = task.messages.first
622
+ msg.ts #=> Integer timestamp
623
+ msg.type #=> "say" or "ask"
624
+ msg.say #=> "text", "api_req_started", "tool", etc.
625
+ msg.text #=> Raw text content
626
+ msg.timestamp #=> Time object
627
+ msg.usage #=> Usage struct (for api_req_started messages)
628
+ msg.to_human #=> Human-friendly description
629
+ ```
630
+
631
+ ---
632
+
633
+ ### `Cline::Logs` β€” log file
634
+
635
+ ```ruby
636
+ logs = config.data.logs
637
+ logs.logs.each { |entry| puts "[#{entry.time}] #{entry.msg}" if entry.is_a?(Cline::Log) }
638
+
639
+ # Add a log entry
640
+ logs << Cline::Log.new(level: 30, time: Time.now.iso8601, msg: 'Hello', pid: 1234, hostname: 'myhost', name: 'myapp', component: 'main')
641
+ logs.save
642
+
643
+ # Monitor logs in real-time
644
+ logs.monitor(on_log: ->(log, _last) { puts log.msg }) { sleep 5 }
645
+ ```
646
+
647
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/logs.rb)
648
+
649
+ ---
650
+
651
+ ### `Cline::Log` β€” log entry schema
652
+
653
+ 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`).
654
+
655
+ Nested schemas: `ErrorCause`, `ApiError`, `Error`, `Properties` (with ulid, api_provider, agent_id, team info, model info, platform info, etc.).
656
+
657
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/log.rb)
658
+
659
+ ---
660
+
661
+ ### `Cline::Secrets` β€” API keys
662
+
663
+ Access stored API keys for 30+ AI providers. All values are `SecretString` (redacted on inspect).
664
+
665
+ ```ruby
666
+ secrets = config.data.secrets
667
+ secrets.open_ai_api_key #=> #<SecretString ...>
668
+ secrets.anthropic_api_key
669
+ secrets.gemini_api_key
670
+ secrets.deep_seek_api_key
671
+ # ... and many more
672
+ ```
673
+
674
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/secrets.rb)
675
+
676
+ ---
677
+
678
+ ### `Cline::Model` β€” cached model info
679
+
680
+ ```ruby
681
+ model = config.data.cline_models['claude-sonnet-4-6']
682
+ model.name #=> "Claude Sonnet 4.6"
683
+ model.max_tokens #=> 8192
684
+ model.context_window #=> 200000
685
+ model.input_price #=> 3.0
686
+ model.output_price #=> 15.0
687
+ model.supports_images #=> true
688
+ model.supports_prompt_cache #=> true
689
+ model.thinking_config #=> Cline::Model::ThinkingConfig (with max_budget)
690
+ ```
691
+
692
+ ---
693
+
694
+ ### `Cline::GlobalState` β€” global Cline state
695
+
696
+ Access via `config.data.global_state`. Includes the following modules:
697
+
698
+ | Module | Provides |
699
+ |---|---|
700
+ | `AutoApproval` | `auto_approval_settings` (`AutoApprovalSettings` with actions toggles), `yolo_mode_toggled` |
701
+ | `Browser` | `browser_settings` (`BrowserSettings` with viewport, remote browser, chrome path) |
702
+ | `Workspace` | `workspace_roots` (array of `WorkspaceRoot`), `primary_root_index`, `multi_root_enabled` |
703
+ | `Features` | `focus_chain_settings`, `cline_web_tools_enabled`, `double_check_completion_enabled`, `subagents_enabled`, etc. |
704
+ | `ApiProviders` | `open_ai_headers`, `anthropic_base_url`, `request_timeout_ms`, `azure_api_version`, etc. |
705
+ | `General` | `welcome_view_completed`, `custom_prompt`, `cline_version`, `telemetry_setting`, `is_new_user`, etc. |
706
+ | `Toggles` | `remote_rules_toggles`, `global_skills_toggles`, `global_workflow_toggles`, etc. |
707
+ | `Models` | Mode-specific model configs (`act_mode_cline_model_id`, `plan_mode_cline_model_id`, etc.) |
708
+
709
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/global_state.rb)
710
+
711
+ ---
712
+
713
+ ### `Cline::GlobalSettings` β€” global settings
714
+
715
+ ```ruby
716
+ settings = config.data.global_settings
717
+ settings.auto_update_enabled #=> true/false
718
+ settings.telemetry_opt_out #=> true/false
719
+ settings.disabled_tools #=> ["tool1", "tool2"]
720
+ ```
721
+
722
+ ---
723
+
724
+ ### `Cline::McpSettings` β€” MCP server settings
725
+
726
+ ```ruby
727
+ mcp = config.data.mcp_settings
728
+ mcp.mcp_servers['my-server'].type #=> "sse" or "stdio"
729
+ mcp.mcp_servers['my-server'].url #=> "https://..."
730
+ mcp.mcp_servers['my-server'].timeout #=> 60
731
+ mcp.mcp_servers['my-server'].auto_approve #=> ["tool1", "tool2"]
732
+ mcp.mcp_servers['my-server'].disabled #=> false
733
+ ```
734
+
735
+ ---
736
+
737
+ ### `Cline::Providers` β€” provider configurations
738
+
739
+ ```ruby
740
+ providers = config.data.providers
741
+ providers.version #=> 1
742
+ providers.last_used_provider #=> "anthropic"
743
+ providers.providers['anthropic'].settings.api_key #=> SecretString
744
+ providers.providers['anthropic'].settings.model #=> "claude-sonnet-4-6"
745
+ providers.providers['anthropic'].updated_at #=> ISO 8601 timestamp
746
+ providers.providers['anthropic'].token_source #=> "manual"
747
+ ```
748
+
749
+ ---
750
+
751
+ ### `Cline::Workspace` β€” workspace data
752
+
753
+ ```ruby
754
+ workspace = config.data.workspaces['workspace-id']
755
+ workspace.settings.local_skills_toggles #=> { "skill1" => true }
756
+ workspace.settings.workflow_toggles #=> { "wf1" => false }
757
+ ```
758
+
759
+ ---
760
+
761
+ ### `Cline::Usage` β€” token usage statistics
762
+
763
+ A `Struct` with `cost`, `input_tokens`, `output_tokens`, `cache_read_tokens`, `cache_write_tokens`, `cline_model`.
764
+
765
+ ```ruby
766
+ usage = msg.usage
767
+ usage.context_tokens #=> input + output + cache reads + cache writes
768
+ usage.context_tokens_limit #=> model's context_window
769
+ ```
770
+
771
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/usage.rb)
772
+
773
+ ---
774
+
775
+ ### `Cline::Schema` β€” base JSON schema class
776
+
777
+ Base class for all domain objects. Provides JSON serialization with automatic camelCase↔snake_case mapping.
778
+
779
+ ```ruby
780
+ class MyConfig < Cline::Schema
781
+ attribute :my_field, :string
782
+ end
783
+
784
+ obj = MyConfig.of_hash({ "myField" => "value" })
785
+ obj.to_cline_json #=> '{"myField":"value"}'
786
+ obj.to_hash #=> { my_field: "value", extra_attributes: nil }
787
+ ```
788
+
789
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/schema.rb)
790
+
791
+ ---
792
+
793
+ ### `Cline::FileContent` β€” skill file content
794
+
795
+ ```ruby
796
+ file = skill.files['SKILL.md']
797
+ file.content #=> The raw file content
798
+ ```
799
+
800
+ ---
801
+
802
+ ### `Cline::Utils::FileMonitor` β€” file change monitor
803
+
804
+ Polls a file for changes on a background thread.
805
+
806
+ ```ruby
807
+ monitor = Cline::Utils::FileMonitor.new(
808
+ 'path/to/file.log',
809
+ on_change: ->(mtime) { puts "File changed at #{mtime}" },
810
+ monitoring_interval_secs: 2
811
+ )
812
+ monitor.start
813
+ # ... later ...
814
+ monitor.stop
815
+ ```
816
+
817
+ [View source](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/utils/file_monitor.rb)
818
+
819
+ ## Documentation
820
+
821
+ - **[README.md](https://github.com/Muriel-Salvan/cline-rb/blob/main/README.md)** β€” Main project overview, installation, development and contributing instructions.
822
+ - **[RubyDoc.info](https://www.rubydoc.info/gems/cline-rb)** β€” Auto-generated API documentation for the `cline-rb` gem, with detailed class and method references from YARD doc comments.
823
+ - **[YARD API Documentation](https://github.com/Muriel-Salvan/cline-rb)** β€” Run `bundle exec yard doc` locally to generate HTML docs in the `doc/` directory. The project enforces 100% documented code via CI.
824
+ - **[CHANGELOG.md](https://github.com/Muriel-Salvan/cline-rb/blob/main/CHANGELOG.md)** β€” Release history, auto-generated by semantic-release.
825
+ - **[TODO.md](https://github.com/Muriel-Salvan/cline-rb/blob/main/TODO.md)** β€” Project roadmap and pending development tasks.
826
+ - **[LICENSE](https://github.com/Muriel-Salvan/cline-rb/blob/main/LICENSE)** β€” BSD 3-Clause License.
827
+ - **[GitHub Repository](https://github.com/Muriel-Salvan/cline-rb)** β€” Source code, issue tracker, and pull requests.
828
+ - **[Continuous Integration](https://github.com/Muriel-Salvan/cline-rb/blob/main/.github/workflows/continuous_integration.yml)** β€” CI workflow definition running tests, linting, and release automation.
829
+ - **[Documentation Specs](https://github.com/Muriel-Salvan/cline-rb/blob/main/spec/scenarios/documentation_spec.rb)** β€” RSpec tests verifying YARD documentation generation and 100% doc coverage.
830
+
831
+ ## How it works
832
+
833
+ ### Architecture overview
834
+
835
+ **cline-rb** is organized in **three layers** that mirror the Cline filesystem layout:
836
+
837
+ ```mermaid
838
+ graph TD
839
+ subgraph "1️⃣ Configuration Layer"
840
+ Config[Config] -->|reads| HD[~/.cline - Global]
841
+ Config -->|reads| PD[.cline - Project]
842
+ Config -->|merges| OH[OverlayHash]
843
+ end
844
+
845
+ subgraph "2️⃣ Data Layer"
846
+ Data[Data] -->|JSON files| GS[GlobalSettings]
847
+ Data -->|JSON files| GV[GlobalState]
848
+ Data -->|JSON files| ST[Secrets]
849
+ Data -->|JSON files| PS[Providers]
850
+ Data -->|JSON files| MS[McpSettings]
851
+ Data -->|directories| SS[Sessions]
852
+ Data -->|directories| TS[Tasks]
853
+ Data -->|directories| WS[Workspaces]
854
+ Data -->|text file| LG[Logs]
855
+ Data -->|JSON| MM[ClineModels]
856
+ end
857
+
858
+ subgraph "3️⃣ CLI Layer"
859
+ Cli[Cli] -->|PTY.spawn| CMD[Cline CLI Process]
860
+ Cli -->|parses| STDOUT[stdout]
861
+ Cli -->|monitors| MMON[Message Monitor]
862
+ end
863
+
864
+ Config --> Data
865
+ Data --> Cli
866
+ ```
867
+
868
+ ### 1️⃣ Auto-loading with Zeitwerk ⚑
869
+
870
+ The gem uses [Zeitwerk](https://github.com/fxn/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'`.
871
+
872
+ ### 2️⃣ Schema & Serialization (the core pattern) πŸ”Œ
873
+
874
+ Every domain object (settings, providers, sessions, tasks...) extends `Cline::Schema < Shale::Mapper` ([schema.rb](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/schema.rb)). This provides:
875
+
876
+ - **Declarative attributes** β€” `attribute :my_field, :string` mirrors the JSON schema.
877
+ - **Automatic camelCase↔snake_case mapping** β€” Cline stores `autoUpdateEnabled`, Ruby gets `auto_update_enabled`.
878
+ - **Extra attribute preservation** β€” unknown JSON keys are stored in `extra_attributes` and re-serialized back, maintaining round-trip fidelity.
879
+
880
+ 3 mixin modules handle file persistence β€” classes include them to "come alive" from disk:
881
+
882
+ | Mixin | Purpose |
883
+ |---|---|
884
+ | `Serializable::Dir` | Opens from a **directory** (config, sessions, tasks, skills) |
885
+ | `Serializable::File` | Opens from a single **file** (logs) |
886
+ | `Serializable::ClineData` | Opens from a **JSON file** relative to a base directory (settings, secrets, providers) |
887
+
888
+ ### 3️⃣ Config β€” the entry point πŸšͺ
889
+
890
+ `Cline::Config` ([config.rb](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/config.rb)) is the main access point. It provides 3 static factories:
891
+
892
+ - `Config.global` β€” reads `~/.cline`
893
+ - `Config.project` β€” reads `.cline` (current directory)
894
+ - `Config.main` β€” merges global + project via `OverlayHash`
895
+
896
+ `Data` ([data.rb](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/data.rb)) lazily loads sub-objects (sessions, tasks, secrets...) with per-attribute caching (`@variable ||= ...`).
897
+
898
+ ### 4️⃣ CLI interaction β€” PTY-based process control πŸ€–
899
+
900
+ `Cline::Cli` ([cli.rb](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/cli.rb)) wraps the Cline CLI binary using Ruby's `PTY.spawn`:
901
+
902
+ - πŸ—οΈ Builds command-line arguments from a declarative `COMMANDS` hash (global options + task/auth options).
903
+ - πŸ”„ **Real-time monitoring** β€” streams stdout line-by-line via `reader.each_line`, calling `on_message`/`on_stdout` callbacks.
904
+ - πŸ›‘ **Interrupt support** β€” kills the entire Cline process tree recursively (using `Sys::ProcTable`).
905
+ - πŸ§ͺ **Harvests results** β€” parses the last JSON output message, detects completion/failure/interactive-question states.
906
+ - πŸ› **Debug mode** β€” when `Cline.config.debug == true`, all CLI calls are logged with redacted output.
907
+
908
+ ### 5️⃣ File monitoring β€” polling-based change detection πŸ‘€
909
+
910
+ `Utils::FileMonitor` ([file_monitor.rb](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/utils/file_monitor.rb)) polls a file's mtime in a background `Thread.new` at configurable intervals. This is used by:
911
+ - `Task#monitor_messages` β€” detects new/updated task messages in `ui_messages.json`
912
+ - `Session#monitor_messages` β€” detects new/updated session messages in `*.messages.json`
913
+ - `Logs#monitor` β€” watches `cline.log` for new log entries
914
+
915
+ Thread-safe detection compares the current message state (keyed by timestamp) to detect updates vs. new messages.
916
+
917
+ ### 6️⃣ OverlayHash β€” merging global + project config πŸ”—
918
+
919
+ `OverlayHash` ([overlay_hash.rb](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/overlay_hash.rb)) implements a **layered hash pattern**:
920
+
921
+ - **Reads** β€” iterates layers in priority order, returns first match.
922
+ - **Writes** β€” always go to the top (global) layer.
923
+ - **Used by** β€” `Config#skills` merges global + project skills transparently.
924
+
925
+ ### 7️⃣ Skill management πŸ“
926
+
927
+ Skills ([skill.rb](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/skill.rb)) are directory-based, containing a `SKILL.md` file with YAML front matter. The gem:
928
+ - Parses YAML front matter using `front_matter_parser`.
929
+ - Detects `disabled: true` to determine enabled state.
930
+ - Provides `enable`/`disable` methods that rewrite the `SKILL.md` front matter in-place.
931
+ - Caches file discovery in an `@files` hash, lazily loaded.
932
+
933
+ ### 8️⃣ Secret handling πŸ”
934
+
935
+ `SecretString` ([secret_string.rb](https://github.com/Muriel-Salvan/cline-rb/blob/main/lib/cline/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:
936
+ - **Redacted on inspect** β€” `"sk-...********"`
937
+ - **Stored securely** β€” using secret_string's safe memory handling
938
+
939
+ ### Data flow (end-to-end) 🎯
940
+
941
+ ```mermaid
942
+ sequenceDiagram
943
+ participant U as User Code
944
+ participant C as Config
945
+ participant D as Data
946
+ participant CLI as Cli
947
+ participant PTY as Cline Process
948
+
949
+ U->>C: Config.main
950
+ C->>D: Lazily loads data/*
951
+ U->>D: .sessions / .tasks / .secrets
952
+ D-->>U: Schema objects (JSON β†’ Ruby)
953
+
954
+ U->>CLI: .task(prompt, on_message:)
955
+ CLI->>PTY: PTY.spawn(cmd, args...)
956
+ PTY-->>CLI: stdout lines (streaming)
957
+ CLI-->>U: on_message callbacks (real-time)
958
+ Note over CLI,PTY: On completion:
959
+ CLI->>CLI: Parse last JSON message
960
+ CLI-->>U: { stdout:, message:, status: }
961
+
962
+ U->>CLI: .interrupt
963
+ CLI->>PTY: Kill process tree (recursive)
964
+ ```
965
+
966
+ ## Development
967
+
968
+ ### Prerequisites
969
+
970
+ - **Ruby >= 3.1** (the CI runs against Ruby 3.4)
971
+ - **Bundler** (`gem install bundler`)
972
+ - **Cline CLI** installed and available on your `PATH` (needed for integration tests β€” see [cline.bot](https://cline.bot/))
973
+ - **Node.js** (needed for `node-pty` on Windows β€” see `.gitignore`)
974
+
975
+ ### Clone the repository
976
+
977
+ ```bash
978
+ git clone https://github.com/Muriel-Salvan/cline-rb.git
979
+ cd cline-rb
980
+ ```
981
+
982
+ ### Install dependencies
983
+
984
+ ```bash
985
+ bundle install
986
+ ```
987
+
988
+ For 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.
989
+
990
+ > **Note:** This is a Ruby gem, so `Gemfile.lock` is in `.gitignore` and not committed.
991
+
992
+ ### Project structure
993
+
994
+ ```
995
+ cline-rb/
996
+ β”œβ”€β”€ lib/ # Library source code (autoloaded via Zeitwerk)
997
+ β”‚ β”œβ”€β”€ cline.rb # Entry point β€” requires Zeitwerk loader
998
+ β”‚ β”œβ”€β”€ cline/ # Core domain modules
999
+ β”‚ β”‚ β”œβ”€β”€ version.rb # Gem version (Cline::VERSION)
1000
+ β”‚ β”‚ β”œβ”€β”€ cli.rb # CLI interaction (task, auth, interrupt)
1001
+ β”‚ β”‚ β”œβ”€β”€ config.rb # Configuration reader
1002
+ β”‚ β”‚ β”œβ”€β”€ data.rb # Cline data directory
1003
+ β”‚ β”‚ β”œβ”€β”€ schema.rb # Base JSON schema / serialization
1004
+ β”‚ β”‚ β”œβ”€β”€ global_state/ # Global state sub-models (browser, toggles, features…)
1005
+ β”‚ β”‚ β”œβ”€β”€ serializable/ # File/directory serialization mixins
1006
+ β”‚ β”‚ └── utils/ # Utilities (file monitor, enumerable dir objects)
1007
+ β”œβ”€β”€ spec/ # RSpec test suite
1008
+ β”‚ β”œβ”€β”€ spec_helper.rb # Test bootstrap (SimpleCov, Zeitwerk loader, RSpec config)
1009
+ β”‚ β”œβ”€β”€ cline_test/ # Test support (helpers, CLI stub, mock configs)
1010
+ β”‚ └── scenarios/ # High-level scenario specs (packaging, code quality, docs)
1011
+ β”œβ”€β”€ tools/ # Compatibility check scripts
1012
+ β”œβ”€β”€ .rspec # RSpec CLI options
1013
+ β”œβ”€β”€ .rubocop.yml # RuboCop configuration
1014
+ β”œβ”€β”€ .releaserc # semantic-release configuration
1015
+ β”œβ”€β”€ cline-rb.gemspec # Gem specification
1016
+ β”œβ”€β”€ Gemfile # Main Gemfile
1017
+ └── Gemfile.local # Local development Gemfile (adds debug, ruby-lsp)
1018
+ ```
1019
+
1020
+ ### Running tests
1021
+
1022
+ The project uses **RSpec** (~> 3.13) with **SimpleCov** enforcing **98% minimum code coverage**.
1023
+
1024
+ ```bash
1025
+ # Run the full test suite
1026
+ bundle exec rspec
1027
+
1028
+ # Run with documentation format (also used in CI)
1029
+ bundle exec rspec --format documentation
1030
+
1031
+ # Run a specific spec file
1032
+ bundle exec rspec spec/scenarios/packaging_spec.rb
1033
+
1034
+ # Run specs matching a tag
1035
+ bundle exec rspec --tag focus
1036
+
1037
+ # Test debug mode β€” prints verbose test debug output
1038
+ TEST_DEBUG=1 bundle exec rspec
1039
+ ```
1040
+
1041
+ The `.rspec` file configures `--color` and `--require spec_helper` by default.
1042
+
1043
+ ### Code linting
1044
+
1045
+ The project uses **RuboCop** (~> 1.86) with `rubocop-rspec` and `rubocop-yard` plugins.
1046
+
1047
+ ```bash
1048
+ # Run RuboCop linter
1049
+ bundle exec rubocop
1050
+
1051
+ # Auto-correct fixable offenses
1052
+ bundle exec rubocop -a
1053
+
1054
+ # Auto-correct all fixable offenses (including unsafe)
1055
+ bundle exec rubocop -A
1056
+ ```
1057
+
1058
+ The 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.
1059
+
1060
+ ### Code coverage
1061
+
1062
+ SimpleCov runs automatically with every test execution. After running the suite, open the report:
1063
+
1064
+ ```bash
1065
+ # Coverage report is generated at coverage/index.html
1066
+ open coverage/index.html # macOS
1067
+ start coverage/index.html # Windows
1068
+ xdg-open coverage/index.html # Linux
1069
+ ```
1070
+
1071
+ ### Building the gem
1072
+
1073
+ Build the gem locally to verify packaging:
1074
+
1075
+ ```bash
1076
+ gem build cline-rb.gemspec
1077
+ ```
1078
+
1079
+ This produces `cline-rb-<VERSION>.gem` in the current directory. The version is defined in `lib/cline/version.rb` (currently `0.1.0`).
1080
+
1081
+ ### Generating YARD documentation
1082
+
1083
+ The project uses **YARD** for API documentation. The test suite enforces **100% documented code**.
1084
+
1085
+ ```bash
1086
+ # Generate docs
1087
+ bundle exec yard doc
1088
+
1089
+ # Generate docs with strict mode (fails on warnings)
1090
+ bundle exec yard doc --fail-on-warning
1091
+
1092
+ # Check documentation coverage
1093
+ bundle exec yard stats --list-undoc
1094
+ ```
1095
+
1096
+ Generated docs are output to the `doc/` directory (gitignored).
1097
+
1098
+ ### Common development tasks
1099
+
1100
+ **Adding a new runtime dependency:**
1101
+
1102
+ Add the dependency to `cline-rb.gemspec` using `spec.add_dependency`, then run `bundle install`.
1103
+
1104
+ **Adding development / test dependencies:**
1105
+
1106
+ Add them to `Gemfile` (shared across the team) or `Gemfile.local` (local only), then run `bundle install`.
1107
+
1108
+ **Running compatibility checks:**
1109
+
1110
+ The `tools/` directory contains scripts that verify cline-rb can read and write real Cline configuration data:
1111
+
1112
+ ```bash
1113
+ bundle exec ruby tools/compatibility_check_settings
1114
+ bundle exec ruby tools/compatibility_check_tasks
1115
+ bundle exec ruby tools/compatibility_check_workspaces
1116
+ ```
1117
+
1118
+ **Publishing a new release:**
1119
+
1120
+ 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](https://rubygems.org) when commits are pushed to `main`.
1121
+
1122
+ To manually publish a pre-release version:
1123
+
1124
+ ```bash
1125
+ # 1. Update the version in lib/cline/version.rb
1126
+ # 2. Build the gem
1127
+ gem build cline-rb.gemspec
1128
+ # 3. Push to Rubygems.org
1129
+ gem push cline-rb-<VERSION>.gem
1130
+ ```
1131
+
1132
+ > Note: Rubygems.org requires **MFA** to be enabled for gem owners (see `spec.metadata['rubygems_mfa_required'] = 'true'` in the gemspec).
1133
+
1134
+ ## Contributing
1135
+
1136
+ 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.
1137
+
1138
+ ### πŸ“ Issues
1139
+
1140
+ - πŸ› **Bug reports** β€” Open an issue on the [GitHub issue tracker](https://github.com/Muriel-Salvan/cline-rb/issues). Please include:
1141
+ - A clear and descriptive title.
1142
+ - Steps to reproduce the problem.
1143
+ - The Ruby version, OS, and Cline CLI version you're using.
1144
+ - Any relevant error output or stack traces.
1145
+ - ✨ **Feature requests** β€” Describe what you'd like to see added, why it's useful, and (if applicable) how you envision the API.
1146
+ - Before opening a new issue, please search the [existing issues](https://github.com/Muriel-Salvan/cline-rb/issues) to avoid duplicates.
1147
+
1148
+ ### 🍴 Fork & Pull Request workflow
1149
+
1150
+ 1. **Fork** the repository on GitHub.
1151
+ 2. **Create a feature branch** from `main`: `git checkout -b my-feature-branch`.
1152
+ 3. **Make your changes** β€” follow the existing code style and conventions (RuboCop enforces them).
1153
+ 4. **Write or update tests** β€” all changes should be covered by RSpec examples, and the **minimum code coverage is 98%** (enforced by SimpleCov).
1154
+ 5. **Run the full test suite** before committing:
1155
+ ```bash
1156
+ bundle exec rspec
1157
+ ```
1158
+ Run a single spec file to iterate faster:
1159
+ ```bash
1160
+ bundle exec rspec spec/my_spec.rb
1161
+ ```
1162
+ Run a specific example by line number:
1163
+ ```bash
1164
+ bundle exec rspec spec/my_spec.rb:42
1165
+ ```
1166
+ 6. **Run the linter** to ensure code quality:
1167
+ ```bash
1168
+ bundle exec rubocop
1169
+ ```
1170
+ The CI also runs RuboCop and enforces **no offenses detected** (check [`spec/scenarios/code_quality_spec.rb`](https://github.com/Muriel-Salvan/cline-rb/blob/main/spec/scenarios/code_quality_spec.rb)).
1171
+ 7. **Document your code** β€” this project enforces **100% documented code** via YARD (see [`spec/scenarios/documentation_spec.rb`](https://github.com/Muriel-Salvan/cline-rb/blob/main/spec/scenarios/documentation_spec.rb)). Run locally to verify:
1172
+ ```bash
1173
+ bundle exec yard doc --fail-on-warning
1174
+ bundle exec yard stats --list-undoc --fail-on-warning
1175
+ ```
1176
+ 8. **Commit your changes** using clear, descriptive commit messages.
1177
+ 9. **Push** to your fork and open a **Pull Request** against the `main` branch of the upstream repository.
1178
+
1179
+ ### βœ… Pull Request guidelines
1180
+
1181
+ - Reference any related issue(s) in the PR description (e.g. "Fixes #123" or "Closes #456").
1182
+ - Keep PRs focused β€” one feature or fix per PR is ideal.
1183
+ - Ensure **all checks pass** on GitHub (when CI is configured): tests, linting, documentation coverage, and gem packaging.
1184
+ - The project uses [**semantic-release**](https://github.com/semantic-release/semantic-release) for versioning. Commit messages should follow conventional commit format for automatic changelog generation.
1185
+
1186
+ ### πŸ” CI & Quality gates
1187
+
1188
+ The repository enforces the following checks (see [`spec/scenarios/`](https://github.com/Muriel-Salvan/cline-rb/tree/main/spec/scenarios)):
1189
+
1190
+ | Check | Requirement |
1191
+ |---|---|
1192
+ | βœ… **Tests** | All RSpec examples pass (run via `bundle exec rspec`) |
1193
+ | 🧹 **RuboCop** | Zero offenses (`rubocop` must pass) |
1194
+ | πŸ“– **YARD docs** | 100% documented code |
1195
+ | πŸ“¦ **Gem packaging** | The gem must build successfully (`gem build cline-rb.gemspec`) |
1196
+ | πŸ“Š **Code coverage** | Minimum **98%** coverage (SimpleCov) |
1197
+
1198
+ ### πŸ“„ License
1199
+
1200
+ By contributing, you agree that your contributions will be licensed under the [BSD 3-Clause License](https://github.com/Muriel-Salvan/cline-rb/blob/main/LICENSE).
1201
+
1202
+ ### πŸ’¬ Questions?
1203
+
1204
+ If you have any questions or need help, feel free to open a [discussion](https://github.com/Muriel-Salvan/cline-rb/discussions) or reach out to the maintainer.
1205
+
1206
+ ---
1207
+
1208
+ *Thank you for taking the time to contribute! πŸ™Œ*
1209
+
1210
+ ## License
1211
+
1212
+ The gem is available as open source under the terms of the [BSD 3-Clause License](LICENSE).
1213
+
1214
+ Copyright (c) 2026, Muriel Salvan. All rights reserved.
1215
+
1216
+ See the [LICENSE](LICENSE) file for the full license text.