opencode-froggy 0.3.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/README.md +179 -59
- package/command/agent-promote.md +5 -0
- package/command/commit-push.md +8 -3
- package/command/diff-summary.md +51 -0
- package/command/doc-changes.md +1 -1
- package/command/gh-create-pr.md +18 -0
- package/command/review-changes.md +15 -1
- package/command/review-pr.md +4 -2
- package/command/simplify-changes.md +1 -1
- package/dist/index.js +11 -5
- package/dist/tools/agent-promote-core.d.ts +6 -0
- package/dist/tools/agent-promote-core.js +14 -0
- package/dist/tools/agent-promote.d.ts +19 -0
- package/dist/tools/agent-promote.js +39 -0
- package/dist/tools/agent-promote.test.d.ts +1 -0
- package/dist/tools/agent-promote.test.js +71 -0
- package/dist/tools/blockchain/eth-transaction.d.ts +15 -1
- package/dist/tools/blockchain/eth-transaction.js +180 -17
- package/dist/tools/blockchain/etherscan-client.d.ts +3 -2
- package/dist/tools/blockchain/etherscan-client.js +23 -5
- package/dist/tools/blockchain/event-decoder.d.ts +14 -0
- package/dist/tools/blockchain/event-decoder.js +96 -0
- package/dist/tools/blockchain/event-decoder.test.d.ts +1 -0
- package/dist/tools/blockchain/event-decoder.test.js +197 -0
- package/dist/tools/blockchain/types.d.ts +64 -0
- package/dist/tools/blockchain/viem-client.d.ts +9 -0
- package/dist/tools/blockchain/viem-client.js +98 -0
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +1 -1
- package/package.json +3 -2
- package/skill/code-simplify/SKILL.md +6 -0
- package/dist/tools/diff-summary.d.ts +0 -20
- package/dist/tools/diff-summary.js +0 -111
- package/dist/tools/reply-child.d.ts +0 -19
- package/dist/tools/reply-child.js +0 -42
package/README.md
CHANGED
|
@@ -18,10 +18,11 @@ Plugin providing Claude Code–style hooks, specialized agents (doc-writer, code
|
|
|
18
18
|
- [Agents](#agents)
|
|
19
19
|
- [Tools](#tools)
|
|
20
20
|
- [gitingest](#gitingest)
|
|
21
|
-
- [diff-summary](#diff-summary)
|
|
22
21
|
- [prompt-session](#prompt-session)
|
|
23
22
|
- [list-child-sessions](#list-child-sessions)
|
|
23
|
+
- [agent-promote](#agent-promote)
|
|
24
24
|
- [Blockchain](#blockchain)
|
|
25
|
+
- [Configuration](#configuration)
|
|
25
26
|
- [eth-transaction](#eth-transaction)
|
|
26
27
|
- [eth-address-balance](#eth-address-balance)
|
|
27
28
|
- [eth-address-txs](#eth-address-txs)
|
|
@@ -65,7 +66,9 @@ Alternatively, clone or copy the plugin files to one of these directories:
|
|
|
65
66
|
|
|
66
67
|
| Command | Description | Agent |
|
|
67
68
|
|---------|-------------|-------|
|
|
69
|
+
| `/agent-promote <name> <grade>` | Change the type of a plugin agent at runtime. Grades: `subagent`, `primary`, `all` | - |
|
|
68
70
|
| `/commit-push` | Stage, commit, and push changes with user confirmation | `build` |
|
|
71
|
+
| `/diff-summary [source] [target]` | Show working tree changes or diff between branches | - |
|
|
69
72
|
| `/doc-changes` | Update documentation based on uncommitted changes (new features only) | `doc-writer` |
|
|
70
73
|
| `/review-changes` | Review uncommitted changes (staged + unstaged, including untracked files) | `code-reviewer` |
|
|
71
74
|
| `/review-pr <source> <target>` | Review changes from source branch into target branch | `code-reviewer` |
|
|
@@ -73,6 +76,28 @@ Alternatively, clone or copy the plugin files to one of these directories:
|
|
|
73
76
|
| `/simplify-changes` | Simplify uncommitted changes (staged + unstaged, including untracked files) | `code-simplifier` |
|
|
74
77
|
| `/tests-coverage` | Run the full test suite with coverage report and suggest fixes for failures | `build` |
|
|
75
78
|
|
|
79
|
+
### /diff-summary
|
|
80
|
+
|
|
81
|
+
The `/diff-summary` command supports two modes:
|
|
82
|
+
|
|
83
|
+
**Working tree mode** (no parameters):
|
|
84
|
+
```bash
|
|
85
|
+
/diff-summary
|
|
86
|
+
```
|
|
87
|
+
Shows staged changes, unstaged changes, and untracked file contents.
|
|
88
|
+
|
|
89
|
+
**Branch comparison mode** (with parameters):
|
|
90
|
+
```bash
|
|
91
|
+
# Compare a branch with the current branch (HEAD)
|
|
92
|
+
/diff-summary feature-branch
|
|
93
|
+
|
|
94
|
+
# Compare two specific branches
|
|
95
|
+
/diff-summary feature-branch main
|
|
96
|
+
```
|
|
97
|
+
Shows stats overview, commits, files changed, and full diff between branches.
|
|
98
|
+
|
|
99
|
+
> **Note:** The `/review-pr` command uses `/diff-summary` internally to generate the diff for code review.
|
|
100
|
+
|
|
76
101
|
---
|
|
77
102
|
|
|
78
103
|
## Agents
|
|
@@ -138,62 +163,6 @@ gitingest({
|
|
|
138
163
|
|
|
139
164
|
---
|
|
140
165
|
|
|
141
|
-
### diff-summary
|
|
142
|
-
|
|
143
|
-
Generate a structured summary of git diffs. Use for reviewing branch comparisons or working tree changes. Returns stats, commits, files changed, and full diff in a structured markdown format.
|
|
144
|
-
|
|
145
|
-
#### Parameters
|
|
146
|
-
|
|
147
|
-
| Parameter | Type | Required | Default | Description |
|
|
148
|
-
|-----------|------|----------|---------|-------------|
|
|
149
|
-
| `source` | `string` | No | - | Source branch to compare (e.g., `feature-branch`). If omitted, analyzes working tree changes. |
|
|
150
|
-
| `target` | `string` | No | `main` | Target branch to compare against |
|
|
151
|
-
| `remote` | `string` | No | `origin` | Git remote name |
|
|
152
|
-
|
|
153
|
-
#### Usage Examples
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
// Analyze working tree changes (staged, unstaged, and untracked files)
|
|
157
|
-
diffSummary({})
|
|
158
|
-
|
|
159
|
-
// Compare feature branch against main
|
|
160
|
-
diffSummary({ source: "feature-branch" })
|
|
161
|
-
|
|
162
|
-
// Compare feature branch against develop
|
|
163
|
-
diffSummary({
|
|
164
|
-
source: "feature-branch",
|
|
165
|
-
target: "develop"
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
// Compare branches on a different remote
|
|
169
|
-
diffSummary({
|
|
170
|
-
source: "feature-branch",
|
|
171
|
-
target: "main",
|
|
172
|
-
remote: "upstream"
|
|
173
|
-
})
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
#### Output Structure
|
|
177
|
-
|
|
178
|
-
**For branch comparisons:**
|
|
179
|
-
- Stats Overview: Summary of changes (insertions, deletions)
|
|
180
|
-
- Commits to Review: List of commits in the range
|
|
181
|
-
- Files Changed: List of modified files
|
|
182
|
-
- Full Diff: Complete diff with context
|
|
183
|
-
|
|
184
|
-
**For working tree changes:**
|
|
185
|
-
- Status Overview: Git status output
|
|
186
|
-
- Staged Changes: Stats, files, and diff for staged changes
|
|
187
|
-
- Unstaged Changes: Stats, files, and diff for unstaged changes
|
|
188
|
-
- Untracked Files: List and diffs for new untracked files
|
|
189
|
-
|
|
190
|
-
#### Notes
|
|
191
|
-
|
|
192
|
-
- When comparing branches, the tool fetches from the remote before generating the diff
|
|
193
|
-
- Diffs include 5 lines of context and function context for better readability
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
166
|
### prompt-session
|
|
198
167
|
|
|
199
168
|
Send a message to a child session (subagent) to continue the conversation. Useful for iterating with subagents without creating new sessions.
|
|
@@ -261,6 +230,46 @@ Child sessions (2):
|
|
|
261
230
|
|
|
262
231
|
---
|
|
263
232
|
|
|
233
|
+
### agent-promote
|
|
234
|
+
|
|
235
|
+
Change the type of a plugin agent at runtime. Promotes subagents to primary agents (visible in Tab selection) or demotes them back.
|
|
236
|
+
|
|
237
|
+
#### Parameters
|
|
238
|
+
|
|
239
|
+
| Parameter | Type | Required | Description |
|
|
240
|
+
|-----------|------|----------|-------------|
|
|
241
|
+
| `name` | `string` | Yes | Name of the plugin agent (e.g., `rubber-duck`, `architect`) |
|
|
242
|
+
| `grade` | `string` | Yes | Target type: `subagent`, `primary`, or `all` |
|
|
243
|
+
|
|
244
|
+
#### Grade Types
|
|
245
|
+
|
|
246
|
+
| Grade | Effect |
|
|
247
|
+
|-------|--------|
|
|
248
|
+
| `subagent` | Available only as a subagent (default for most agents) |
|
|
249
|
+
| `primary` | Appears in Tab selection for direct use |
|
|
250
|
+
| `all` | Available both as primary and subagent |
|
|
251
|
+
|
|
252
|
+
#### Usage Examples
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
# Promote rubber-duck to use it directly via Tab
|
|
256
|
+
/agent-promote rubber-duck primary
|
|
257
|
+
|
|
258
|
+
# Make architect available everywhere
|
|
259
|
+
/agent-promote architect all
|
|
260
|
+
|
|
261
|
+
# Revert code-reviewer to subagent only
|
|
262
|
+
/agent-promote code-reviewer subagent
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### Notes
|
|
266
|
+
|
|
267
|
+
- Only agents from this plugin can be promoted (see [Agents](#agents) table)
|
|
268
|
+
- Changes persist in memory until OpenCode restarts
|
|
269
|
+
- After promotion, use `Tab` or `<leader>a` to select the agent
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
264
273
|
### Blockchain
|
|
265
274
|
|
|
266
275
|
Tools for querying Ethereum and EVM-compatible blockchains via Etherscan APIs.
|
|
@@ -279,9 +288,35 @@ All blockchain tools support multiple chains via the `chainId` parameter:
|
|
|
279
288
|
| `250` | Fantom |
|
|
280
289
|
| `324` | zkSync |
|
|
281
290
|
|
|
291
|
+
#### Configuration
|
|
292
|
+
|
|
293
|
+
The blockchain tools use Etherscan-compatible APIs. An API key is optional but recommended.
|
|
294
|
+
|
|
295
|
+
**Environment Variable:**
|
|
296
|
+
|
|
297
|
+
| Variable | Required | Description |
|
|
298
|
+
|----------|----------|-------------|
|
|
299
|
+
| `ETHERSCAN_API_KEY` | No | API key for Etherscan and compatible explorers |
|
|
300
|
+
|
|
301
|
+
**Without an API key:** Requests are rate-limited (typically 1 request per 5 seconds).
|
|
302
|
+
|
|
303
|
+
**With an API key:** Higher rate limits and more reliable access.
|
|
304
|
+
|
|
305
|
+
**Getting an API key:**
|
|
306
|
+
|
|
307
|
+
1. Create a free account at [etherscan.io](https://etherscan.io/register)
|
|
308
|
+
2. Navigate to API Keys in your account settings
|
|
309
|
+
3. Generate a new API key
|
|
310
|
+
|
|
311
|
+
**Setting the environment variable:**
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
export ETHERSCAN_API_KEY="your-api-key-here"
|
|
315
|
+
```
|
|
316
|
+
|
|
282
317
|
#### eth-transaction
|
|
283
318
|
|
|
284
|
-
Get Ethereum transaction details by transaction hash. Returns status, block, addresses, gas costs, and
|
|
319
|
+
Get Ethereum transaction details by transaction hash. Returns status, block, addresses, gas costs in JSON format. Use optional parameters to include internal transactions, token transfers, and decoded event logs.
|
|
285
320
|
|
|
286
321
|
##### Parameters
|
|
287
322
|
|
|
@@ -289,11 +324,14 @@ Get Ethereum transaction details by transaction hash. Returns status, block, add
|
|
|
289
324
|
|-----------|------|----------|---------|-------------|
|
|
290
325
|
| `hash` | `string` | Yes | - | Transaction hash (0x...) |
|
|
291
326
|
| `chainId` | `string` | No | `"1"` | Chain ID (see table above) |
|
|
327
|
+
| `includeInternalTxs` | `boolean` | No | `false` | Include internal transactions (ETH transfers between contracts) |
|
|
328
|
+
| `includeTokenTransfers` | `boolean` | No | `false` | Include ERC-20 token transfers |
|
|
329
|
+
| `decodeLogs` | `boolean` | No | `false` | Decode event logs (Transfer, Approval, Deposit, Withdrawal) |
|
|
292
330
|
|
|
293
331
|
##### Usage Examples
|
|
294
332
|
|
|
295
333
|
```typescript
|
|
296
|
-
// Get transaction on Ethereum mainnet
|
|
334
|
+
// Get basic transaction details on Ethereum mainnet
|
|
297
335
|
ethTransaction({ hash: "0x123abc..." })
|
|
298
336
|
|
|
299
337
|
// Get transaction on Polygon
|
|
@@ -301,8 +339,90 @@ ethTransaction({
|
|
|
301
339
|
hash: "0x123abc...",
|
|
302
340
|
chainId: "137"
|
|
303
341
|
})
|
|
342
|
+
|
|
343
|
+
// Get transaction with internal transactions and token transfers
|
|
344
|
+
ethTransaction({
|
|
345
|
+
hash: "0x123abc...",
|
|
346
|
+
includeInternalTxs: true,
|
|
347
|
+
includeTokenTransfers: true
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
// Get full transaction details with decoded event logs
|
|
351
|
+
ethTransaction({
|
|
352
|
+
hash: "0x123abc...",
|
|
353
|
+
includeInternalTxs: true,
|
|
354
|
+
includeTokenTransfers: true,
|
|
355
|
+
decodeLogs: true
|
|
356
|
+
})
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
##### Output Structure
|
|
360
|
+
|
|
361
|
+
The tool returns JSON with labeled addresses (contract names resolved via Etherscan):
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
{
|
|
365
|
+
"hash": "0x123...",
|
|
366
|
+
"status": "success",
|
|
367
|
+
"block": 12345678,
|
|
368
|
+
"from": { "address": "0xabc...", "label": "Uniswap V3: Router" },
|
|
369
|
+
"to": { "address": "0xdef...", "label": "WETH" },
|
|
370
|
+
"value": "0",
|
|
371
|
+
"gas": { "used": 150000, "price": "20000000000", "cost": "0.003" }
|
|
372
|
+
}
|
|
304
373
|
```
|
|
305
374
|
|
|
375
|
+
With `includeInternalTxs: true`:
|
|
376
|
+
```json
|
|
377
|
+
{
|
|
378
|
+
"internalTransactions": [
|
|
379
|
+
{
|
|
380
|
+
"from": { "address": "0x...", "label": "Uniswap V3: Router" },
|
|
381
|
+
"to": { "address": "0x...", "label": null },
|
|
382
|
+
"value": "1.5",
|
|
383
|
+
"type": "call"
|
|
384
|
+
}
|
|
385
|
+
]
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
With `includeTokenTransfers: true`:
|
|
390
|
+
```json
|
|
391
|
+
{
|
|
392
|
+
"tokenTransfers": [
|
|
393
|
+
{
|
|
394
|
+
"token": { "address": "0x...", "name": "Wrapped Ether", "symbol": "WETH", "decimals": 18 },
|
|
395
|
+
"from": { "address": "0x...", "label": null },
|
|
396
|
+
"to": { "address": "0x...", "label": "Uniswap V3: Router" },
|
|
397
|
+
"value": "1.5"
|
|
398
|
+
}
|
|
399
|
+
]
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
With `decodeLogs: true`:
|
|
404
|
+
```json
|
|
405
|
+
{
|
|
406
|
+
"decodedEvents": [
|
|
407
|
+
{
|
|
408
|
+
"name": "Transfer",
|
|
409
|
+
"address": { "address": "0x...", "label": "WETH" },
|
|
410
|
+
"params": { "from": "0x...", "to": "0x...", "value": "1500000000000000000" }
|
|
411
|
+
}
|
|
412
|
+
],
|
|
413
|
+
"undecodedEventsCount": 2
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
##### Supported Decoded Events
|
|
418
|
+
|
|
419
|
+
| Event | Description |
|
|
420
|
+
|-------|-------------|
|
|
421
|
+
| `Transfer` | ERC-20 token transfer |
|
|
422
|
+
| `Approval` | ERC-20 approval for spending |
|
|
423
|
+
| `Deposit` | WETH deposit (ETH → WETH) |
|
|
424
|
+
| `Withdrawal` | WETH withdrawal (WETH → ETH) |
|
|
425
|
+
|
|
306
426
|
#### eth-address-balance
|
|
307
427
|
|
|
308
428
|
Get the ETH balance of an Ethereum address. Returns balance in both ETH and Wei.
|
package/command/commit-push.md
CHANGED
|
@@ -9,12 +9,17 @@ agent: build
|
|
|
9
9
|
|
|
10
10
|
## Your task
|
|
11
11
|
|
|
12
|
-
1.
|
|
12
|
+
1. Run `/diff-summary` to analyze all working tree changes
|
|
13
13
|
2. Present a summary to the user:
|
|
14
14
|
- Files modified/added/deleted with stats
|
|
15
15
|
- Proposed commit message based on the changes
|
|
16
|
-
3.
|
|
17
|
-
|
|
16
|
+
3. **If the current branch is `master`, `main`, `develop`, or `dev`:**
|
|
17
|
+
- Warn the user that committing directly to this branch is discouraged
|
|
18
|
+
- Propose to create a new feature branch with a suggested name based on the changes
|
|
19
|
+
- Ask the user if they want to: (a) create the suggested branch, (b) provide a custom branch name, or (c) continue on the current branch anyway
|
|
20
|
+
- If the user chooses to create a branch, create it and switch to it before proceeding
|
|
21
|
+
4. Ask the user for confirmation before proceeding
|
|
22
|
+
5. Only if the user confirms:
|
|
18
23
|
- Stage all changes (`git add -A`)
|
|
19
24
|
- Create the commit with the agreed message
|
|
20
25
|
- Push to origin on the current branch
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show working tree changes or diff between branches ($1=source, $2=target)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Diff Summary
|
|
6
|
+
|
|
7
|
+
!`bash -c '
|
|
8
|
+
SOURCE="$1"
|
|
9
|
+
TARGET="$2"
|
|
10
|
+
TARGET="${TARGET:-HEAD}"
|
|
11
|
+
|
|
12
|
+
if [ -n "$SOURCE" ]; then
|
|
13
|
+
echo "## Branch Comparison: $SOURCE → $TARGET"
|
|
14
|
+
echo ""
|
|
15
|
+
git fetch --all --prune 2>/dev/null || true
|
|
16
|
+
|
|
17
|
+
echo "### Stats Overview"
|
|
18
|
+
git diff --stat "$TARGET"..."$SOURCE"
|
|
19
|
+
|
|
20
|
+
echo ""
|
|
21
|
+
echo "### Commits"
|
|
22
|
+
git log --oneline --no-merges "$TARGET".."$SOURCE"
|
|
23
|
+
|
|
24
|
+
echo ""
|
|
25
|
+
echo "### Files Changed"
|
|
26
|
+
git diff --name-only "$TARGET"..."$SOURCE"
|
|
27
|
+
|
|
28
|
+
echo ""
|
|
29
|
+
echo "### Full Diff"
|
|
30
|
+
git diff "$TARGET"..."$SOURCE"
|
|
31
|
+
else
|
|
32
|
+
echo "## Status"
|
|
33
|
+
git status --porcelain
|
|
34
|
+
|
|
35
|
+
echo ""
|
|
36
|
+
echo "## Staged Changes"
|
|
37
|
+
git diff --cached --stat
|
|
38
|
+
git diff --cached
|
|
39
|
+
|
|
40
|
+
echo ""
|
|
41
|
+
echo "## Unstaged Changes"
|
|
42
|
+
git diff --stat
|
|
43
|
+
git diff
|
|
44
|
+
|
|
45
|
+
echo ""
|
|
46
|
+
echo "## Untracked Files Content"
|
|
47
|
+
git ls-files --others --exclude-standard | while read f; do
|
|
48
|
+
[ -f "$f" ] && echo "=== $f ===" && sed -n "1,50p" "$f" && sed -n "51p" "$f" | grep -q . && echo "... (truncated)"
|
|
49
|
+
done
|
|
50
|
+
fi
|
|
51
|
+
'`
|
package/command/doc-changes.md
CHANGED
|
@@ -5,7 +5,7 @@ agent: doc-writer
|
|
|
5
5
|
|
|
6
6
|
## Analysis Phase
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Run `/diff-summary` to get the working tree changes, then:
|
|
9
9
|
|
|
10
10
|
1. **Identify new features** in the changes:
|
|
11
11
|
- New public APIs, functions, or methods
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Commit, push and create a GitHub PR
|
|
3
|
+
agent: build
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
- Current branch: !`git branch --show-current`
|
|
9
|
+
- Default branch: !`gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'`
|
|
10
|
+
|
|
11
|
+
## Your task
|
|
12
|
+
|
|
13
|
+
1. Execute `/commit-push` to commit and push all changes
|
|
14
|
+
2. Once the push is complete, create a PR using `gh pr create`:
|
|
15
|
+
- Use the commit message as PR title
|
|
16
|
+
- Generate a brief PR description summarizing the changes
|
|
17
|
+
- Target the repository's default branch
|
|
18
|
+
3. Display the PR URL to the user
|
|
@@ -5,4 +5,18 @@ agent: code-reviewer
|
|
|
5
5
|
|
|
6
6
|
# Review: Working Tree → HEAD
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## Status
|
|
9
|
+
!`git status --porcelain`
|
|
10
|
+
|
|
11
|
+
## Staged Changes
|
|
12
|
+
!`git diff --cached --stat`
|
|
13
|
+
!`git diff --cached`
|
|
14
|
+
|
|
15
|
+
## Unstaged Changes
|
|
16
|
+
!`git diff --stat`
|
|
17
|
+
!`git diff`
|
|
18
|
+
|
|
19
|
+
## Untracked Files Content
|
|
20
|
+
!`bash -c 'git ls-files --others --exclude-standard | while read f; do [ -f "$f" ] && echo "=== $f ===" && sed -n "1,50p" "$f" && sed -n "51p" "$f" | grep -q . && echo "... (truncated)"; done'`
|
|
21
|
+
|
|
22
|
+
Review the above changes for quality, correctness, and adherence to project guidelines.
|
package/command/review-pr.md
CHANGED
|
@@ -3,6 +3,8 @@ description: Review changes from source branch into target branch
|
|
|
3
3
|
agent: code-reviewer
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Review:
|
|
6
|
+
# Review: $1 → $2
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/diff-summary $1 $2
|
|
9
|
+
|
|
10
|
+
Review the above changes for quality, correctness, and adherence to project guidelines.
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { getGlobalHookDir, getProjectHookDir } from "./config-paths";
|
|
|
5
5
|
import { hasCodeExtension } from "./code-files";
|
|
6
6
|
import { log } from "./logger";
|
|
7
7
|
import { executeBashAction, DEFAULT_BASH_TIMEOUT, } from "./bash-executor";
|
|
8
|
-
import { gitingestTool,
|
|
8
|
+
import { gitingestTool, createPromptSessionTool, createListChildSessionsTool, createAgentPromoteTool, getPromotedAgents, ethTransactionTool, ethAddressTxsTool, ethAddressBalanceTool, ethTokenTransfersTool, } from "./tools";
|
|
9
9
|
export { parseFrontmatter, loadAgents, loadCommands } from "./loaders";
|
|
10
10
|
// ============================================================================
|
|
11
11
|
// CONSTANTS
|
|
@@ -32,7 +32,7 @@ const SmartfrogPlugin = async (ctx) => {
|
|
|
32
32
|
hooks: Array.from(hooks.keys()),
|
|
33
33
|
tools: [
|
|
34
34
|
"gitingest",
|
|
35
|
-
"
|
|
35
|
+
"agent-promote",
|
|
36
36
|
"eth-transaction",
|
|
37
37
|
"eth-address-txs",
|
|
38
38
|
"eth-address-balance",
|
|
@@ -174,8 +174,14 @@ const SmartfrogPlugin = async (ctx) => {
|
|
|
174
174
|
}
|
|
175
175
|
return {
|
|
176
176
|
config: async (config) => {
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
const loadedAgents = loadAgents(AGENT_DIR);
|
|
178
|
+
for (const [name, mode] of getPromotedAgents()) {
|
|
179
|
+
if (loadedAgents[name]) {
|
|
180
|
+
loadedAgents[name].mode = mode;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (Object.keys(loadedAgents).length > 0) {
|
|
184
|
+
config.agent = { ...(config.agent ?? {}), ...loadedAgents };
|
|
179
185
|
}
|
|
180
186
|
if (Object.keys(commands).length > 0) {
|
|
181
187
|
config.command = { ...(config.command ?? {}), ...commands };
|
|
@@ -183,9 +189,9 @@ const SmartfrogPlugin = async (ctx) => {
|
|
|
183
189
|
},
|
|
184
190
|
tool: {
|
|
185
191
|
gitingest: gitingestTool,
|
|
186
|
-
"diff-summary": createDiffSummaryTool(ctx.directory),
|
|
187
192
|
"prompt-session": createPromptSessionTool(ctx.client),
|
|
188
193
|
"list-child-sessions": createListChildSessionsTool(ctx.client),
|
|
194
|
+
"agent-promote": createAgentPromoteTool(ctx.client, Object.keys(agents)),
|
|
189
195
|
"eth-transaction": ethTransactionTool,
|
|
190
196
|
"eth-address-txs": ethAddressTxsTool,
|
|
191
197
|
"eth-address-balance": ethAddressBalanceTool,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type AgentMode = "subagent" | "primary" | "all";
|
|
2
|
+
export declare const VALID_GRADES: AgentMode[];
|
|
3
|
+
export declare function getPromotedAgents(): ReadonlyMap<string, AgentMode>;
|
|
4
|
+
export declare function setPromotedAgent(name: string, mode: AgentMode): void;
|
|
5
|
+
export declare function validateGrade(grade: string): grade is AgentMode;
|
|
6
|
+
export declare function validateAgentName(name: string, pluginAgentNames: string[]): boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const VALID_GRADES = ["subagent", "primary", "all"];
|
|
2
|
+
const promotedAgents = new Map();
|
|
3
|
+
export function getPromotedAgents() {
|
|
4
|
+
return promotedAgents;
|
|
5
|
+
}
|
|
6
|
+
export function setPromotedAgent(name, mode) {
|
|
7
|
+
promotedAgents.set(name, mode);
|
|
8
|
+
}
|
|
9
|
+
export function validateGrade(grade) {
|
|
10
|
+
return VALID_GRADES.includes(grade);
|
|
11
|
+
}
|
|
12
|
+
export function validateAgentName(name, pluginAgentNames) {
|
|
13
|
+
return pluginAgentNames.includes(name);
|
|
14
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ToolContext } from "@opencode-ai/plugin";
|
|
2
|
+
import type { createOpencodeClient } from "@opencode-ai/sdk";
|
|
3
|
+
export { type AgentMode, VALID_GRADES, getPromotedAgents, setPromotedAgent, validateGrade, validateAgentName, } from "./agent-promote-core";
|
|
4
|
+
type Client = ReturnType<typeof createOpencodeClient>;
|
|
5
|
+
export interface AgentPromoteArgs {
|
|
6
|
+
name: string;
|
|
7
|
+
grade: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function createAgentPromoteTool(client: Client, pluginAgentNames: string[]): {
|
|
10
|
+
description: string;
|
|
11
|
+
args: {
|
|
12
|
+
name: import("zod").ZodString;
|
|
13
|
+
grade: import("zod").ZodString;
|
|
14
|
+
};
|
|
15
|
+
execute(args: {
|
|
16
|
+
name: string;
|
|
17
|
+
grade: string;
|
|
18
|
+
}, context: ToolContext): Promise<string>;
|
|
19
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
import { log } from "../logger";
|
|
3
|
+
import { VALID_GRADES, setPromotedAgent, validateGrade, validateAgentName, } from "./agent-promote-core";
|
|
4
|
+
export { VALID_GRADES, getPromotedAgents, setPromotedAgent, validateGrade, validateAgentName, } from "./agent-promote-core";
|
|
5
|
+
export function createAgentPromoteTool(client, pluginAgentNames) {
|
|
6
|
+
return tool({
|
|
7
|
+
description: "Change the type of an agent to primary, subagent or all",
|
|
8
|
+
args: {
|
|
9
|
+
name: tool.schema.string().describe("Name of the agent"),
|
|
10
|
+
grade: tool.schema.string().describe("Target type: 'subagent', 'primary', or 'all'"),
|
|
11
|
+
},
|
|
12
|
+
async execute(args, _context) {
|
|
13
|
+
const { name, grade } = args;
|
|
14
|
+
if (!validateGrade(grade)) {
|
|
15
|
+
return `Invalid grade "${grade}". Valid grades: ${VALID_GRADES.join(", ")}`;
|
|
16
|
+
}
|
|
17
|
+
if (!validateAgentName(name, pluginAgentNames)) {
|
|
18
|
+
return `Agent "${name}" not found in this plugin. Available: ${pluginAgentNames.join(", ")}`;
|
|
19
|
+
}
|
|
20
|
+
const agentsResp = await client.app.agents();
|
|
21
|
+
const agents = agentsResp.data ?? [];
|
|
22
|
+
const existingAgent = agents.find((a) => a.name === name);
|
|
23
|
+
if (existingAgent && existingAgent.mode === grade) {
|
|
24
|
+
return `Agent "${name}" is already of type "${grade}"`;
|
|
25
|
+
}
|
|
26
|
+
setPromotedAgent(name, grade);
|
|
27
|
+
log("[agent-promote] Agent type changed", { name, grade });
|
|
28
|
+
await client.tui.showToast({
|
|
29
|
+
body: {
|
|
30
|
+
message: `Promoting agent "${name}" to "${grade}"...`,
|
|
31
|
+
variant: "success",
|
|
32
|
+
duration: 3000,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
await client.instance.dispose();
|
|
36
|
+
return `Agent "${name}" changed to type "${grade}". Use Tab or <leader>a to select it.`;
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { validateGrade, validateAgentName, getPromotedAgents, setPromotedAgent, VALID_GRADES, } from "./agent-promote-core";
|
|
3
|
+
describe("agent-promote", () => {
|
|
4
|
+
const pluginAgentNames = ["rubber-duck", "architect", "code-reviewer"];
|
|
5
|
+
describe("VALID_GRADES", () => {
|
|
6
|
+
it("should contain subagent, primary, and all", () => {
|
|
7
|
+
expect(VALID_GRADES).toContain("subagent");
|
|
8
|
+
expect(VALID_GRADES).toContain("primary");
|
|
9
|
+
expect(VALID_GRADES).toContain("all");
|
|
10
|
+
expect(VALID_GRADES).toHaveLength(3);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
describe("validateGrade", () => {
|
|
14
|
+
it("should return true for valid grade: subagent", () => {
|
|
15
|
+
expect(validateGrade("subagent")).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
it("should return true for valid grade: primary", () => {
|
|
18
|
+
expect(validateGrade("primary")).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it("should return true for valid grade: all", () => {
|
|
21
|
+
expect(validateGrade("all")).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it("should return false for invalid grade", () => {
|
|
24
|
+
expect(validateGrade("invalid")).toBe(false);
|
|
25
|
+
expect(validateGrade("foo")).toBe(false);
|
|
26
|
+
expect(validateGrade("")).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe("validateAgentName", () => {
|
|
30
|
+
it("should return true for agent in plugin", () => {
|
|
31
|
+
expect(validateAgentName("rubber-duck", pluginAgentNames)).toBe(true);
|
|
32
|
+
expect(validateAgentName("architect", pluginAgentNames)).toBe(true);
|
|
33
|
+
expect(validateAgentName("code-reviewer", pluginAgentNames)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
it("should return false for agent not in plugin", () => {
|
|
36
|
+
expect(validateAgentName("unknown", pluginAgentNames)).toBe(false);
|
|
37
|
+
expect(validateAgentName("build", pluginAgentNames)).toBe(false);
|
|
38
|
+
expect(validateAgentName("", pluginAgentNames)).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe("promotedAgents Map", () => {
|
|
42
|
+
it("should set and get promoted agent", () => {
|
|
43
|
+
setPromotedAgent("test-agent-1", "primary");
|
|
44
|
+
const promoted = getPromotedAgents();
|
|
45
|
+
expect(promoted.get("test-agent-1")).toBe("primary");
|
|
46
|
+
});
|
|
47
|
+
it("should update existing promotion", () => {
|
|
48
|
+
setPromotedAgent("test-agent-2", "primary");
|
|
49
|
+
expect(getPromotedAgents().get("test-agent-2")).toBe("primary");
|
|
50
|
+
setPromotedAgent("test-agent-2", "all");
|
|
51
|
+
expect(getPromotedAgents().get("test-agent-2")).toBe("all");
|
|
52
|
+
setPromotedAgent("test-agent-2", "subagent");
|
|
53
|
+
expect(getPromotedAgents().get("test-agent-2")).toBe("subagent");
|
|
54
|
+
});
|
|
55
|
+
it("should handle multiple agents", () => {
|
|
56
|
+
setPromotedAgent("agent-a", "primary");
|
|
57
|
+
setPromotedAgent("agent-b", "all");
|
|
58
|
+
setPromotedAgent("agent-c", "subagent");
|
|
59
|
+
const promoted = getPromotedAgents();
|
|
60
|
+
expect(promoted.get("agent-a")).toBe("primary");
|
|
61
|
+
expect(promoted.get("agent-b")).toBe("all");
|
|
62
|
+
expect(promoted.get("agent-c")).toBe("subagent");
|
|
63
|
+
});
|
|
64
|
+
it("should return readonly map", () => {
|
|
65
|
+
const promoted = getPromotedAgents();
|
|
66
|
+
expect(typeof promoted.get).toBe("function");
|
|
67
|
+
expect(typeof promoted.has).toBe("function");
|
|
68
|
+
expect(typeof promoted.forEach).toBe("function");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|