composable_agents 1.0.0 โ†’ 1.0.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72f4795399719375c8c55c7efea5b4e0ddb18918b1d72473040d39cd2c1b7ecd
4
- data.tar.gz: a70a660e7ee3ad362976336b8bf75abac96e931f36bfcdca7cf755853326090a
3
+ metadata.gz: 3f9996134748aa3bf3ad0893b706a99a9c4611adcf5455591ef427230cebd64f
4
+ data.tar.gz: 283f258cf24bf3dfca6575a0fe29f5cee5d8096de2cf0b40c73ac6060a78f76d
5
5
  SHA512:
6
- metadata.gz: af2e51d1e5e4f10641e162f081d2daee958cc6571f35f60ea7266eb04470a2d3894a8b5bfc0744d5f0077b010bb43e76a61556823e2edcdf920c578f8b7b915c
7
- data.tar.gz: f63f287abc216032f4abc77b0c60906b59e0cd21fee222b3dfc606d96fc760c09d24d8b56f94ccf5660b6b953e7e06b3edf2171fadea1ed278dcd5a226f897db
6
+ metadata.gz: e7009799753fdd7216b8ef26c7756da633aa17665e4ca9ae6fc4f586fcc2501c2eade1e2ab7ac906fba5ddfe42888f4317cdd1611e20649d93eb3ab624ca58bc
7
+ data.tar.gz: 5f374253ab859627a3c3a745f12b003e53149c3a358808990d67daef639013c0f57b3cbb6687fc2366d784937b09d932afaac893887f1d14e9a9ea3529298d3e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # [v1.0.1](https://github.com/Muriel-Salvan/composable_agents/compare/v1.0.0...v1.0.1) (2026-07-01 16:37:12)
2
+
3
+ ### Patches
4
+
5
+ * [docs: expand README with full documentation, usage examples, and badges](https://github.com/Muriel-Salvan/composable_agents/commit/d7ba08fe6408248a2a00338a6638135ba9626116)
6
+
1
7
  # [v0.0.1](https://github.com/Muriel-Salvan/composable_agents/compare/...v0.0.1) (2026-07-01 15:49:59)
2
8
 
3
9
  ### Patches
data/README.md CHANGED
@@ -1,29 +1,869 @@
1
+ <div align="center">
2
+
1
3
  # composable_agents
2
4
 
3
- Composable AI agents framework.
5
+ A Ruby framework for building **composable, prompt-driven AI agent pipelines** โ€” mix, match, and orchestrate agents into reusable workflows.
6
+
7
+ [![Build](https://github.com/Muriel-Salvan/composable_agents/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/Muriel-Salvan/composable_agents/actions/workflows/continuous_integration.yml)
8
+ [![Test Coverage](https://img.shields.io/codecov/c/gh/Muriel-Salvan/composable_agents)](https://codecov.io/gh/Muriel-Salvan/composable_agents)
9
+ [![GitHub stars](https://img.shields.io/github/stars/Muriel-Salvan/composable_agents)](https://github.com/Muriel-Salvan/composable_agents/stargazers)
10
+ [![License](https://img.shields.io/github/license/Muriel-Salvan/composable_agents)](LICENSE)
11
+ [![Gem Version](https://img.shields.io/gem/v/composable_agents)](https://rubygems.org/gems/composable_agents)
12
+ [![Gem Total Downloads](https://img.shields.io/gem/dt/composable_agents)](https://rubygems.org/gems/composable_agents)
13
+
14
+ </div>
15
+
16
+ **composable_agents** is a Ruby gem that lets you build modular AI agent pipelines ๐Ÿงฉ โ€” compose simple agents together into complex, resumable workflows.
17
+
18
+ Think of it as **LEGOยฎ for AI agents**: each agent is a self-contained unit that takes input artifacts, processes them (via an LLM, custom Ruby code, or a sub-agent), and produces output artifacts. You can:
19
+
20
+ - ๐Ÿง  **Create prompt-driven agents** with role, objective, instructions, and constraints
21
+ - ๐Ÿ”„ **Chain agents together** so the output of one becomes the input of another
22
+ - ๐Ÿ“ฆ **Define typed artifact contracts** with validation for inputs/outputs
23
+ - ๐Ÿ’พ **Resume interrupted runs** โ€” long workflows keep their state between executions
24
+ - ๐Ÿ—ฃ๏ธ **Let agents ask users questions** when they need clarification
25
+ - ๐ŸŽฏ **Integrate with multiple LLM backends** via [cline-rb](https://github.com/Muriel-Salvan/cline-rb) or [ai-agents](https://github.com/nicbarker/ai-agents)
26
+ - ๐Ÿ“ **Use flexible prompt rendering** (Markdown, or heavy Markdown with structured outputs)
27
+
28
+ Whether you're building a code review assistant, a document summarizer, or a multi-step research pipeline, composable_agents gives you the building blocks to design, test, and run AI agent systems โ€” all from Ruby.
29
+
30
+ ## Table of contents
31
+
32
+ - [Quick start](#quick-start)
33
+ - [Installation](#installation)
34
+ - [Basic usage: create a composed pipeline of agents](#basic-usage-create-a-composed-pipeline-of-agents)
35
+ - [Using the Cline backend instead](#using-the-cline-backend-instead)
36
+ - [Next steps](#next-steps)
37
+ - [Requirements](#requirements)
38
+ - [Features](#features)
39
+ - [Public API](#public-api)
40
+ - [Module constant](#module-constant)
41
+ - [`ComposableAgents::VERSION`](#composableagentsversion)
42
+ - [Core agent classes](#core-agent-classes)
43
+ - [`ComposableAgents::Agent`](#composableagentsagent)
44
+ - [`ComposableAgents::RubyAgent < Agent`](#composableagentsrubyagent--agent)
45
+ - [`ComposableAgents::Instructions`](#composableagentsinstructions)
46
+ - [`ComposableAgents::PromptDrivenAgent < Agent`](#composableagentspromptdrivenagent--agent)
47
+ - [LLM backend agent classes](#llm-backend-agent-classes)
48
+ - [`ComposableAgents::AiAgents::Agent < PromptDrivenAgent`](#composableagentsaiagentsagent--promptdrivenagent)
49
+ - [`ComposableAgents::Cline::Agent < PromptDrivenAgent`](#composableagentsclineagent--promptdrivenagent)
50
+ - [`ComposableAgents::Cline::MissingSkillError < RuntimeError`](#composableagentsclinemissingskillerror--runtimeerror)
51
+ - [Mixins](#mixins)
52
+ - [`ComposableAgents::Mixins::Logger`](#composableagentsmixinslogger)
53
+ - [`ComposableAgents::Mixins::Resumable`](#composableagentsmixinsresumable)
54
+ - [`ComposableAgents::Mixins::UserInteraction`](#composableagentsmixinsuserinteraction)
55
+ - [`ComposableAgents::Mixins::ArtifactContract`](#composableagentsmixinsartifactcontract)
56
+ - [Additional notes](#additional-notes)
57
+ - [Documentation](#documentation)
58
+ - [How it works](#how-it-works)
59
+ - [Architecture overview ๐Ÿ—๏ธ](#architecture-overview-)
60
+ - [Composition: the pipeline pattern ๐Ÿ”„](#composition-the-pipeline-pattern-)
61
+ - [Prompt-driven execution flow ๐Ÿง ](#prompt-driven-execution-flow-)
62
+ - [Prompt rendering strategies ๐Ÿ“](#prompt-rendering-strategies-)
63
+ - [LLM backends: ai-agents vs cline-rb ๐ŸŽฏ](#llm-backends-ai-agents-vs-cline-rb-)
64
+ - [Mixin system โ€” augment agents with capabilities ๐Ÿงฐ](#mixin-system--augment-agents-with-capabilities-)
65
+ - [Instruction system ๐Ÿ“‹](#instruction-system-)
66
+ - [Code loading โšก](#code-loading-)
67
+ - [State persistence for resumable workflows ๐Ÿ’พ](#state-persistence-for-resumable-workflows-)
68
+ - [Development](#development)
69
+ - [Prerequisites](#prerequisites)
70
+ - [Clone the repository](#clone-the-repository)
71
+ - [Install dependencies](#install-dependencies)
72
+ - [Project structure (high-level)](#project-structure-high-level)
73
+ - [Run tests](#run-tests)
74
+ - [Test debugging](#test-debugging)
75
+ - [Code coverage](#code-coverage)
76
+ - [Code linting](#code-linting)
77
+ - [Generate documentation](#generate-documentation)
78
+ - [Package the gem](#package-the-gem)
79
+ - [Common development tasks](#common-development-tasks)
80
+ - [Adding a new feature](#adding-a-new-feature)
81
+ - [Adding a test helper](#adding-a-test-helper)
82
+ - [Running examples](#running-examples)
83
+ - [CI pipeline](#ci-pipeline)
84
+ - [Release process](#release-process)
85
+ - [Contributing](#contributing)
86
+ - [๐Ÿ› Issues](#-issues)
87
+ - [๐Ÿด Fork & Branch](#-fork--branch)
88
+ - [๐Ÿงช Setting up test dependencies & running tests](#-setting-up-test-dependencies--running-tests)
89
+ - [โœ… Linting & code style](#-linting--code-style)
90
+ - [๐Ÿ” CI / Build pipeline](#-ci--build-pipeline)
91
+ - [๐Ÿ“ Pull request guidelines](#-pull-request-guidelines)
92
+ - [๐Ÿ“„ License](#-license)
93
+ - [License](#license)
94
+
95
+ ## Quick start
96
+
97
+ ### Installation
98
+
99
+ Add the gem to your application's Gemfile:
100
+
101
+ ```bash
102
+ bundle add composable_agents
103
+ ```
104
+
105
+ Or install it globally:
106
+
107
+ ```bash
108
+ gem install composable_agents
109
+ ```
110
+
111
+ Requires **Ruby >= 3.1**.
112
+
113
+ ### Basic usage: create a composed pipeline of agents
114
+
115
+ Here's a minimal example that chains three agents together to build a holiday planner.
116
+
117
+ ```ruby
118
+ require 'composable_agents'
119
+
120
+ # --- 1. Define the agents ---
121
+
122
+ # An LLM-powered agent (uses ai-agents gem, needs an API key)
123
+ class ItineraryAgent < ComposableAgents::AiAgents::Agent
124
+ def initialize
125
+ super(
126
+ role: 'You are a travel planner',
127
+ objective: 'Find cities matching the user preferences',
128
+ system_instructions: <<~EO_INSTRUCTIONS,
129
+ Get the user preferences from the artifact named `preferences`.
130
+ Find the best cities.
131
+ Create an artifact named `cities` as a JSON list of city names.
132
+ EO_INSTRUCTIONS
133
+ model: 'openai/gpt-4o-mini' # or any model supported by your provider
134
+ )
135
+ end
136
+ end
137
+
138
+ # A plain Ruby agent (no LLM needed)
139
+ class BudgetAgent < ComposableAgents::RubyAgent
140
+ def initialize
141
+ super(proc do |input_artifacts|
142
+ cities = JSON.parse(input_artifacts[:cities])
143
+ { budget: cities.size * 1000 }
144
+ end)
145
+ end
146
+ end
147
+
148
+ # --- 2. Configure the LLM provider (for ai-agents backend) ---
149
+ require 'agents'
150
+ Agents.configure do |config|
151
+ config.openrouter_api_key = ENV.fetch('OPENROUTER_API_KEY', nil)
152
+ end
153
+
154
+ # --- 3. Compose and run them ---
155
+ preferences = { preferences: 'Cultural city trips in Europe' }
156
+
157
+ itinerary_outputs = ItineraryAgent.new.run(**preferences)
158
+ budget_outputs = BudgetAgent.new.run(**itinerary_outputs)
159
+
160
+ puts "Cities: #{itinerary_outputs[:cities]}"
161
+ puts "Budget: $#{budget_outputs[:budget]}"
162
+ ```
163
+
164
+ ### Using the Cline backend instead
165
+
166
+ If you prefer the `cline-rb` backend, set the `CLINE_API_KEY` environment variable:
167
+
168
+ ```ruby
169
+ # Use Cline-powered agents instead
170
+ itinerary_agent = ComposableAgents::Cline::Agent.new(
171
+ role: 'You are a travel planner',
172
+ objective: 'Find cities matching the user preferences',
173
+ model: 'anthropic/claude-sonnet-4.6',
174
+ api_key: ENV.fetch('CLINE_API_KEY', nil),
175
+ input_artifacts_contracts: { preferences: 'User travel preferences' },
176
+ output_artifacts_contracts: { cities: 'List of best cities' }
177
+ )
178
+ ```
179
+
180
+ ### Next steps
181
+
182
+ - Browse the [examples/](https://github.com/Muriel-Salvan/composable_agents/tree/main/examples) directory for full working scripts.
183
+ - Use the `ArtifactContract` mixin to validate inputs/outputs.
184
+ - Use the `Resumable` mixin to persist and resume long-running workflows.
185
+ - Use the `AiAgentUserInteraction` mixin to let agents ask the user questions.
186
+
187
+ ## Requirements
188
+
189
+ - **Ruby** >= 3.1 โ€” The gem requires Ruby 3.1 or newer.
190
+ - **Bundler** โ€” Used to install the gem and manage its dependencies (comes with Ruby).
191
+ - **Node.js** โ€” Required at runtime by the `cline-rb` backend for pseudo-terminal (PTY) support via `node-pty`.
192
+ - **An LLM provider API key** โ€” One of the following (depending on the agent backend you use):
193
+ - **OpenRouter API key** โ€” Set via the `OPENROUTER_API_KEY` environment variable when using the `AiAgents` backend.
194
+ - **Cline API key** โ€” Set via the `CLINE_API_KEY` environment variable when using the `Cline` backend.
195
+
196
+ ## Features
197
+
198
+ **composable_agents** is a Ruby framework for building **modular, prompt-driven AI agent pipelines** ๐Ÿงฉ. Here are its key capabilities:
199
+
200
+ - ๐Ÿง  **Three agent types** โ€” Create LLM-powered agents via [`PromptDrivenAgent`](lib/composable_agents/prompt_driven_agent.rb), wrap plain Ruby logic with [`RubyAgent`](lib/composable_agents/ruby_agent.rb), or compose complex multi-step workflows using the [`Resumable`](lib/composable_agents/mixins/resumable.rb) mixin.
201
+ - ๐Ÿ”„ **Composable pipelines** โ€” Pass output artifacts from one agent directly as input to another, forming reusable, chainable workflows.
202
+ - ๐ŸŽฏ **Multiple LLM backends** โ€” Plug into different AI providers via the [`ai-agents`](https://github.com/nicbarker/ai-agents) gem (OpenRouter) or [`cline-rb`](https://github.com/Muriel-Salvan/cline-rb) (Claude, GPT, and many more).
203
+ - ๐Ÿ“ **Two prompt rendering strategies** โ€” Choose between clean **Markdown** for simple agents, or **MarkdownHeavy** with structured output parsing, execution checklists, and typed artifact support for complex agentic systems.
204
+ - ๐Ÿ“ฆ **Typed artifact contracts** โ€” Define and validate input/output schemas with descriptions, optional flags, and types (`:text`, `:markdown`, `:json`). The framework raises clear `MissingInputArtifactError`, `MissingOutputArtifactError`, or `ArtifactTypeError` on violations.
205
+ - ๐Ÿ’พ **Resumable execution** โ€” Persist step-by-step state to disk (via the `Resumable` mixin). Interrupted runs can be resumed seamlessly โ€” previously completed steps are skipped, saving time and API costs.
206
+ - ๐Ÿ—ฃ๏ธ **User interaction** โ€” Agents can ask users clarifying questions mid-execution. Works out of the box via the terminal, or through an `ai-agents` tool integration for LLM-controlled workflows.
207
+ - ๐Ÿ“‹ **Automatic conversation tracking** โ€” Every prompt and response is automatically recorded with timestamps in a structured `conversation` store, ready for debugging or replay.
208
+ - โš™๏ธ **Flexible instruction system** โ€” Use raw text, structured ordered-lists, or a mix of both to define agent instructions, rendered consistently by the chosen strategy.
209
+ - ๐Ÿ› ๏ธ **Cline skill support** โ€” Select and enable specific Cline skills per agent, with automatic dependency resolution.
210
+ - ๐Ÿ” **State export/import** โ€” Agents can serialize and restore their internal state via `export_state`/`import_state`, enabling deep integration with the resumable workflow system.
211
+ - ๐Ÿ› **Debug logging** โ€” Toggle verbose debug output with the `COMPOSABLE_AGENTS_DEBUG=1` environment variable.
212
+ - ๐Ÿ“ **Markdown header alignment** โ€” A built-in utility normalizes Markdown header levels across composed prompts, maintaining a clean document hierarchy.
213
+
214
+ ## Public API
215
+
216
+ This section documents all public entry points of the **composable_agents** gem.
217
+ The project is a Ruby library (not a CLI) โ€” users install the gem and use its classes and mixins in their own Ruby code.
218
+
219
+ ---
220
+
221
+ ### Module constant
222
+
223
+ #### `ComposableAgents::VERSION`
224
+
225
+ - **Description:** The current version of the gem (`'0.1.0'`).
226
+ - **Full documentation:** [version.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/version.rb)
227
+
228
+ ---
229
+
230
+ ### Core agent classes
231
+
232
+ #### `ComposableAgents::Agent`
233
+
234
+ - **Description:** Abstract base class for all agents. An agent is a computational unit that transforms input artifacts into output artifacts. Agents are stateless by default.
235
+ - **Public methods:**
236
+ - `#initialize(name: nil, composable_agents_dir: '.composable_agents')` โ€” Create a new agent with an optional name and a working directory.
237
+ - `#name` โ€” Return the agent's name (`String`, or `nil`).
238
+ - `#full_name` โ€” Return a human-readable full name for logs and traces (can be overridden by subclasses).
239
+ - **Usage example:**
240
+ ```ruby
241
+ class MyCustomAgent < ComposableAgents::Agent
242
+ def run(**input_artifacts)
243
+ # Process input_artifacts and return output artifacts
244
+ { result: input_artifacts[:data].upcase }
245
+ end
246
+ end
247
+
248
+ agent = MyCustomAgent.new(name: 'uppercaser')
249
+ output = agent.run(data: 'hello')
250
+ puts output[:result] # => "HELLO"
251
+ ```
252
+ - **Full documentation:** [Agent on RubyDoc](https://www.rubydoc.info/gems/composable_agents/ComposableAgents/Agent) | [agent.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/agent.rb)
253
+
254
+ #### `ComposableAgents::RubyAgent < Agent`
255
+
256
+ - **Description:** An agent that wraps arbitrary Ruby logic as a `Proc`. No LLM is needed โ€” ideal for deterministic or simple processing steps.
257
+ - **Public methods:**
258
+ - `#initialize(processor, *args, **kwargs)` โ€” The `processor` is a `#call`-able object (e.g. a `Proc`) that receives input artifacts and returns output artifacts.
259
+ - `#run(**input_artifacts)` โ€” Execute the proc with the given input artifacts.
260
+ - **Usage example:**
261
+ ```ruby
262
+ # A simple agent that doubles a number
263
+ double_agent = ComposableAgents::RubyAgent.new(
264
+ proc { |inputs| { double: inputs[:number] * 2 } }
265
+ )
266
+ result = double_agent.run(number: 21)
267
+ puts result[:double] # => 42
268
+ ```
269
+ - **Full documentation:** [RubyAgent on RubyDoc](https://www.rubydoc.info/gems/composable_agents/ComposableAgents/RubyAgent) | [ruby_agent.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/ruby_agent.rb)
270
+
271
+ #### `ComposableAgents::Instructions`
272
+
273
+ - **Description:** Normalizes instructions (system prompts, user prompts) into a canonical list format. Supports plain text strings and structured hashes (e.g. with `ordered_list` keys). Includes `Enumerable`.
274
+ - **Public methods:**
275
+ - `#initialize(instructions)` โ€” Accepts a `String`, an `Array`, or a `Hash{text:, ordered_list:}`.
276
+ - `#each(&)` โ€” Iterate over each instruction as `(type, content)` pairs.
277
+ - **Usage example:**
278
+ ```ruby
279
+ instructions = ComposableAgents::Instructions.new({
280
+ ordered_list: ['Step one', 'Step two']
281
+ })
282
+ instructions.each do |type, content|
283
+ puts "#{type}: #{content}"
284
+ end
285
+ ```
286
+ - **Full documentation:** [Instructions on RubyDoc](https://www.rubydoc.info/gems/composable_agents/ComposableAgents/Instructions) | [instructions.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/instructions.rb)
287
+
288
+ #### `ComposableAgents::PromptDrivenAgent < Agent`
289
+
290
+ - **Description:** An agent that uses a **prompt rendering strategy** (Markdown or MarkdownHeavy) to build prompts for an LLM. It manages role, objective, instructions, constraints, and a conversation history.
291
+ - **Public methods:**
292
+ - `#role` / `#role=` โ€” Agent's role description.
293
+ - `#objective` / `#objective=` โ€” Agent's objective.
294
+ - `#system_instructions` / `#system_instructions=` โ€” Instructions for the agent.
295
+ - `#constraints` / `#constraints=` โ€” Constraints the agent must respect.
296
+ - `#conversation` โ€” Read the conversation history (array of message hashes).
297
+ - `#initialize(*args, role:, objective:, system_instructions:, constraints:, strategy:, **kwargs)` โ€” The `strategy` defaults to `PromptRenderingStrategy::Markdown`.
298
+ - `#full_name` โ€” Human-readable name for logs.
299
+ - `#run(user_instructions: nil, **input_artifacts)` โ€” Execute the agent and produce output artifacts.
300
+ - **Usage example:**
301
+ ```ruby
302
+ agent = ComposableAgents::PromptDrivenAgent.new(
303
+ role: 'A helpful assistant',
304
+ objective: 'Answer user questions'
305
+ )
306
+ # Subclass and implement #prompt(user_prompt) to provide the LLM backend.
307
+ ```
308
+ - **Full documentation:** [PromptDrivenAgent on RubyDoc](https://www.rubydoc.info/gems/composable_agents/ComposableAgents/PromptDrivenAgent) | [prompt_driven_agent.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/prompt_driven_agent.rb)
309
+
310
+ ---
311
+
312
+ ### LLM backend agent classes
313
+
314
+ #### `ComposableAgents::AiAgents::Agent < PromptDrivenAgent`
315
+
316
+ - **Description:** An agent that uses the [`ai-agents`](https://github.com/nicbarker/ai-agents) gem as its LLM backend. Requires an OpenRouter API key configured via `Agents.configure`.
317
+ - **Public methods:**
318
+ - `#initialize(*args, model:, params:, handoff_agents:, **kwargs)` โ€” Specify the `model` (e.g. `'openai/gpt-4o-mini'`), optional `params` for model configuration, and a list of `handoff_agents`.
319
+ - `#full_name` โ€” Returns `"<name> (AiAgent <model>)"`.
320
+ - **Usage example:**
321
+ ```ruby
322
+ require 'agents'
323
+ Agents.configure { |c| c.openrouter_api_key = ENV['OPENROUTER_API_KEY'] }
324
+
325
+ agent = ComposableAgents::AiAgents::Agent.new(
326
+ role: 'Travel planner',
327
+ objective: 'Suggest destinations',
328
+ system_instructions: 'Create an artifact named `cities` with city names.',
329
+ model: 'openai/gpt-4o-mini'
330
+ )
331
+ result = agent.run(preferences: 'beach holidays')
332
+ puts result[:cities]
333
+ ```
334
+ - **Full documentation:** [AiAgents::Agent on RubyDoc](https://www.rubydoc.info/gems/composable_agents/ComposableAgents/AiAgents/Agent) | [ai_agents/agent.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/ai_agents/agent.rb)
335
+
336
+ #### `ComposableAgents::Cline::Agent < PromptDrivenAgent`
337
+
338
+ - **Description:** An agent that uses the [`cline-rb`](https://github.com/Muriel-Salvan/cline-rb) gem as its LLM backend. Requires a `CLINE_API_KEY` environment variable. Automatically prepends the `ArtifactContract` mixin.
339
+ - **Public methods:**
340
+ - `#initialize(*args, strategy:, provider:, model:, api_key:, configure_provider:, configure_global:, skills:, cli_options:, **kwargs)` โ€” Configure provider, model, optional skill list, and CLI options. Defaults to `'cline'` provider, `'anthropic/claude-sonnet-4.6'` model.
341
+ - `#full_name` โ€” Returns `"<name> (Cline <provider>/<model>)"`.
342
+ - **Usage example:**
343
+ ```ruby
344
+ agent = ComposableAgents::Cline::Agent.new(
345
+ role: 'Travel planner',
346
+ objective: 'Suggest destinations',
347
+ model: 'deepseek/deepseek-v4-flash',
348
+ api_key: ENV['CLINE_API_KEY'],
349
+ input_artifacts_contracts: { preferences: 'User preferences' },
350
+ output_artifacts_contracts: { cities: 'City list' }
351
+ )
352
+ result = agent.run(preferences: 'cultural trips in Italy')
353
+ puts result[:cities]
354
+ ```
355
+ - **Full documentation:** [Cline::Agent on RubyDoc](https://www.rubydoc.info/gems/composable_agents/ComposableAgents/Cline/Agent) | [cline/agent.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/cline/agent.rb)
356
+
357
+ #### `ComposableAgents::Cline::MissingSkillError < RuntimeError`
358
+
359
+ - **Description:** Raised by `Cline::Agent` when a referenced skill is not found in the global or project Cline configuration.
360
+ - **Full documentation:** [cline/agent.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/cline/agent.rb)
361
+
362
+ ---
363
+
364
+ ### Mixins
365
+
366
+ Mixins are `prepend`-ed into an agent class to add specific capabilities.
367
+
368
+ #### `ComposableAgents::Mixins::Logger`
369
+
370
+ - **Description:** Provides debug and info logging to agents. Debug mode is enabled by setting `COMPOSABLE_AGENTS_DEBUG=1` in the environment.
371
+ - **Public methods (class-level):**
372
+ - `self.debug?` โ€” Returns `true` if debug mode is enabled (`ENV['COMPOSABLE_AGENTS_DEBUG'] == '1'`).
373
+ - **Usage example:**
374
+ ```ruby
375
+ # Enable debug logs
376
+ ENV['COMPOSABLE_AGENTS_DEBUG'] = '1'
377
+ puts ComposableAgents::Mixins::Logger.debug? # => true
378
+ ```
379
+ - **Full documentation:** [Mixins::Logger on RubyDoc](https://www.rubydoc.info/gems/composable_agents/ComposableAgents/Mixins/Logger) | [logger.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/logger.rb)
380
+
381
+ #### `ComposableAgents::Mixins::Resumable`
382
+
383
+ - **Description:** Adds step-level persistence and resumption capabilities to agents. Steps and their artifacts are serialized to JSON on disk so that long-running workflows can be interrupted and resumed.
384
+ - **Public methods:**
385
+ - `#initialize(*args, run_id:, **kwargs)` โ€” The `run_id` identifies the persisted run.
386
+ - **Usage example:**
387
+ ```ruby
388
+ class WorkflowAgent < ComposableAgents::Agent
389
+ prepend ComposableAgents::Mixins::Resumable
390
+
391
+ def run(**inputs)
392
+ @artifacts = inputs
393
+ step(:process_data) { @artifacts[:result] = @artifacts[:data].upcase }
394
+ step(:finalize) { @artifacts[:done] = true }
395
+ @artifacts
396
+ end
397
+ end
398
+
399
+ # If interrupted between steps, re-running with the same run_id skips completed steps
400
+ WorkflowAgent.new(run_id: 'my_workflow').run(data: 'hello')
401
+ ```
402
+ - **Full documentation:** [Mixins::Resumable on RubyDoc](https://www.rubydoc.info/gems/composable_agents/ComposableAgents/Mixins/Resumable) | [resumable.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/resumable.rb)
403
+
404
+ #### `ComposableAgents::Mixins::UserInteraction`
405
+
406
+ - **Description:** Adds a simple question-and-answer interface for agents. By default, questions are printed to the terminal and answers are read from `$stdin`. Override `#answer_to` for custom behavior.
407
+ - **Public methods:**
408
+ - `#ask(question)` โ€” Ask the user a question and return the answer.
409
+ - **Usage example:**
410
+ ```ruby
411
+ class InteractiveAgent < ComposableAgents::Agent
412
+ include ComposableAgents::Mixins::UserInteraction
413
+
414
+ def run(**)
415
+ name = ask('What is your name?')
416
+ { greeting: "Hello, #{name}!" }
417
+ end
418
+ end
419
+ ```
420
+ - **Full documentation:** [Mixins::UserInteraction on RubyDoc](https://www.rubydoc.info/gems/composable_agents/ComposableAgents/Mixins/UserInteraction) | [user_interaction.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/user_interaction.rb)
421
+
422
+ #### `ComposableAgents::Mixins::ArtifactContract`
423
+
424
+ - **Description:** Validates input and output artifacts against declared contracts before and after running an agent. Contracts specify description, optionality, and expected type (`:text`, `:markdown`, `:json`).
425
+ - **Public error classes:**
426
+ - `MissingInputArtifactError < RuntimeError` โ€” Raised when required input artifacts are missing.
427
+ - `MissingOutputArtifactError < RuntimeError` โ€” Raised when expected output artifacts are missing after execution.
428
+ - `ArtifactTypeError < RuntimeError` โ€” Raised when an artifact's content does not match its declared type.
429
+ - **Public methods:**
430
+ - `#initialize(*args, input_artifacts_contracts:, output_artifacts_contracts:, **kwargs)` โ€” Contracts are `Hash{Symbol => String}` (simple description) or `Hash{Symbol => Hash{description:, optional:, type:}}`.
431
+ - **Usage example:**
432
+ ```ruby
433
+ class ValidatedAgent < ComposableAgents::Agent
434
+ prepend ComposableAgents::Mixins::ArtifactContract
435
+
436
+ def input_artifacts_contracts
437
+ { name: { description: 'User name', type: :text } }
438
+ end
439
+
440
+ def output_artifacts_contracts
441
+ { greeting: { description: 'Greeting message', type: :text } }
442
+ end
443
+
444
+ def run(**inputs)
445
+ { greeting: "Hello, #{inputs[:name]}!" }
446
+ end
447
+ end
448
+
449
+ agent = ValidatedAgent.new
450
+ agent.run(name: 'World') # => { greeting: "Hello, World!" }
451
+ agent.run(foo: 'bar') # Raises MissingInputArtifactError
452
+ ```
453
+ - **Full documentation:** [Mixins::ArtifactContract on RubyDoc](https://www.rubydoc.info/gems/composable_agents/ComposableAgents/Mixins/ArtifactContract) | [artifact_contract.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/artifact_contract.rb)
454
+
455
+ ---
456
+
457
+ ### Additional notes
458
+
459
+ - **No executables / CLI** โ€” This gem is a library only; there are no scripts in `bin/`.
460
+ - **Prompt rendering strategies** (`PromptRenderingStrategy::Markdown` and `PromptRenderingStrategy::MarkdownHeavy`) are not part of the public API themselves โ€” they are included automatically by the agent's `strategy:` parameter.
461
+ - **The `AiAgentUserInteraction` mixin** (`ComposableAgents::Mixins::AiAgentUserInteraction`) is an internal bridge that combines `UserInteraction` with `AiAgents::Agent`; it is used by prepending it to an `AiAgents::Agent` subclass.
462
+ - See the [examples/](https://github.com/Muriel-Salvan/composable_agents/tree/main/examples) directory for complete, runnable scripts demonstrating all the above APIs.
463
+
464
+ ## Documentation
465
+
466
+ - **๐Ÿ“– Main README** โ€” [README.md](https://github.com/Muriel-Salvan/composable_agents#readme) โ€” Overview, installation, usage, and development instructions.
467
+ - **๐Ÿ“š RubyDoc.info (API reference)** โ€” [composable_agents on RubyDoc](https://www.rubydoc.info/gems/composable_agents) โ€” Auto-generated YARD documentation for all public classes, modules, and methods. Covers the full API with 100% documented coverage.
468
+ - **๐Ÿ  GitHub Repository** โ€” [github.com/Muriel-Salvan/composable_agents](https://github.com/Muriel-Salvan/composable_agents) โ€” Source code, issue tracker, and pull requests.
469
+ - **๐Ÿ“„ License** โ€” [BSD-3-Clause License](https://github.com/Muriel-Salvan/composable_agents/blob/main/LICENSE) โ€” The gem is available as open source under the BSD 3-Clause License.
470
+ - **๐Ÿ’ก Examples** โ€” [`examples/` directory](https://github.com/Muriel-Salvan/composable_agents/tree/main/examples) โ€” Runnable Ruby scripts demonstrating simple pipelines, resumable workflows, and user interaction patterns.
471
+ - **โš™๏ธ CI / Build** โ€” [Continuous Integration workflow](https://github.com/Muriel-Salvan/composable_agents/blob/main/.github/workflows/continuous_integration.yml) โ€” GitHub Actions configuration for running tests and publishing releases.
472
+ - **๐Ÿ“ Source Code** โ€” Browse the [`lib/` directory](https://github.com/Muriel-Salvan/composable_agents/tree/main/lib/composable_agents) for inline YARD-annotated source documentation of all agents, mixins, and rendering strategies:
473
+ - [`Agent`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/agent.rb) โ€” Abstract base class
474
+ - [`PromptDrivenAgent`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/prompt_driven_agent.rb) โ€” LLM-prompted agent
475
+ - [`RubyAgent`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/ruby_agent.rb) โ€” Plain Ruby logic agent
476
+ - [`AiAgents::Agent`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/ai_agents/agent.rb) โ€” ai-agents backend
477
+ - [`Cline::Agent`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/cline/agent.rb) โ€” cline-rb backend
478
+ - [`Instructions`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/instructions.rb) โ€” Instruction system
479
+ - [`Mixins::Resumable`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/resumable.rb) โ€” Resumable workflow mixin
480
+ - [`Mixins::ArtifactContract`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/artifact_contract.rb) โ€” Artifact validation mixin
481
+ - [`Mixins::UserInteraction`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/user_interaction.rb) โ€” User question-asking mixin
482
+ - [`Mixins::Logger`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/logger.rb) โ€” Debug logging mixin
483
+ - [`PromptRenderingStrategy::Markdown`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/prompt_rendering_strategy/markdown.rb) โ€” Markdown prompt strategy
484
+ - [`PromptRenderingStrategy::MarkdownHeavy`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/prompt_rendering_strategy/markdown_heavy.rb) โ€” Heavy Markdown prompt strategy
485
+ - [`Utils::Markdown`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/utils/markdown.rb) โ€” Markdown header alignment utilities
486
+
487
+ ## How it works
488
+
489
+ **composable_agents** is built on a simple principle: every agent is a **stateless function** that takes input artifacts (a `Hash{Symbol => Object}`) and returns output artifacts. Chain them โ€” one agent's outputs become the next agent's inputs.
490
+
491
+ ### Architecture overview ๐Ÿ—๏ธ
492
+
493
+ ```mermaid
494
+ classDiagram
495
+ class Agent {
496
+ +run(**input_artifacts)~Hash~
497
+ +full_name()~String~
498
+ }
499
+ class PromptDrivenAgent {
500
+ +String role
501
+ +String objective
502
+ +String system_instructions
503
+ +String constraints
504
+ +Array conversation
505
+ #prompt(user_prompt)~String~
506
+ }
507
+ class RubyAgent {
508
+ -Proc processor
509
+ +run(**input_artifacts)~Hash~
510
+ }
511
+ class AiAgents_Agent {
512
+ -AgentRunner agent_runner
513
+ #prompt(user_prompt)~String~
514
+ }
515
+ class Cline_Agent {
516
+ -Cline::Config cline_config
517
+ #prompt(user_prompt)~String~
518
+ }
519
+ Agent <|-- PromptDrivenAgent : extends
520
+ Agent <|-- RubyAgent : extends
521
+ PromptDrivenAgent <|-- AiAgents_Agent : extends
522
+ PromptDrivenAgent <|-- Cline_Agent : extends
523
+ ```
524
+
525
+ The framework provides a clean **4-class hierarchy**:
526
+
527
+ - **`Agent`** โ€” Abstract base class. Defines the `run(**input_artifacts)` contract and includes the [`Mixins::Logger`](lib/composable_agents/mixins/logger.rb) for debug/info logging.
528
+ - **`RubyAgent`** โ€” Wraps any Ruby `Proc` as an agent. No LLM involved: call `proc.call(input_artifacts)` and return a hash. Ideal for deterministic logic.
529
+ - **`PromptDrivenAgent`** โ€” Base for LLM-powered agents. Holds a **role**, **objective**, **system_instructions**, and **constraints**. Renders them via a pluggable **prompt rendering strategy** and records every prompt/response in a `conversation` array.
530
+ - **`AiAgents::Agent` / `Cline::Agent`** โ€” Concrete LLM backends: one wraps the [`ai-agents`](https://github.com/nicbarker/ai-agents) gem, the other wraps [`cline-rb`](https://github.com/Muriel-Salvan/cline-rb). Both implement `#prompt(user_prompt)` to send the rendered prompt to the LLM.
531
+
532
+ ### Composition: the pipeline pattern ๐Ÿ”„
4
533
 
5
- ## Installation
534
+ Agents communicate exclusively through **artifacts** โ€” named key/value pairs in a Ruby Hash:
6
535
 
7
- Install the gem and add to the application's Gemfile by executing:
536
+ ```ruby
537
+ outputs = agent.run(**inputs)
538
+ # outputs[:city] can feed into next_agent.run(city: outputs[:city])
539
+ ```
8
540
 
9
- $ bundle add composable_agents
541
+ > ๐Ÿ’ก Agents are **stateless by design** โ€” no hidden mutable state. This makes them easy to test, debug, and reorder in pipelines.
10
542
 
11
- If bundler is not being used to manage dependencies, install the gem by executing:
543
+ ### Prompt-driven execution flow ๐Ÿง 
12
544
 
13
- $ gem install composable_agents
545
+ Here's what happens when you call `run(**inputs)` on a `PromptDrivenAgent`:
14
546
 
15
- ## Usage
547
+ ```mermaid
548
+ flowchart TD
549
+ A[run] --> B[Render system prompt]
550
+ B --> C[Call #prompt with system prompt]
551
+ C --> D{Missing output\nartifacts?}
552
+ D -->|Yes| E[Render retry prompt]
553
+ E --> F[Call #prompt again]
554
+ F --> D
555
+ D -->|No| G[Return output artifacts]
556
+ ```
16
557
 
17
- TODO: Write usage instructions here
558
+ 1. **`render_system_prompt`** โ€” Assembles role, objective, instructions, and constraints into a structured Markdown document (via the chosen strategy).
559
+ 2. **`#prompt(user_prompt)`** โ€” Sends the rendered prompt to the LLM backend. The backend (ai-agents or cline-rb) manages tool calls, context, and the LLM conversation.
560
+ 3. **Retry loop** โ€” If expected output artifacts are missing, a retry prompt is generated and sent again.
561
+ 4. Returns the collected `{ artifact_name => content }` hash.
562
+
563
+ ### Prompt rendering strategies ๐Ÿ“
564
+
565
+ Two strategies are included, mixed into the agent at initialization via `singleton_class.include strategy`:
566
+
567
+ - **`PromptRenderingStrategy::Markdown`** โ€” Clean, minimal Markdown. Simple instructions and artifact references.
568
+ - **`PromptRenderingStrategy::MarkdownHeavy`** (default for Cline) โ€” Elaborate prompts with execution checklists, structured artifact definition sections, and **JSON-based output parsing**. Agents format their artifacts as JSON blocks tagged with `output_artifact=NAME`, which the strategy parses back into the output hash. Includes type-aware parsing (`:text`, `:markdown`, `:json`).
569
+
570
+ ### LLM backends: ai-agents vs cline-rb ๐ŸŽฏ
571
+
572
+ | Feature | [`AiAgents::Agent`](lib/composable_agents/ai_agents/agent.rb) | [`Cline::Agent`](lib/composable_agents/cline/agent.rb) |
573
+ |---|---|---|
574
+ | Underlying gem | [ai-agents](https://github.com/nicbarker/ai-agents) | [cline-rb](https://github.com/Muriel-Salvan/cline-rb) |
575
+ | Tools | Exposes `CreateArtifactTool`, `GetArtifactTool` to the LLM | Uses Cline's skill system |
576
+ | State persistence | Marshal + Base64 via `export_state`/`import_state` | Direct JSON serialization of context array |
577
+ | Default rendering | `Markdown` | `MarkdownHeavy` (with structured output parsing) |
578
+ | User interaction | Optional `AskUserTool` via [`AiAgentUserInteraction`](lib/composable_agents/mixins/ai_agent_user_interaction.rb) | N/A (uses Cline's own interaction) |
579
+
580
+ ### Mixin system โ€” augment agents with capabilities ๐Ÿงฐ
581
+
582
+ Mixins are **prepended** (using `prepend`) or **included** (using `include`) to override `#run` or add new methods:
583
+
584
+ - **[`Mixins::ArtifactContract`](lib/composable_agents/mixins/artifact_contract.rb)** โ€” Wraps `#run` to validate inputs before and outputs after execution against declared contracts. Raises `MissingInputArtifactError`, `MissingOutputArtifactError`, or `ArtifactTypeError` on violations.
585
+ - **[`Mixins::Resumable`](lib/composable_agents/mixins/resumable.rb)** โ€” Overrides `#run` with a **step-based execution model**. Each `step` block is persisted to `.composable_agents/runs/{run_id}/` as JSON. On re-run, completed steps are skipped โ€” only new steps execute. Supports nested steps and agent state serialization via `export_state`/`import_state`.
586
+ - **[`Mixins::UserInteraction`](lib/composable_agents/mixins/user_interaction.rb)** โ€” Adds an `#ask(question)` method. By default prompts the terminal; override `#answer_to` for custom behavior.
587
+ - **[`Mixins::Logger`](lib/composable_agents/mixins/logger.rb)** โ€” Provides `log_debug`/`log_info` methods. Debug output is toggled via the `COMPOSABLE_AGENTS_DEBUG=1` environment variable.
588
+
589
+ ### Instruction system ๐Ÿ“‹
590
+
591
+ The [`Instructions`](lib/composable_agents/instructions.rb) class normalizes instructions into a standard list format. Each instruction can be:
592
+ - **`{ text: "..." }`** โ€” Free-form text
593
+ - **`{ ordered_list: ["Step 1", "Step 2"] }`** โ€” Sequential steps
594
+
595
+ The rendering strategy then renders each type appropriately (`#render_instruction_text`, `#render_instruction_ordered_list`).
596
+
597
+ ### Code loading โšก
598
+
599
+ Uses [`zeitwerk`](https://github.com/fxn/zeitwerk) for automatic, thread-safe code autoloading โ€” no manual `require` calls needed beyond the top-level entry point.
600
+
601
+ ### State persistence for resumable workflows ๐Ÿ’พ
602
+
603
+ ```mermaid
604
+ flowchart LR
605
+ A[Step: fetch_data] --> B{Step JSON\nexists?}
606
+ B -->|Yes| C[Deserialize state\nSkip execution]
607
+ B -->|No| D[Execute block]
608
+ D --> E[Serialize artifacts\n+ agent state to JSON]
609
+ C --> F[Continue with\nrestored artifacts]
610
+ E --> F
611
+ ```
612
+
613
+ The `Resumable` mixin tracks a **hierarchical step index** (`@steps_idx`) that mirrors the nesting of `step` blocks. Each step's input/output state is saved as a JSON file. On re-execution with the same `run_id`, the framework loads the saved state instead of re-running completed steps โ€” saving both time and API costs.
18
614
 
19
615
  ## Development
20
616
 
21
- After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rspec` to run the tests.
617
+ ### Prerequisites
618
+
619
+ - **Ruby** >= 3.1
620
+ - **Bundler** (comes with Ruby)
621
+ - **Node.js** โ€” required by the `cline-rb` backend for pseudo-terminal support (`node-pty`)
622
+
623
+ ### Clone the repository
624
+
625
+ ```bash
626
+ git clone https://github.com/Muriel-Salvan/composable_agents.git
627
+ cd composable_agents
628
+ ```
629
+
630
+ ### Install dependencies
631
+
632
+ ```bash
633
+ bundle install
634
+ ```
635
+
636
+ Additionally, install the Node.js pseudo-terminal dependency that the `cline-rb` backend expects:
637
+
638
+ ```bash
639
+ npm install node-pty
640
+ ```
641
+
642
+ ### Project structure (high-level)
643
+
644
+ ```
645
+ .
646
+ โ”œโ”€โ”€ lib/ # Source code (autoloaded via Zeitwerk)
647
+ โ”‚ โ””โ”€โ”€ composable_agents/ # Main library modules
648
+ โ”‚ โ”œโ”€โ”€ agent.rb # Abstract base agent
649
+ โ”‚ โ”œโ”€โ”€ prompt_driven_agent.rb # LLM-prompted agent
650
+ โ”‚ โ”œโ”€โ”€ ruby_agent.rb # Plain Ruby logic agent
651
+ โ”‚ โ”œโ”€โ”€ instructions.rb # Instruction system
652
+ โ”‚ โ”œโ”€โ”€ ai_agents/ # ai-agents backend
653
+ โ”‚ โ”œโ”€โ”€ cline/ # cline-rb backend
654
+ โ”‚ โ”œโ”€โ”€ mixins/ # Resumable, ArtifactContract, UserInteraction, Logger
655
+ โ”‚ โ”œโ”€โ”€ prompt_rendering_strategy/ # Markdown & MarkdownHeavy strategies
656
+ โ”‚ โ””โ”€โ”€ utils/ # Markdown utilities
657
+ โ”œโ”€โ”€ spec/ # Test suite
658
+ โ”‚ โ”œโ”€โ”€ scenarios/ # RSpec test cases
659
+ โ”‚ โ””โ”€โ”€ composable_agents_test/ # Test helpers, spies, stubs
660
+ โ”œโ”€โ”€ examples/ # Runnable usage examples
661
+ โ”œโ”€โ”€ Gemfile # Dependencies
662
+ โ”œโ”€โ”€ composable_agents.gemspec # Gem specification
663
+ โ”œโ”€โ”€ .rubocop.yml # RuboCop configuration
664
+ โ””โ”€โ”€ .github/workflows/ # CI pipeline
665
+ ```
666
+
667
+ ### Run tests
668
+
669
+ Run the full test suite with RSpec:
670
+
671
+ ```bash
672
+ bundle exec rspec
673
+ ```
674
+
675
+ Run a specific test file:
676
+
677
+ ```bash
678
+ bundle exec rspec spec/scenarios/composable_agents/cline/agent_spec.rb
679
+ ```
680
+
681
+ Run tests with verbose documentation output:
682
+
683
+ ```bash
684
+ bundle exec rspec --format documentation
685
+ ```
686
+
687
+ #### Test debugging
688
+
689
+ Set the `TEST_DEBUG=1` environment variable to enable verbose debug output during test execution:
690
+
691
+ ```bash
692
+ TEST_DEBUG=1 bundle exec rspec
693
+ ```
694
+
695
+ #### Code coverage
696
+
697
+ The test suite enforces **99% minimum code coverage** via SimpleCov. Coverage reports are generated in Cobertura format and automatically uploaded to Codecov in CI.
698
+
699
+ ### Code linting
700
+
701
+ This project uses **RuboCop** with the `rubocop-rspec` and `rubocop-yard` plugins. Run the linter:
702
+
703
+ ```bash
704
+ bundle exec rubocop
705
+ ```
706
+
707
+ To auto-correct fixable offenses:
708
+
709
+ ```bash
710
+ bundle exec rubocop -a
711
+ ```
712
+
713
+ Linting is also verified as part of the test suite via the `Code Quality` spec (`spec/scenarios/code_quality_spec.rb`), which runs `rubocop` and asserts that no offenses are detected.
714
+
715
+ ### Generate documentation
716
+
717
+ API documentation is generated with **YARD**. The project enforces **100% documented code**:
718
+
719
+ ```bash
720
+ bundle exec yard doc --fail-on-warning
721
+ ```
722
+
723
+ Check documentation coverage stats:
724
+
725
+ ```bash
726
+ bundle exec yard stats --list-undoc --fail-on-warning
727
+ ```
728
+
729
+ Documentation generation and coverage are also verified as part of the test suite via the `Documentation generation` spec.
730
+
731
+ ### Package the gem
732
+
733
+ Build the gem locally:
734
+
735
+ ```bash
736
+ gem build composable_agents.gemspec
737
+ ```
738
+
739
+ This produces a `.gem` file (e.g., `composable_agents-0.1.0.gem`) in the current directory. The packaging process is also verified by the `Gem packaging` spec.
740
+
741
+ ### Common development tasks
742
+
743
+ #### Adding a new feature
744
+
745
+ 1. Write the feature code under `lib/composable_agents/` โ€” files are autoloaded by **Zeitwerk**, so name them according to the module/class namespace (e.g., `lib/composable_agents/my_feature.rb` for `ComposableAgents::MyFeature`).
746
+ 2. Add RSpec tests under `spec/scenarios/composable_agents/` following the existing patterns.
747
+ 3. Document all public methods with YARD annotations.
748
+ 4. Run the full test suite and linting before committing:
749
+
750
+ ```bash
751
+ bundle exec rspec && bundle exec rubocop
752
+ ```
753
+
754
+ #### Adding a test helper
755
+
756
+ Place reusable test helpers, spies, or stubs under `spec/composable_agents_test/`. They are autoloaded via Zeitwerk under the `ComposableAgentsTest` namespace and included automatically via the spec helper.
757
+
758
+ #### Running examples
759
+
760
+ Example scripts are located in the `examples/` directory. Run any example directly with Ruby:
761
+
762
+ ```bash
763
+ bundle exec ruby examples/compose_without_ai.rb
764
+ ```
765
+
766
+ Some examples require an LLM provider API key. Set the appropriate environment variable before running:
767
+
768
+ ```bash
769
+ OPENROUTER_API_KEY=your_key bundle exec ruby examples/compose_with_ai.rb
770
+ # or
771
+ CLINE_API_KEY=your_key bundle exec ruby examples/compose_with_cline.rb
772
+ ```
773
+
774
+ #### CI pipeline
775
+
776
+ The project uses **GitHub Actions** (defined in `.github/workflows/continuous_integration.yml`):
777
+
778
+ - **`test` job** โ€” Runs the full RSpec suite on push (Ruby 3.4, with Bundler cache and `node-pty` installed). Coverage is uploaded to Codecov.
779
+ - **`package` job** โ€” Runs after tests pass, using **semantic-release** to publish the gem to RubyGems.org when a new version is tagged.
780
+
781
+ #### Release process
782
+
783
+ Releases are automated via **semantic-release**. Pushing a tag with a commit message following conventional commits format triggers the CI `package` job, which:
784
+
785
+ 1. Builds the gem
786
+ 2. Publishes it to RubyGems.org
787
+ 3. Creates a GitHub release with changelog
788
+
789
+ Manual gem publishing can also be done with:
790
+
791
+ ```bash
792
+ GEM_HOST_API_KEY=your_key gem push composable_agents-*.gem
793
+ ```
22
794
 
23
795
  ## Contributing
24
796
 
25
- Bug reports and pull requests are welcome on GitHub at https://github.com/Muriel-Salvan/composable_agents.
797
+ Bug reports, feature suggestions, and pull requests are warmly welcomed on GitHub at [github.com/Muriel-Salvan/composable_agents](https://github.com/Muriel-Salvan/composable_agents). Please follow the guidelines below to keep things running smoothly.
798
+
799
+ ### ๐Ÿ› Issues
800
+
801
+ - **Before opening an issue**, search the [existing tracker](https://github.com/Muriel-Salvan/composable_agents/issues) to avoid duplicates.
802
+ - For a **bug report**, include:
803
+ - Ruby version (`ruby -v`)
804
+ - gem version
805
+ - a minimal code snippet that reproduces the problem
806
+ - the full error output or unexpected behaviour
807
+ - For a **feature request**, describe what you'd like to do and why, and if possible sketch how it could fit into the existing agent/mixin architecture.
808
+
809
+ ### ๐Ÿด Fork & Branch
810
+
811
+ 1. Fork the repository on GitHub.
812
+ 2. Clone your fork locally:
813
+ ```bash
814
+ git clone https://github.com/<your-username>/composable_agents.git
815
+ cd composable_agents
816
+ ```
817
+ 3. Create a feature branch from `main`:
818
+ ```bash
819
+ git checkout -b feat/my-awesome-feature
820
+ ```
821
+ We follow a [semantic-release](https://semantic-release.gitbook.io/) workflow, so branch names like `feat/โ€ฆ`, `fix/โ€ฆ`, `chore/โ€ฆ` help the CI determine the next version bump.
822
+
823
+ ### ๐Ÿงช Setting up test dependencies & running tests
824
+
825
+ After checking out the repo, install all dependencies with `bundle install`. Then run the full test suite with `bundle exec rspec` (add `--format documentation` for verbose output). To run a single spec file, point to it directly, e.g. `bundle exec rspec spec/composable_agents_test/prompt_driven_agent_spies.rb`; to run a specific example, append `:line_number`, e.g. `bundle exec rspec spec/composable_agents_test/agent_spec.rb:42`. The CI (GitHub Actions, see [`.github/workflows/continuous_integration.yml`](https://github.com/Muriel-Salvan/composable_agents/blob/main/.github/workflows/continuous_integration.yml)) runs `bundle exec rspec --format documentation` on Ruby 3.4 โ€” your changes must pass all tests and should not drop code coverage below **99 %** (enforced via [SimpleCov](https://github.com/simplecov-ruby/simplecov)).
826
+
827
+ ### โœ… Linting & code style
828
+
829
+ The project uses [RuboCop](https://github.com/rubocop/rubocop) with the `rubocop-rspec` plugin. Run the linter before pushing:
830
+ ```bash
831
+ bundle exec rubocop
832
+ ```
833
+ Configuration lives in [`.rubocop.yml`](https://github.com/Muriel-Salvan/composable_agents/blob/main/.rubocop.yml). Key allowances (long methods, nested RSpec groups, etc.) are already tuned to the codebase โ€” please keep them as they are.
834
+
835
+ ### ๐Ÿ” CI / Build pipeline
836
+
837
+ | Job | When | What it does |
838
+ |-----|------|-------------|
839
+ | **test** | Every push | Installs Ruby 3.4 + Node (for `node-pty`), runs `bundle exec rspec`, uploads coverage to [Codecov](https://codecov.io/) |
840
+ | **package** | After tests pass | Runs `semantic-release` to auto-publish the gem to [RubyGems](https://rubygems.org/gems/composable_agents) and create a GitHub release with a generated changelog |
841
+
842
+ Pull requests must pass the **test** job before they can be merged.
843
+
844
+ ### ๐Ÿ“ Pull request guidelines
845
+
846
+ - Keep PRs focused โ€” one feature or fix per pull request.
847
+ - Write a clear title and description. Reference any related issues (e.g. "Closes #42").
848
+ - Ensure all existing tests still pass (`bundle exec rspec`) and **add new specs** for your changes.
849
+ - Unit specs go under [`spec/composable_agents_test/`](https://github.com/Muriel-Salvan/composable_agents/tree/main/spec/composable_agents_test).
850
+ - Scenario specs (integration, documentation, packaging) go under [`spec/scenarios/`](https://github.com/Muriel-Salvan/composable_agents/tree/main/spec/scenarios).
851
+ - Run RuboCop and address any offenses.
852
+ - If your change adds a new public API method, document it with [YARD](https://yardoc.org/) โ€” the project enforces 100 % documented coverage.
853
+ - Commits should follow the [Conventional Commits](https://www.conventionalcommits.org/) style (e.g. `feat:`, `fix:`, `chore:`, `docs:`) so that `semantic-release` can determine the next version number automatically.
854
+
855
+ ### ๐Ÿ“„ License
856
+
857
+ By contributing, you agree that your contributions will be licensed under the [BSD-3-Clause License](https://github.com/Muriel-Salvan/composable_agents/blob/main/LICENSE) that covers the project.
858
+
859
+ ---
860
+
861
+ *Questions? Open a [Discussion](https://github.com/Muriel-Salvan/composable_agents/discussions) or tag `@Muriel-Salvan` in an issue.* ๐Ÿ’ฌ
26
862
 
27
863
  ## License
28
864
 
29
- The gem is available as open source under the terms of the [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause).
865
+ The project is licensed under the **BSD 3-Clause License**.
866
+
867
+ See the [LICENSE](https://github.com/Muriel-Salvan/composable_agents/blob/main/LICENSE) file for the full terms.
868
+
869
+ Copyright ยฉ 2026, Muriel Salvan. All rights reserved.
data/TODO.md CHANGED
@@ -1,4 +1,3 @@
1
- * README.rb.
2
1
  * Check Rubocop-rspec exceptions and try to remove them.
3
2
  * Add agents manifesto for contributors.
4
3
  * Add some info logging (not debug) that would allow detecting when infinite loops or blocked agents occur.
@@ -2,5 +2,5 @@ module ComposableAgents
2
2
  # @!group Public API
3
3
 
4
4
  # Gem version
5
- VERSION = '1.0.0'
5
+ VERSION = '1.0.1'
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: composable_agents
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Muriel Salvan