botiasloop 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +343 -0
- data/bin/botiasloop +155 -0
- data/data/skills/skill-creator/SKILL.md +329 -0
- data/data/skills/skill-creator/assets/ruby_api_cli_template.rb +151 -0
- data/data/skills/skill-creator/references/specification.md +99 -0
- data/lib/botiasloop/agent.rb +112 -0
- data/lib/botiasloop/channels/base.rb +248 -0
- data/lib/botiasloop/channels/cli.rb +101 -0
- data/lib/botiasloop/channels/telegram.rb +348 -0
- data/lib/botiasloop/channels.rb +64 -0
- data/lib/botiasloop/channels_manager.rb +299 -0
- data/lib/botiasloop/commands/archive.rb +109 -0
- data/lib/botiasloop/commands/base.rb +54 -0
- data/lib/botiasloop/commands/compact.rb +78 -0
- data/lib/botiasloop/commands/context.rb +34 -0
- data/lib/botiasloop/commands/conversations.rb +40 -0
- data/lib/botiasloop/commands/help.rb +30 -0
- data/lib/botiasloop/commands/label.rb +64 -0
- data/lib/botiasloop/commands/new.rb +21 -0
- data/lib/botiasloop/commands/registry.rb +121 -0
- data/lib/botiasloop/commands/reset.rb +18 -0
- data/lib/botiasloop/commands/status.rb +32 -0
- data/lib/botiasloop/commands/switch.rb +76 -0
- data/lib/botiasloop/commands/system_prompt.rb +20 -0
- data/lib/botiasloop/commands.rb +22 -0
- data/lib/botiasloop/config.rb +58 -0
- data/lib/botiasloop/conversation.rb +189 -0
- data/lib/botiasloop/conversation_manager.rb +225 -0
- data/lib/botiasloop/database.rb +92 -0
- data/lib/botiasloop/loop.rb +115 -0
- data/lib/botiasloop/skills/loader.rb +58 -0
- data/lib/botiasloop/skills/registry.rb +42 -0
- data/lib/botiasloop/skills/skill.rb +75 -0
- data/lib/botiasloop/systemd_service.rb +300 -0
- data/lib/botiasloop/tool.rb +24 -0
- data/lib/botiasloop/tools/registry.rb +68 -0
- data/lib/botiasloop/tools/shell.rb +50 -0
- data/lib/botiasloop/tools/web_search.rb +64 -0
- data/lib/botiasloop/version.rb +5 -0
- data/lib/botiasloop.rb +45 -0
- metadata +250 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: skill-creator
|
|
3
|
+
description: Create new Agent Skills following the agentskills.io specification. Use when the user wants to create a skill for a specific task, domain, or API. This skill guides you through the complete skill creation process including directory structure, SKILL.md format, and API client creation when needed.
|
|
4
|
+
metadata:
|
|
5
|
+
author: botiasloop
|
|
6
|
+
version: "1.0"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Skill Creator
|
|
10
|
+
|
|
11
|
+
## Purpose
|
|
12
|
+
|
|
13
|
+
You are a skill creator agent. Your job is to create well-structured, useful skills that help accomplish specific tasks. Skills live in `~/skills/<skill-name>/` directories and follow the agentskills.io specification.
|
|
14
|
+
|
|
15
|
+
## When to Use
|
|
16
|
+
|
|
17
|
+
Activate this skill when:
|
|
18
|
+
- User explicitly asks to create a skill
|
|
19
|
+
- User mentions "skill for X" or "create a skill"
|
|
20
|
+
- User wants help with a recurring task that needs instructions
|
|
21
|
+
- User needs integration with an API or service
|
|
22
|
+
|
|
23
|
+
## Critical Rules
|
|
24
|
+
|
|
25
|
+
1. **ALWAYS source from official documentation** - Never guess or hallucinate API details
|
|
26
|
+
2. **Follow the specification exactly** - Invalid skills won't work
|
|
27
|
+
3. **Keep SKILL.md compact** - Move details to `references/`
|
|
28
|
+
4. **Use templates when creating scripts** - Don't reinvent patterns
|
|
29
|
+
|
|
30
|
+
## Step-by-Step Process
|
|
31
|
+
|
|
32
|
+
### Step 1: Understand the Request
|
|
33
|
+
|
|
34
|
+
Ask clarifying questions:
|
|
35
|
+
- What specific task should this skill help with?
|
|
36
|
+
- Is this for an API integration? Which API?
|
|
37
|
+
- What are the common use cases?
|
|
38
|
+
|
|
39
|
+
### Step 2: Research API Documentation (If Applicable)
|
|
40
|
+
|
|
41
|
+
If the skill involves an API:
|
|
42
|
+
|
|
43
|
+
1. **Search for official documentation first**
|
|
44
|
+
- Use web_search to find official API docs
|
|
45
|
+
- Look for docs at: `https://api.service.com/docs`, `https://developer.service.com/`
|
|
46
|
+
- Check for OpenAPI specs, README files, or official guides
|
|
47
|
+
|
|
48
|
+
2. **If official docs NOT found:**
|
|
49
|
+
- STOP immediately
|
|
50
|
+
- Tell the user: "I cannot find official documentation for [API name]. Creating a skill without proper documentation would result in incorrect or broken integration."
|
|
51
|
+
- Ask: "Do you have access to official documentation, API reference, or a specification document I can use?"
|
|
52
|
+
- Do NOT proceed without official docs
|
|
53
|
+
|
|
54
|
+
3. **If alternative (unofficial) docs found:**
|
|
55
|
+
- Tell the user: "I found [alternative source] but could not locate official documentation."
|
|
56
|
+
- Ask: "Should I proceed with this unofficial reference, or do you have official docs?"
|
|
57
|
+
- Only proceed with user explicit permission
|
|
58
|
+
|
|
59
|
+
4. **Once you have docs, READ them thoroughly:**
|
|
60
|
+
- Use Read tool on documentation files
|
|
61
|
+
- Extract: authentication methods, endpoints, request/response formats, error codes
|
|
62
|
+
- Note any code examples or SDK references
|
|
63
|
+
|
|
64
|
+
### Step 3: Create the Skill Directory
|
|
65
|
+
|
|
66
|
+
Create directory structure:
|
|
67
|
+
```
|
|
68
|
+
~/skills/<skill-name>/
|
|
69
|
+
├── SKILL.md (required)
|
|
70
|
+
├── scripts/ (optional, for API clients)
|
|
71
|
+
├── references/ (optional, for detailed docs)
|
|
72
|
+
└── assets/ (optional, for templates/data)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Naming the skill:**
|
|
76
|
+
- Max 64 characters
|
|
77
|
+
- Lowercase letters, numbers, hyphens only
|
|
78
|
+
- No leading/trailing hyphens
|
|
79
|
+
- No consecutive hyphens
|
|
80
|
+
- Must describe what it does: `github-api`, `pdf-processor`, `csv-analysis`
|
|
81
|
+
|
|
82
|
+
### Step 4: Write SKILL.md
|
|
83
|
+
|
|
84
|
+
**Frontmatter (REQUIRED):**
|
|
85
|
+
```yaml
|
|
86
|
+
---
|
|
87
|
+
name: skill-name
|
|
88
|
+
description: Clear description of what this skill does and when to use it. Include keywords that help identify relevant tasks.
|
|
89
|
+
metadata:
|
|
90
|
+
author: user-name
|
|
91
|
+
version: "1.0"
|
|
92
|
+
---
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Body Content:**
|
|
96
|
+
|
|
97
|
+
Structure for progressive disclosure:
|
|
98
|
+
|
|
99
|
+
1. **Quick Start** (2-3 sentences) - What this skill enables
|
|
100
|
+
2. **When to Use** - Specific scenarios and keywords
|
|
101
|
+
3. **Prerequisites** - Required tools, accounts, environment variables
|
|
102
|
+
4. **Main Instructions** - Step-by-step guidance for the agent
|
|
103
|
+
5. **Examples** - Common patterns, inputs/outputs
|
|
104
|
+
6. **Error Handling** - Common issues and solutions
|
|
105
|
+
7. **References** - Links to detailed docs in `references/`
|
|
106
|
+
|
|
107
|
+
**Content Guidelines:**
|
|
108
|
+
- Keep under 500 lines
|
|
109
|
+
- Be specific and actionable
|
|
110
|
+
- Include concrete examples
|
|
111
|
+
- Don't omit critical information
|
|
112
|
+
- Focus on what the agent should DO
|
|
113
|
+
|
|
114
|
+
### Step 5: Create API Client Script (If API Skill)
|
|
115
|
+
|
|
116
|
+
If the skill uses an API, create a CLI script in `scripts/`:
|
|
117
|
+
|
|
118
|
+
**Location:** `~/skills/<skill-name>/scripts/<api-name>.rb`
|
|
119
|
+
|
|
120
|
+
**Process:**
|
|
121
|
+
1. Copy the template from `assets/ruby_api_cli_template.rb`
|
|
122
|
+
2. Customize for the specific API using docs you researched
|
|
123
|
+
3. Implement key endpoints based on common use cases
|
|
124
|
+
4. Add error handling for API responses
|
|
125
|
+
5. Test logic mentally against docs
|
|
126
|
+
|
|
127
|
+
**Template location:** `data/skills/skill-creator/assets/ruby_api_cli_template.rb`
|
|
128
|
+
|
|
129
|
+
**Usage in skill instructions:**
|
|
130
|
+
```markdown
|
|
131
|
+
To call the API, use the CLI script:
|
|
132
|
+
`API_KEY=xxx ~/skills/<skill-name>/scripts/<api-name>.rb [options]`
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Step 6: Add References (Optional)
|
|
136
|
+
|
|
137
|
+
Move detailed documentation to `references/`:
|
|
138
|
+
- `references/api-reference.md` - Full API docs
|
|
139
|
+
- `references/examples.md` - More examples
|
|
140
|
+
- `references/troubleshooting.md` - Common issues
|
|
141
|
+
|
|
142
|
+
Reference them in SKILL.md:
|
|
143
|
+
```markdown
|
|
144
|
+
## References
|
|
145
|
+
|
|
146
|
+
- [API Reference](references/api-reference.md) - Full endpoint documentation
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Step 7: Validate the Skill
|
|
150
|
+
|
|
151
|
+
Checklist before finishing:
|
|
152
|
+
- [ ] Frontmatter has name and description
|
|
153
|
+
- [ ] Name matches directory name
|
|
154
|
+
- [ ] Name follows format rules (lowercase, hyphens, max 64 chars)
|
|
155
|
+
- [ ] Description explains what AND when to use
|
|
156
|
+
- [ ] Body is under 500 lines
|
|
157
|
+
- [ ] Includes clear step-by-step instructions
|
|
158
|
+
- [ ] API skills have working CLI script (if applicable)
|
|
159
|
+
- [ ] All file references use relative paths
|
|
160
|
+
|
|
161
|
+
### Step 8: Create the Skill
|
|
162
|
+
|
|
163
|
+
Use shell commands to:
|
|
164
|
+
1. Create directory structure
|
|
165
|
+
2. Write SKILL.md file
|
|
166
|
+
3. Create script files (if applicable)
|
|
167
|
+
4. Create reference files (if applicable)
|
|
168
|
+
|
|
169
|
+
**Example:**
|
|
170
|
+
```bash
|
|
171
|
+
mkdir -p ~/skills/my-api-skill/scripts
|
|
172
|
+
mkdir -p ~/skills/my-api-skill/references
|
|
173
|
+
|
|
174
|
+
# Write SKILL.md
|
|
175
|
+
cat > ~/skills/my-api-skill/SKILL.md << 'SKILL_EOF'
|
|
176
|
+
---
|
|
177
|
+
name: my-api-skill
|
|
178
|
+
description: Interact with MyAPI to fetch data and manage resources.
|
|
179
|
+
metadata:
|
|
180
|
+
author: user
|
|
181
|
+
version: "1.0"
|
|
182
|
+
---
|
|
183
|
+
# MyAPI Skill
|
|
184
|
+
|
|
185
|
+
## Quick Start
|
|
186
|
+
|
|
187
|
+
Use this skill to interact with MyAPI for fetching data and managing resources.
|
|
188
|
+
|
|
189
|
+
## When to Use
|
|
190
|
+
|
|
191
|
+
- User mentions "MyAPI" or "my api"
|
|
192
|
+
- Need to fetch data from MyAPI
|
|
193
|
+
- Managing resources on MyAPI
|
|
194
|
+
|
|
195
|
+
## Prerequisites
|
|
196
|
+
|
|
197
|
+
- API key in MYAPI_KEY environment variable
|
|
198
|
+
|
|
199
|
+
## Instructions
|
|
200
|
+
|
|
201
|
+
1. **Authentication**: Set MYAPI_KEY environment variable
|
|
202
|
+
2. **Make requests**: Use the CLI script at `scripts/myapi.rb`
|
|
203
|
+
3. **Handle errors**: Check exit codes and error messages
|
|
204
|
+
|
|
205
|
+
## Examples
|
|
206
|
+
|
|
207
|
+
### Fetch user data
|
|
208
|
+
```bash
|
|
209
|
+
MYAPI_KEY=xxx ~/skills/my-api-skill/scripts/myapi.rb --endpoint users --method GET
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## References
|
|
213
|
+
|
|
214
|
+
- [API Reference](references/api-reference.md)
|
|
215
|
+
SKILL_EOF
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Working with API Documentation
|
|
219
|
+
|
|
220
|
+
### Finding Official Docs
|
|
221
|
+
|
|
222
|
+
**Search strategies:**
|
|
223
|
+
1. `service.com/api/docs`
|
|
224
|
+
2. `developer.service.com`
|
|
225
|
+
3. `docs.service.com/api`
|
|
226
|
+
4. GitHub repos: `service/api-docs`
|
|
227
|
+
5. API specifications (OpenAPI/Swagger)
|
|
228
|
+
|
|
229
|
+
**What to extract:**
|
|
230
|
+
- Base URL
|
|
231
|
+
- Authentication method (API key, OAuth, token)
|
|
232
|
+
- Key endpoints for common operations
|
|
233
|
+
- Request/response formats (JSON schema)
|
|
234
|
+
- Rate limits
|
|
235
|
+
- Error codes
|
|
236
|
+
|
|
237
|
+
### When Documentation is Incomplete
|
|
238
|
+
|
|
239
|
+
If official docs are missing key details:
|
|
240
|
+
1. Look for SDK source code on GitHub
|
|
241
|
+
2. Search for API examples in issues/PRs
|
|
242
|
+
3. Use web search for "service API example"
|
|
243
|
+
4. Ask user if they have internal docs
|
|
244
|
+
|
|
245
|
+
### Never Proceed Without Docs
|
|
246
|
+
|
|
247
|
+
**Critical:** If you cannot find sufficient documentation to implement the API client correctly, you MUST:
|
|
248
|
+
1. Stop and explain the situation
|
|
249
|
+
2. Request documentation from the user
|
|
250
|
+
3. Do not create a broken skill
|
|
251
|
+
|
|
252
|
+
## Error Handling in Skills
|
|
253
|
+
|
|
254
|
+
Skills should anticipate and handle:
|
|
255
|
+
- Missing environment variables
|
|
256
|
+
- Invalid inputs
|
|
257
|
+
- API errors (4xx, 5xx responses)
|
|
258
|
+
- Rate limiting
|
|
259
|
+
- Network failures
|
|
260
|
+
|
|
261
|
+
Document error handling in SKILL.md body.
|
|
262
|
+
|
|
263
|
+
## Examples
|
|
264
|
+
|
|
265
|
+
### Example 1: Simple Task Skill
|
|
266
|
+
|
|
267
|
+
**Request:** "Create a skill for optimizing PNG images"
|
|
268
|
+
|
|
269
|
+
**Process:**
|
|
270
|
+
1. No API research needed (uses local tools)
|
|
271
|
+
2. Name: `png-optimizer`
|
|
272
|
+
3. Directory: `~/skills/png-optimizer/`
|
|
273
|
+
4. SKILL.md includes: pngquant/oxipng usage, quality options, batch processing
|
|
274
|
+
|
|
275
|
+
### Example 2: API Integration Skill
|
|
276
|
+
|
|
277
|
+
**Request:** "Create a skill for the WeatherAPI"
|
|
278
|
+
|
|
279
|
+
**Process:**
|
|
280
|
+
1. Search for "weatherapi.com documentation"
|
|
281
|
+
2. Read docs at `https://www.weatherapi.com/docs/`
|
|
282
|
+
3. Extract: endpoints, API key auth, response format
|
|
283
|
+
4. Name: `weather-api`
|
|
284
|
+
5. Create CLI script using template
|
|
285
|
+
6. SKILL.md includes: authentication, common queries, response parsing
|
|
286
|
+
|
|
287
|
+
### Example 3: Rejected (No Docs)
|
|
288
|
+
|
|
289
|
+
**Request:** "Create a skill for SomeCorp's internal API"
|
|
290
|
+
|
|
291
|
+
**Response:**
|
|
292
|
+
"I cannot find official documentation for SomeCorp's API. Creating a skill without proper documentation would result in incorrect or potentially harmful integration.
|
|
293
|
+
|
|
294
|
+
Do you have:
|
|
295
|
+
1. Official API documentation (docs site, PDF, README)?
|
|
296
|
+
2. API specification (OpenAPI/Swagger)?
|
|
297
|
+
3. Example code or SDK?
|
|
298
|
+
|
|
299
|
+
Please provide documentation and I'll create a complete skill for you."
|
|
300
|
+
|
|
301
|
+
## Template Usage
|
|
302
|
+
|
|
303
|
+
When creating API client scripts, always use the Ruby CLI template:
|
|
304
|
+
|
|
305
|
+
**Template path:** `data/skills/skill-creator/assets/ruby_api_cli_template.rb`
|
|
306
|
+
|
|
307
|
+
**To use:**
|
|
308
|
+
1. Read the template
|
|
309
|
+
2. Copy to `~/skills/<skill-name>/scripts/<api-name>.rb`
|
|
310
|
+
3. Customize based on API documentation
|
|
311
|
+
4. Ensure error handling matches API error responses
|
|
312
|
+
|
|
313
|
+
## Important Reminders
|
|
314
|
+
|
|
315
|
+
1. **Always verify with official docs** - Never trust memory or assumptions
|
|
316
|
+
2. **Be transparent about documentation gaps** - Tell user when you can't find docs
|
|
317
|
+
3. **Don't create broken skills** - Better to wait for docs than ship broken code
|
|
318
|
+
4. **Test mentally** - Walk through the skill instructions as if you were executing them
|
|
319
|
+
5. **Follow the spec** - Invalid skills won't be recognized by the system
|
|
320
|
+
|
|
321
|
+
## Success Criteria
|
|
322
|
+
|
|
323
|
+
A skill is complete when:
|
|
324
|
+
- Directory structure is correct
|
|
325
|
+
- SKILL.md has valid frontmatter and body
|
|
326
|
+
- Name matches directory and follows format rules
|
|
327
|
+
- Instructions are clear and actionable
|
|
328
|
+
- API clients are based on official documentation
|
|
329
|
+
- User has been informed if documentation was limited
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# API Client Template
|
|
5
|
+
# Copy this template when creating API client scripts for skills
|
|
6
|
+
# Customize based on specific API documentation
|
|
7
|
+
|
|
8
|
+
require "optparse"
|
|
9
|
+
require "json"
|
|
10
|
+
require "net/http"
|
|
11
|
+
require "uri"
|
|
12
|
+
|
|
13
|
+
# Configuration
|
|
14
|
+
BASE_URL = ENV.fetch("API_BASE_URL", "https://api.example.com")
|
|
15
|
+
API_KEY = ENV["API_KEY"]
|
|
16
|
+
|
|
17
|
+
# Command-line options
|
|
18
|
+
options = {
|
|
19
|
+
verbose: false,
|
|
20
|
+
endpoint: nil,
|
|
21
|
+
method: "GET",
|
|
22
|
+
data: nil,
|
|
23
|
+
params: {}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
parser = OptionParser.new do |opts|
|
|
27
|
+
opts.banner = "Usage: api_client.rb [options]"
|
|
28
|
+
opts.separator ""
|
|
29
|
+
opts.separator "Options:"
|
|
30
|
+
|
|
31
|
+
opts.on("-e", "--endpoint ENDPOINT", "API endpoint path (e.g., /users)") do |e|
|
|
32
|
+
options[:endpoint] = e
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
opts.on("-m", "--method METHOD", "HTTP method (GET, POST, PUT, DELETE)") do |m|
|
|
36
|
+
options[:method] = m.upcase
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
opts.on("-d", "--data DATA", "JSON data for POST/PUT requests") do |d|
|
|
40
|
+
options[:data] = d
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
opts.on("-p", "--param KEY=VALUE", "Query parameters (can be used multiple times)") do |param|
|
|
44
|
+
key, value = param.split("=", 2)
|
|
45
|
+
options[:params][key] = value
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
opts.on("-v", "--verbose", "Enable verbose output") do
|
|
49
|
+
options[:verbose] = true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
opts.on("-h", "--help", "Show this help") do
|
|
53
|
+
puts opts
|
|
54
|
+
exit 0
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
begin
|
|
59
|
+
parser.parse!
|
|
60
|
+
rescue OptionParser::InvalidOption => e
|
|
61
|
+
warn "Error: #{e.message}"
|
|
62
|
+
warn "Use --help for usage information"
|
|
63
|
+
exit 1
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Validate required options
|
|
67
|
+
unless options[:endpoint]
|
|
68
|
+
warn "Error: --endpoint is required"
|
|
69
|
+
warn "Use --help for usage information"
|
|
70
|
+
exit 1
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Validate API key
|
|
74
|
+
unless API_KEY
|
|
75
|
+
warn "Error: API_KEY environment variable is required"
|
|
76
|
+
warn "Example: API_KEY=xxx ./api_client.rb --endpoint /users"
|
|
77
|
+
exit 1
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Build URI
|
|
81
|
+
uri = URI.parse("#{BASE_URL}#{options[:endpoint]}")
|
|
82
|
+
uri.query = URI.encode_www_form(options[:params]) unless options[:params].empty?
|
|
83
|
+
|
|
84
|
+
# Create HTTP request
|
|
85
|
+
request = case options[:method]
|
|
86
|
+
when "GET"
|
|
87
|
+
Net::HTTP::Get.new(uri)
|
|
88
|
+
when "POST"
|
|
89
|
+
req = Net::HTTP::Post.new(uri)
|
|
90
|
+
req.body = options[:data] if options[:data]
|
|
91
|
+
req["Content-Type"] = "application/json" if options[:data]
|
|
92
|
+
req
|
|
93
|
+
when "PUT"
|
|
94
|
+
req = Net::HTTP::Put.new(uri)
|
|
95
|
+
req.body = options[:data] if options[:data]
|
|
96
|
+
req["Content-Type"] = "application/json" if options[:data]
|
|
97
|
+
req
|
|
98
|
+
when "DELETE"
|
|
99
|
+
Net::HTTP::Delete.new(uri)
|
|
100
|
+
else
|
|
101
|
+
warn "Error: Unsupported HTTP method: #{options[:method]}"
|
|
102
|
+
exit 1
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Set headers
|
|
106
|
+
request["Authorization"] = "Bearer #{API_KEY}"
|
|
107
|
+
request["Accept"] = "application/json"
|
|
108
|
+
request["User-Agent"] = "BotiasLoop-Skill/1.0"
|
|
109
|
+
|
|
110
|
+
# Debug output
|
|
111
|
+
if options[:verbose]
|
|
112
|
+
warn "Request: #{options[:method]} #{uri}"
|
|
113
|
+
warn "Headers:"
|
|
114
|
+
request.each_header { |k, v| warn " #{k}: #{v}" }
|
|
115
|
+
warn "Body: #{options[:data]}" if options[:data]
|
|
116
|
+
warn ""
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Execute request
|
|
120
|
+
begin
|
|
121
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
122
|
+
http.use_ssl = uri.scheme == "https"
|
|
123
|
+
http.open_timeout = 10
|
|
124
|
+
http.read_timeout = 30
|
|
125
|
+
|
|
126
|
+
response = http.request(request)
|
|
127
|
+
|
|
128
|
+
# Debug response
|
|
129
|
+
if options[:verbose]
|
|
130
|
+
warn "Response: #{response.code} #{response.message}"
|
|
131
|
+
warn "Headers:"
|
|
132
|
+
response.each_header { |k, v| warn " #{k}: #{v}" }
|
|
133
|
+
warn ""
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Output response body
|
|
137
|
+
puts response.body
|
|
138
|
+
|
|
139
|
+
# Exit with non-zero code on HTTP errors
|
|
140
|
+
exit 1 unless response.is_a?(Net::HTTPSuccess)
|
|
141
|
+
rescue SocketError, Net::OpenTimeout, Net::ReadTimeout => e
|
|
142
|
+
warn "Error: Network error - #{e.message}"
|
|
143
|
+
exit 1
|
|
144
|
+
rescue JSON::ParserError => e
|
|
145
|
+
warn "Error: Failed to parse response as JSON - #{e.message}"
|
|
146
|
+
puts response.body # Still output raw body
|
|
147
|
+
exit 1
|
|
148
|
+
rescue => e
|
|
149
|
+
warn "Error: #{e.class} - #{e.message}"
|
|
150
|
+
exit 1
|
|
151
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Agent Skills Specification
|
|
2
|
+
|
|
3
|
+
Source: https://agentskills.io/specification.md
|
|
4
|
+
|
|
5
|
+
This document defines the Agent Skills format used by botiasloop.
|
|
6
|
+
|
|
7
|
+
## Directory structure
|
|
8
|
+
|
|
9
|
+
A skill is a directory containing at minimum a `SKILL.md` file:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
skill-name/
|
|
13
|
+
└── SKILL.md # Required
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Optionally include additional directories:
|
|
17
|
+
- `scripts/` - Executable code
|
|
18
|
+
- `references/` - Additional documentation
|
|
19
|
+
- `assets/` - Static resources (templates, data files)
|
|
20
|
+
|
|
21
|
+
## SKILL.md format
|
|
22
|
+
|
|
23
|
+
The `SKILL.md` file must contain YAML frontmatter followed by Markdown content.
|
|
24
|
+
|
|
25
|
+
### Frontmatter (required)
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
---
|
|
29
|
+
name: skill-name
|
|
30
|
+
description: A description of what this skill does and when to use it.
|
|
31
|
+
---
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
With optional fields:
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
---
|
|
38
|
+
name: pdf-processing
|
|
39
|
+
description: Extract text and tables from PDF files, fill forms, merge documents.
|
|
40
|
+
license: Apache-2.0
|
|
41
|
+
metadata:
|
|
42
|
+
author: example-org
|
|
43
|
+
version: "1.0"
|
|
44
|
+
---
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Field Reference
|
|
48
|
+
|
|
49
|
+
| Field | Required | Constraints |
|
|
50
|
+
| --------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
51
|
+
| `name` | Yes | Max 64 characters. Lowercase letters, numbers, and hyphens only. Must not start or end with a hyphen. |
|
|
52
|
+
| `description` | Yes | Max 1024 characters. Non-empty. Describes what the skill does and when to use it. |
|
|
53
|
+
| `license` | No | License name or reference to a bundled license file. |
|
|
54
|
+
| `compatibility` | No | Max 500 characters. Indicates environment requirements. |
|
|
55
|
+
| `metadata` | No | Arbitrary key-value mapping for additional metadata. |
|
|
56
|
+
| `allowed-tools` | No | Space-delimited list of pre-approved tools the skill may use. (Experimental) |
|
|
57
|
+
|
|
58
|
+
### Name Field Rules
|
|
59
|
+
|
|
60
|
+
- Must be 1-64 characters
|
|
61
|
+
- May only contain lowercase alphanumeric characters and hyphens (`a-z` and `-`)
|
|
62
|
+
- Must not start or end with `-`
|
|
63
|
+
- Must not contain consecutive hyphens (`--`)
|
|
64
|
+
- Must match the parent directory name
|
|
65
|
+
|
|
66
|
+
Valid: `pdf-processing`, `data-analysis`, `code-review`
|
|
67
|
+
Invalid: `PDF-Processing` (uppercase), `-pdf` (leading hyphen), `pdf--processing` (consecutive hyphens)
|
|
68
|
+
|
|
69
|
+
### Body content
|
|
70
|
+
|
|
71
|
+
The Markdown body after the frontmatter contains the skill instructions. There are no format restrictions.
|
|
72
|
+
|
|
73
|
+
Recommended sections:
|
|
74
|
+
- Step-by-step instructions
|
|
75
|
+
- Examples of inputs and outputs
|
|
76
|
+
- Common edge cases
|
|
77
|
+
|
|
78
|
+
Keep under 500 lines. Move detailed content to `references/`.
|
|
79
|
+
|
|
80
|
+
## Progressive Disclosure
|
|
81
|
+
|
|
82
|
+
Skills are structured for efficient use of context:
|
|
83
|
+
|
|
84
|
+
1. **Metadata** (~100 tokens): The `name` and `description` fields are loaded at startup for all skills
|
|
85
|
+
2. **Instructions** (< 5000 tokens recommended): The full `SKILL.md` body is loaded when the skill is activated
|
|
86
|
+
3. **Resources** (as needed): Files in `scripts/`, `references/`, or `assets/` are loaded only when required
|
|
87
|
+
|
|
88
|
+
## File References
|
|
89
|
+
|
|
90
|
+
When referencing other files in your skill, use relative paths from the skill root:
|
|
91
|
+
|
|
92
|
+
```markdown
|
|
93
|
+
See [the reference guide](references/REFERENCE.md) for details.
|
|
94
|
+
|
|
95
|
+
Run the extraction script:
|
|
96
|
+
scripts/extract.py
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Keep file references one level deep from `SKILL.md`. Avoid deeply nested reference chains.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ruby_llm"
|
|
4
|
+
require "logger"
|
|
5
|
+
|
|
6
|
+
module Botiasloop
|
|
7
|
+
class Agent
|
|
8
|
+
# Initialize the agent
|
|
9
|
+
#
|
|
10
|
+
# @param config [Config, nil] Configuration instance (loads default if nil)
|
|
11
|
+
def initialize(config = nil)
|
|
12
|
+
@config = config || Config.new
|
|
13
|
+
@logger = Logger.new($stderr)
|
|
14
|
+
setup_ruby_llm
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Send a message and get a response
|
|
18
|
+
#
|
|
19
|
+
# @param message [String] User message
|
|
20
|
+
# @param conversation [Conversation, nil] Existing conversation
|
|
21
|
+
# @return [String] Assistant response
|
|
22
|
+
def chat(message, conversation: nil)
|
|
23
|
+
conversation ||= Conversation.new
|
|
24
|
+
|
|
25
|
+
registry = create_registry
|
|
26
|
+
provider, model = create_provider_and_model
|
|
27
|
+
loop = Loop.new(provider, model, registry, max_iterations: @config.max_iterations)
|
|
28
|
+
|
|
29
|
+
loop.run(conversation, message)
|
|
30
|
+
rescue MaxIterationsExceeded => e
|
|
31
|
+
e.message
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def setup_ruby_llm
|
|
37
|
+
provider_name, provider_config = @config.active_provider
|
|
38
|
+
|
|
39
|
+
RubyLLM.configure do |config|
|
|
40
|
+
configure_provider(config, provider_name, provider_config)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def configure_provider(config, provider_name, provider_config)
|
|
45
|
+
case provider_name
|
|
46
|
+
when "openai"
|
|
47
|
+
config.openai_api_key = provider_config["api_key"]
|
|
48
|
+
config.openai_organization_id = provider_config["organization_id"] if provider_config["organization_id"]
|
|
49
|
+
config.openai_project_id = provider_config["project_id"] if provider_config["project_id"]
|
|
50
|
+
config.openai_api_base = provider_config["api_base"] if provider_config["api_base"]
|
|
51
|
+
when "anthropic"
|
|
52
|
+
config.anthropic_api_key = provider_config["api_key"]
|
|
53
|
+
when "gemini"
|
|
54
|
+
config.gemini_api_key = provider_config["api_key"]
|
|
55
|
+
config.gemini_api_base = provider_config["api_base"] if provider_config["api_base"]
|
|
56
|
+
when "vertexai"
|
|
57
|
+
config.vertexai_project_id = provider_config["project_id"]
|
|
58
|
+
config.vertexai_location = provider_config["location"] if provider_config["location"]
|
|
59
|
+
when "deepseek"
|
|
60
|
+
config.deepseek_api_key = provider_config["api_key"]
|
|
61
|
+
when "mistral"
|
|
62
|
+
config.mistral_api_key = provider_config["api_key"]
|
|
63
|
+
when "perplexity"
|
|
64
|
+
config.perplexity_api_key = provider_config["api_key"]
|
|
65
|
+
when "openrouter"
|
|
66
|
+
config.openrouter_api_key = provider_config["api_key"]
|
|
67
|
+
when "ollama"
|
|
68
|
+
config.ollama_api_base = provider_config["api_base"] || "http://localhost:11434/v1"
|
|
69
|
+
when "gpustack"
|
|
70
|
+
config.gpustack_api_base = provider_config["api_base"]
|
|
71
|
+
config.gpustack_api_key = provider_config["api_key"] if provider_config["api_key"]
|
|
72
|
+
when "bedrock"
|
|
73
|
+
config.bedrock_api_key = provider_config["api_key"] if provider_config["api_key"]
|
|
74
|
+
config.bedrock_secret_key = provider_config["secret_key"] if provider_config["secret_key"]
|
|
75
|
+
config.bedrock_region = provider_config["region"] if provider_config["region"]
|
|
76
|
+
config.bedrock_session_token = provider_config["session_token"] if provider_config["session_token"]
|
|
77
|
+
when "azure"
|
|
78
|
+
config.azure_api_base = provider_config["api_base"]
|
|
79
|
+
if provider_config["api_key"]
|
|
80
|
+
config.azure_api_key = provider_config["api_key"]
|
|
81
|
+
elsif provider_config["ai_auth_token"]
|
|
82
|
+
config.azure_ai_auth_token = provider_config["ai_auth_token"]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def create_provider_and_model
|
|
88
|
+
_provider_name, provider_config = @config.active_provider
|
|
89
|
+
model_id = provider_config["model"]
|
|
90
|
+
model = RubyLLM::Models.find(model_id)
|
|
91
|
+
provider_class = RubyLLM::Provider.for(model_id)
|
|
92
|
+
provider = provider_class.new(RubyLLM.config)
|
|
93
|
+
[provider, model]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def create_registry
|
|
97
|
+
registry = Tools::Registry.new
|
|
98
|
+
registry.register(Tools::Shell)
|
|
99
|
+
registry.register(Tools::WebSearch, searxng_url: web_search_url) if web_search_configured?
|
|
100
|
+
registry
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def web_search_configured?
|
|
104
|
+
url = web_search_url
|
|
105
|
+
url && !url.empty?
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def web_search_url
|
|
109
|
+
@config.tools["web_search"]["searxng_url"]
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|