robot_lab 0.2.8 → 0.3.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.
Files changed (262) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +65 -0
  3. data/README.md +16 -18
  4. data/lib/robot_lab/ask_user.rb +3 -3
  5. data/lib/robot_lab/config/defaults.yml +5 -5
  6. data/lib/robot_lab/config.rb +2 -2
  7. data/lib/robot_lab/mcp/transports/streamable_http.rb +2 -4
  8. data/lib/robot_lab/memory.rb +16 -7
  9. data/lib/robot_lab/robot/hooking.rb +26 -0
  10. data/lib/robot_lab/robot/result_building.rb +119 -0
  11. data/lib/robot_lab/robot.rb +37 -120
  12. data/lib/robot_lab/run_config.rb +52 -20
  13. data/lib/robot_lab/tool.rb +7 -12
  14. data/lib/robot_lab/version.rb +1 -1
  15. data/lib/robot_lab.rb +3 -3
  16. metadata +15 -274
  17. data/.architecture/AGENTS.md +0 -32
  18. data/.architecture/config.yml +0 -8
  19. data/.architecture/members.yml +0 -60
  20. data/.architecture/reviews/feature-free-will.md +0 -490
  21. data/.architecture/reviews/overall-codebase.md +0 -427
  22. data/.claude/settings.local.json +0 -57
  23. data/.codex/config.toml +0 -2
  24. data/.envrc +0 -6
  25. data/.github/workflows/deploy-github-pages.yml +0 -52
  26. data/.irbrc +0 -6
  27. data/.loki +0 -65
  28. data/.quality/reek_baseline.txt +0 -43
  29. data/.rubocop.yml +0 -10
  30. data/Archspec.rb +0 -44
  31. data/CLAUDE.md +0 -139
  32. data/COMMITS.md +0 -196
  33. data/Rakefile +0 -193
  34. data/_typos.toml +0 -21
  35. data/agent2agent_review.md +0 -192
  36. data/agentf_improvements.md +0 -253
  37. data/agents.md +0 -14
  38. data/docs/api/core/index.md +0 -147
  39. data/docs/api/core/memory.md +0 -578
  40. data/docs/api/core/network.md +0 -553
  41. data/docs/api/core/result.md +0 -211
  42. data/docs/api/core/robot.md +0 -1667
  43. data/docs/api/core/state.md +0 -145
  44. data/docs/api/core/tool.md +0 -520
  45. data/docs/api/errors.md +0 -323
  46. data/docs/api/hooks.md +0 -469
  47. data/docs/api/index.md +0 -137
  48. data/docs/api/mcp/client.md +0 -387
  49. data/docs/api/mcp/index.md +0 -267
  50. data/docs/api/mcp/server.md +0 -252
  51. data/docs/api/mcp/transports.md +0 -339
  52. data/docs/api/messages/index.md +0 -126
  53. data/docs/api/messages/text-message.md +0 -170
  54. data/docs/api/messages/tool-call-message.md +0 -206
  55. data/docs/api/messages/tool-result-message.md +0 -242
  56. data/docs/api/messages/user-message.md +0 -232
  57. data/docs/api/skills.md +0 -423
  58. data/docs/api/streaming/context.md +0 -280
  59. data/docs/api/streaming/events.md +0 -185
  60. data/docs/api/streaming/index.md +0 -156
  61. data/docs/api/support.md +0 -846
  62. data/docs/architecture/core-concepts.md +0 -575
  63. data/docs/architecture/index.md +0 -252
  64. data/docs/architecture/message-flow.md +0 -366
  65. data/docs/architecture/network-orchestration.md +0 -478
  66. data/docs/architecture/robot-execution.md +0 -396
  67. data/docs/architecture/state-management.md +0 -410
  68. data/docs/assets/css/custom.css +0 -56
  69. data/docs/assets/images/robot_lab.jpg +0 -0
  70. data/docs/concepts.md +0 -641
  71. data/docs/examples/basic-chat.md +0 -258
  72. data/docs/examples/index.md +0 -425
  73. data/docs/examples/mcp-server.md +0 -403
  74. data/docs/examples/multi-robot-network.md +0 -476
  75. data/docs/examples/tool-usage.md +0 -468
  76. data/docs/getting-started/configuration.md +0 -645
  77. data/docs/getting-started/index.md +0 -56
  78. data/docs/getting-started/installation.md +0 -200
  79. data/docs/getting-started/quick-start.md +0 -293
  80. data/docs/guides/building-robots.md +0 -1187
  81. data/docs/guides/creating-networks.md +0 -736
  82. data/docs/guides/hooks.md +0 -1206
  83. data/docs/guides/index.md +0 -78
  84. data/docs/guides/knowledge.md +0 -219
  85. data/docs/guides/mcp-integration.md +0 -740
  86. data/docs/guides/memory.md +0 -458
  87. data/docs/guides/observability.md +0 -733
  88. data/docs/guides/streaming.md +0 -323
  89. data/docs/guides/using-tools.md +0 -780
  90. data/docs/index.md +0 -280
  91. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  92. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  93. data/examples/.envrc +0 -1
  94. data/examples/01_simple_robot.rb +0 -31
  95. data/examples/02_tools.rb +0 -109
  96. data/examples/03_network.rb +0 -118
  97. data/examples/04_mcp.rb +0 -215
  98. data/examples/05_streaming.rb +0 -137
  99. data/examples/06_prompt_templates.rb +0 -325
  100. data/examples/07_network_memory.rb +0 -313
  101. data/examples/08_llm_config.rb +0 -162
  102. data/examples/09_chaining.rb +0 -251
  103. data/examples/10_memory.rb +0 -315
  104. data/examples/11_network_introspection.rb +0 -239
  105. data/examples/12_message_bus.rb +0 -67
  106. data/examples/13_spawn.rb +0 -85
  107. data/examples/14_rusty_circuit/.envrc +0 -1
  108. data/examples/14_rusty_circuit/.gitignore +0 -1
  109. data/examples/14_rusty_circuit/README.md +0 -1
  110. data/examples/14_rusty_circuit/comic.rb +0 -149
  111. data/examples/14_rusty_circuit/display.rb +0 -217
  112. data/examples/14_rusty_circuit/heckler.rb +0 -65
  113. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  114. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  115. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  116. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  117. data/examples/14_rusty_circuit/scout.rb +0 -165
  118. data/examples/14_rusty_circuit/show.log +0 -234
  119. data/examples/15_memory_network_and_bus/.envrc +0 -1
  120. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  121. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  122. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  123. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  124. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  125. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -13
  126. data/examples/15_memory_network_and_bus/output/final_article.md +0 -13
  127. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  128. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  129. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  130. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -18
  131. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -13
  132. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  133. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  134. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  135. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  136. data/examples/16_writers_room/.envrc +0 -1
  137. data/examples/16_writers_room/display.rb +0 -158
  138. data/examples/16_writers_room/output/.gitignore +0 -4
  139. data/examples/16_writers_room/output/README.md +0 -69
  140. data/examples/16_writers_room/output/opus_001.md +0 -263
  141. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  142. data/examples/16_writers_room/output/opus_002.md +0 -245
  143. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  144. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  145. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  146. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  147. data/examples/16_writers_room/prompts/writer.md +0 -37
  148. data/examples/16_writers_room/room.rb +0 -186
  149. data/examples/16_writers_room/tools.rb +0 -173
  150. data/examples/16_writers_room/writer.rb +0 -121
  151. data/examples/16_writers_room/writers_room.rb +0 -256
  152. data/examples/17_skills.rb +0 -234
  153. data/examples/18_rails/.envrc +0 -3
  154. data/examples/18_rails/.gitignore +0 -5
  155. data/examples/18_rails/Gemfile +0 -11
  156. data/examples/18_rails/README.md +0 -67
  157. data/examples/18_rails/Rakefile +0 -4
  158. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  159. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  160. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  161. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  162. data/examples/18_rails/app/models/application_record.rb +0 -5
  163. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  164. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  165. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  166. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  167. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  168. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  169. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  170. data/examples/18_rails/bin/dev +0 -7
  171. data/examples/18_rails/bin/rails +0 -6
  172. data/examples/18_rails/bin/setup +0 -15
  173. data/examples/18_rails/config/application.rb +0 -33
  174. data/examples/18_rails/config/cable.yml +0 -2
  175. data/examples/18_rails/config/database.yml +0 -5
  176. data/examples/18_rails/config/environment.rb +0 -4
  177. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  178. data/examples/18_rails/config/routes.rb +0 -6
  179. data/examples/18_rails/config.ru +0 -4
  180. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  181. data/examples/19_token_tracking.rb +0 -138
  182. data/examples/20_circuit_breaker.rb +0 -147
  183. data/examples/21_learning_loop.rb +0 -181
  184. data/examples/22_context_compression.rb +0 -172
  185. data/examples/23_convergence.rb +0 -133
  186. data/examples/24_structured_delegation.rb +0 -151
  187. data/examples/25_history_search/conversation.jsonl +0 -30
  188. data/examples/25_history_search.rb +0 -133
  189. data/examples/26_document_store/api_versioning_adr.md +0 -52
  190. data/examples/26_document_store/incident_postmortem.md +0 -46
  191. data/examples/26_document_store/postgres_runbook.md +0 -49
  192. data/examples/26_document_store/redis_caching_guide.md +0 -48
  193. data/examples/26_document_store/sidekiq_guide.md +0 -51
  194. data/examples/26_document_store.rb +0 -140
  195. data/examples/27_incident_response/incident_response.rb +0 -261
  196. data/examples/28_mcp_discovery.rb +0 -113
  197. data/examples/29_ractor_tools.rb +0 -240
  198. data/examples/30_ractor_network.rb +0 -252
  199. data/examples/31_launch_assessment.rb +0 -246
  200. data/examples/32_newsletter_reader.rb +0 -193
  201. data/examples/33_stock_generator.rb +0 -80
  202. data/examples/33_stock_predictor.rb +0 -327
  203. data/examples/34_agentskills.rb +0 -76
  204. data/examples/35_hooks.rb +0 -266
  205. data/examples/README.md +0 -533
  206. data/examples/common.rb +0 -148
  207. data/examples/prompts/assistant.md +0 -5
  208. data/examples/prompts/audit_trail.md +0 -8
  209. data/examples/prompts/billing.md +0 -10
  210. data/examples/prompts/classifier.md +0 -7
  211. data/examples/prompts/comedian.md +0 -6
  212. data/examples/prompts/comedy_critic.md +0 -10
  213. data/examples/prompts/configurable.md +0 -9
  214. data/examples/prompts/dispatcher.md +0 -12
  215. data/examples/prompts/entity_extractor.md +0 -14
  216. data/examples/prompts/escalation.md +0 -42
  217. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  218. data/examples/prompts/frontmatter_named_test.md +0 -5
  219. data/examples/prompts/frontmatter_tools_test.md +0 -6
  220. data/examples/prompts/general.md +0 -7
  221. data/examples/prompts/github_assistant.md +0 -14
  222. data/examples/prompts/helper.md +0 -4
  223. data/examples/prompts/incident_responder.md +0 -18
  224. data/examples/prompts/keyword_extractor.md +0 -11
  225. data/examples/prompts/llm_config_demo.md +0 -20
  226. data/examples/prompts/order_support.md +0 -35
  227. data/examples/prompts/os_advocate.md +0 -13
  228. data/examples/prompts/os_chief.md +0 -13
  229. data/examples/prompts/os_editor.md +0 -13
  230. data/examples/prompts/parameterized_main_test.md +0 -6
  231. data/examples/prompts/pii_redactor.md +0 -7
  232. data/examples/prompts/product_support.md +0 -37
  233. data/examples/prompts/runbook_protocol.md +0 -12
  234. data/examples/prompts/sentiment_analyzer.md +0 -12
  235. data/examples/prompts/skill_a_test.md +0 -4
  236. data/examples/prompts/skill_b_test.md +0 -4
  237. data/examples/prompts/skill_config_test.md +0 -5
  238. data/examples/prompts/skill_cycle_a_test.md +0 -6
  239. data/examples/prompts/skill_cycle_b_test.md +0 -6
  240. data/examples/prompts/skill_description_test.md +0 -4
  241. data/examples/prompts/skill_leaf_test.md +0 -4
  242. data/examples/prompts/skill_nested_test.md +0 -6
  243. data/examples/prompts/skill_refs_main_test.md +0 -6
  244. data/examples/prompts/skill_self_ref_test.md +0 -6
  245. data/examples/prompts/skill_with_mcp_test.md +0 -9
  246. data/examples/prompts/skill_with_params_test.md +0 -6
  247. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  248. data/examples/prompts/skill_with_tools_test.md +0 -6
  249. data/examples/prompts/sre_compliance.md +0 -10
  250. data/examples/prompts/structured_output.md +0 -7
  251. data/examples/prompts/synthesizer.md +0 -17
  252. data/examples/prompts/technical.md +0 -10
  253. data/examples/prompts/template_with_skills_test.md +0 -6
  254. data/examples/prompts/triage.md +0 -22
  255. data/examples/ruboruby.md +0 -423
  256. data/examples/xyzzy.rb +0 -97
  257. data/logfile +0 -8
  258. data/mkdocs.yml +0 -209
  259. data/robot_concurrency.md +0 -38
  260. data/simple_acp_review.md +0 -298
  261. data/temp.md +0 -6
  262. data/tool_manifest_plan.md +0 -155
