@every-env/compound-plugin 0.2.0 → 0.5.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 (100) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.github/workflows/ci.yml +1 -1
  3. package/.github/workflows/deploy-docs.yml +3 -3
  4. package/.github/workflows/publish.yml +37 -0
  5. package/README.md +12 -3
  6. package/docs/index.html +13 -13
  7. package/docs/pages/changelog.html +39 -0
  8. package/docs/plans/2026-02-08-feat-convert-local-md-settings-for-opencode-codex-plan.md +143 -0
  9. package/docs/plans/2026-02-08-feat-simplify-plugin-settings-plan.md +195 -0
  10. package/docs/plans/2026-02-08-refactor-reduce-plugin-context-token-usage-plan.md +212 -0
  11. package/docs/plans/2026-02-09-refactor-dspy-ruby-skill-update-plan.md +104 -0
  12. package/docs/plans/2026-02-12-feat-add-cursor-cli-target-provider-plan.md +306 -0
  13. package/docs/specs/cursor.md +85 -0
  14. package/package.json +1 -1
  15. package/plugins/compound-engineering/.claude-plugin/plugin.json +2 -2
  16. package/plugins/compound-engineering/CHANGELOG.md +64 -0
  17. package/plugins/compound-engineering/README.md +5 -3
  18. package/plugins/compound-engineering/agents/design/design-implementation-reviewer.md +16 -1
  19. package/plugins/compound-engineering/agents/design/design-iterator.md +28 -1
  20. package/plugins/compound-engineering/agents/design/figma-design-sync.md +19 -1
  21. package/plugins/compound-engineering/agents/docs/ankane-readme-writer.md +16 -1
  22. package/plugins/compound-engineering/agents/research/best-practices-researcher.md +16 -1
  23. package/plugins/compound-engineering/agents/research/framework-docs-researcher.md +16 -1
  24. package/plugins/compound-engineering/agents/research/git-history-analyzer.md +16 -1
  25. package/plugins/compound-engineering/agents/research/learnings-researcher.md +22 -1
  26. package/plugins/compound-engineering/agents/research/repo-research-analyst.md +22 -1
  27. package/plugins/compound-engineering/agents/review/agent-native-reviewer.md +16 -1
  28. package/plugins/compound-engineering/agents/review/architecture-strategist.md +16 -1
  29. package/plugins/compound-engineering/agents/review/code-simplicity-reviewer.md +16 -1
  30. package/plugins/compound-engineering/agents/review/data-integrity-guardian.md +16 -1
  31. package/plugins/compound-engineering/agents/review/data-migration-expert.md +16 -1
  32. package/plugins/compound-engineering/agents/review/deployment-verification-agent.md +16 -1
  33. package/plugins/compound-engineering/agents/review/dhh-rails-reviewer.md +22 -1
  34. package/plugins/compound-engineering/agents/review/julik-frontend-races-reviewer.md +20 -21
  35. package/plugins/compound-engineering/agents/review/kieran-python-reviewer.md +30 -1
  36. package/plugins/compound-engineering/agents/review/kieran-rails-reviewer.md +30 -1
  37. package/plugins/compound-engineering/agents/review/kieran-typescript-reviewer.md +30 -1
  38. package/plugins/compound-engineering/agents/review/pattern-recognition-specialist.md +16 -1
  39. package/plugins/compound-engineering/agents/review/performance-oracle.md +28 -1
  40. package/plugins/compound-engineering/agents/review/schema-drift-detector.md +16 -1
  41. package/plugins/compound-engineering/agents/review/security-sentinel.md +22 -1
  42. package/plugins/compound-engineering/agents/workflow/bug-reproduction-validator.md +16 -1
  43. package/plugins/compound-engineering/agents/workflow/every-style-editor.md +1 -1
  44. package/plugins/compound-engineering/agents/workflow/pr-comment-resolver.md +16 -1
  45. package/plugins/compound-engineering/agents/workflow/spec-flow-analyzer.md +22 -1
  46. package/plugins/compound-engineering/commands/agent-native-audit.md +1 -0
  47. package/plugins/compound-engineering/commands/changelog.md +1 -0
  48. package/plugins/compound-engineering/commands/create-agent-skill.md +1 -0
  49. package/plugins/compound-engineering/commands/deploy-docs.md +1 -0
  50. package/plugins/compound-engineering/commands/generate_command.md +1 -0
  51. package/plugins/compound-engineering/commands/heal-skill.md +1 -0
  52. package/plugins/compound-engineering/commands/lfg.md +1 -0
  53. package/plugins/compound-engineering/commands/report-bug.md +1 -0
  54. package/plugins/compound-engineering/commands/reproduce-bug.md +1 -0
  55. package/plugins/compound-engineering/commands/resolve_parallel.md +1 -0
  56. package/plugins/compound-engineering/commands/slfg.md +1 -0
  57. package/plugins/compound-engineering/commands/{xcode-test.md → test-xcode.md} +2 -1
  58. package/plugins/compound-engineering/commands/triage.md +1 -0
  59. package/plugins/compound-engineering/commands/workflows/brainstorm.md +6 -1
  60. package/plugins/compound-engineering/commands/workflows/compound.md +1 -0
  61. package/plugins/compound-engineering/commands/workflows/review.md +23 -21
  62. package/plugins/compound-engineering/commands/workflows/work.md +29 -15
  63. package/plugins/compound-engineering/skills/compound-docs/SKILL.md +1 -0
  64. package/plugins/compound-engineering/skills/dspy-ruby/SKILL.md +539 -396
  65. package/plugins/compound-engineering/skills/dspy-ruby/assets/config-template.rb +159 -331
  66. package/plugins/compound-engineering/skills/dspy-ruby/assets/module-template.rb +210 -236
  67. package/plugins/compound-engineering/skills/dspy-ruby/assets/signature-template.rb +173 -95
  68. package/plugins/compound-engineering/skills/dspy-ruby/references/core-concepts.md +552 -143
  69. package/plugins/compound-engineering/skills/dspy-ruby/references/observability.md +366 -0
  70. package/plugins/compound-engineering/skills/dspy-ruby/references/optimization.md +440 -460
  71. package/plugins/compound-engineering/skills/dspy-ruby/references/providers.md +305 -225
  72. package/plugins/compound-engineering/skills/dspy-ruby/references/toolsets.md +502 -0
  73. package/plugins/compound-engineering/skills/file-todos/SKILL.md +1 -0
  74. package/plugins/compound-engineering/skills/orchestrating-swarms/SKILL.md +1 -0
  75. package/plugins/compound-engineering/skills/setup/SKILL.md +168 -0
  76. package/plugins/compound-engineering/skills/skill-creator/SKILL.md +1 -0
  77. package/src/commands/convert.ts +10 -5
  78. package/src/commands/install.ts +10 -5
  79. package/src/converters/claude-to-codex.ts +9 -3
  80. package/src/converters/claude-to-cursor.ts +166 -0
  81. package/src/converters/claude-to-droid.ts +174 -0
  82. package/src/converters/claude-to-opencode.ts +9 -2
  83. package/src/parsers/claude.ts +4 -0
  84. package/src/targets/cursor.ts +48 -0
  85. package/src/targets/droid.ts +50 -0
  86. package/src/targets/index.ts +18 -0
  87. package/src/types/claude.ts +2 -0
  88. package/src/types/cursor.ts +29 -0
  89. package/src/types/droid.ts +20 -0
  90. package/tests/claude-parser.test.ts +24 -2
  91. package/tests/codex-converter.test.ts +100 -0
  92. package/tests/converter.test.ts +76 -0
  93. package/tests/cursor-converter.test.ts +347 -0
  94. package/tests/cursor-writer.test.ts +137 -0
  95. package/tests/droid-converter.test.ts +277 -0
  96. package/tests/droid-writer.test.ts +100 -0
  97. package/tests/fixtures/sample-plugin/commands/disabled-command.md +7 -0
  98. package/tests/fixtures/sample-plugin/skills/disabled-skill/SKILL.md +7 -0
  99. package/plugins/compound-engineering/commands/technical_review.md +0 -7
  100. /package/{plugins/compound-engineering → .claude}/commands/release-docs.md +0 -0
