@yeyuan98/opencode-bioresearcher-plugin 1.3.1 → 1.4.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/README.md +14 -0
- package/dist/index.js +4 -1
- package/dist/misc-tools/index.d.ts +3 -0
- package/dist/misc-tools/index.js +3 -0
- package/dist/misc-tools/json-extract.d.ts +13 -0
- package/dist/misc-tools/json-extract.js +394 -0
- package/dist/misc-tools/json-infer.d.ts +13 -0
- package/dist/misc-tools/json-infer.js +199 -0
- package/dist/misc-tools/json-tools.d.ts +33 -0
- package/dist/misc-tools/json-tools.js +187 -0
- package/dist/misc-tools/json-validate.d.ts +13 -0
- package/dist/misc-tools/json-validate.js +228 -0
- package/dist/skills/bioresearcher-core/README.md +210 -0
- package/dist/skills/bioresearcher-core/SKILL.md +128 -0
- package/dist/skills/bioresearcher-core/examples/contexts.json +29 -0
- package/dist/skills/bioresearcher-core/examples/data-exchange-example.md +303 -0
- package/dist/skills/bioresearcher-core/examples/template.md +49 -0
- package/dist/skills/bioresearcher-core/patterns/calculator.md +215 -0
- package/dist/skills/bioresearcher-core/patterns/data-exchange.md +406 -0
- package/dist/skills/bioresearcher-core/patterns/json-tools.md +263 -0
- package/dist/skills/bioresearcher-core/patterns/progress.md +127 -0
- package/dist/skills/bioresearcher-core/patterns/retry.md +110 -0
- package/dist/skills/bioresearcher-core/patterns/shell-commands.md +79 -0
- package/dist/skills/bioresearcher-core/patterns/subagent-waves.md +186 -0
- package/dist/skills/bioresearcher-core/patterns/table-tools.md +260 -0
- package/dist/skills/bioresearcher-core/patterns/user-confirmation.md +187 -0
- package/dist/skills/bioresearcher-core/python/template.md +273 -0
- package/dist/skills/bioresearcher-core/python/template.py +323 -0
- package/dist/skills/long-table-summary/SKILL.md +437 -0
- package/dist/skills/long-table-summary/combine_outputs.py +336 -0
- package/dist/skills/long-table-summary/generate_prompts.py +211 -0
- package/dist/skills/long-table-summary/pyproject.toml +8 -0
- package/dist/skills/pubmed-weekly/SKILL.md +329 -329
- package/dist/skills/pubmed-weekly/pubmed_weekly.py +411 -411
- package/dist/skills/pubmed-weekly/pyproject.toml +8 -8
- package/package.json +7 -2
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Subagent Waves Pattern
|
|
2
|
+
|
|
3
|
+
Process multiple items with parallel subagents organized in waves.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Use this pattern when you need to process many items in parallel while controlling concurrency.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
> **CRITICAL:** This pattern requires the `Task` tool to be available in your environment.
|
|
12
|
+
>
|
|
13
|
+
> - If the Task tool is not available, **subagent waves are not possible**
|
|
14
|
+
> - Alternative: Process items sequentially or use external batch processing
|
|
15
|
+
> - Check tool availability before implementing this pattern
|
|
16
|
+
>
|
|
17
|
+
> To verify Task tool availability, check your environment's tool list.
|
|
18
|
+
|
|
19
|
+
## Pattern Algorithm
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
1. Calculate total items and wave_size
|
|
23
|
+
2. Group items into waves of wave_size items each
|
|
24
|
+
3. For each wave:
|
|
25
|
+
a. Launch wave_size subagents in parallel via Task tool
|
|
26
|
+
b. Wait for all subagents to complete
|
|
27
|
+
c. Track progress (use progress pattern)
|
|
28
|
+
d. Validate outputs using jsonExtract + jsonValidate
|
|
29
|
+
e. Collect validated outputs
|
|
30
|
+
4. Handle failed items (use retry pattern)
|
|
31
|
+
5. Combine all outputs using jsonExtract or table tools
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Parameters
|
|
35
|
+
|
|
36
|
+
| Parameter | Default | Description |
|
|
37
|
+
|-----------|---------|-------------|
|
|
38
|
+
| `wave_size` | 3 | Number of parallel subagents per wave |
|
|
39
|
+
| `subagent_type` | "general" | Type of subagent to launch |
|
|
40
|
+
|
|
41
|
+
## Key Principles
|
|
42
|
+
|
|
43
|
+
1. **File-based prompts**: Subagents read prompt files, not inline prompts
|
|
44
|
+
2. **File-based outputs**: Subagents write to output files as JSON
|
|
45
|
+
3. **Schema validation**: Every output validated before processing
|
|
46
|
+
4. **Wave coordination**: Wait for entire wave before starting next
|
|
47
|
+
5. **Progress tracking**: Report after each wave completes
|
|
48
|
+
|
|
49
|
+
## Tool: Task
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
task(
|
|
53
|
+
subagent_type: string,
|
|
54
|
+
description: string,
|
|
55
|
+
prompt: string
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Example: Launch Wave of 3 Subagents
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// Wave 1 - Launch 3 subagents in parallel
|
|
63
|
+
task(
|
|
64
|
+
subagent_type="general",
|
|
65
|
+
description="Process batch 001",
|
|
66
|
+
prompt="Read your prompt from ./.work/batch001.md and perform the task described there exactly as written."
|
|
67
|
+
)
|
|
68
|
+
task(
|
|
69
|
+
subagent_type="general",
|
|
70
|
+
description="Process batch 002",
|
|
71
|
+
prompt="Read your prompt from ./.work/batch002.md and perform the task described there exactly as written."
|
|
72
|
+
)
|
|
73
|
+
task(
|
|
74
|
+
subagent_type="general",
|
|
75
|
+
description="Process batch 003",
|
|
76
|
+
prompt="Read your prompt from ./.work/batch003.md and perform the task described there exactly as written."
|
|
77
|
+
)
|
|
78
|
+
// Wait for all 3 to complete before Wave 2
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Example: Full Wave Processing Workflow
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
# Configuration
|
|
85
|
+
total_items = 12
|
|
86
|
+
wave_size = 3
|
|
87
|
+
total_waves = ceil(total_items / wave_size) # 4 waves
|
|
88
|
+
|
|
89
|
+
# Create prompt files first (using template.py)
|
|
90
|
+
uv run python <skill_path>/python/template.py generate-batches \
|
|
91
|
+
--template template.md \
|
|
92
|
+
--contexts contexts.json \
|
|
93
|
+
--output-dir ./prompts
|
|
94
|
+
|
|
95
|
+
# Process in waves
|
|
96
|
+
for wave_num in range(1, total_waves + 1):
|
|
97
|
+
# Launch wave
|
|
98
|
+
start_idx = (wave_num - 1) * wave_size + 1
|
|
99
|
+
end_idx = min(wave_num * wave_size, total_items)
|
|
100
|
+
|
|
101
|
+
for batch_num in range(start_idx, end_idx + 1):
|
|
102
|
+
task(
|
|
103
|
+
subagent_type="general",
|
|
104
|
+
description=f"Process batch {batch_num:03d}",
|
|
105
|
+
prompt=f"Read your prompt from ./prompts/batch{batch_num:03d}.md and perform the task."
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Wait for wave completion
|
|
109
|
+
# (Task tool handles this - next task call waits)
|
|
110
|
+
|
|
111
|
+
# Report progress
|
|
112
|
+
completed = end_idx
|
|
113
|
+
percent = calculator(formula=f"({completed} / {total_items}) * 100")
|
|
114
|
+
report(f"Progress: {completed}/{total_items} batches ({percent}%)")
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Output Validation
|
|
118
|
+
|
|
119
|
+
After each wave, validate outputs:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
# For each output file
|
|
123
|
+
result = jsonExtract(file_path="./outputs/batch001.md")
|
|
124
|
+
if not result.success:
|
|
125
|
+
log_error("Failed to extract JSON from batch001.md")
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
# Validate against schema
|
|
129
|
+
validation = jsonValidate(
|
|
130
|
+
data=json.dumps(result.data),
|
|
131
|
+
schema=expected_schema
|
|
132
|
+
)
|
|
133
|
+
if not validation.valid:
|
|
134
|
+
log_error(f"Schema validation failed for batch001.md: {validation.errors}")
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
# Collect valid output
|
|
138
|
+
valid_outputs.append(result.data)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Handling Failed Batches
|
|
142
|
+
|
|
143
|
+
After all waves complete:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
# Check for missing outputs
|
|
147
|
+
expected_files = [f"batch{i:03d}.md" for i in range(1, total_items + 1)]
|
|
148
|
+
missing = [f for f in expected_files if not exists(f"./outputs/{f}")]
|
|
149
|
+
|
|
150
|
+
if missing:
|
|
151
|
+
# Use retry pattern
|
|
152
|
+
for batch_file in missing:
|
|
153
|
+
retry_subagent(batch_file, max_attempts=3, delay=2)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Combining Outputs
|
|
157
|
+
|
|
158
|
+
For small batches (<10 files), use table tools:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
# Extract all JSON
|
|
162
|
+
all_data = []
|
|
163
|
+
for file in output_files:
|
|
164
|
+
result = jsonExtract(file_path=file)
|
|
165
|
+
if result.success:
|
|
166
|
+
all_data.extend(result.data["summaries"])
|
|
167
|
+
|
|
168
|
+
# Create combined Excel
|
|
169
|
+
tableCreateFile(
|
|
170
|
+
file_path="./combined.xlsx",
|
|
171
|
+
sheet_name="Results",
|
|
172
|
+
data=all_data
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
For large batches (>10 files), use Python script for efficiency.
|
|
177
|
+
|
|
178
|
+
## Best Practices
|
|
179
|
+
|
|
180
|
+
1. **Verify Task tool availability** before implementing this pattern
|
|
181
|
+
2. **Keep wave_size reasonable**: 3-5 subagents per wave
|
|
182
|
+
3. **Use descriptive descriptions**: "Process batch 001" not "Batch 1"
|
|
183
|
+
4. **Always use file-based prompts**: Never inline large prompts
|
|
184
|
+
5. **Validate every output**: Don't skip schema validation
|
|
185
|
+
6. **Report progress after waves**: Not during individual completions
|
|
186
|
+
7. **Have fallback plan**: If Task tool unavailable, use sequential processing
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# Table Tools Pattern
|
|
2
|
+
|
|
3
|
+
Guide for combining subagent outputs using table tools.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Table tools can be used to combine JSON outputs into Excel/CSV files without Python scripts for small batches.
|
|
8
|
+
|
|
9
|
+
## When to Use Table Tools vs Python
|
|
10
|
+
|
|
11
|
+
| Scenario | Use Table Tools | Use Python Script |
|
|
12
|
+
|----------|-----------------|-------------------|
|
|
13
|
+
| Files to combine | <10 files | >=10 files |
|
|
14
|
+
| Data size | Small (<1000 rows total) | Large (>1000 rows) |
|
|
15
|
+
| Complexity | Simple merge | Complex transformations |
|
|
16
|
+
| Performance | Acceptable overhead | Need efficiency |
|
|
17
|
+
|
|
18
|
+
## Tool: tableCreateFile
|
|
19
|
+
|
|
20
|
+
Create a new Excel/CSV file from data.
|
|
21
|
+
|
|
22
|
+
### Signature
|
|
23
|
+
```
|
|
24
|
+
tableCreateFile(
|
|
25
|
+
file_path: string,
|
|
26
|
+
sheet_name: string = "Sheet1",
|
|
27
|
+
data: array // Array of arrays OR array of objects
|
|
28
|
+
)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Return Format
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"success": true,
|
|
35
|
+
"file_path": "./output.xlsx",
|
|
36
|
+
"sheet_name": "Sheet1",
|
|
37
|
+
"rows_created": 100,
|
|
38
|
+
"message": "Successfully created Excel file with 100 rows"
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Examples
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
# Create from array of objects
|
|
46
|
+
tableCreateFile(
|
|
47
|
+
file_path="./output.xlsx",
|
|
48
|
+
sheet_name="Results",
|
|
49
|
+
data=[
|
|
50
|
+
{"row_number": 1, "name": "Alice", "score": 95},
|
|
51
|
+
{"row_number": 2, "name": "Bob", "score": 87}
|
|
52
|
+
]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Create from array of arrays
|
|
56
|
+
tableCreateFile(
|
|
57
|
+
file_path="./output.csv",
|
|
58
|
+
sheet_name="Sheet1",
|
|
59
|
+
data=[
|
|
60
|
+
["row_number", "name", "score"],
|
|
61
|
+
[1, "Alice", 95],
|
|
62
|
+
[2, "Bob", 87]
|
|
63
|
+
]
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Tool: tableAppendRows
|
|
68
|
+
|
|
69
|
+
Append rows to an existing table file.
|
|
70
|
+
|
|
71
|
+
### Signature
|
|
72
|
+
```
|
|
73
|
+
tableAppendRows(
|
|
74
|
+
file_path: string,
|
|
75
|
+
sheet_name: string?, // Optional, uses first sheet
|
|
76
|
+
rows: array // Array of arrays OR array of objects
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Return Format
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"success": true,
|
|
84
|
+
"file_path": "./output.xlsx",
|
|
85
|
+
"sheet_name": "Sheet1",
|
|
86
|
+
"rows_appended": 50,
|
|
87
|
+
"message": "Successfully appended 50 rows"
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Examples
|
|
92
|
+
|
|
93
|
+
**RECOMMENDED: Append using array-of-arrays format (no header duplication):**
|
|
94
|
+
```
|
|
95
|
+
tableAppendRows(
|
|
96
|
+
file_path="./output.xlsx",
|
|
97
|
+
rows=[
|
|
98
|
+
[3, "Charlie", 92],
|
|
99
|
+
[4, "Diana", 88]
|
|
100
|
+
]
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Alternative: Append using objects (may duplicate headers):**
|
|
105
|
+
```
|
|
106
|
+
tableAppendRows(
|
|
107
|
+
file_path="./output.xlsx",
|
|
108
|
+
rows=[
|
|
109
|
+
{"row_number": 3, "name": "Charlie", "score": 92}
|
|
110
|
+
]
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
> **Note:** When appending object-format data, some implementations may insert a duplicate header row. For reliable results, use array-of-arrays format for append operations.
|
|
115
|
+
|
|
116
|
+
## Combining JSON Outputs Workflow
|
|
117
|
+
|
|
118
|
+
### Step 1: Extract All JSON
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
# Extract JSON from each output file
|
|
122
|
+
all_rows = []
|
|
123
|
+
|
|
124
|
+
for batch_num in range(1, num_batches + 1):
|
|
125
|
+
file_path = f"./outputs/batch{batch_num:03d}.md"
|
|
126
|
+
result = jsonExtract(file_path=file_path)
|
|
127
|
+
|
|
128
|
+
if result.success:
|
|
129
|
+
# Assuming data has "summaries" array
|
|
130
|
+
summaries = result.data.get("summaries", [])
|
|
131
|
+
all_rows.extend(summaries)
|
|
132
|
+
else:
|
|
133
|
+
log_error(f"Failed to extract from {file_path}")
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Step 2: Create Combined File
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
# Create Excel file with all rows
|
|
140
|
+
tableCreateFile(
|
|
141
|
+
file_path="./combined_summary.xlsx",
|
|
142
|
+
sheet_name="Summary",
|
|
143
|
+
data=all_rows
|
|
144
|
+
)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Step 3: Append Additional Data (Optional)
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
# If processing in chunks, append to existing file using array format
|
|
151
|
+
# First, get headers from the created file
|
|
152
|
+
headers = list(all_rows[0].keys()) if all_rows else []
|
|
153
|
+
|
|
154
|
+
for chunk in chunks:
|
|
155
|
+
rows = extract_rows(chunk)
|
|
156
|
+
# Convert to arrays to avoid header duplication
|
|
157
|
+
rows_as_arrays = [[item.get(h) for h in headers] for item in rows]
|
|
158
|
+
tableAppendRows(
|
|
159
|
+
file_path="./combined_summary.xlsx",
|
|
160
|
+
rows=rows_as_arrays
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Complete Example: Combining Batch Outputs
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
# Configuration
|
|
168
|
+
output_dir = "./outputs"
|
|
169
|
+
num_batches = 9
|
|
170
|
+
combined_file = "./combined_summary.xlsx"
|
|
171
|
+
|
|
172
|
+
# Collect all rows
|
|
173
|
+
all_rows = []
|
|
174
|
+
failed_batches = []
|
|
175
|
+
|
|
176
|
+
for batch_num in range(1, num_batches + 1):
|
|
177
|
+
file_path = f"{output_dir}/batch{batch_num:03d}.md"
|
|
178
|
+
|
|
179
|
+
# Extract JSON
|
|
180
|
+
result = jsonExtract(file_path=file_path)
|
|
181
|
+
|
|
182
|
+
if not result.success:
|
|
183
|
+
failed_batches.append(batch_num)
|
|
184
|
+
continue
|
|
185
|
+
|
|
186
|
+
# Get summaries from batch output
|
|
187
|
+
summaries = result.data.get("summaries", [])
|
|
188
|
+
all_rows.extend(summaries)
|
|
189
|
+
|
|
190
|
+
# Report failures
|
|
191
|
+
if failed_batches:
|
|
192
|
+
log_error(f"Failed batches: {failed_batches}")
|
|
193
|
+
|
|
194
|
+
# Sort by row_number
|
|
195
|
+
all_rows.sort(key=lambda x: x.get("row_number", 0))
|
|
196
|
+
|
|
197
|
+
# Create combined Excel
|
|
198
|
+
result = tableCreateFile(
|
|
199
|
+
file_path=combined_file,
|
|
200
|
+
sheet_name="Combined",
|
|
201
|
+
data=all_rows
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# Report result
|
|
205
|
+
report(f"Created {combined_file} with {result.rows_created} rows")
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Incremental Append Strategy
|
|
209
|
+
|
|
210
|
+
For large datasets, append incrementally using array-of-arrays format:
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
# Get column headers from first batch
|
|
214
|
+
first_batch = jsonExtract(file_path="./outputs/batch001.md")
|
|
215
|
+
headers = ["row_number", "field1", "field2"] # Define your headers
|
|
216
|
+
|
|
217
|
+
# Create file with first batch (using objects for auto-headers)
|
|
218
|
+
tableCreateFile(
|
|
219
|
+
file_path="./combined.xlsx",
|
|
220
|
+
sheet_name="Data",
|
|
221
|
+
data=first_batch.data.get("summaries", [])
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Get header order for array format
|
|
225
|
+
headers = list(first_batch.data.get("summaries", [{}])[0].keys())
|
|
226
|
+
|
|
227
|
+
# Append remaining batches using array format
|
|
228
|
+
for batch_num in range(2, num_batches + 1):
|
|
229
|
+
file_path = f"./outputs/batch{batch_num:03d}.md"
|
|
230
|
+
result = jsonExtract(file_path=file_path)
|
|
231
|
+
|
|
232
|
+
if result.success:
|
|
233
|
+
# Convert objects to arrays to avoid header duplication
|
|
234
|
+
rows_as_arrays = [
|
|
235
|
+
[item.get(h) for h in headers]
|
|
236
|
+
for item in result.data.get("summaries", [])
|
|
237
|
+
]
|
|
238
|
+
tableAppendRows(
|
|
239
|
+
file_path="./combined.xlsx",
|
|
240
|
+
rows=rows_as_arrays
|
|
241
|
+
)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Supported File Formats
|
|
245
|
+
|
|
246
|
+
| Format | Extension | Notes |
|
|
247
|
+
|--------|-----------|-------|
|
|
248
|
+
| Excel | .xlsx | Recommended |
|
|
249
|
+
| Excel (Legacy) | .xls | Limited support |
|
|
250
|
+
| ODS | .ods | OpenDocument Spreadsheet |
|
|
251
|
+
| CSV | .csv | Text-based, no sheets |
|
|
252
|
+
|
|
253
|
+
## Best Practices
|
|
254
|
+
|
|
255
|
+
1. **Sort before writing**: Sort rows by key field before creating file
|
|
256
|
+
2. **Handle failures gracefully**: Log failed extractions, continue with others
|
|
257
|
+
3. **Use object format for creation**: Array of objects auto-generates headers
|
|
258
|
+
4. **Use array format for appends**: Prefer array-of-arrays when using tableAppendRows to avoid potential header duplication
|
|
259
|
+
5. **Check row counts**: Verify expected vs actual row counts
|
|
260
|
+
6. **For large batches**: Use Python script for better performance
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# User Confirmation Pattern
|
|
2
|
+
|
|
3
|
+
Request user confirmation before destructive or significant operations.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Use this pattern before operations that:
|
|
8
|
+
- Delete or modify files
|
|
9
|
+
- Make network requests
|
|
10
|
+
- Incur costs (API calls, cloud resources)
|
|
11
|
+
- Take significant time
|
|
12
|
+
- Cannot be easily undone
|
|
13
|
+
|
|
14
|
+
## Tool: question
|
|
15
|
+
|
|
16
|
+
> **Note:** The tool is invoked as `question` (lowercase). Some documentation may reference `Question` (capitalized) but both refer to the same tool.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
question(questions: [{
|
|
20
|
+
header: string, // Short label (max 30 chars)
|
|
21
|
+
question: string, // Complete question
|
|
22
|
+
options: [{
|
|
23
|
+
label: string, // Display text (1-5 words)
|
|
24
|
+
description: string // Explanation of choice
|
|
25
|
+
}],
|
|
26
|
+
multiple: boolean // Allow multiple selections (default: false)
|
|
27
|
+
}])
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Example: Before Destructive Operation
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
question(questions=[{
|
|
34
|
+
"header": "Delete files",
|
|
35
|
+
"question": "This will delete 15 files in ./temp/. Continue?",
|
|
36
|
+
"options": [
|
|
37
|
+
{"label": "Yes, delete", "description": "Permanently delete all 15 files"},
|
|
38
|
+
{"label": "No, cancel", "description": "Keep files and stop this operation"}
|
|
39
|
+
]
|
|
40
|
+
}])
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Example: Continue After Failures
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
question(questions=[{
|
|
47
|
+
"header": "Retry failed",
|
|
48
|
+
"question": "3 batches failed after all retry attempts. How would you like to proceed?",
|
|
49
|
+
"options": [
|
|
50
|
+
{"label": "Continue", "description": "Skip failed batches and continue with remaining"},
|
|
51
|
+
{"label": "Retry now", "description": "Try failed batches one more time"},
|
|
52
|
+
{"label": "Abort", "description": "Stop the entire workflow"}
|
|
53
|
+
]
|
|
54
|
+
}])
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Example: Configuration Choice
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
question(questions=[{
|
|
61
|
+
"header": "Batch size",
|
|
62
|
+
"question": "How many rows should each batch contain?",
|
|
63
|
+
"options": [
|
|
64
|
+
{"label": "30 rows (Recommended)", "description": "Balanced for most use cases"},
|
|
65
|
+
{"label": "50 rows", "description": "Fewer batches, larger context per subagent"},
|
|
66
|
+
{"label": "10 rows", "description": "More batches, smaller context per subagent"}
|
|
67
|
+
]
|
|
68
|
+
}])
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Example: Multiple Selection
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
question(questions=[{
|
|
75
|
+
"header": "Select sheets",
|
|
76
|
+
"question": "Which sheets should be processed?",
|
|
77
|
+
"options": [
|
|
78
|
+
{"label": "Sheet1", "description": "Main data sheet (1000 rows)"},
|
|
79
|
+
{"label": "Sheet2", "description": "Secondary data (500 rows)"},
|
|
80
|
+
{"label": "Summary", "description": "Pre-computed summaries (50 rows)"}
|
|
81
|
+
],
|
|
82
|
+
"multiple": true
|
|
83
|
+
}])
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## When to Ask for Confirmation
|
|
87
|
+
|
|
88
|
+
| Operation Type | Confirmation Needed |
|
|
89
|
+
|---------------|---------------------|
|
|
90
|
+
| Read file | No |
|
|
91
|
+
| Write new file | No |
|
|
92
|
+
| Overwrite existing file | Yes |
|
|
93
|
+
| Delete file | Yes |
|
|
94
|
+
| Delete directory | Yes |
|
|
95
|
+
| API call (free) | No |
|
|
96
|
+
| API call (paid) | Yes |
|
|
97
|
+
| Long operation (>5 min) | Yes |
|
|
98
|
+
| Network upload | Yes |
|
|
99
|
+
| Network download | No (usually) |
|
|
100
|
+
|
|
101
|
+
## Response Handling
|
|
102
|
+
|
|
103
|
+
The question tool returns selected option labels as an array:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
# Single selection
|
|
107
|
+
response = question(...)
|
|
108
|
+
if response[0] == "Yes, delete":
|
|
109
|
+
proceed_with_deletion()
|
|
110
|
+
else:
|
|
111
|
+
cancel_operation()
|
|
112
|
+
|
|
113
|
+
# Multiple selection
|
|
114
|
+
response = question(..., multiple=true)
|
|
115
|
+
selected_sheets = response # ["Sheet1", "Sheet2"]
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Example: Conditional Logic Flow
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
# Ask for confirmation
|
|
122
|
+
response = question(questions=[{
|
|
123
|
+
"header": "Overwrite",
|
|
124
|
+
"question": "File 'output.xlsx' already exists. Overwrite?",
|
|
125
|
+
"options": [
|
|
126
|
+
{"label": "Overwrite", "description": "Replace existing file"},
|
|
127
|
+
{"label": "Append", "description": "Add to existing file"},
|
|
128
|
+
{"label": "Cancel", "description": "Don't modify the file"}
|
|
129
|
+
]
|
|
130
|
+
}])
|
|
131
|
+
|
|
132
|
+
# Handle response
|
|
133
|
+
if response[0] == "Overwrite":
|
|
134
|
+
write_file(mode="write")
|
|
135
|
+
elif response[0] == "Append":
|
|
136
|
+
write_file(mode="append")
|
|
137
|
+
else:
|
|
138
|
+
report("Operation cancelled by user")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Best Practices
|
|
142
|
+
|
|
143
|
+
1. **Clear header**: Max 30 chars, summarize the decision
|
|
144
|
+
2. **Descriptive question**: Explain what will happen
|
|
145
|
+
3. **Helpful options**: Include descriptions that guide the user
|
|
146
|
+
4. **Recommended option**: Mark with "(Recommended)" in label
|
|
147
|
+
5. **Safe default**: Put safer option first
|
|
148
|
+
6. **Cancel option**: Always include a way to abort
|
|
149
|
+
|
|
150
|
+
## Timeout and No-Response Handling
|
|
151
|
+
|
|
152
|
+
When users don't respond to confirmation prompts, implement a default behavior:
|
|
153
|
+
|
|
154
|
+
### Default After Timeout
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
# If no response after reasonable time, default to safe option
|
|
158
|
+
# Most patterns should default to "cancel" for safety
|
|
159
|
+
|
|
160
|
+
if no_response_after(timeout=60):
|
|
161
|
+
log("No user response, defaulting to cancel")
|
|
162
|
+
cancel_operation()
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Integration with Retry Pattern
|
|
166
|
+
|
|
167
|
+
For critical operations requiring user input:
|
|
168
|
+
1. Ask for confirmation
|
|
169
|
+
2. If no response, wait and retry with `blockingTimer`
|
|
170
|
+
3. After N attempts, use safe default or abort
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
attempts = 0
|
|
174
|
+
max_attempts = 3
|
|
175
|
+
while attempts < max_attempts:
|
|
176
|
+
response = question(...)
|
|
177
|
+
if response:
|
|
178
|
+
handle_response(response)
|
|
179
|
+
break
|
|
180
|
+
attempts += 1
|
|
181
|
+
if attempts < max_attempts:
|
|
182
|
+
blockingTimer(delay=10) # Wait before re-prompting
|
|
183
|
+
|
|
184
|
+
# Default to safe option if no response
|
|
185
|
+
if attempts >= max_attempts:
|
|
186
|
+
cancel_operation()
|
|
187
|
+
```
|