@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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.github/workflows/ci.yml +1 -1
- package/.github/workflows/deploy-docs.yml +3 -3
- package/.github/workflows/publish.yml +37 -0
- package/README.md +12 -3
- package/docs/index.html +13 -13
- package/docs/pages/changelog.html +39 -0
- package/docs/plans/2026-02-08-feat-convert-local-md-settings-for-opencode-codex-plan.md +143 -0
- package/docs/plans/2026-02-08-feat-simplify-plugin-settings-plan.md +195 -0
- package/docs/plans/2026-02-08-refactor-reduce-plugin-context-token-usage-plan.md +212 -0
- package/docs/plans/2026-02-09-refactor-dspy-ruby-skill-update-plan.md +104 -0
- package/docs/plans/2026-02-12-feat-add-cursor-cli-target-provider-plan.md +306 -0
- package/docs/specs/cursor.md +85 -0
- package/package.json +1 -1
- package/plugins/compound-engineering/.claude-plugin/plugin.json +2 -2
- package/plugins/compound-engineering/CHANGELOG.md +64 -0
- package/plugins/compound-engineering/README.md +5 -3
- package/plugins/compound-engineering/agents/design/design-implementation-reviewer.md +16 -1
- package/plugins/compound-engineering/agents/design/design-iterator.md +28 -1
- package/plugins/compound-engineering/agents/design/figma-design-sync.md +19 -1
- package/plugins/compound-engineering/agents/docs/ankane-readme-writer.md +16 -1
- package/plugins/compound-engineering/agents/research/best-practices-researcher.md +16 -1
- package/plugins/compound-engineering/agents/research/framework-docs-researcher.md +16 -1
- package/plugins/compound-engineering/agents/research/git-history-analyzer.md +16 -1
- package/plugins/compound-engineering/agents/research/learnings-researcher.md +22 -1
- package/plugins/compound-engineering/agents/research/repo-research-analyst.md +22 -1
- package/plugins/compound-engineering/agents/review/agent-native-reviewer.md +16 -1
- package/plugins/compound-engineering/agents/review/architecture-strategist.md +16 -1
- package/plugins/compound-engineering/agents/review/code-simplicity-reviewer.md +16 -1
- package/plugins/compound-engineering/agents/review/data-integrity-guardian.md +16 -1
- package/plugins/compound-engineering/agents/review/data-migration-expert.md +16 -1
- package/plugins/compound-engineering/agents/review/deployment-verification-agent.md +16 -1
- package/plugins/compound-engineering/agents/review/dhh-rails-reviewer.md +22 -1
- package/plugins/compound-engineering/agents/review/julik-frontend-races-reviewer.md +20 -21
- package/plugins/compound-engineering/agents/review/kieran-python-reviewer.md +30 -1
- package/plugins/compound-engineering/agents/review/kieran-rails-reviewer.md +30 -1
- package/plugins/compound-engineering/agents/review/kieran-typescript-reviewer.md +30 -1
- package/plugins/compound-engineering/agents/review/pattern-recognition-specialist.md +16 -1
- package/plugins/compound-engineering/agents/review/performance-oracle.md +28 -1
- package/plugins/compound-engineering/agents/review/schema-drift-detector.md +16 -1
- package/plugins/compound-engineering/agents/review/security-sentinel.md +22 -1
- package/plugins/compound-engineering/agents/workflow/bug-reproduction-validator.md +16 -1
- package/plugins/compound-engineering/agents/workflow/every-style-editor.md +1 -1
- package/plugins/compound-engineering/agents/workflow/pr-comment-resolver.md +16 -1
- package/plugins/compound-engineering/agents/workflow/spec-flow-analyzer.md +22 -1
- package/plugins/compound-engineering/commands/agent-native-audit.md +1 -0
- package/plugins/compound-engineering/commands/changelog.md +1 -0
- package/plugins/compound-engineering/commands/create-agent-skill.md +1 -0
- package/plugins/compound-engineering/commands/deploy-docs.md +1 -0
- package/plugins/compound-engineering/commands/generate_command.md +1 -0
- package/plugins/compound-engineering/commands/heal-skill.md +1 -0
- package/plugins/compound-engineering/commands/lfg.md +1 -0
- package/plugins/compound-engineering/commands/report-bug.md +1 -0
- package/plugins/compound-engineering/commands/reproduce-bug.md +1 -0
- package/plugins/compound-engineering/commands/resolve_parallel.md +1 -0
- package/plugins/compound-engineering/commands/slfg.md +1 -0
- package/plugins/compound-engineering/commands/{xcode-test.md → test-xcode.md} +2 -1
- package/plugins/compound-engineering/commands/triage.md +1 -0
- package/plugins/compound-engineering/commands/workflows/brainstorm.md +6 -1
- package/plugins/compound-engineering/commands/workflows/compound.md +1 -0
- package/plugins/compound-engineering/commands/workflows/review.md +23 -21
- package/plugins/compound-engineering/commands/workflows/work.md +29 -15
- package/plugins/compound-engineering/skills/compound-docs/SKILL.md +1 -0
- package/plugins/compound-engineering/skills/dspy-ruby/SKILL.md +539 -396
- package/plugins/compound-engineering/skills/dspy-ruby/assets/config-template.rb +159 -331
- package/plugins/compound-engineering/skills/dspy-ruby/assets/module-template.rb +210 -236
- package/plugins/compound-engineering/skills/dspy-ruby/assets/signature-template.rb +173 -95
- package/plugins/compound-engineering/skills/dspy-ruby/references/core-concepts.md +552 -143
- package/plugins/compound-engineering/skills/dspy-ruby/references/observability.md +366 -0
- package/plugins/compound-engineering/skills/dspy-ruby/references/optimization.md +440 -460
- package/plugins/compound-engineering/skills/dspy-ruby/references/providers.md +305 -225
- package/plugins/compound-engineering/skills/dspy-ruby/references/toolsets.md +502 -0
- package/plugins/compound-engineering/skills/file-todos/SKILL.md +1 -0
- package/plugins/compound-engineering/skills/orchestrating-swarms/SKILL.md +1 -0
- package/plugins/compound-engineering/skills/setup/SKILL.md +168 -0
- package/plugins/compound-engineering/skills/skill-creator/SKILL.md +1 -0
- package/src/commands/convert.ts +10 -5
- package/src/commands/install.ts +10 -5
- package/src/converters/claude-to-codex.ts +9 -3
- package/src/converters/claude-to-cursor.ts +166 -0
- package/src/converters/claude-to-droid.ts +174 -0
- package/src/converters/claude-to-opencode.ts +9 -2
- package/src/parsers/claude.ts +4 -0
- package/src/targets/cursor.ts +48 -0
- package/src/targets/droid.ts +50 -0
- package/src/targets/index.ts +18 -0
- package/src/types/claude.ts +2 -0
- package/src/types/cursor.ts +29 -0
- package/src/types/droid.ts +20 -0
- package/tests/claude-parser.test.ts +24 -2
- package/tests/codex-converter.test.ts +100 -0
- package/tests/converter.test.ts +76 -0
- package/tests/cursor-converter.test.ts +347 -0
- package/tests/cursor-writer.test.ts +137 -0
- package/tests/droid-converter.test.ts +277 -0
- package/tests/droid-writer.test.ts +100 -0
- package/tests/fixtures/sample-plugin/commands/disabled-command.md +7 -0
- package/tests/fixtures/sample-plugin/skills/disabled-skill/SKILL.md +7 -0
- package/plugins/compound-engineering/commands/technical_review.md +0 -7
- /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
|