@@ -0,0 +1,502 @@
1
+ # DSPy.rb Toolsets
2
+
3
+ ## Tools::Base
4
+
5
+ `DSPy::Tools::Base` is the base class for single-purpose tools. Each subclass exposes one operation to an LLM agent through a `call` method.
6
+
7
+ ### Defining a Tool
8
+
9
+ Set the tool's identity with the `tool_name` and `tool_description` class-level DSL methods. Define the `call` instance method with a Sorbet `sig` declaration so DSPy.rb can generate the JSON schema the LLM uses to invoke the tool.
10
+
11
+ ```ruby
12
+ class WeatherLookup < DSPy::Tools::Base
13
+ extend T::Sig
14
+
15
+ tool_name "weather_lookup"
16
+ tool_description "Look up current weather for a given city"
17
+
18
+ sig { params(city: String, units: T.nilable(String)).returns(String) }
19
+ def call(city:, units: nil)
20
+ # Fetch weather data and return a string summary
21
+ "72F and sunny in #{city}"
22
+ end
23
+ end
24
+ ```
25
+
26
+ Key points:
27
+
28
+ - Inherit from `DSPy::Tools::Base`, not `DSPy::Tool`.
29
+ - Use `tool_name` (class method) to set the name the LLM sees. Without it, the class name is lowercased as a fallback.
30
+ - Use `tool_description` (class method) to set the human-readable description surfaced in the tool schema.
31
+ - The `call` method must use **keyword arguments**. Positional arguments are supported but keyword arguments produce better schemas.
32
+ - Always attach a Sorbet `sig` to `call`. Without a signature, the generated schema has empty properties and the LLM cannot determine parameter types.
33
+
34
+ ### Schema Generation
35
+
36
+ `call_schema_object` introspects the Sorbet signature on `call` and returns a hash representing the JSON Schema `parameters` object:
37
+
38
+ ```ruby
39
+ WeatherLookup.call_schema_object
40
+ # => {
41
+ # type: "object",
42
+ # properties: {
43
+ # city: { type: "string", description: "Parameter city" },
44
+ # units: { type: "string", description: "Parameter units (optional)" }
45
+ # },
46
+ # required: ["city"]
47
+ # }
48
+ ```
49
+
50
+ `call_schema` wraps this in the full LLM tool-calling format:
51
+
52
+ ```ruby
53
+ WeatherLookup.call_schema
54
+ # => {
55
+ # type: "function",
56
+ # function: {
57
+ # name: "call",
58
+ # description: "Call the WeatherLookup tool",
59
+ # parameters: { ... }
60
+ # }
61
+ # }
62
+ ```
63
+
64
+ ### Using Tools with ReAct
65
+
66
+ Pass tool instances in an array to `DSPy::ReAct`:
67
+
68
+ ```ruby
69
+ agent = DSPy::ReAct.new(
70
+ MySignature,
71
+ tools: [WeatherLookup.new, AnotherTool.new]
72
+ )
73
+
74
+ result = agent.call(question: "What is the weather in Berlin?")
75
+ puts result.answer
76
+ ```
77
+
78
+ Access output fields with dot notation (`result.answer`), not hash access (`result[:answer]`).
79
+
80
+ ---
81
+
82
+ ## Tools::Toolset
83
+
84
+ `DSPy::Tools::Toolset` groups multiple related methods into a single class. Each exposed method becomes an independent tool from the LLM's perspective.
85
+
86
+ ### Defining a Toolset
87
+
88
+ ```ruby
89
+ class DatabaseToolset < DSPy::Tools::Toolset
90
+ extend T::Sig
91
+
92
+ toolset_name "db"
93
+
94
+ tool :query, description: "Run a read-only SQL query"
95
+ tool :insert, description: "Insert a record into a table"
96
+ tool :delete, description: "Delete a record by ID"
97
+
98
+ sig { params(sql: String).returns(String) }
99
+ def query(sql:)
100
+ # Execute read query
101
+ end
102
+
103
+ sig { params(table: String, data: T::Hash[String, String]).returns(String) }
104
+ def insert(table:, data:)
105
+ # Insert record
106
+ end
107
+
108
+ sig { params(table: String, id: Integer).returns(String) }
109
+ def delete(table:, id:)
110
+ # Delete record
111
+ end
112
+ end
113
+ ```
114
+
115
+ ### DSL Methods
116
+
117
+ **`toolset_name(name)`** -- Set the prefix for all generated tool names. If omitted, the class name minus `Toolset` suffix is lowercased (e.g., `DatabaseToolset` becomes `database`).
118
+
119
+ ```ruby
120
+ toolset_name "db"
121
+ # tool :query produces a tool named "db_query"
122
+ ```
123
+
124
+ **`tool(method_name, tool_name:, description:)`** -- Expose a method as a tool.
125
+
126
+ - `method_name` (Symbol, required) -- the instance method to expose.
127
+ - `tool_name:` (String, optional) -- override the default `<toolset_name>_<method_name>` naming.
128
+ - `description:` (String, optional) -- description shown to the LLM. Defaults to a humanized version of the method name.
129
+
130
+ ```ruby
131
+ tool :word_count, tool_name: "text_wc", description: "Count lines, words, and characters"
132
+ # Produces a tool named "text_wc" instead of "text_word_count"
133
+ ```
134
+
135
+ ### Converting to a Tool Array
136
+
137
+ Call `to_tools` on the class (not an instance) to get an array of `ToolProxy` objects compatible with `DSPy::Tools::Base`:
138
+
139
+ ```ruby
140
+ agent = DSPy::ReAct.new(
141
+ AnalyzeText,
142
+ tools: DatabaseToolset.to_tools
143
+ )
144
+ ```
145
+
146
+ Each `ToolProxy` wraps one method, delegates `call` to the underlying toolset instance, and generates its own JSON schema from the method's Sorbet signature.
147
+
148
+ ### Shared State
149
+
150
+ All tool proxies from a single `to_tools` call share one toolset instance. Store shared state (connections, caches, configuration) in the toolset's `initialize`:
151
+
152
+ ```ruby
153
+ class ApiToolset < DSPy::Tools::Toolset
154
+ extend T::Sig
155
+
156
+ toolset_name "api"
157
+
158
+ tool :get, description: "Make a GET request"
159
+ tool :post, description: "Make a POST request"
160
+
161
+ sig { params(base_url: String).void }
162
+ def initialize(base_url:)
163
+ @base_url = base_url
164
+ @client = HTTP.persistent(base_url)
165
+ end
166
+
167
+ sig { params(path: String).returns(String) }
168
+ def get(path:)
169
+ @client.get("#{@base_url}#{path}").body.to_s
170
+ end
171
+
172
+ sig { params(path: String, body: String).returns(String) }
173
+ def post(path:, body:)
174
+ @client.post("#{@base_url}#{path}", body: body).body.to_s
175
+ end
176
+ end
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Type Safety
182
+
183
+ Sorbet signatures on tool methods drive both JSON schema generation and automatic type coercion of LLM responses.
184
+
185
+ ### Basic Types
186
+
187
+ ```ruby
188
+ sig { params(
189
+ text: String,
190
+ count: Integer,
191
+ score: Float,
192
+ enabled: T::Boolean,
193
+ threshold: Numeric
194
+ ).returns(String) }
195
+ def analyze(text:, count:, score:, enabled:, threshold:)
196
+ # ...
197
+ end
198
+ ```
199
+
200
+ | Sorbet Type | JSON Schema |
201
+ |------------------|----------------------------------------------------|
202
+ | `String` | `{"type": "string"}` |
203
+ | `Integer` | `{"type": "integer"}` |
204
+ | `Float` | `{"type": "number"}` |
205
+ | `Numeric` | `{"type": "number"}` |
206
+ | `T::Boolean` | `{"type": "boolean"}` |
207
+ | `T::Enum` | `{"type": "string", "enum": [...]}` |
208
+ | `T::Struct` | `{"type": "object", "properties": {...}}` |
209
+ | `T::Array[Type]` | `{"type": "array", "items": {...}}` |
210
+ | `T::Hash[K, V]` | `{"type": "object", "additionalProperties": {...}}`|
211
+ | `T.nilable(Type)`| `{"type": [original, "null"]}` |
212
+ | `T.any(T1, T2)` | `{"oneOf": [{...}, {...}]}` |
213
+ | `T.class_of(X)` | `{"type": "string"}` |
214
+
215
+ ### T::Enum Parameters
216
+
217
+ Define a `T::Enum` and reference it in a tool signature. DSPy.rb generates a JSON Schema `enum` constraint and automatically deserializes the LLM's string response into the correct enum instance.
218
+
219
+ ```ruby
220
+ class Priority < T::Enum
221
+ enums do
222
+ Low = new('low')
223
+ Medium = new('medium')
224
+ High = new('high')
225
+ Critical = new('critical')
226
+ end
227
+ end
228
+
229
+ class Status < T::Enum
230
+ enums do
231
+ Pending = new('pending')
232
+ InProgress = new('in-progress')
233
+ Completed = new('completed')
234
+ end
235
+ end
236
+
237
+ sig { params(priority: Priority, status: Status).returns(String) }
238
+ def update_task(priority:, status:)
239
+ "Updated to #{priority.serialize} / #{status.serialize}"
240
+ end
241
+ ```
242
+
243
+ The generated schema constrains the parameter to valid values:
244
+
245
+ ```json
246
+ {
247
+ "priority": {
248
+ "type": "string",
249
+ "enum": ["low", "medium", "high", "critical"]
250
+ }
251
+ }
252
+ ```
253
+
254
+ **Case-insensitive matching**: When the LLM returns `"HIGH"` or `"High"` instead of `"high"`, DSPy.rb first tries an exact `try_deserialize`, then falls back to a case-insensitive lookup. This prevents failures caused by LLM casing variations.
255
+
256
+ ### T::Struct Parameters
257
+
258
+ Use `T::Struct` for complex nested objects. DSPy.rb generates nested JSON Schema properties and recursively coerces the LLM's hash response into struct instances.
259
+
260
+ ```ruby
261
+ class TaskMetadata < T::Struct
262
+ prop :id, String
263
+ prop :priority, Priority
264
+ prop :tags, T::Array[String]
265
+ prop :estimated_hours, T.nilable(Float), default: nil
266
+ end
267
+
268
+ class TaskRequest < T::Struct
269
+ prop :title, String
270
+ prop :description, String
271
+ prop :status, Status
272
+ prop :metadata, TaskMetadata
273
+ prop :assignees, T::Array[String]
274
+ end
275
+
276
+ sig { params(task: TaskRequest).returns(String) }
277
+ def create_task(task:)
278
+ "Created: #{task.title} (#{task.status.serialize})"
279
+ end
280
+ ```
281
+
282
+ The LLM sees the full nested object schema and DSPy.rb reconstructs the struct tree from the JSON response, including enum fields inside nested structs.
283
+
284
+ ### Nilable Parameters
285
+
286
+ Mark optional parameters with `T.nilable(...)` and provide a default value of `nil` in the method signature. These parameters are excluded from the JSON Schema `required` array.
287
+
288
+ ```ruby
289
+ sig { params(
290
+ query: String,
291
+ max_results: T.nilable(Integer),
292
+ filter: T.nilable(String)
293
+ ).returns(String) }
294
+ def search(query:, max_results: nil, filter: nil)
295
+ # query is required; max_results and filter are optional
296
+ end
297
+ ```
298
+
299
+ ### Collections
300
+
301
+ Typed arrays and hashes generate precise item/value schemas:
302
+
303
+ ```ruby
304
+ sig { params(
305
+ tags: T::Array[String],
306
+ priorities: T::Array[Priority],
307
+ config: T::Hash[String, T.any(String, Integer, Float)]
308
+ ).returns(String) }
309
+ def configure(tags:, priorities:, config:)
310
+ # Array elements and hash values are validated and coerced
311
+ end
312
+ ```
313
+
314
+ ### Union Types
315
+
316
+ `T.any(...)` generates a `oneOf` JSON Schema. When one of the union members is a `T::Struct`, DSPy.rb uses the `_type` discriminator field to select the correct struct class during coercion.
317
+
318
+ ```ruby
319
+ sig { params(value: T.any(String, Integer, Float)).returns(String) }
320
+ def handle_flexible(value:)
321
+ # Accepts multiple types
322
+ end
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Built-in Toolsets
328
+
329
+ ### TextProcessingToolset
330
+
331
+ `DSPy::Tools::TextProcessingToolset` provides Unix-style text analysis and manipulation operations. Toolset name prefix: `text`.
332
+
333
+ | Tool Name | Method | Description |
334
+ |-----------------------------------|-------------------|--------------------------------------------|
335
+ | `text_grep` | `grep` | Search for patterns with optional case-insensitive and count-only modes |
336
+ | `text_wc` | `word_count` | Count lines, words, and characters |
337
+ | `text_rg` | `ripgrep` | Fast pattern search with context lines |
338
+ | `text_extract_lines` | `extract_lines` | Extract a range of lines by number |
339
+ | `text_filter_lines` | `filter_lines` | Keep or reject lines matching a regex |
340
+ | `text_unique_lines` | `unique_lines` | Deduplicate lines, optionally preserving order |
341
+ | `text_sort_lines` | `sort_lines` | Sort lines alphabetically or numerically |
342
+ | `text_summarize_text` | `summarize_text` | Produce a statistical summary (counts, averages, frequent words) |
343
+
344
+ Usage:
345
+
346
+ ```ruby
347
+ agent = DSPy::ReAct.new(
348
+ AnalyzeText,
349
+ tools: DSPy::Tools::TextProcessingToolset.to_tools
350
+ )
351
+
352
+ result = agent.call(text: log_contents, question: "How many error lines are there?")
353
+ puts result.answer
354
+ ```
355
+
356
+ ### GitHubCLIToolset
357
+
358
+ `DSPy::Tools::GitHubCLIToolset` wraps the `gh` CLI for read-oriented GitHub operations. Toolset name prefix: `github`.
359
+
360
+ | Tool Name | Method | Description |
361
+ |------------------------|-------------------|---------------------------------------------------|
362
+ | `github_list_issues` | `list_issues` | List issues filtered by state, labels, assignee |
363
+ | `github_list_prs` | `list_prs` | List pull requests filtered by state, author, base|
364
+ | `github_get_issue` | `get_issue` | Retrieve details of a single issue |
365
+ | `github_get_pr` | `get_pr` | Retrieve details of a single pull request |
366
+ | `github_api_request` | `api_request` | Make an arbitrary GET request to the GitHub API |
367
+ | `github_traffic_views` | `traffic_views` | Fetch repository traffic view counts |
368
+ | `github_traffic_clones`| `traffic_clones` | Fetch repository traffic clone counts |
369
+
370
+ This toolset uses `T::Enum` parameters (`IssueState`, `PRState`, `ReviewState`) for state filters, demonstrating enum-based tool signatures in practice.
371
+
372
+ ```ruby
373
+ agent = DSPy::ReAct.new(
374
+ RepoAnalysis,
375
+ tools: DSPy::Tools::GitHubCLIToolset.to_tools
376
+ )
377
+ ```
378
+
379
+ ---
380
+
381
+ ## Testing
382
+
383
+ ### Unit Testing Individual Tools
384
+
385
+ Test `DSPy::Tools::Base` subclasses by instantiating and calling `call` directly:
386
+
387
+ ```ruby
388
+ RSpec.describe WeatherLookup do
389
+ subject(:tool) { described_class.new }
390
+
391
+ it "returns weather for a city" do
392
+ result = tool.call(city: "Berlin")
393
+ expect(result).to include("Berlin")
394
+ end
395
+
396
+ it "exposes the correct tool name" do
397
+ expect(tool.name).to eq("weather_lookup")
398
+ end
399
+
400
+ it "generates a valid schema" do
401
+ schema = described_class.call_schema_object
402
+ expect(schema[:required]).to include("city")
403
+ expect(schema[:properties]).to have_key(:city)
404
+ end
405
+ end
406
+ ```
407
+
408
+ ### Unit Testing Toolsets
409
+
410
+ Test toolset methods directly on an instance. Verify tool generation with `to_tools`:
411
+
412
+ ```ruby
413
+ RSpec.describe DatabaseToolset do
414
+ subject(:toolset) { described_class.new }
415
+
416
+ it "executes a query" do
417
+ result = toolset.query(sql: "SELECT 1")
418
+ expect(result).to be_a(String)
419
+ end
420
+
421
+ it "generates tools with correct names" do
422
+ tools = described_class.to_tools
423
+ names = tools.map(&:name)
424
+ expect(names).to contain_exactly("db_query", "db_insert", "db_delete")
425
+ end
426
+
427
+ it "generates tool descriptions" do
428
+ tools = described_class.to_tools
429
+ query_tool = tools.find { |t| t.name == "db_query" }
430
+ expect(query_tool.description).to eq("Run a read-only SQL query")
431
+ end
432
+ end
433
+ ```
434
+
435
+ ### Mocking Predictions Inside Tools
436
+
437
+ When a tool calls a DSPy predictor internally, stub the predictor to isolate tool logic from LLM calls:
438
+
439
+ ```ruby
440
+ class SmartSearchTool < DSPy::Tools::Base
441
+ extend T::Sig
442
+
443
+ tool_name "smart_search"
444
+ tool_description "Search with query expansion"
445
+
446
+ sig { void }
447
+ def initialize
448
+ @expander = DSPy::Predict.new(QueryExpansionSignature)
449
+ end
450
+
451
+ sig { params(query: String).returns(String) }
452
+ def call(query:)
453
+ expanded = @expander.call(query: query)
454
+ perform_search(expanded.expanded_query)
455
+ end
456
+
457
+ private
458
+
459
+ def perform_search(query)
460
+ # actual search logic
461
+ end
462
+ end
463
+
464
+ RSpec.describe SmartSearchTool do
465
+ subject(:tool) { described_class.new }
466
+
467
+ before do
468
+ expansion_result = double("result", expanded_query: "expanded test query")
469
+ allow_any_instance_of(DSPy::Predict).to receive(:call).and_return(expansion_result)
470
+ end
471
+
472
+ it "expands the query before searching" do
473
+ allow(tool).to receive(:perform_search).with("expanded test query").and_return("found 3 results")
474
+ result = tool.call(query: "test")
475
+ expect(result).to eq("found 3 results")
476
+ end
477
+ end
478
+ ```
479
+
480
+ ### Testing Enum Coercion
481
+
482
+ Verify that string values from LLM responses deserialize into the correct enum instances:
483
+
484
+ ```ruby
485
+ RSpec.describe "enum coercion" do
486
+ it "handles case-insensitive enum values" do
487
+ toolset = GitHubCLIToolset.new
488
+ # The LLM may return "OPEN" instead of "open"
489
+ result = toolset.list_issues(state: IssueState::Open)
490
+ expect(result).to be_a(String)
491
+ end
492
+ end
493
+ ```
494
+
495
+ ---
496
+
497
+ ## Constraints
498
+
499
+ - All exposed tool methods must use **keyword arguments**. Positional-only parameters generate schemas but keyword arguments produce more reliable LLM interactions.
500
+ - Each exposed method becomes a **separate, independent tool**. Method chaining or multi-step sequences within a single tool call are not supported.
501
+ - Shared state across tool proxies is scoped to a single `to_tools` call. Separate `to_tools` invocations create separate toolset instances.
502
+ - Methods without a Sorbet `sig` produce an empty parameter schema. The LLM will not know what arguments to pass.
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: file-todos
3
3
  description: This skill should be used when managing the file-based todo tracking system in the todos/ directory. It provides workflows for creating todos, managing status and dependencies, conducting triage, and integrating with slash commands and code review processes.