@@ -1,247 +0,0 @@
1
- # AgentSkills.io Support — Design Spec
2
-
3
- **Date:** 2026-05-06
4
- **Status:** Approved
5
-
6
- ## Overview
7
-
8
- Add support for the [AgentSkills.io](https://agentskills.io) open standard to RobotLab's existing skills system. Skills are currently single `.md` prompt_manager template files referenced by symbol ID. This spec extends `skills:` to also recognize the AgentSkills folder format (`~/.prompts/skills/<name>/SKILL.md`) with bundled scripts auto-exposed as tools and runtime embedding-based matching for progressive disclosure.
9
-
10
- ## Goals
11
-
12
- - Unified `skills:` API — no new constructor params; format detected automatically
13
- - AgentSkills folder skills use runtime embedding similarity to decide injection per `run()` call
14
- - Scripts in `scripts/` auto-wrapped as `RobotLab::Tool` subclasses
15
- - Zero breakage to existing PM template skills behavior
16
- - Graceful degradation when embeddings fail or no skills match
17
-
18
- ## Non-Goals
19
-
20
- - Automatic catalog-wide skill discovery without explicit `skills:` listing
21
- - Fetching remote skills from registries
22
- - Skills referencing `references/` or `assets/` sub-folders (deferred)
23
- - Modifying the AgentSkills.io specification
24
-
25
- ---
26
-
27
- ## Discovery Path
28
-
29
- AgentSkill folders are resolved from a single fixed root: `~/.prompts/skills/`. Given `skills: [:check_style]`, the loader checks `~/.prompts/skills/check_style/SKILL.md`. If that file exists, it is an AgentSkill. If not, the existing PM template resolution applies.
30
-
31
- This root is not configurable in v1.
32
-
33
- ---
34
-
35
- ## New Classes
36
-
37
- ### `RobotLab::AgentSkill`
38
-
39
- Plain Ruby value object. Constructed by the catalog or the expand_skills loader.
40
-
41
- ```
42
- AgentSkill
43
- name: String # from SKILL.md front matter
44
- description: String # from SKILL.md front matter — used for embedding
45
- path: Pathname # directory path
46
- instructions: String # lazy: SKILL.md body — everything after the closing ---
47
- scripts: Array<Pathname> # lazy: glob of scripts/*
48
- ```
49
-
50
- - `instructions` and `scripts` are loaded on first access (lazy).
51
- - Raises `RobotLab::ConfigurationError` on construction if `SKILL.md` is missing `name` or `description`.
52
- - `description_vector` — memoized fastembed **passage** embedding of `description` (`DocumentStore#passage_vector`), computed on first match attempt.
53
-
54
- ### `RobotLab::AgentSkillCatalog`
55
-
56
- Module-level singleton (`RobotLab::AgentSkillCatalog`). Scans `~/.prompts/skills/` at process start (lazy, on first access). Caches `AgentSkill` objects keyed by name symbol.
57
-
58
- Responsibilities:
59
- - `find(id)` — return `AgentSkill` for a given symbol, or `nil`
60
- - `all` — return all discovered skills
61
- - Internal: compute and cache description vectors using the `DocumentStore` fastembed infrastructure
62
-
63
- ### `RobotLab::AgentSkillMatching`
64
-
65
- Module included in `Robot`. Contains the `run()` override and the similarity logic.
66
-
67
- ### `RobotLab::ScriptTool`
68
-
69
- Factory method `ScriptTool.from_path(path)` returns an anonymous `RobotLab::Tool` subclass. The tool:
70
- - Name: script filename without extension, underscored (e.g. `check_style`)
71
- - Description: first comment line of the script, or `"Run #{name}"`
72
- - Execution: shells out via `Open3.capture2e`, returns stdout+stderr as the tool result
73
- - Raises `RobotLab::ToolNotFoundError` if the script is not executable
74
-
75
- ---
76
-
77
- ## Modified Code
78
-
79
- ### `Robot::TemplateRendering#expand_skills`
80
-
81
- Before resolving a skill ID as a PM template, check `AgentSkillCatalog.find(skill_id)`:
82
-
83
- ```
84
- expand_skills([:runbook_protocol, :check_style], visited)
85
- :runbook_protocol → catalog.find → nil → PM template (existing path)
86
- :check_style → catalog.find → AgentSkill<check_style>
87
- → store in @pending_agent_skills
88
- → do NOT add to @expanded_skills
89
- ```
90
-
91
- `@expanded_skills` continues to hold only PM-format skill IDs, preserving all existing behavior.
92
-
93
- ### `Robot` constructor
94
-
95
- Add `@pending_agent_skills = []` alongside `@expanded_skills = nil`.
96
-
97
- ### `Robot::AgentSkillMatching#run`
98
-
99
- Prepended to `Robot` to intercept `run()`:
100
-
101
- ```ruby
102
- def run(message, **kwargs)
103
- activated = match_agent_skills(message)
104
- inject_agent_skills(activated)
105
- super
106
- ensure
107
- restore_after_agent_skills(activated)
108
- end
109
- ```
110
-
111
- **`match_agent_skills(message)`**
112
- 1. Return `[]` if `@pending_agent_skills` is empty
113
- 2. Embed `message` as a **query** vector (`DocumentStore#query_vector`) — distinct from the passage embedding used for skill descriptions
114
- 3. For each pending AgentSkill, compute cosine similarity between message query-vector and `skill.description_vector` (passage-vector)
115
- 4. Return skills where similarity >= `SIMILARITY_THRESHOLD` (default: `0.70`)
116
-
117
- **`inject_agent_skills(skills)`**
118
- 1. Prepend each skill's `instructions` to the chat's system prompt (using `with_instructions`)
119
- 2. Instantiate `ScriptTool` for each script in each skill's `scripts/`; add to `@local_tools`
120
-
121
- **`restore_after_agent_skills(skills)`**
122
- 1. Remove injected script tools from `@local_tools`
123
- 2. Restore system prompt to pre-injection state
124
-
125
- The system prompt is snapshotted before injection and restored in the `ensure` block so state does not accumulate across calls.
126
-
127
- ---
128
-
129
- ## SKILL.md Format
130
-
131
- Follows the AgentSkills.io specification. Front matter is YAML; body is Markdown.
132
-
133
- ```markdown
134
- ---
135
- name: check_style
136
- description: Review Ruby code style against project conventions
137
- ---
138
- When reviewing code, check for:
139
- - Frozen string literal comments
140
- - Method length under 20 lines
141
- - No inline rescue
142
- ```
143
-
144
- RobotLab reads only `name` and `description` from front matter. All other front matter keys are ignored (no LLM config via SKILL.md — that remains the PM template system's domain).
145
-
146
- ---
147
-
148
- ## Script Tools
149
-
150
- Given `~/.prompts/skills/check_style/scripts/run_rubocop.sh`:
151
-
152
- - Tool name: `run_rubocop`
153
- - Description: first `# comment` line in the file, else `"Run run_rubocop"`
154
- - Input: optional `args` string passed as CLI arguments
155
- - Execution: `Open3.capture2e("bash #{path} #{args}")`, returns combined output
156
- - Not executable → log warn, skip (no error raised to the robot)
157
-
158
- ---
159
-
160
- ## Similarity Threshold
161
-
162
- Default: `0.70` (cosine similarity, 0..1 range).
163
-
164
- Accessible as `RobotLab::AgentSkillMatching::SIMILARITY_THRESHOLD`. Not yet user-configurable in v1; a future `config.agent_skill_threshold` can override it.
165
-
166
- ---
167
-
168
- ## Error Handling
169
-
170
- | Situation | Behavior |
171
- |---|---|
172
- | `SKILL.md` missing `name` or `description` | `ConfigurationError` raised at catalog load |
173
- | fastembed fails on message | Log warn, skip all AgentSkill injection for that call |
174
- | Script not executable | Log warn, skip that tool; other scripts still wrap |
175
- | No skills match threshold | Normal `run()` with no injection |
176
- | Circular skill reference via AgentSkill | Existing cycle detection in `expand_skills` handles it |
177
-
178
- ---
179
-
180
- ## Testing
181
-
182
- ### Unit tests (`test/robot_lab/agent_skill_test.rb`)
183
- - Parses `SKILL.md` front matter correctly
184
- - Raises `ConfigurationError` when `name` or `description` missing
185
- - `instructions` lazy-loads body content
186
- - `scripts` lazy-globs `scripts/` directory
187
-
188
- ### Unit tests (`test/robot_lab/agent_skill_catalog_test.rb`)
189
- - Scans fixture skills directory
190
- - `find` returns correct `AgentSkill` by symbol
191
- - `find` returns `nil` for unknown ID
192
-
193
- ### Unit tests (`test/robot_lab/script_tool_test.rb`)
194
- - `from_path` produces a tool with correct name and description
195
- - Tool execution returns stdout+stderr
196
- - Non-executable script skipped with warning
197
-
198
- ### Unit tests (`test/robot_lab/robot/agent_skill_matching_test.rb`)
199
- - `match_agent_skills` returns skills above threshold (mock embeddings)
200
- - `match_agent_skills` returns empty when below threshold
201
- - `inject_agent_skills` prepends instructions to system prompt
202
- - `restore_after_agent_skills` removes injected tools and restores prompt
203
- - Empty `@pending_agent_skills` short-circuits without embedding
204
-
205
- ### Integration tests (`test/robot_lab/robot_test.rb`)
206
- - Robot with `skills: [:pm_template, :agent_skill_fixture]` correctly expands PM template eagerly and AgentSkill at runtime
207
- - AgentSkill scripts appear in tool list during run, absent after
208
-
209
- ### Fixtures
210
- - `test/fixtures/skills/test_skill/SKILL.md` — valid skill
211
- - `test/fixtures/skills/bad_skill/SKILL.md` — missing description
212
- - `test/fixtures/skills/scripted_skill/SKILL.md` + `scripts/hello.sh`
213
-
214
- ---
215
-
216
- ## File Layout
217
-
218
- ```
219
- lib/robot_lab/
220
- agent_skill.rb # AgentSkill value object
221
- agent_skill_catalog.rb # Singleton scanner/registry
222
- script_tool.rb # ScriptTool factory
223
- robot/
224
- agent_skill_matching.rb # run() override mixin
225
-
226
- test/robot_lab/
227
- agent_skill_test.rb
228
- agent_skill_catalog_test.rb
229
- script_tool_test.rb
230
- robot/
231
- agent_skill_matching_test.rb
232
-
233
- test/fixtures/skills/
234
- test_skill/SKILL.md
235
- bad_skill/SKILL.md
236
- scripted_skill/SKILL.md
237
- scripted_skill/scripts/hello.sh
238
- ```
239
-
240
- ---
241
-
242
- ## Open Questions (deferred)
243
-
244
- - `references/` and `assets/` subdirectories: make content available as context variables in future
245
- - User-configurable similarity threshold via `RobotLab.config`
246
- - Auto-catalog mode: discover all skills without explicit `skills:` listing
247
- - AgentSkill nesting (a SKILL.md referencing other skill IDs)
data/examples/.envrc DELETED
@@ -1 +0,0 @@
1
- export ROBOT_LAB_TEMPLATE_PATH="${PWD}/prompts"
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 1: Simple Robot
5
- #
6
- # Demonstrates creating and running a basic robot with a template.
7
- #
8
- # Usage:
9
- # ruby examples/01_simple_robot.rb
10
-
11
- require_relative "common"
12
-
13
- # Create a simple robot using a template
14
- robot = RobotLab.build(
15
- **llm_opts,
16
- name: "helper",
17
- template: :helper
18
- )
19
-
20
- banner "Simple Robot"
21
-
22
- # Run the robot
23
- result = robot.run("What is 2 + 2? Please explain your reasoning briefly.")
24
-
25
- # Display the result
26
- puts "Robot: #{robot.name}"
27
- puts "Output:"
28
- result.output.each do |message|
29
- puts " #{message.content}" if message.respond_to?(:content)
30
- end
31
- hr
data/examples/02_tools.rb DELETED
@@ -1,109 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 2: Robot with Tools
5
- #
6
- # Demonstrates creating a robot with custom tools using RubyLLM::Tool.
7
- #
8
- # Usage:
9
- # ruby examples/02_tools.rb
10
-
11
- require_relative "common"
12
-
13
- # Define tools using RubyLLM::Tool
14
- class Calculator < RubyLLM::Tool
15
- description "Performs basic arithmetic operations"
16
-
17
- param :operation,
18
- type: "string",
19
- desc: "The operation to perform (add, subtract, multiply, divide)"
20
-
21
- param :a,
22
- type: "number",
23
- desc: "First operand"
24
-
25
- param :b,
26
- type: "number",
27
- desc: "Second operand"
28
-
29
- def execute(operation:, a:, b:)
30
- case operation
31
- when "add" then a + b
32
- when "subtract" then a - b
33
- when "multiply" then a * b
34
- when "divide" then a.to_f / b
35
- else "Unknown operation: #{operation}"
36
- end
37
- end
38
- end
39
-
40
- class FortuneCookie < RubyLLM::Tool
41
- description "Get a fortune cookie message with wisdom and lucky numbers"
42
-
43
- param :category,
44
- type: "string",
45
- desc: "The category of fortune (wisdom, love, career, adventure)"
46
-
47
- FORTUNES = {
48
- "wisdom" => [
49
- "The obstacle in the path becomes the path.",
50
- "A journey of a thousand miles begins with a single step.",
51
- "The best time to plant a tree was 20 years ago. The second best time is now."
52
- ],
53
- "love" => [
54
- "The heart that loves is always young.",
55
- "To love and be loved is to feel the sun from both sides.",
56
- "Love is not about finding the right person, but being the right person."
57
- ],
58
- "career" => [
59
- "Opportunity dances with those already on the dance floor.",
60
- "Your work is your signature. Sign it with excellence.",
61
- "The expert in anything was once a beginner."
62
- ],
63
- "adventure" => [
64
- "Life shrinks or expands in proportion to one's courage.",
65
- "Not all who wander are lost.",
66
- "The biggest adventure you can take is to live the life of your dreams."
67
- ]
68
- }.freeze
69
-
70
- def execute(category:)
71
- {
72
- category: category,
73
- fortune: FORTUNES.fetch(category, FORTUNES["wisdom"]).sample,
74
- lucky_numbers: Array.new(6) { rand(1..49) }.sort
75
- }
76
- end
77
- end
78
-
79
- # Create robot with tools
80
- robot = RobotLab.build(
81
- **llm_opts,
82
- name: "assistant",
83
- template: :assistant,
84
- local_tools: [Calculator, FortuneCookie]
85
- )
86
-
87
- banner "Robot with Tools"
88
-
89
- # Run the robot.
90
- #
91
- # tools: :inherit is REQUIRED to actually offer the tools to the model.
92
- # Robot#run defaults to tools: :none, which means "send zero tools this turn"
93
- # — the robot still holds them in local_tools, but the provider never sees
94
- # them and the LLM answers from memory instead of calling Calculator.
95
- # :inherit means "no per-turn filter, use every attached tool".
96
- result = robot.run(
97
- "What is 15 multiplied by 7? Also, give me a fortune about my career.",
98
- tools: :inherit
99
- )
100
-
101
- # Display results
102
- puts "Robot: #{robot.name}"
103
- puts "Tools offered: #{robot.local_tools.map(&:name).join(', ')}"
104
- puts "\nOutput:"
105
- result.output.each do |message|
106
- puts " #{message.content}" if message.respond_to?(:content)
107
- end
108
-
109
- hr
@@ -1,118 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 3: Multi-Robot Network
5
- #
6
- # Demonstrates creating a network of robots with conditional routing
7
- # using SimpleFlow's optional step activation.
8
- #
9
- # Usage:
10
- # ruby examples/03_network.rb
11
-
12
- require_relative "common"
13
-
14
- SPECIALIST_TASKS = %i[billing technical general].freeze
15
-
16
- # Classifier robot that activates the appropriate specialist
17
- class ClassifierRobot < RobotLab::Robot
18
- def call(result)
19
- run_context = extract_run_context(result)
20
- message = run_context.delete(:message)
21
- robot_result = run(message, **run_context)
22
-
23
- new_result = result
24
- .with_context(@name.to_sym, robot_result)
25
- .continue(robot_result)
26
-
27
- # Examine LLM output and activate appropriate specialist
28
- category = robot_result.reply.to_s.strip.downcase
29
-
30
- case category
31
- when /billing/
32
- new_result.activate(:billing)
33
- when /technical/
34
- new_result.activate(:technical)
35
- else
36
- new_result.activate(:general)
37
- end
38
- end
39
- end
40
-
41
- # Shared RunConfig — operational defaults every robot in this network inherits.
42
- #
43
- # RunConfig has no `provider` field (see RunConfig::FIELDS), and an Ollama
44
- # model is absent from RubyLLM's registry, so provider and model still travel
45
- # together on each robot via **llm_opts. The RunConfig carries the settings
46
- # that genuinely are shared.
47
- shared_config = RobotLab::RunConfig.new(temperature: 0.3, max_tool_rounds: 5)
48
-
49
- classifier = ClassifierRobot.new(
50
- **llm_opts,
51
- name: "classifier",
52
- template: :classifier,
53
- config: shared_config
54
- )
55
-
56
- billing_robot = RobotLab.build(
57
- **llm_opts,
58
- name: "billing",
59
- template: :billing,
60
- config: shared_config
61
- )
62
-
63
- technical_robot = RobotLab.build(
64
- **llm_opts,
65
- name: "technical",
66
- template: :technical,
67
- config: shared_config
68
- )
69
-
70
- general_robot = RobotLab.build(
71
- **llm_opts,
72
- name: "general",
73
- template: :general,
74
- config: shared_config
75
- )
76
-
77
- # Create network with optional task routing and shared config
78
- network = RobotLab.create_network(name: "support_network", config: shared_config) do
79
- task :classifier, classifier, depends_on: :none
80
- task :billing, billing_robot, depends_on: :optional
81
- task :technical, technical_robot, depends_on: :optional
82
- task :general, general_robot, depends_on: :optional
83
- end
84
-
85
- banner "Multi-Robot Network"
86
- puts "Routing plan:"
87
- puts " Entry task: classifier"
88
- puts " Optional specialists: #{SPECIALIST_TASKS.join(', ')}"
89
- puts " Runtime behavior: classifier activates exactly one specialist"
90
- puts
91
- puts "Underlying SimpleFlow structure:"
92
- puts " Optional tasks: #{network.to_h[:optional_tasks].join(', ')}"
93
- hr
94
-
95
- # Run the network with a billing question
96
- result = network.run(message: "I was charged twice for my subscription last month. Can you help?")
97
-
98
- # Display results
99
- puts "Network: #{network.name}"
100
- puts "Executed tasks: #{result.context.keys.grep_v(:run_params).join(', ')}"
101
- puts "Skipped specialists: #{(SPECIALIST_TASKS - result.context.keys).join(', ')}"
102
- puts "\nConversation flow:"
103
-
104
- # Show classifier result
105
- if result.context[:classifier]
106
- classifier_result = result.context[:classifier]
107
- puts "\n1. Robot: classifier"
108
- puts " Classification: #{classifier_result.reply}"
109
- end
110
-
111
- # Show specialist result (the final value)
112
- if result.value.is_a?(RobotLab::RobotResult)
113
- puts "\n2. Robot: #{result.value.robot_name}"
114
- content = result.value.reply
115
- puts " Response: #{content[0..200]}..." if content
116
- end
117
-
118
- hr