opencode-skills-collection 4.0.66 → 4.0.67
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/bundled-skills/.antigravity-install-manifest.json +3 -1
- package/bundled-skills/beatra-ai-video-studio/SKILL.md +272 -0
- package/bundled-skills/meteora-dlmm-pool-screening/SKILL.md +166 -0
- package/bundled-skills/meteora-dlmm-pool-screening/references/meteora-apis.md +74 -0
- package/bundled-skills/meteora-dlmm-pool-screening/references/meteora-screener.md +352 -0
- package/package.json +1 -1
- package/skills_index.json +118 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"updatedAt": "2026-09-
|
|
3
|
+
"updatedAt": "2026-09-19T01:59:35.529Z",
|
|
4
4
|
"entries": [
|
|
5
5
|
"00-andruia-consultant",
|
|
6
6
|
"007",
|
|
@@ -345,6 +345,7 @@
|
|
|
345
345
|
"bdi-mental-states",
|
|
346
346
|
"bdistill-behavioral-xray",
|
|
347
347
|
"bdistill-knowledge-extraction",
|
|
348
|
+
"beatra-ai-video-studio",
|
|
348
349
|
"beautiful-prose",
|
|
349
350
|
"before-you-build",
|
|
350
351
|
"behavioral-modes",
|
|
@@ -1270,6 +1271,7 @@
|
|
|
1270
1271
|
"mermaid-expert",
|
|
1271
1272
|
"mesh-memory",
|
|
1272
1273
|
"metasploit-framework",
|
|
1274
|
+
"meteora-dlmm-pool-screening",
|
|
1273
1275
|
"micro-saas-launcher",
|
|
1274
1276
|
"microservices-patterns",
|
|
1275
1277
|
"microsoft-azure-webjobs-extensions-authentication-events-dotnet",
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: beatra-ai-video-studio
|
|
3
|
+
description: "Install and use the official Beatra AI Video Studio package, pinned by digest, for paid text-to-video, image-to-video, and video edit or extend jobs on the hosted Beatra service."
|
|
4
|
+
category: media
|
|
5
|
+
risk: critical
|
|
6
|
+
source: community
|
|
7
|
+
source_repo: beatra-ai/beatra-skills
|
|
8
|
+
source_type: official
|
|
9
|
+
date_added: "2026-09-17"
|
|
10
|
+
author: beatra-ai
|
|
11
|
+
tags: [video-generation, text-to-video, image-to-video, video-editing, mcp, paid-api, beatra]
|
|
12
|
+
tools: [claude, codex, cursor, gemini]
|
|
13
|
+
license: "MIT-0"
|
|
14
|
+
license_source: "https://github.com/beatra-ai/beatra-skills/blob/95d662f7aeddd6e2aa6da9e14f9c985e3f6b914d/LICENSE"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Beatra AI Video Studio
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
Beatra AI Video Studio is Beatra's official agent package for short AI video
|
|
22
|
+
work: text-to-video, image-to-video, first/last-frame interpolation,
|
|
23
|
+
reference-guided generation, and editing or extending an existing clip. The
|
|
24
|
+
video is produced on the hosted, paid Beatra service (`mcp.beatra.ai`).
|
|
25
|
+
|
|
26
|
+
This catalog entry is a **reviewed pointer, not the executable package**. It
|
|
27
|
+
contains no client code and performs no Beatra operation by itself. The package
|
|
28
|
+
it points to bundles three standard-library Python scripts that make network
|
|
29
|
+
calls, store a credential, and can replace their own files. Read
|
|
30
|
+
[Install](#install-pinned-verified-approved-twice) and
|
|
31
|
+
[Security & Safety Notes](#security--safety-notes) before activating it.
|
|
32
|
+
|
|
33
|
+
| Pinned identity | Value |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| Package | `beatra-ai-video-studio` `1.2.5` |
|
|
36
|
+
| Archive | `https://cdn.beatra.ai/agent-packages/beatra-ai-video-studio/v1.2.5/beatra-ai-video-studio-skill-1.2.5.zip` |
|
|
37
|
+
| Archive SHA-256 | `679a3ddc06c7be631ef31f002f918465a26f17a189ffac723f45dfd348e1fc4a` |
|
|
38
|
+
| Source tree | [`beatra-ai/beatra-skills@95d662f`](https://github.com/beatra-ai/beatra-skills/tree/95d662f7aeddd6e2aa6da9e14f9c985e3f6b914d/skills/beatra-ai-video-studio) (full SHA `95d662f7aeddd6e2aa6da9e14f9c985e3f6b914d`) |
|
|
39
|
+
| License | MIT No Attribution (MIT-0) |
|
|
40
|
+
|
|
41
|
+
The archive holds 17 regular files (no symlinks, no executable bits, no
|
|
42
|
+
binaries), and every file is byte-identical to the source tree at that commit.
|
|
43
|
+
|
|
44
|
+
## When to Use This Skill
|
|
45
|
+
|
|
46
|
+
- Use when the user explicitly wants a short AI video clip produced on Beatra
|
|
47
|
+
and accepts that the work is paid and runs on a hosted service.
|
|
48
|
+
- Use when a still image should be animated, two frames interpolated, or an
|
|
49
|
+
existing clip edited or extended by Beatra.
|
|
50
|
+
- Do not use for local-only or offline video editing, timeline assembly, or
|
|
51
|
+
when the user has not agreed to send prompts and media to a third party.
|
|
52
|
+
|
|
53
|
+
## Install (pinned, verified, approved twice)
|
|
54
|
+
|
|
55
|
+
### Step 1: Download and verify into a review directory
|
|
56
|
+
|
|
57
|
+
Explain that this downloads an external package from `cdn.beatra.ai`, then ask
|
|
58
|
+
for approval. Only after approval:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
umask 077
|
|
62
|
+
review_dir="$(mktemp -d)"
|
|
63
|
+
cd "$review_dir" || exit 1
|
|
64
|
+
curl -fsSLO "https://cdn.beatra.ai/agent-packages/beatra-ai-video-studio/v1.2.5/beatra-ai-video-studio-skill-1.2.5.zip"
|
|
65
|
+
printf '%s %s\n' \
|
|
66
|
+
'679a3ddc06c7be631ef31f002f918465a26f17a189ffac723f45dfd348e1fc4a' \
|
|
67
|
+
'beatra-ai-video-studio-skill-1.2.5.zip' | shasum -a 256 -c -
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Stop if the check does not print `OK`. The expected digest comes from this
|
|
71
|
+
catalog entry, not from a file on the same CDN.
|
|
72
|
+
|
|
73
|
+
### Step 2: Inspect before activation
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
unzip -l beatra-ai-video-studio-skill-1.2.5.zip
|
|
77
|
+
unzip -q beatra-ai-video-studio-skill-1.2.5.zip
|
|
78
|
+
find beatra-ai-video-studio -type l -print # expect no output
|
|
79
|
+
find beatra-ai-video-studio -type f -perm -111 -print # expect no output
|
|
80
|
+
grep -n '"auto_update": True' beatra-ai-video-studio/scripts/mcp_client.py
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Optionally confirm byte identity with the public source tree (a second,
|
|
84
|
+
independent host):
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
git clone --quiet --filter=blob:none --no-checkout https://github.com/beatra-ai/beatra-skills.git src
|
|
88
|
+
git -C src checkout --quiet 95d662f7aeddd6e2aa6da9e14f9c985e3f6b914d -- skills/beatra-ai-video-studio
|
|
89
|
+
(cd beatra-ai-video-studio && find . -type f | LC_ALL=C sort | xargs shasum -a 256) > archive.sha
|
|
90
|
+
(cd src/skills/beatra-ai-video-studio && find . -type f | LC_ALL=C sort | xargs shasum -a 256) > tree.sha
|
|
91
|
+
cmp archive.sha tree.sha && echo IDENTICAL
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Report to the user what the review found: `SKILL.md`, `manifest.json`, 12
|
|
95
|
+
Markdown references, and `scripts/authorize.py`, `scripts/mcp_client.py`,
|
|
96
|
+
`scripts/uninstall.py` (Python 3.10+, standard library only, no dependency
|
|
97
|
+
installation, no lifecycle hooks). Summarize the network, credential,
|
|
98
|
+
local-state, telemetry, and self-update behavior listed under
|
|
99
|
+
[Security & Safety Notes](#security--safety-notes).
|
|
100
|
+
|
|
101
|
+
### Step 3: Copy in and disable self-update before any other command
|
|
102
|
+
|
|
103
|
+
Ask for a **second, separate** approval, because this changes agent
|
|
104
|
+
configuration. Then copy the reviewed tree to the host's skills directory
|
|
105
|
+
(Claude Code shown; use the equivalent path for other hosts) and immediately
|
|
106
|
+
turn off silent self-update for that exact path:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
dest="$HOME/.claude/skills/beatra-ai-video-studio"
|
|
110
|
+
test ! -e "$dest" || { echo "destination exists; stop and ask the user"; exit 1; }
|
|
111
|
+
cp -R beatra-ai-video-studio "$dest"
|
|
112
|
+
python3 "$dest/scripts/mcp_client.py" update --auto off
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The last command must print
|
|
116
|
+
`Automatic Beatra package updates are disabled.` It writes only
|
|
117
|
+
`~/.beatra/updates/<id>/state.json` and makes no network request. Run it
|
|
118
|
+
before `authorize.py`, `verify`, `tools`, `upload`, or `call`: in version 1.2.5
|
|
119
|
+
self-update is **on by default** (see below). The setting is keyed to the
|
|
120
|
+
resolved install path, so repeat it after moving or re-copying the directory.
|
|
121
|
+
|
|
122
|
+
### Step 4: Authorize as its own decision
|
|
123
|
+
|
|
124
|
+
Authorization opens a browser sign-in and stores a bearer token. Ask first.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
python3 "$dest/scripts/authorize.py"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Start a new agent session if the host discovers skills only at startup.
|
|
131
|
+
|
|
132
|
+
## How It Works
|
|
133
|
+
|
|
134
|
+
1. Read the installed package's `SKILL.md` and its references; they define the
|
|
135
|
+
video routes, payload shapes, and review loop.
|
|
136
|
+
2. Use free, non-billable discovery first (`beatra.models.list`, task list) to
|
|
137
|
+
read current model cards, durations, resolutions, and credit estimates.
|
|
138
|
+
3. Show the user a cost card for exactly one paid call: tool, model, duration,
|
|
139
|
+
resolution, estimate, and which files will be uploaded. Wait for explicit
|
|
140
|
+
approval. Planning or "make the clip" is not approval.
|
|
141
|
+
4. Upload only files the user supplied and approved for this task, then submit
|
|
142
|
+
once with a stable `client_request_id` and poll the returned task.
|
|
143
|
+
5. Report the delivered artifact and `billing.net_charged_credits`, review the
|
|
144
|
+
clip with the user, and ask again before any further paid stage.
|
|
145
|
+
|
|
146
|
+
## Examples
|
|
147
|
+
|
|
148
|
+
### Free connection check (non-billable)
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
python3 "$HOME/.claude/skills/beatra-ai-video-studio/scripts/mcp_client.py" verify
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### One approved text-to-video call
|
|
155
|
+
|
|
156
|
+
After the user approved the cost card for this exact payload:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
printf '%s' '{"prompt":"A close product reveal with one slow push-in.","model":"auto","duration":5,"client_request_id":"req-7f3c2a"}' \
|
|
160
|
+
| python3 "$HOME/.claude/skills/beatra-ai-video-studio/scripts/mcp_client.py" call beatra.videos.generate
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
If the response is lost, retry only with the same `client_request_id` and
|
|
164
|
+
identical arguments; never submit a replacement while the task is running.
|
|
165
|
+
|
|
166
|
+
## Update
|
|
167
|
+
|
|
168
|
+
This entry does not endorse the package's silent self-update. Keep
|
|
169
|
+
`update --auto off` in place and do not run `update --auto on` or a bare
|
|
170
|
+
`update` without explicit approval.
|
|
171
|
+
|
|
172
|
+
To move to a newer version, repeat Steps 1 to 3 for that version in a new
|
|
173
|
+
review directory, verify its digest against the matching tree in
|
|
174
|
+
`beatra-ai/beatra-skills` (a version not listed in this entry has not been
|
|
175
|
+
reviewed here), show the user the file-level changes against the installed
|
|
176
|
+
copy, and replace the directory only after approval. `update --check` reports
|
|
177
|
+
the available version from `beatra.ai` without changing files.
|
|
178
|
+
|
|
179
|
+
## Uninstall
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
dest="$HOME/.claude/skills/beatra-ai-video-studio"
|
|
183
|
+
python3 "$dest/scripts/uninstall.py" --dry-run
|
|
184
|
+
python3 "$dest/scripts/uninstall.py"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Read the JSON decision. `disconnected` means this was the last Beatra skill on
|
|
188
|
+
the device: the token was revoked (when `revoked` is `true`) and the files
|
|
189
|
+
`credentials.json`, `installation.json`, `host.json`, `skills.json`, and
|
|
190
|
+
`registrations.json` under `~/.beatra` were removed. `keep_connection` means
|
|
191
|
+
another Beatra skill still uses the shared token, which stays. Then, after
|
|
192
|
+
confirming the path with the user, delete `$dest`.
|
|
193
|
+
|
|
194
|
+
`uninstall.py` does not remove `~/.beatra/updates/`. When the decision is
|
|
195
|
+
`disconnected` and no other Beatra skill is installed, remove that directory
|
|
196
|
+
too; otherwise leave it. A token that could not be revoked expires after 15
|
|
197
|
+
days idle and can be revoked at once in the Beatra Console under Agents.
|
|
198
|
+
|
|
199
|
+
## Best Practices
|
|
200
|
+
|
|
201
|
+
- ✅ Keep one approval per paid call, per upload set, and per install step.
|
|
202
|
+
- ✅ Start with the shortest duration and lowest resolution the model admits.
|
|
203
|
+
- ✅ Report net charged credits from the terminal task, not the estimate.
|
|
204
|
+
- ❌ Do not re-enable self-update or install an unreviewed version silently.
|
|
205
|
+
- ❌ Do not print, paste, or move the token out of `~/.beatra/credentials.json`.
|
|
206
|
+
- ❌ Do not retry a paid call with a new request ID after an uncertain response.
|
|
207
|
+
|
|
208
|
+
## Limitations
|
|
209
|
+
|
|
210
|
+
- Requires a Beatra account, network access, Python 3.10+, and prepaid credits;
|
|
211
|
+
the sign-up gift usually cannot cover a video generation.
|
|
212
|
+
- Short clips only; not a timeline editor. Multi-shot work is delivered as
|
|
213
|
+
separate clips.
|
|
214
|
+
- Generation is asynchronous and can take minutes. Upload size is capped at
|
|
215
|
+
100 MB and a model may impose a lower limit.
|
|
216
|
+
- Models, prices, schemas, and availability are controlled by Beatra and its
|
|
217
|
+
upstream model providers and can change; the service is provided as is.
|
|
218
|
+
- Outputs are probabilistic, not guaranteed unique or non-infringing, and need
|
|
219
|
+
human review before publication.
|
|
220
|
+
|
|
221
|
+
## Security & Safety Notes
|
|
222
|
+
|
|
223
|
+
`risk: critical` is deliberate.
|
|
224
|
+
|
|
225
|
+
- **Network.** Authorization uses `api.beatra.ai`; every tool call and upload
|
|
226
|
+
goes to `mcp.beatra.ai`. With self-update left on, the client also fetches
|
|
227
|
+
`https://beatra.ai/skills/beatra-ai-video-studio/install.json` and archives
|
|
228
|
+
from `cdn.beatra.ai`.
|
|
229
|
+
- **Self-update (on by default in 1.2.5).** Before `verify`, `tools`, `upload`,
|
|
230
|
+
or `call`, the client checks for a newer release at most once every 24 hours
|
|
231
|
+
and, if one exists, replaces package-owned files without per-update preview
|
|
232
|
+
or approval. It pins host and path, verifies checksums from Beatra's own
|
|
233
|
+
discovery document and CDN, and rolls back on failure, but those digests are
|
|
234
|
+
not independently anchored. This entry therefore requires
|
|
235
|
+
`update --auto off` before first use, as in Step 3.
|
|
236
|
+
- **Uploads and third parties.** Prompts and uploaded media are sent to Beatra
|
|
237
|
+
and forwarded to the third-party model provider executing the task. Beatra
|
|
238
|
+
is hosted outside mainland China. See the
|
|
239
|
+
[privacy policy](https://beatra.ai/en/privacy).
|
|
240
|
+
- **Billing.** Paid tools consume prepaid credits. Credits for tasks that fail
|
|
241
|
+
for reasons attributable to Beatra or the upstream model are returned
|
|
242
|
+
automatically; otherwise purchases are non-refundable, and actual usage can
|
|
243
|
+
exceed the estimate and leave a negative balance. See the
|
|
244
|
+
[terms](https://beatra.ai/en/terms).
|
|
245
|
+
- **Credentials.** `authorize.py` runs an OAuth device flow, opens the browser,
|
|
246
|
+
and stores one bearer token in `~/.beatra/credentials.json` (directory
|
|
247
|
+
`0700`, file `0600`). The token never goes into argv, environment variables,
|
|
248
|
+
or output. It carries full Beatra scope, including `wallet:spend`, has a
|
|
249
|
+
sliding 15-day idle lifetime, and is shared by every Beatra skill on the
|
|
250
|
+
device.
|
|
251
|
+
- **Local state.** `~/.beatra/` also holds `installation.json` (random
|
|
252
|
+
installation reference), `host.json` (agent platform and hostname),
|
|
253
|
+
`skills.json` (installed Beatra skill paths), `registrations.json`, and
|
|
254
|
+
`updates/<id>/state.json` plus update backups when updates run.
|
|
255
|
+
- **Telemetry.** The device flow sends the hostname as the device name, the
|
|
256
|
+
package slug and version, and the detected agent platform (for example
|
|
257
|
+
`claude-code`, from environment variables). On first use the client makes a
|
|
258
|
+
non-billable `beatra.installations.register` call, and every tool call adds
|
|
259
|
+
`source_package_slug` and `source_platform`. There is no opt-out for these
|
|
260
|
+
fields.
|
|
261
|
+
- **Retention.** Account data is kept for the life of the account. Task
|
|
262
|
+
records, artifacts, and billing ledgers are retained after account closure
|
|
263
|
+
for audit and tax purposes.
|
|
264
|
+
- **Content rights.** Users keep rights in their inputs and receive Beatra's
|
|
265
|
+
rights in outputs; they must hold rights to uploaded media and must not
|
|
266
|
+
create deceptive impersonations.
|
|
267
|
+
|
|
268
|
+
## Additional Resources
|
|
269
|
+
|
|
270
|
+
- [Source tree at the pinned commit](https://github.com/beatra-ai/beatra-skills/tree/95d662f7aeddd6e2aa6da9e14f9c985e3f6b914d/skills/beatra-ai-video-studio)
|
|
271
|
+
- [Product page](https://beatra.ai/skills/beatra-ai-video-studio)
|
|
272
|
+
- [Terms](https://beatra.ai/en/terms) and [privacy policy](https://beatra.ai/en/privacy)
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: meteora-dlmm-pool-screening
|
|
3
|
+
risk: safe
|
|
4
|
+
source: community
|
|
5
|
+
source_repo: romankurnovskii/etemaro
|
|
6
|
+
source_type: community
|
|
7
|
+
date_added: "2026-09-17"
|
|
8
|
+
description: >
|
|
9
|
+
Screen and rank Meteora DLMM pools for LP quality using public Meteora APIs (fee/TVL,
|
|
10
|
+
bin step, organic score). Read-only: never deploys, swaps, or signs.
|
|
11
|
+
metadata:
|
|
12
|
+
version: "1.0.0"
|
|
13
|
+
author: etemaro
|
|
14
|
+
license: MIT
|
|
15
|
+
compatibility: Network access to public Meteora datapi. No API key. The bundled Python 3
|
|
16
|
+
stdlib screener is embedded in this file under "Screener script".
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Meteora DLMM pool screening
|
|
20
|
+
|
|
21
|
+
Rank Meteora DLMM pools the way an LP screener should: **hard-filter first, then sort by
|
|
22
|
+
windowed fee / active TVL**. Public APIs only. No keys, no transactions.
|
|
23
|
+
|
|
24
|
+
The embedded screener script below encodes the gates so every run uses the same numbers.
|
|
25
|
+
Save it to a scratch directory (for example `mktemp -d`), run it with `python3`, and
|
|
26
|
+
delete the copy when done. It only ever **GET**s the public endpoints documented in
|
|
27
|
+
[references/meteora-apis.md](references/meteora-apis.md).
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
python3 screen.py # trending volatile (default)
|
|
31
|
+
python3 screen.py --preset stable
|
|
32
|
+
python3 screen.py --query BONK # pair search; preset defaults to loose
|
|
33
|
+
python3 screen.py --query BONK --preset volatile
|
|
34
|
+
python3 screen.py --json --limit 8
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
If you do not materialize the script, curl the same endpoints in
|
|
38
|
+
[references/meteora-apis.md](references/meteora-apis.md).
|
|
39
|
+
Always send a `User-Agent` — unauthenticated requests without one get `403`.
|
|
40
|
+
|
|
41
|
+
## When to use
|
|
42
|
+
|
|
43
|
+
- User wants a ranked Meteora DLMM candidate list (trending or a token/pair).
|
|
44
|
+
- User asks which bin step / pool to LP for a pair.
|
|
45
|
+
- User wants a fee/TVL screen, not a single-pool deep dive.
|
|
46
|
+
|
|
47
|
+
Not this skill: deploying, claiming, closing, swapping, wallet hygiene, or a full
|
|
48
|
+
token-holder / narrative research dump. Stop after the ranked table and verdicts.
|
|
49
|
+
|
|
50
|
+
## Method
|
|
51
|
+
|
|
52
|
+
1. **Universe** — trending discovery (`category=trending`) unless the user named a token,
|
|
53
|
+
then query that mint/symbol. Pair query defaults to `--preset loose` so bin-step
|
|
54
|
+
tradeoffs stay visible; pass `--preset volatile` only when the user wants that gate.
|
|
55
|
+
2. **Hard filters** — reject before ranking. A high fee/TVL pool that fails a gate is a
|
|
56
|
+
skip, not a "maybe".
|
|
57
|
+
3. **Score** — `fee_active_tvl_ratio * 1000 + organic * 10 + volume / 100 + holders / 100`.
|
|
58
|
+
Fee/TVL dominates; organic and activity break ties.
|
|
59
|
+
4. **Verdict** — `pass` (clears gates, top of list), `watch` (clears gates but thin
|
|
60
|
+
activity, unverified token, or awkward bin step), `skip` (failed a gate).
|
|
61
|
+
5. **Stop** — print the table. Do not fetch a wallet, do not build a tx, do not call Etemaro CLI.
|
|
62
|
+
|
|
63
|
+
## Presets
|
|
64
|
+
|
|
65
|
+
Defaults match a volatile/narrative Solana LP screen (wide bin step, mid TVL). Change
|
|
66
|
+
preset when the user says stable pair or blue-chip.
|
|
67
|
+
|
|
68
|
+
| Preset | bin_step | TVL USD | min fee/active TVL | min organic | min holders | min volume |
|
|
69
|
+
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
70
|
+
| `volatile` (default) | 80–125 | 10k–150k | 0.05 | 60 | 500 | 500 |
|
|
71
|
+
| `stable` | 1–50 | 100k–5m | 0.02 | 70 | 2000 | 5000 |
|
|
72
|
+
| `bluechip` | 1–25 | 500k–10m | 0.01 | 80 | 5000 | 10000 |
|
|
73
|
+
| `loose` | any | ≥1k | 0 | 0 | 0 | 0 |
|
|
74
|
+
|
|
75
|
+
Always reject: dead pools (zero volume and zero fee/TVL). Any preset except `loose` also
|
|
76
|
+
rejects critical token warnings, high single-ownership, non-DLMM pool type.
|
|
77
|
+
|
|
78
|
+
Timeframe: `30m` default. `5m` is noisier (spikes look like yield). `24h` is smoother but
|
|
79
|
+
lags a dead pool. State the timeframe in the report — windowed fee/TVL is not 24h APR.
|
|
80
|
+
|
|
81
|
+
## Report shape
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
# Meteora DLMM screening
|
|
85
|
+
Universe: trending | query=<token> Timeframe: 30m Preset: volatile
|
|
86
|
+
Protocol: tvl=$… vol_24h=$… pools=…
|
|
87
|
+
|
|
88
|
+
## Ranked
|
|
89
|
+
| # | name | bin | fee/TVL | tvl | vol | organic | holders | verdict | why |
|
|
90
|
+
...
|
|
91
|
+
|
|
92
|
+
## Rejects (sample)
|
|
93
|
+
- NAME — reason
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Keep `why` to one clause (e.g. "fee/TVL 0.24, organic 67, bin 80"). Cite pool address.
|
|
97
|
+
If the API returns zero rows, say so and loosen one gate at a time (usually `maxTvl` or
|
|
98
|
+
`minFeeActiveTvlRatio`) — do not invent pools.
|
|
99
|
+
|
|
100
|
+
## Read-only safety
|
|
101
|
+
|
|
102
|
+
This skill only **GET**s public Meteora JSON. No `.env`, no keystore, no signing, no
|
|
103
|
+
`deploy` / `swap` / `claim` / `close`. If the user wants live execution, point them at
|
|
104
|
+
[Etemaro](https://etemaro.com) (repo: https://github.com/romankurnovskii/etemaro) and stop.
|
|
105
|
+
|
|
106
|
+
## Limitations
|
|
107
|
+
|
|
108
|
+
- Depends on Meteora's public datapi endpoints, which are undocumented, rate limited,
|
|
109
|
+
and can change or disappear without notice; this skill is not affiliated with Meteora.
|
|
110
|
+
- All metrics are **windowed** (default 30m). Fee/TVL is not 24h APR, and past yield
|
|
111
|
+
does not predict future yield; impermanent loss, bin-step drift, and fill risk are
|
|
112
|
+
not modeled here.
|
|
113
|
+
- Screening output is informational only and is **not financial advice**; a `pass`
|
|
114
|
+
verdict is not a recommendation to deposit funds.
|
|
115
|
+
- Token scores rely on third-party organic-score and holder fields that may be stale,
|
|
116
|
+
manipulated, or wrong for new or low-liquidity tokens.
|
|
117
|
+
- The embedded screener reads public data only; it never signs, swaps, or deploys, and
|
|
118
|
+
it intentionally has no execution path.
|
|
119
|
+
- Respect the public API: send a User-Agent and poll conservatively.
|
|
120
|
+
|
|
121
|
+
## Go deeper — Etemaro
|
|
122
|
+
|
|
123
|
+
Etemaro runs this screen on a cron, adds holder/bot/launchpad gates, pool memory, and
|
|
124
|
+
can deploy. The skill is the analysis half; the product is the loop.
|
|
125
|
+
|
|
126
|
+
## Prompt examples
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
screen trending Meteora DLMM pools for LP
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
which Meteora pool should I LP for BONK?
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
rank SOL-USDC DLMM pools by fee/TVL and bin step
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
dex-pool-screening on Meteora, volatile preset, top 8
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Tips
|
|
145
|
+
|
|
146
|
+
- Always send a User-Agent to Meteora datapi or you get HTTP 403.
|
|
147
|
+
- `fee_active_tvl_ratio` is **windowed** (default 30m), not 24h APR. Label the window.
|
|
148
|
+
- Read-only. Deploying is Etemaro, not this skill.
|
|
149
|
+
- Pair query (`--query BONK`) defaults to `loose` so you can compare bin steps. Dead pools
|
|
150
|
+
(zero volume and fee/TVL) are still dropped.
|
|
151
|
+
- `loose` is for pair comparison / empty-result debugging, not a live LP pick.
|
|
152
|
+
|
|
153
|
+
## Screener script
|
|
154
|
+
|
|
155
|
+
Standard library only. Read-only GET. The full source lives in
|
|
156
|
+
[references/meteora-screener.md](references/meteora-screener.md); copy it into a scratch
|
|
157
|
+
directory (for example via `mktemp -d`), run it with Python 3.10+, and delete the
|
|
158
|
+
copy when done. It only ever GETs the public endpoints documented in
|
|
159
|
+
[references/meteora-apis.md](references/meteora-apis.md).
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
#!/usr/bin/env python3
|
|
164
|
+
"""Rank Meteora DLMM pools from public datapi. Stdlib only. Read-only GET."""
|
|
165
|
+
... full source in references/meteora-screener.md ...
|
|
166
|
+
```
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Meteora public APIs used by this skill
|
|
2
|
+
|
|
3
|
+
No API key. Send `User-Agent` and `Accept: application/json` or Cloudflare returns 403.
|
|
4
|
+
|
|
5
|
+
Base hosts:
|
|
6
|
+
|
|
7
|
+
- Discovery (trending + filters): `https://pool-discovery-api.datapi.meteora.ag`
|
|
8
|
+
- DLMM datapi (pair query, protocol stats, pool detail): `https://dlmm.datapi.meteora.ag`
|
|
9
|
+
|
|
10
|
+
## Endpoints
|
|
11
|
+
|
|
12
|
+
### Trending / filtered universe
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
GET /pools?page_size=50&timeframe=30m&category=trending&filter_by=<filters>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`filter_by` is `&&`-joined predicates. Example (volatile preset):
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
pool_type=dlmm
|
|
22
|
+
&&base_token_has_critical_warnings=false
|
|
23
|
+
&"e_token_has_critical_warnings=false
|
|
24
|
+
&&base_token_has_high_single_ownership=false
|
|
25
|
+
&&tvl>=10000&&tvl<=150000
|
|
26
|
+
&&dlmm_bin_step>=80&&dlmm_bin_step<=125
|
|
27
|
+
&&fee_active_tvl_ratio>=0.05
|
|
28
|
+
&&base_token_organic_score>=60
|
|
29
|
+
&&base_token_holders>=500
|
|
30
|
+
&&volume>=500
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Response: `{ total, page_size, data[], has_more }`.
|
|
34
|
+
|
|
35
|
+
Useful `data[]` fields: `pool_address`, `name`, `pool_type`, `tvl`, `active_tvl`,
|
|
36
|
+
`fee_active_tvl_ratio`, `volume`, `volatility`, `base_token_holders`,
|
|
37
|
+
`dlmm_params.bin_step`, `token_x.{symbol,address,organic_score,market_cap,warnings}`.
|
|
38
|
+
|
|
39
|
+
### Pair / token query
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
GET /pools?query=<symbol_or_mint>&sort_by=tvl:desc
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
on `dlmm.datapi.meteora.ag`. Response `{ total, data[] }`.
|
|
46
|
+
|
|
47
|
+
Fee/TVL and volume are **buckets**: `{ "30m", "1h", "2h", "4h", "12h", "24h" }`.
|
|
48
|
+
Use the same key as `--timeframe`. Bin step is `pool_config.bin_step`. Address is `address`.
|
|
49
|
+
|
|
50
|
+
### Protocol snapshot
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
GET https://dlmm.datapi.meteora.ag/stats/protocol_metrics
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`total_tvl`, `volume_24h`, `fee_24h`, `total_pools`. One-line context only.
|
|
57
|
+
|
|
58
|
+
### Single pool
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
GET https://dlmm.datapi.meteora.ag/pools/{poolAddress}
|
|
62
|
+
GET https://pool-discovery-api.datapi.meteora.ag/pools?page_size=1&filter_by=pool_address={poolAddress}&timeframe=30m
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Discovery lag: a brand-new pool may exist on DLMM datapi before discovery indexes it.
|
|
66
|
+
|
|
67
|
+
## Score (keep in sync with scripts/screen.py)
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
score = fee_tvl * 1000 + organic * 10 + volume / 100 + holders / 100
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`fee_tvl` is the windowed ratio (`fee_active_tvl_ratio` on discovery, bucketed
|
|
74
|
+
`fee_tvl_ratio[timeframe]` on pair query).
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# Meteora screener script
|
|
2
|
+
|
|
3
|
+
Embedded copy of the read-only screener. Save the fenced Python block below as
|
|
4
|
+
`screen.py` in a scratch directory and run it with Python 3.10+.
|
|
5
|
+
|
|
6
|
+
```python
|
|
7
|
+
#!/usr/bin/env python3
|
|
8
|
+
"""Rank Meteora DLMM pools from public datapi. Stdlib only. Read-only GET."""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
import urllib.error
|
|
16
|
+
import urllib.parse
|
|
17
|
+
import urllib.request
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
DISCOVERY = "https://pool-discovery-api.datapi.meteora.ag"
|
|
21
|
+
DLMM = "https://dlmm.datapi.meteora.ag"
|
|
22
|
+
UA = "Mozilla/5.0 (compatible; etemaro-skill/1.0; +https://etemaro.com)"
|
|
23
|
+
TIMEOUT = 25
|
|
24
|
+
|
|
25
|
+
PRESETS: dict[str, dict[str, Any]] = {
|
|
26
|
+
"volatile": {
|
|
27
|
+
"min_bin": 80,
|
|
28
|
+
"max_bin": 125,
|
|
29
|
+
"min_tvl": 10_000,
|
|
30
|
+
"max_tvl": 150_000,
|
|
31
|
+
"min_fee_tvl": 0.05,
|
|
32
|
+
"min_organic": 60,
|
|
33
|
+
"min_holders": 500,
|
|
34
|
+
"min_volume": 500,
|
|
35
|
+
"hard_warnings": True,
|
|
36
|
+
},
|
|
37
|
+
"stable": {
|
|
38
|
+
"min_bin": 1,
|
|
39
|
+
"max_bin": 50,
|
|
40
|
+
"min_tvl": 100_000,
|
|
41
|
+
"max_tvl": 5_000_000,
|
|
42
|
+
"min_fee_tvl": 0.02,
|
|
43
|
+
"min_organic": 70,
|
|
44
|
+
"min_holders": 2_000,
|
|
45
|
+
"min_volume": 5_000,
|
|
46
|
+
"hard_warnings": True,
|
|
47
|
+
},
|
|
48
|
+
"bluechip": {
|
|
49
|
+
"min_bin": 1,
|
|
50
|
+
"max_bin": 25,
|
|
51
|
+
"min_tvl": 500_000,
|
|
52
|
+
"max_tvl": 10_000_000,
|
|
53
|
+
"min_fee_tvl": 0.01,
|
|
54
|
+
"min_organic": 80,
|
|
55
|
+
"min_holders": 5_000,
|
|
56
|
+
"min_volume": 10_000,
|
|
57
|
+
"hard_warnings": True,
|
|
58
|
+
},
|
|
59
|
+
"loose": {
|
|
60
|
+
"min_bin": None,
|
|
61
|
+
"max_bin": None,
|
|
62
|
+
"min_tvl": 1_000,
|
|
63
|
+
"max_tvl": None,
|
|
64
|
+
"min_fee_tvl": 0.0,
|
|
65
|
+
"min_organic": 0,
|
|
66
|
+
"min_holders": 0,
|
|
67
|
+
"min_volume": 0,
|
|
68
|
+
"hard_warnings": False,
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_json(url: str) -> Any:
|
|
74
|
+
req = urllib.request.Request(url, headers={"User-Agent": UA, "Accept": "application/json"})
|
|
75
|
+
try:
|
|
76
|
+
with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:
|
|
77
|
+
return json.loads(resp.read().decode())
|
|
78
|
+
except urllib.error.HTTPError as exc:
|
|
79
|
+
body = exc.read().decode(errors="replace")[:200]
|
|
80
|
+
raise SystemExit(f"HTTP {exc.code} {url}\n{body}") from exc
|
|
81
|
+
except urllib.error.URLError as exc:
|
|
82
|
+
raise SystemExit(f"network error {url}: {exc.reason}") from exc
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def num(value: Any) -> float | None:
|
|
86
|
+
try:
|
|
87
|
+
n = float(value)
|
|
88
|
+
except (TypeError, ValueError):
|
|
89
|
+
return None
|
|
90
|
+
return n if n == n else None # NaN check
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def bucket(obj: Any, timeframe: str) -> float | None:
|
|
94
|
+
if isinstance(obj, dict):
|
|
95
|
+
return num(obj.get(timeframe))
|
|
96
|
+
return num(obj)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def score(fee_tvl: float, organic: float, volume: float, holders: float) -> float:
|
|
100
|
+
return fee_tvl * 1000 + organic * 10 + volume / 100 + holders / 100
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def discovery_filters(p: dict[str, Any]) -> str:
|
|
104
|
+
parts = ["pool_type=dlmm"]
|
|
105
|
+
if p["hard_warnings"]:
|
|
106
|
+
parts += [
|
|
107
|
+
"base_token_has_critical_warnings=false",
|
|
108
|
+
"quote_token_has_critical_warnings=false",
|
|
109
|
+
"base_token_has_high_single_ownership=false",
|
|
110
|
+
]
|
|
111
|
+
if p["min_tvl"] is not None:
|
|
112
|
+
parts.append(f"tvl>={int(p['min_tvl'])}")
|
|
113
|
+
if p["max_tvl"] is not None:
|
|
114
|
+
parts.append(f"tvl<={int(p['max_tvl'])}")
|
|
115
|
+
if p["min_bin"] is not None:
|
|
116
|
+
parts.append(f"dlmm_bin_step>={int(p['min_bin'])}")
|
|
117
|
+
if p["max_bin"] is not None:
|
|
118
|
+
parts.append(f"dlmm_bin_step<={int(p['max_bin'])}")
|
|
119
|
+
if p["min_fee_tvl"]:
|
|
120
|
+
parts.append(f"fee_active_tvl_ratio>={p['min_fee_tvl']}")
|
|
121
|
+
if p["min_organic"]:
|
|
122
|
+
parts.append(f"base_token_organic_score>={int(p['min_organic'])}")
|
|
123
|
+
if p["min_holders"]:
|
|
124
|
+
parts.append(f"base_token_holders>={int(p['min_holders'])}")
|
|
125
|
+
if p["min_volume"]:
|
|
126
|
+
parts.append(f"volume>={int(p['min_volume'])}")
|
|
127
|
+
return "&&".join(parts)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def reject_reason(row: dict[str, Any], p: dict[str, Any]) -> str | None:
|
|
131
|
+
fee = row.get("fee_tvl")
|
|
132
|
+
vol = row.get("volume")
|
|
133
|
+
if (fee is None or fee <= 0) and (vol is None or vol <= 0):
|
|
134
|
+
return "dead pool (zero volume and fee/TVL)"
|
|
135
|
+
if p["hard_warnings"] and row.get("critical"):
|
|
136
|
+
return "critical token warning"
|
|
137
|
+
bin_step = row.get("bin_step")
|
|
138
|
+
tvl = row.get("tvl")
|
|
139
|
+
organic = row.get("organic")
|
|
140
|
+
holders = row.get("holders")
|
|
141
|
+
if p["min_bin"] is not None and (bin_step is None or bin_step < p["min_bin"]):
|
|
142
|
+
return f"bin_step {bin_step} < {p['min_bin']}"
|
|
143
|
+
if p["max_bin"] is not None and bin_step is not None and bin_step > p["max_bin"]:
|
|
144
|
+
return f"bin_step {bin_step} > {p['max_bin']}"
|
|
145
|
+
if tvl is None or tvl < p["min_tvl"]:
|
|
146
|
+
return f"tvl {tvl} < {p['min_tvl']}"
|
|
147
|
+
if p["max_tvl"] is not None and tvl is not None and tvl > p["max_tvl"]:
|
|
148
|
+
return f"tvl {tvl} > {p['max_tvl']}"
|
|
149
|
+
if fee is None or fee < p["min_fee_tvl"]:
|
|
150
|
+
return f"fee/TVL {fee} < {p['min_fee_tvl']}"
|
|
151
|
+
if vol is None or vol < p["min_volume"]:
|
|
152
|
+
return f"volume {vol} < {p['min_volume']}"
|
|
153
|
+
if organic is not None and organic < p["min_organic"]:
|
|
154
|
+
return f"organic {organic} < {p['min_organic']}"
|
|
155
|
+
if holders is not None and holders < p["min_holders"]:
|
|
156
|
+
return f"holders {holders} < {p['min_holders']}"
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def verdict(row: dict[str, Any], p: dict[str, Any]) -> str:
|
|
161
|
+
if reject_reason(row, p):
|
|
162
|
+
return "skip"
|
|
163
|
+
warnings = row.get("warnings") or []
|
|
164
|
+
unverified = any("NOT_VERIFIED" in str(w) for w in warnings)
|
|
165
|
+
thin = (row.get("volume") or 0) < p["min_volume"] * 2 if p["min_volume"] else False
|
|
166
|
+
no_yield = (row.get("fee_tvl") or 0) <= 0
|
|
167
|
+
if unverified or thin or no_yield:
|
|
168
|
+
return "watch"
|
|
169
|
+
return "pass"
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def from_discovery(raw: dict[str, Any], timeframe: str) -> dict[str, Any]:
|
|
173
|
+
token_x = raw.get("token_x") or {}
|
|
174
|
+
warnings = token_x.get("warnings") or []
|
|
175
|
+
critical = bool(raw.get("base_token_has_critical_warnings")) or any(
|
|
176
|
+
(w.get("severity") if isinstance(w, dict) else None) == "critical" for w in warnings if isinstance(w, dict)
|
|
177
|
+
)
|
|
178
|
+
bin_step = num((raw.get("dlmm_params") or {}).get("bin_step"))
|
|
179
|
+
return {
|
|
180
|
+
"name": raw.get("name"),
|
|
181
|
+
"pool": raw.get("pool_address"),
|
|
182
|
+
"bin_step": bin_step,
|
|
183
|
+
"fee_tvl": num(raw.get("fee_active_tvl_ratio")),
|
|
184
|
+
"tvl": num(raw.get("tvl")),
|
|
185
|
+
"active_tvl": num(raw.get("active_tvl")),
|
|
186
|
+
"volume": num(raw.get("volume")),
|
|
187
|
+
"organic": num(token_x.get("organic_score")),
|
|
188
|
+
"holders": num(raw.get("base_token_holders")),
|
|
189
|
+
"mcap": num(token_x.get("market_cap")),
|
|
190
|
+
"volatility": num(raw.get("volatility")),
|
|
191
|
+
"mint": token_x.get("address"),
|
|
192
|
+
"warnings": warnings,
|
|
193
|
+
"critical": critical,
|
|
194
|
+
"source": "discovery",
|
|
195
|
+
"timeframe": timeframe,
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def from_dlmm(raw: dict[str, Any], timeframe: str) -> dict[str, Any]:
|
|
200
|
+
token_x = raw.get("token_x") or {}
|
|
201
|
+
cfg = raw.get("pool_config") or {}
|
|
202
|
+
return {
|
|
203
|
+
"name": raw.get("name"),
|
|
204
|
+
"pool": raw.get("address"),
|
|
205
|
+
"bin_step": num(cfg.get("bin_step")),
|
|
206
|
+
"fee_tvl": bucket(raw.get("fee_tvl_ratio"), timeframe),
|
|
207
|
+
"tvl": num(raw.get("tvl")),
|
|
208
|
+
"active_tvl": num(raw.get("tvl")),
|
|
209
|
+
"volume": bucket(raw.get("volume"), timeframe),
|
|
210
|
+
"organic": None,
|
|
211
|
+
"holders": num(token_x.get("holders")),
|
|
212
|
+
"mcap": num(token_x.get("market_cap")),
|
|
213
|
+
"volatility": None,
|
|
214
|
+
"mint": token_x.get("address"),
|
|
215
|
+
"warnings": [],
|
|
216
|
+
"critical": bool(raw.get("is_blacklisted")),
|
|
217
|
+
"source": "dlmm",
|
|
218
|
+
"timeframe": timeframe,
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def fmt(n: float | None, digits: int = 2) -> str:
|
|
223
|
+
if n is None:
|
|
224
|
+
return "—"
|
|
225
|
+
if abs(n) >= 1_000_000:
|
|
226
|
+
return f"{n / 1_000_000:.{digits}f}M"
|
|
227
|
+
if abs(n) >= 1_000:
|
|
228
|
+
return f"{n / 1_000:.{digits}f}k"
|
|
229
|
+
return f"{n:.{digits}f}"
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def fmt_bin(n: float | None) -> str:
|
|
233
|
+
if n is None:
|
|
234
|
+
return "—"
|
|
235
|
+
return str(int(n)) if float(n).is_integer() else fmt(n, 1)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def print_table(rows: list[dict[str, Any]], rejects: list[tuple[str, str]], meta: dict[str, Any]) -> None:
|
|
239
|
+
print(f"# Meteora DLMM screening")
|
|
240
|
+
print(
|
|
241
|
+
f"Universe: {meta['universe']} Timeframe: {meta['timeframe']} "
|
|
242
|
+
f"Preset: {meta['preset']} Source: {meta['source']}"
|
|
243
|
+
)
|
|
244
|
+
proto = meta.get("protocol") or {}
|
|
245
|
+
if proto:
|
|
246
|
+
print(
|
|
247
|
+
f"Protocol: tvl=${fmt(proto.get('total_tvl'))} "
|
|
248
|
+
f"vol_24h=${fmt(proto.get('volume_24h'))} pools={proto.get('total_pools')}"
|
|
249
|
+
)
|
|
250
|
+
print(f"Fetched: {meta['fetched']} Ranked: {len(rows)} Rejected: {len(rejects)}")
|
|
251
|
+
print()
|
|
252
|
+
print("## Ranked")
|
|
253
|
+
print("| # | name | bin | fee/TVL | tvl | vol | organic | holders | verdict | why |")
|
|
254
|
+
print("|---|---|---|---|---|---|---|---|---|---|")
|
|
255
|
+
for i, row in enumerate(rows, 1):
|
|
256
|
+
why = (
|
|
257
|
+
f"fee/TVL {fmt(row.get('fee_tvl'), 3)}, "
|
|
258
|
+
f"bin {fmt_bin(row.get('bin_step'))}, "
|
|
259
|
+
f"{row.get('pool')}"
|
|
260
|
+
)
|
|
261
|
+
print(
|
|
262
|
+
f"| {i} | {row.get('name') or '?'} | {fmt_bin(row.get('bin_step'))} | "
|
|
263
|
+
f"{fmt(row.get('fee_tvl'), 3)} | {fmt(row.get('tvl'))} | {fmt(row.get('volume'))} | "
|
|
264
|
+
f"{fmt(row.get('organic'), 1)} | {fmt(row.get('holders'), 0)} | "
|
|
265
|
+
f"{row.get('verdict')} | {why} |"
|
|
266
|
+
)
|
|
267
|
+
if not rows:
|
|
268
|
+
print("_no pools passed gates — loosen max TVL or min fee/TVL, or try --preset loose_")
|
|
269
|
+
if rejects:
|
|
270
|
+
print()
|
|
271
|
+
print("## Rejects (sample)")
|
|
272
|
+
for name, reason in rejects[:12]:
|
|
273
|
+
print(f"- {name} — {reason}")
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def main() -> None:
|
|
277
|
+
parser = argparse.ArgumentParser(description="Screen Meteora DLMM pools (read-only).")
|
|
278
|
+
parser.add_argument("--preset", choices=sorted(PRESETS), default="volatile")
|
|
279
|
+
parser.add_argument("--query", help="Token symbol or mint (pair search via DLMM datapi)")
|
|
280
|
+
parser.add_argument("--timeframe", default="30m", choices=["5m", "30m", "1h", "2h", "4h", "12h", "24h"])
|
|
281
|
+
parser.add_argument("--limit", type=int, default=10)
|
|
282
|
+
parser.add_argument("--page-size", type=int, default=50)
|
|
283
|
+
parser.add_argument("--json", action="store_true")
|
|
284
|
+
args = parser.parse_args()
|
|
285
|
+
# Pair search is "which pool for this token", not a trending meme screen.
|
|
286
|
+
if args.query and "--preset" not in sys.argv:
|
|
287
|
+
args.preset = "loose"
|
|
288
|
+
preset = PRESETS[args.preset]
|
|
289
|
+
|
|
290
|
+
protocol = get_json(f"{DLMM}/stats/protocol_metrics")
|
|
291
|
+
|
|
292
|
+
if args.query:
|
|
293
|
+
qs = urllib.parse.urlencode({"query": args.query, "sort_by": "tvl:desc"})
|
|
294
|
+
payload = get_json(f"{DLMM}/pools?{qs}")
|
|
295
|
+
raw_list = payload.get("data") or []
|
|
296
|
+
rows = [from_dlmm(p, args.timeframe) for p in raw_list if isinstance(p, dict)]
|
|
297
|
+
source = "dlmm"
|
|
298
|
+
universe = f"query={args.query}"
|
|
299
|
+
fetched = payload.get("total", len(rows))
|
|
300
|
+
else:
|
|
301
|
+
qs = urllib.parse.urlencode(
|
|
302
|
+
{
|
|
303
|
+
"page_size": args.page_size,
|
|
304
|
+
"timeframe": args.timeframe,
|
|
305
|
+
"category": "trending",
|
|
306
|
+
"filter_by": discovery_filters(preset),
|
|
307
|
+
}
|
|
308
|
+
)
|
|
309
|
+
payload = get_json(f"{DISCOVERY}/pools?{qs}")
|
|
310
|
+
raw_list = payload.get("data") or []
|
|
311
|
+
rows = [from_discovery(p, args.timeframe) for p in raw_list if isinstance(p, dict)]
|
|
312
|
+
source = "discovery"
|
|
313
|
+
universe = "trending"
|
|
314
|
+
fetched = payload.get("total", len(rows))
|
|
315
|
+
|
|
316
|
+
rejects: list[tuple[str, str]] = []
|
|
317
|
+
kept: list[dict[str, Any]] = []
|
|
318
|
+
for row in rows:
|
|
319
|
+
reason = reject_reason(row, preset)
|
|
320
|
+
if reason:
|
|
321
|
+
rejects.append((str(row.get("name") or row.get("pool") or "?"), reason))
|
|
322
|
+
continue
|
|
323
|
+
row["score"] = score(
|
|
324
|
+
row.get("fee_tvl") or 0,
|
|
325
|
+
row.get("organic") or 0,
|
|
326
|
+
row.get("volume") or 0,
|
|
327
|
+
row.get("holders") or 0,
|
|
328
|
+
)
|
|
329
|
+
row["verdict"] = verdict(row, preset)
|
|
330
|
+
kept.append(row)
|
|
331
|
+
|
|
332
|
+
kept.sort(key=lambda r: r.get("score") or 0, reverse=True)
|
|
333
|
+
kept = kept[: max(1, args.limit)]
|
|
334
|
+
|
|
335
|
+
meta = {
|
|
336
|
+
"universe": universe,
|
|
337
|
+
"timeframe": args.timeframe,
|
|
338
|
+
"preset": args.preset,
|
|
339
|
+
"source": source,
|
|
340
|
+
"fetched": fetched,
|
|
341
|
+
"protocol": protocol if isinstance(protocol, dict) else {},
|
|
342
|
+
}
|
|
343
|
+
if args.json:
|
|
344
|
+
json.dump({"meta": meta, "ranked": kept, "rejects": rejects[:20]}, sys.stdout, indent=2)
|
|
345
|
+
sys.stdout.write("\n")
|
|
346
|
+
return
|
|
347
|
+
print_table(kept, rejects, meta)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
if __name__ == "__main__":
|
|
351
|
+
main()
|
|
352
|
+
```
|
package/package.json
CHANGED
package/skills_index.json
CHANGED
|
@@ -25244,6 +25244,93 @@
|
|
|
25244
25244
|
"reasons": []
|
|
25245
25245
|
}
|
|
25246
25246
|
},
|
|
25247
|
+
{
|
|
25248
|
+
"id": "idea-evaluator",
|
|
25249
|
+
"path": "skills/idea-evaluator",
|
|
25250
|
+
"category": "agent-orchestration",
|
|
25251
|
+
"name": "idea-evaluator",
|
|
25252
|
+
"description": "Evaluates an idea by hosting a multi-turn debate between a Pro and Con agent, delivering a final verdict on whether it's worth pursuing.",
|
|
25253
|
+
"risk": "safe",
|
|
25254
|
+
"source": "self",
|
|
25255
|
+
"date_added": "2026-09-18",
|
|
25256
|
+
"plugin": {
|
|
25257
|
+
"targets": {
|
|
25258
|
+
"codex": "supported",
|
|
25259
|
+
"claude": "supported"
|
|
25260
|
+
},
|
|
25261
|
+
"setup": {
|
|
25262
|
+
"type": "none",
|
|
25263
|
+
"summary": "",
|
|
25264
|
+
"docs": null
|
|
25265
|
+
},
|
|
25266
|
+
"reasons": []
|
|
25267
|
+
},
|
|
25268
|
+
"source_type": "self",
|
|
25269
|
+
"tags": [
|
|
25270
|
+
"ideation",
|
|
25271
|
+
"validation",
|
|
25272
|
+
"debate",
|
|
25273
|
+
"multi-agent"
|
|
25274
|
+
]
|
|
25275
|
+
},
|
|
25276
|
+
{
|
|
25277
|
+
"id": "idea-evaluator-con",
|
|
25278
|
+
"path": "skills/idea-evaluator/idea-evaluator-con",
|
|
25279
|
+
"category": "agent-persona",
|
|
25280
|
+
"name": "idea-evaluator-con",
|
|
25281
|
+
"description": "The Con Agent persona for idea evaluation. Critiques an idea by identifying potential flaws, risks, and market challenges.",
|
|
25282
|
+
"risk": "safe",
|
|
25283
|
+
"source": "self",
|
|
25284
|
+
"date_added": "2026-09-18",
|
|
25285
|
+
"plugin": {
|
|
25286
|
+
"targets": {
|
|
25287
|
+
"codex": "supported",
|
|
25288
|
+
"claude": "supported"
|
|
25289
|
+
},
|
|
25290
|
+
"setup": {
|
|
25291
|
+
"type": "none",
|
|
25292
|
+
"summary": "",
|
|
25293
|
+
"docs": null
|
|
25294
|
+
},
|
|
25295
|
+
"reasons": []
|
|
25296
|
+
},
|
|
25297
|
+
"source_type": "self",
|
|
25298
|
+
"tags": [
|
|
25299
|
+
"ideation",
|
|
25300
|
+
"validation",
|
|
25301
|
+
"persona",
|
|
25302
|
+
"critic"
|
|
25303
|
+
]
|
|
25304
|
+
},
|
|
25305
|
+
{
|
|
25306
|
+
"id": "idea-evaluator-pro",
|
|
25307
|
+
"path": "skills/idea-evaluator/idea-evaluator-pro",
|
|
25308
|
+
"category": "agent-persona",
|
|
25309
|
+
"name": "idea-evaluator-pro",
|
|
25310
|
+
"description": "The Pro Agent persona for idea evaluation. Logically supports an idea, arguing for its market fit, feasibility, and potential.",
|
|
25311
|
+
"risk": "safe",
|
|
25312
|
+
"source": "self",
|
|
25313
|
+
"date_added": "2026-09-18",
|
|
25314
|
+
"plugin": {
|
|
25315
|
+
"targets": {
|
|
25316
|
+
"codex": "supported",
|
|
25317
|
+
"claude": "supported"
|
|
25318
|
+
},
|
|
25319
|
+
"setup": {
|
|
25320
|
+
"type": "none",
|
|
25321
|
+
"summary": "",
|
|
25322
|
+
"docs": null
|
|
25323
|
+
},
|
|
25324
|
+
"reasons": []
|
|
25325
|
+
},
|
|
25326
|
+
"source_type": "self",
|
|
25327
|
+
"tags": [
|
|
25328
|
+
"ideation",
|
|
25329
|
+
"validation",
|
|
25330
|
+
"persona",
|
|
25331
|
+
"supporter"
|
|
25332
|
+
]
|
|
25333
|
+
},
|
|
25247
25334
|
{
|
|
25248
25335
|
"id": "idea-os",
|
|
25249
25336
|
"path": "skills/idea-os",
|
|
@@ -50185,6 +50272,37 @@
|
|
|
50185
50272
|
"reasons": []
|
|
50186
50273
|
}
|
|
50187
50274
|
},
|
|
50275
|
+
{
|
|
50276
|
+
"id": "weather-model-data-fetching",
|
|
50277
|
+
"path": "skills/weather-model-data-fetching",
|
|
50278
|
+
"category": "data",
|
|
50279
|
+
"name": "weather-model-data-fetching",
|
|
50280
|
+
"description": "Retrieve numerical weather prediction data from public AWS S3 and HTTP archives using GRIB2 inventories, byte ranges, Herbie, provider fallbacks, and verified caching.",
|
|
50281
|
+
"risk": "safe",
|
|
50282
|
+
"source": "self",
|
|
50283
|
+
"date_added": "2026-09-18",
|
|
50284
|
+
"plugin": {
|
|
50285
|
+
"targets": {
|
|
50286
|
+
"codex": "supported",
|
|
50287
|
+
"claude": "supported"
|
|
50288
|
+
},
|
|
50289
|
+
"setup": {
|
|
50290
|
+
"type": "none",
|
|
50291
|
+
"summary": "",
|
|
50292
|
+
"docs": null
|
|
50293
|
+
},
|
|
50294
|
+
"reasons": []
|
|
50295
|
+
},
|
|
50296
|
+
"source_type": "self",
|
|
50297
|
+
"tags": [
|
|
50298
|
+
"weather",
|
|
50299
|
+
"grib2",
|
|
50300
|
+
"aws-s3",
|
|
50301
|
+
"herbie",
|
|
50302
|
+
"noaa",
|
|
50303
|
+
"nwp"
|
|
50304
|
+
]
|
|
50305
|
+
},
|
|
50188
50306
|
{
|
|
50189
50307
|
"id": "weaviate",
|
|
50190
50308
|
"path": "skills/weaviate",
|