4
+ disable-model-invocation: true
4
5
  ---
5
6
 
6
7
  # File-Based Todo Tracking Skill
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: orchestrating-swarms
3
3
  description: This skill should be used when orchestrating multi-agent swarms using Claude Code's TeammateTool and Task system. It applies when coordinating multiple agents, running parallel code reviews, creating pipeline workflows with dependencies, building self-organizing task queues, or any task benefiting from divide-and-conquer patterns.
4
+ disable-model-invocation: true
4
5
  ---
5
6
 
6
7
  # Claude Code Swarm Orchestration
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: setup
3
+ description: Configure which review agents run for your project. Auto-detects stack and writes compound-engineering.local.md.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Compound Engineering Setup
8
+
9
+ Interactive setup for `compound-engineering.local.md` — configures which agents run during `/workflows:review` and `/workflows:work`.
10
+
11
+ ## Step 1: Check Existing Config
12
+
13
+ Read `compound-engineering.local.md` in the project root. If it exists, display current settings summary and use AskUserQuestion:
14
+
15
+ ```
16
+ question: "Settings file already exists. What would you like to do?"
17
+ header: "Config"
18
+ options:
19
+ - label: "Reconfigure"
20
+ description: "Run the interactive setup again from scratch"
21
+ - label: "View current"
22
+ description: "Show the file contents, then stop"
23
+ - label: "Cancel"
24
+ description: "Keep current settings"
25
+ ```
26
+
27
+ If "View current": read and display the file, then stop.
28
+ If "Cancel": stop.
29
+
30
+ ## Step 2: Detect and Ask
31
+
32
+ Auto-detect the project stack:
33
+
34
+ ```bash
35
+ test -f Gemfile && test -f config/routes.rb && echo "rails" || \
36
+ test -f Gemfile && echo "ruby" || \
37
+ test -f tsconfig.json && echo "typescript" || \
38
+ test -f package.json && echo "javascript" || \
39
+ test -f pyproject.toml && echo "python" || \
40
+ test -f requirements.txt && echo "python" || \
41
+ echo "general"
42
+ ```
43
+
44
+ Use AskUserQuestion:
45
+
46
+ ```
47
+ question: "Detected {type} project. How would you like to configure?"
48
+ header: "Setup"
49
+ options:
50
+ - label: "Auto-configure (Recommended)"
51
+ description: "Use smart defaults for {type}. Done in one click."
52
+ - label: "Customize"
53
+ description: "Choose stack, focus areas, and review depth."
54
+ ```
55
+
56
+ ### If Auto-configure → Skip to Step 4 with defaults:
57
+
58
+ - **Rails:** `[kieran-rails-reviewer, dhh-rails-reviewer, code-simplicity-reviewer, security-sentinel, performance-oracle]`
59
+ - **Python:** `[kieran-python-reviewer, code-simplicity-reviewer, security-sentinel, performance-oracle]`
60
+ - **TypeScript:** `[kieran-typescript-reviewer, code-simplicity-reviewer, security-sentinel, performance-oracle]`
61
+ - **General:** `[code-simplicity-reviewer, security-sentinel, performance-oracle, architecture-strategist]`
62
+
63
+ ### If Customize → Step 3
64
+
65
+ ## Step 3: Customize (3 questions)
66
+
67
+ **a. Stack** — confirm or override:
68
+
69
+ ```
70
+ question: "Which stack should we optimize for?"
71
+ header: "Stack"
72
+ options:
73
+ - label: "{detected_type} (Recommended)"
74
+ description: "Auto-detected from project files"
75
+ - label: "Rails"
76
+ description: "Ruby on Rails — adds DHH-style and Rails-specific reviewers"
77
+ - label: "Python"
78
+ description: "Python — adds Pythonic pattern reviewer"
79
+ - label: "TypeScript"
80
+ description: "TypeScript — adds type safety reviewer"
81
+ ```
82
+
83
+ Only show options that differ from the detected type.
84
+
85
+ **b. Focus areas** — multiSelect:
86
+
87
+ ```
88
+ question: "Which review areas matter most?"
89
+ header: "Focus"
90
+ multiSelect: true
91
+ options:
92
+ - label: "Security"
93
+ description: "Vulnerability scanning, auth, input validation (security-sentinel)"
94
+ - label: "Performance"
95
+ description: "N+1 queries, memory leaks, complexity (performance-oracle)"
96
+ - label: "Architecture"
97
+ description: "Design patterns, SOLID, separation of concerns (architecture-strategist)"
98
+ - label: "Code simplicity"
99
+ description: "Over-engineering, YAGNI violations (code-simplicity-reviewer)"
100
+ ```
101
+
102
+ **c. Depth:**
103
+
104
+ ```
105
+ question: "How thorough should reviews be?"
106
+ header: "Depth"
107
+ options:
108
+ - label: "Thorough (Recommended)"
109
+ description: "Stack reviewers + all selected focus agents."
110
+ - label: "Fast"
111
+ description: "Stack reviewers + code simplicity only. Less context, quicker."
112
+ - label: "Comprehensive"
113
+ description: "All above + git history, data integrity, agent-native checks."
114
+ ```
115
+
116
+ ## Step 4: Build Agent List and Write File
117
+
118
+ **Stack-specific agents:**
119
+ - Rails → `kieran-rails-reviewer, dhh-rails-reviewer`
120
+ - Python → `kieran-python-reviewer`
121
+ - TypeScript → `kieran-typescript-reviewer`
122
+ - General → (none)
123
+
124
+ **Focus area agents:**
125
+ - Security → `security-sentinel`
126
+ - Performance → `performance-oracle`
127
+ - Architecture → `architecture-strategist`
128
+ - Code simplicity → `code-simplicity-reviewer`
129
+
130
+ **Depth:**
131
+ - Thorough: stack + selected focus areas
132
+ - Fast: stack + `code-simplicity-reviewer` only
133
+ - Comprehensive: all above + `git-history-analyzer, data-integrity-guardian, agent-native-reviewer`
134
+
135
+ **Plan review agents:** stack-specific reviewer + `code-simplicity-reviewer`.
136
+
137
+ Write `compound-engineering.local.md`:
138
+
139
+ ```markdown
140
+ ---
141
+ review_agents: [{computed agent list}]
142
+ plan_review_agents: [{computed plan agent list}]
143
+ ---
144
+
145
+ # Review Context
146
+
147
+ Add project-specific review instructions here.
148
+ These notes are passed to all review agents during /workflows:review and /workflows:work.
149
+
150
+ Examples:
151
+ - "We use Turbo Frames heavily — check for frame-busting issues"
152
+ - "Our API is public — extra scrutiny on input validation"
153
+ - "Performance-critical: we serve 10k req/s on this endpoint"
154
+ ```
155
+
156
+ ## Step 5: Confirm
157
+
158
+ ```
159
+ Saved to compound-engineering.local.md
160
+
161
+ Stack: {type}
162
+ Review depth: {depth}
163
+ Agents: {count} configured
164
+ {agent list, one per line}
165
+
166
+ Tip: Edit the "Review Context" section to add project-specific instructions.
167
+ Re-run this setup anytime to reconfigure.
168
+ ```
@@ -2,6 +2,7 @@
2
2
  name: skill-creator
3
3
  description: Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.
4
4
  license: Complete terms in LICENSE.txt
5
+ disable-model-invocation: true
5
6
  ---
6
7
 
7
8
  # Skill Creator