opencode-skills-collection 4.0.47 → 4.0.49
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 +11 -2
- package/bundled-skills/agent-qa-authoring/SKILL.md +3 -1
- package/bundled-skills/agy-delegate/SKILL.md +1 -1
- package/bundled-skills/aider-delegate/SKILL.md +1 -1
- package/bundled-skills/api-rate-limit-handler/SKILL.md +1 -1
- package/bundled-skills/atlas-cloud-media/SKILL.md +52 -18
- package/bundled-skills/babysit-pr/SKILL.md +1 -1
- package/bundled-skills/boost-asio-pro/references/pre-cpp20.md +8 -1
- package/bundled-skills/check-identity-pack/SKILL.md +88 -0
- package/bundled-skills/claude-delegate/SKILL.md +1 -1
- package/bundled-skills/cline-delegate/SKILL.md +1 -1
- package/bundled-skills/codex-delegate/SKILL.md +1 -1
- package/bundled-skills/commandcode-delegate/SKILL.md +1 -1
- package/bundled-skills/copilot-delegate/SKILL.md +1 -1
- package/bundled-skills/cursor-delegate/SKILL.md +1 -1
- package/bundled-skills/detect-ai-text/SKILL.md +91 -0
- package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
- package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
- package/bundled-skills/docs/maintainers/repo-growth-seo.md +1 -1
- package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
- package/bundled-skills/docs/users/aas-core.md +1 -1
- package/bundled-skills/docs/users/bundles.md +1 -1
- package/bundled-skills/docs/users/claude-code-skills.md +1 -1
- package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
- package/bundled-skills/docs/users/kiro-integration.md +1 -1
- package/bundled-skills/docs/users/usage.md +3 -3
- package/bundled-skills/docs/users/visual-guide.md +4 -4
- package/bundled-skills/extract-document-data/SKILL.md +99 -0
- package/bundled-skills/falsify/SKILL.md +18 -1
- package/bundled-skills/find-matching-tenders/SKILL.md +100 -0
- package/bundled-skills/graceful-shutdown/SKILL.md +76 -70
- package/bundled-skills/grok-delegate/SKILL.md +1 -1
- package/bundled-skills/inngest/SKILL.md +2 -0
- package/bundled-skills/kimi-delegate/SKILL.md +1 -1
- package/bundled-skills/liuguang-banlan-ui/SKILL.md +6 -4
- package/bundled-skills/liuguang-banlan-ui/scripts/manifest_parser.py +178 -0
- package/bundled-skills/liuguang-banlan-ui/scripts/measure_preview.py +14 -17
- package/bundled-skills/liuguang-banlan-ui/scripts/validate_manifest.py +12 -16
- package/bundled-skills/lovable-cleanup/SKILL.md +32 -6
- package/bundled-skills/lovable-cleanup/references/favicon-vercel-cleanup.md +106 -0
- package/bundled-skills/lovable-cleanup/scripts/write-transparent-favicon.js +88 -0
- package/bundled-skills/multi-source-search/scripts/validate_report.py +5 -0
- package/bundled-skills/omp-delegate/SKILL.md +1 -1
- package/bundled-skills/opencode-delegate/SKILL.md +1 -1
- package/bundled-skills/pentest-tools/references/pentest-ai-agents-matrix.md +8 -5
- package/bundled-skills/pi-delegate/SKILL.md +1 -1
- package/bundled-skills/prompt-caching/SKILL.md +3 -1
- package/bundled-skills/qoder-delegate/SKILL.md +1 -1
- package/bundled-skills/screen-adverse-media/SKILL.md +100 -0
- package/bundled-skills/slideops/SKILL.md +201 -0
- package/bundled-skills/trigger-dev/SKILL.md +2 -0
- package/bundled-skills/unsloth-finetuning/SKILL.md +45 -2
- package/bundled-skills/upstash-qstash/SKILL.md +1 -1
- package/bundled-skills/upstash-ratelimit/SKILL.md +186 -0
- package/bundled-skills/upstash-redis/SKILL.md +156 -0
- package/bundled-skills/verify-citations/SKILL.md +88 -0
- package/bundled-skills/verify-document/SKILL.md +94 -0
- package/bundled-skills/vibe-delegate/SKILL.md +1 -1
- package/bundled-skills/warp-delegate/SKILL.md +1 -1
- package/bundled-skills/zcode-delegate/SKILL.md +1 -1
- package/package.json +1 -1
- package/skills_index.json +347 -54
- package/bundled-skills/ui-slop-score/SKILL.md +0 -80
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: screen-adverse-media
|
|
3
|
+
description: Screen a person or organisation for adverse media coverage, PEP status, and sanctions exposure — corroboration-gated, returns "review" never "guilty". Use when the user asks to screen someone before onboarding, partnership, or investment; for AML/CTF flows; or for ongoing counterparty monitoring.
|
|
4
|
+
category: document-verification
|
|
5
|
+
risk: critical
|
|
6
|
+
source: community
|
|
7
|
+
source_repo: Sketchjar/stipple-agent-skills
|
|
8
|
+
source_type: community
|
|
9
|
+
date_added: "2026-08-31"
|
|
10
|
+
author: Sketchjar
|
|
11
|
+
tags: [document-verification, fact-checking, stipple, authenticity]
|
|
12
|
+
tools: [claude, cursor, gemini, codex]
|
|
13
|
+
license: "Apache-2.0"
|
|
14
|
+
license_source: "https://github.com/Sketchjar/stipple-agent-skills/blob/main/LICENSE"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Screen Adverse Media
|
|
18
|
+
|
|
19
|
+
Screen a person or organisation for adverse media coverage, PEP (Politically Exposed Person) status, and sanctions exposure. Every hit is **corroboration-gated**: the screen returns "review", never "guilty", and "nothing found" is never a clean record. Uses the Stipple API (free anonymous tier).
|
|
20
|
+
|
|
21
|
+
## When to use
|
|
22
|
+
|
|
23
|
+
- AML/CTF onboarding (Tranche 2 reforms make this mandatory for more Australian businesses)
|
|
24
|
+
- Vendor/supplier due diligence before signing
|
|
25
|
+
- Investor or LP vetting
|
|
26
|
+
- Ongoing counterparty monitoring (re-run periodically, compare warrant IDs)
|
|
27
|
+
|
|
28
|
+
## Instructions
|
|
29
|
+
|
|
30
|
+
1. **Get the target.** Name of the person or organisation. For document-based screening, a PDF/image of their ID or company extract can also be submitted.
|
|
31
|
+
|
|
32
|
+
2. **Run the screen.**
|
|
33
|
+
|
|
34
|
+
Name-based (via MCP — the REST intake is document-based):
|
|
35
|
+
```bash
|
|
36
|
+
# Via any MCP client pointed at https://www.stipple.sh/mcp:
|
|
37
|
+
# tool: screen_adverse_media
|
|
38
|
+
# args: {"name": "John Citizen", "entity_type": "person"}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Document-based (REST — screens the person/org named in the uploaded document):
|
|
42
|
+
```bash
|
|
43
|
+
curl -X POST https://www.stipple.sh/v1/adverse-media \
|
|
44
|
+
-F "file=@company-extract.pdf" \
|
|
45
|
+
-H "Authorization: Bearer $STIPPLE_API_KEY"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
3. **Interpret the response.**
|
|
49
|
+
|
|
50
|
+
- `rating` — the overall screening result ("review", "clear", etc.)
|
|
51
|
+
- `hits[]` — adverse media articles: date, title, source, URL, summary
|
|
52
|
+
- `pep[]` — PEP signals (empty = none found)
|
|
53
|
+
- `sanctions[]` — sanctions list signals (empty = none found)
|
|
54
|
+
|
|
55
|
+
4. **Report with the right framing.** Non-negotiable framing rules:
|
|
56
|
+
|
|
57
|
+
| Finding | Correct framing |
|
|
58
|
+
|---|---|
|
|
59
|
+
| Hits found | "Review recommended — see articles below" (never "guilty") |
|
|
60
|
+
| Nothing found | "No corroborated adverse media found — this is NOT a clean record; coverage is bounded by the source list" |
|
|
61
|
+
| PEP signal | "PEP status identified — enhanced due diligence may apply" |
|
|
62
|
+
|
|
63
|
+
5. **Contextualize.** Every hit is corroborated across sources before inclusion — but the screen is the start of human review, not the end of it. Date-range and source-coverage limitations are real.
|
|
64
|
+
|
|
65
|
+
## Output format
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
screening rating: review
|
|
69
|
+
|
|
70
|
+
adverse media hits:
|
|
71
|
+
- [2025-11-02] Court action over unpaid supplier debts
|
|
72
|
+
source: The Age https://...
|
|
73
|
+
- [2024-06-18] ASIC disqualification
|
|
74
|
+
source: ASIC media releases https://...
|
|
75
|
+
|
|
76
|
+
PEP signals: 0
|
|
77
|
+
sanctions list signals: 0
|
|
78
|
+
|
|
79
|
+
This screen returns "review", never "guilty" — every hit is corroboration-gated.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Limitations and Safety
|
|
83
|
+
|
|
84
|
+
- Screening processes personal data and potentially damaging allegations through a
|
|
85
|
+
hosted third party. Confirm a lawful purpose, obtain any required approval, send
|
|
86
|
+
only the minimum identifiers needed, and verify current retention, residency,
|
|
87
|
+
access, and deletion terms before transmission.
|
|
88
|
+
- Name matches, PEP signals, sanctions hits, and media reports can be incomplete,
|
|
89
|
+
stale, misattributed, or false. Corroborate every consequential result with the
|
|
90
|
+
original source and authoritative registers; never publish an allegation as fact.
|
|
91
|
+
- This is not a legal AML/CTF determination. A qualified human reviewer must resolve
|
|
92
|
+
identity ambiguity, document the reasoning, and provide an appropriate correction
|
|
93
|
+
or appeal path before rejecting or restricting a person or organization.
|
|
94
|
+
|
|
95
|
+
## Notes
|
|
96
|
+
|
|
97
|
+
- Costs 3 credits per screen; free weekly allowance applies
|
|
98
|
+
- For AML flows, pair with `check-identity-pack` (identity) and `verify-document` (document genuineness)
|
|
99
|
+
- "Nothing found" ≠ "clean record" — always state this in reports
|
|
100
|
+
- Free key at https://www.stipple.sh for metering beyond the anonymous allowance
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: slideops
|
|
3
|
+
description: "Turn a repository into a cited HTML slide deck and detect the day it drifts from the code. Citations record file, lines, and hash; a stdlib check reports CURRENT, MOVED, or CHANGED."
|
|
4
|
+
category: content
|
|
5
|
+
risk: critical
|
|
6
|
+
source: community
|
|
7
|
+
source_repo: glukicov/slideops
|
|
8
|
+
source_type: community
|
|
9
|
+
date_added: "2026-08-31"
|
|
10
|
+
author: glukicov
|
|
11
|
+
tags: [slides, presentations, documentation, docs-as-code, drift-detection, citations, html]
|
|
12
|
+
tools: [claude-code, codex-cli, copilot, opencode]
|
|
13
|
+
license: "MIT"
|
|
14
|
+
license_source: "https://github.com/glukicov/slideops/blob/main/LICENSE"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# SlideOps: slides from a repo, that tell you when they go stale
|
|
18
|
+
|
|
19
|
+
> **Catalog copy, frozen at v1.0.0.** The canonical source is
|
|
20
|
+
> [glukicov/slideops](https://github.com/glukicov/slideops), which also carries the deck
|
|
21
|
+
> template, the two citation scripts, the reference docs, and the companion
|
|
22
|
+
> `slides-to-pdf` skill. Install from there; this page summarizes the workflow.
|
|
23
|
+
|
|
24
|
+
## Overview
|
|
25
|
+
|
|
26
|
+
SlideOps has two jobs, and the second one is the point. **Build**: turn a repository into
|
|
27
|
+
a single self-contained HTML slide deck whose every claim came from the code, not from a
|
|
28
|
+
model's impression of the code. **Keep in sync**: make that deck able to prove, months
|
|
29
|
+
later, whether it still matches the repository.
|
|
30
|
+
|
|
31
|
+
The mechanism joining them is a citation. Every quoted snippet records the file, the line
|
|
32
|
+
range, and a hash of those source lines at build time, and the deck records the commit it
|
|
33
|
+
was built from. "Are these slides still accurate?" becomes a command instead of a
|
|
34
|
+
re-read: a standard-library Python script diffs each citation against the current code
|
|
35
|
+
and reports `CURRENT`, `MOVED`, `CHANGED`, or `MISSING`. No model, no network, no tokens,
|
|
36
|
+
milliseconds to run.
|
|
37
|
+
|
|
38
|
+
## When to Use This Skill
|
|
39
|
+
|
|
40
|
+
- Use when the user asks for slides, a slide deck, or a presentation about a code
|
|
41
|
+
repository, one of its subsystems, a feature, or its recent changes: "make slides",
|
|
42
|
+
"overview deck", "team update slides", "HTML slides for this repo".
|
|
43
|
+
- Use when the user asks whether an existing deck still matches the code, or wants one
|
|
44
|
+
rechecked, refreshed, or kept in sync: "is this deck still accurate", "check the
|
|
45
|
+
slides against the code", "these docs are stale".
|
|
46
|
+
- Use when the user wants deck freshness wired into CI, a pull request check, or an
|
|
47
|
+
agent hook: "fail the build when the deck stops matching the code".
|
|
48
|
+
- Do **not** use for slide decks about anything other than a codebase (a sales deck, a
|
|
49
|
+
lecture); the citation mechanism assumes a git repository as the source of truth.
|
|
50
|
+
|
|
51
|
+
## How It Works
|
|
52
|
+
|
|
53
|
+
### Step 1: Install from the canonical repo
|
|
54
|
+
|
|
55
|
+
In Claude Code, as a plugin (both skills, background updates):
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
/plugin marketplace add glukicov/slideops
|
|
59
|
+
/plugin install slideops@slideops
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Or for Codex CLI, Copilot CLI, and OpenCode, one installer covers all of them:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
git clone https://github.com/glukicov/slideops && cd slideops
|
|
66
|
+
git checkout ba43e89bc7936649be36a1796a62203f704f8c60 # the v1.0.0 release commit
|
|
67
|
+
./install.sh
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The checkout pins the exact commit this catalog copy froze at, which is what a reader
|
|
71
|
+
can verify independently. (The canonical repo also blocks retargeting of `v*` tags with
|
|
72
|
+
an active tag ruleset, but a SHA does not ask you to trust that.) `install.sh` symlinks
|
|
73
|
+
the two skills into `~/.claude/skills` (read by Claude Code and OpenCode) and
|
|
74
|
+
`~/.agents/skills` (read by Codex CLI and Copilot CLI).
|
|
75
|
+
|
|
76
|
+
### Step 2: Build a deck
|
|
77
|
+
|
|
78
|
+
The skill walks a fixed pipeline: a two-minute repo scan, one compact intake (topic,
|
|
79
|
+
audience, length, theme, scope, extras), an outline checkpoint before any HTML is
|
|
80
|
+
written, then slide-by-slide construction from a verified template. Every snippet is
|
|
81
|
+
cited as it is written. Run the citation script from inside the repository being
|
|
82
|
+
presented, via its installed path (an agent resolves `scripts/` against the skill's own
|
|
83
|
+
directory automatically; the paths below are for running it yourself after
|
|
84
|
+
`install.sh`):
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
python3 ~/.agents/skills/slideops/scripts/cite.py app/main.py:40-58 --repo . --snippet # prints data-src + data-sha256
|
|
88
|
+
python3 ~/.agents/skills/slideops/scripts/cite.py --stamp deck.html --repo . # stamps the build commit
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`--repo` always points at the repository the deck is about, never at the SlideOps
|
|
92
|
+
checkout.
|
|
93
|
+
|
|
94
|
+
Every slide is then rendered with headless Chrome and visually verified before the deck
|
|
95
|
+
is considered done.
|
|
96
|
+
|
|
97
|
+
### Step 3: Check it later, for free
|
|
98
|
+
|
|
99
|
+
From inside the repository being presented, same path convention as Step 2:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
python3 ~/.agents/skills/slideops/scripts/check.py docs/slides/ --repo .
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Real output, from the demo deck that ships with the skill:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
Deck: skill-demo.html
|
|
109
|
+
Built: commit=179bbdb date=2026-08-28 repo=slideops
|
|
110
|
+
|
|
111
|
+
slide 9 THEMING skills/slideops/assets/template.html:22-45 CURRENT
|
|
112
|
+
slide 14 MERMAID skills/slideops/references/diagrams.md:55-59 CURRENT
|
|
113
|
+
|
|
114
|
+
2 current, 0 stale, 2 cited in total.
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`MOVED` means only line numbers shifted (update two attributes, leave the prose).
|
|
118
|
+
`CHANGED` means the quoted code was edited (read the diff, decide whether the slide's
|
|
119
|
+
claim survived). `MISSING` means the file is gone (the slide is probably obsolete). The
|
|
120
|
+
`--json` flag emits a complete repair brief per stale citation, so an agent can fix
|
|
121
|
+
drift without re-reading the repository.
|
|
122
|
+
|
|
123
|
+
## Examples
|
|
124
|
+
|
|
125
|
+
### Example 1: New deck
|
|
126
|
+
|
|
127
|
+
```text
|
|
128
|
+
User: make slides about this repo
|
|
129
|
+
Agent: [scans repo, proposes 3-4 concrete topics with a "why now" each,
|
|
130
|
+
asks one compact intake, shows an outline, then builds and
|
|
131
|
+
visually verifies a cited HTML deck at docs/slides/]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Example 2: Freshness check in CI
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
python3 tools/slideops-check.py docs/slides/ --repo . --exit-zero # report-only PR annotation
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The canonical repo's `references/automation.md` has the PR-check workflow, advisory hook
|
|
141
|
+
variants, and a delegated-refresh recipe. `check.py` is one dependency-free file, meant
|
|
142
|
+
to be vendored into the deck's own repo.
|
|
143
|
+
|
|
144
|
+
## Best Practices
|
|
145
|
+
|
|
146
|
+
- ✅ Cite with `cite.py`, never by hand: a hand-computed hash silently reports `CHANGED`
|
|
147
|
+
months later and nobody can tell whether the code moved or the build was sloppy.
|
|
148
|
+
- ✅ Repair a drifted deck; do not rebuild it. Fix only the slides whose citations went
|
|
149
|
+
stale, then re-stamp.
|
|
150
|
+
- ✅ Automate the check for evergreen decks (onboarding, architecture); leave snapshots
|
|
151
|
+
(sprint updates, conference talks) frozen deliberately.
|
|
152
|
+
- ❌ Do not run `check.py` against a PDF export: citations live in the HTML, so the PDF
|
|
153
|
+
reports "No citations found in this deck".
|
|
154
|
+
- ❌ Do not block every commit on the check. Report-only on pull requests first; a docs
|
|
155
|
+
gate on the fast path trains people to pass `--no-verify`.
|
|
156
|
+
|
|
157
|
+
## Limitations
|
|
158
|
+
|
|
159
|
+
- Needs a headless Chrome or Chromium binary (Playwright cache or system install) for
|
|
160
|
+
the visual verification pass, and Python 3 for the citation scripts.
|
|
161
|
+
- Works offline except two opt-ins: Mermaid diagrams (a one-time `npx` download) and
|
|
162
|
+
brand-color extraction from a live style guide.
|
|
163
|
+
- The check verifies quoted snippets against the code; it cannot verify prose claims
|
|
164
|
+
that cite nothing.
|
|
165
|
+
- Drift detection assumes the deck and the code share a git repository.
|
|
166
|
+
|
|
167
|
+
## Security & Safety Notes
|
|
168
|
+
|
|
169
|
+
- Ask before cloning the upstream repository, running `install.sh`, creating skill
|
|
170
|
+
symlinks in the user's home directory, downloading an optional `npx` package, or
|
|
171
|
+
writing deck output. Show the exact pinned commit and destination paths first, and
|
|
172
|
+
preserve any existing skill entries instead of overwriting them silently.
|
|
173
|
+
- The citation scripts are standard-library-only Python: no dependencies, no network,
|
|
174
|
+
no tokens.
|
|
175
|
+
- The skill declares no `allowed-tools`, deliberately: the host agent's own permission
|
|
176
|
+
model stays in charge, and headless Chrome keeps its sandbox.
|
|
177
|
+
- The skill carries an explicit confidentiality rule for deck content: never read or
|
|
178
|
+
quote secrets, keys, `.env` files, production logs, or customer data; redact internal
|
|
179
|
+
hostnames and identifiers; and finish with a redaction scan of the rendered slides,
|
|
180
|
+
because decks are documents that leave the repository.
|
|
181
|
+
- File writes are limited to the deck output folder (default `docs/slides/`) plus its
|
|
182
|
+
companion README.
|
|
183
|
+
|
|
184
|
+
## Common Pitfalls
|
|
185
|
+
|
|
186
|
+
- **Problem:** `check.py` on an exported PDF reports no citations.
|
|
187
|
+
**Solution:** Run it against the HTML deck; the PDF is a derived artifact from the
|
|
188
|
+
companion `slides-to-pdf` skill.
|
|
189
|
+
- **Problem:** A freshly built deck already reports `CHANGED`.
|
|
190
|
+
**Solution:** That is a build defect, not a future problem: a snippet was quoted and
|
|
191
|
+
then edited, or a hash was hand-computed. Re-cite with `cite.py` before shipping.
|
|
192
|
+
- **Problem:** Chrome is not found on the verification step.
|
|
193
|
+
**Solution:** The canonical repo's `references/verification.md` has the cross-platform
|
|
194
|
+
discovery recipe (Playwright cache first, then system installs).
|
|
195
|
+
|
|
196
|
+
## Related Skills
|
|
197
|
+
|
|
198
|
+
- `slides-to-pdf` (same canonical repo): screenshots every slide at 2x, prints a
|
|
199
|
+
page-per-slide PDF, and verifies the result by rendering the PDF back to images.
|
|
200
|
+
- `@2slides-ppt-generator`: API-driven deck generation from text or documents; use it
|
|
201
|
+
when the source material is not a code repository.
|
|
@@ -38,6 +38,7 @@ execution with excellent developer experience and TypeScript-first design.
|
|
|
38
38
|
## Scope
|
|
39
39
|
|
|
40
40
|
- redis-queues -> bullmq-specialist
|
|
41
|
+
- serverless-queues -> upstash-qstash
|
|
41
42
|
- pure-event-driven -> inngest
|
|
42
43
|
- workflow-orchestration -> temporal-craftsman
|
|
43
44
|
- infrastructure -> infra-architect
|
|
@@ -891,6 +892,7 @@ Fix action: Batch items and use fewer waits, or split into subtasks
|
|
|
891
892
|
### Delegation Triggers
|
|
892
893
|
|
|
893
894
|
- redis|bullmq|traditional queue -> bullmq-specialist (Need Redis-backed queues instead of managed service)
|
|
895
|
+
- serverless queue|http queue|no workers -> upstash-qstash (Need HTTP-delivered queues and schedules without a worker runtime)
|
|
894
896
|
- vercel|deployment|serverless -> vercel-deployment (Trigger.dev needs deployment config)
|
|
895
897
|
- database|postgres|supabase -> supabase-backend (Tasks need database access)
|
|
896
898
|
- openai|anthropic|ai model|llm -> llm-architect (Tasks need AI model integration)
|
|
@@ -72,10 +72,20 @@ libraries at import time; importing them first silently disables the optimizatio
|
|
|
72
72
|
|
|
73
73
|
```python
|
|
74
74
|
import unsloth # must be first
|
|
75
|
+
import os
|
|
76
|
+
import re
|
|
75
77
|
from unsloth import FastLanguageModel
|
|
76
78
|
|
|
79
|
+
def reviewed_revision(variable):
|
|
80
|
+
revision = os.environ.get(variable, "")
|
|
81
|
+
if re.fullmatch(r"[0-9a-fA-F]{40}", revision) is None:
|
|
82
|
+
raise RuntimeError(f"{variable} must be a reviewed full 40-character Hub commit SHA")
|
|
83
|
+
return revision.lower()
|
|
84
|
+
|
|
85
|
+
model_revision = reviewed_revision("UNSLOTH_MODEL_REVISION")
|
|
77
86
|
model, tokenizer = FastLanguageModel.from_pretrained(
|
|
78
87
|
model_name = "unsloth/Qwen3-8B",
|
|
88
|
+
revision = model_revision,
|
|
79
89
|
max_seq_length = 2048,
|
|
80
90
|
load_in_4bit = True,
|
|
81
91
|
dtype = None, # auto-detects bf16 where supported
|
|
@@ -87,6 +97,9 @@ Pick the loader that matches the modality: `FastLanguageModel` for text-only cau
|
|
|
87
97
|
|
|
88
98
|
The `unsloth/` Hub namespace holds pre-quantized copies that download faster and skip a local
|
|
89
99
|
quantization pass. Upstream repos such as `Qwen/` or `meta-llama/` work identically.
|
|
100
|
+
Before setting `UNSLOTH_MODEL_REVISION`, inspect that exact Hub commit and obtain approval for the
|
|
101
|
+
download. Record the repository and full revision with the run; never substitute a branch, tag,
|
|
102
|
+
range, or moving default.
|
|
90
103
|
|
|
91
104
|
### Step 3: Fix the chat template before training
|
|
92
105
|
|
|
@@ -194,20 +207,35 @@ cannot be cleanly re-quantized afterwards.
|
|
|
194
207
|
|
|
195
208
|
```python
|
|
196
209
|
import unsloth
|
|
210
|
+
import os
|
|
211
|
+
import re
|
|
197
212
|
from unsloth import FastLanguageModel
|
|
198
213
|
from unsloth.chat_templates import get_chat_template, train_on_responses_only
|
|
199
214
|
from datasets import load_dataset
|
|
200
215
|
from trl import SFTTrainer, SFTConfig
|
|
201
216
|
|
|
217
|
+
def reviewed_revision(variable):
|
|
218
|
+
revision = os.environ.get(variable, "")
|
|
219
|
+
if re.fullmatch(r"[0-9a-fA-F]{40}", revision) is None:
|
|
220
|
+
raise RuntimeError(f"{variable} must be a reviewed full 40-character Hub commit SHA")
|
|
221
|
+
return revision.lower()
|
|
222
|
+
|
|
223
|
+
model_revision = reviewed_revision("UNSLOTH_MODEL_REVISION")
|
|
224
|
+
dataset_revision = reviewed_revision("UNSLOTH_DATASET_REVISION")
|
|
202
225
|
model, tokenizer = FastLanguageModel.from_pretrained(
|
|
203
226
|
model_name = "unsloth/Qwen3-8B",
|
|
227
|
+
revision = model_revision,
|
|
204
228
|
max_seq_length = 2048,
|
|
205
229
|
load_in_4bit = True,
|
|
206
230
|
)
|
|
207
231
|
model = FastLanguageModel.get_peft_model(model, r = 16, lora_alpha = 16)
|
|
208
232
|
|
|
209
233
|
tokenizer = get_chat_template(tokenizer, chat_template = "qwen3")
|
|
210
|
-
dataset = load_dataset(
|
|
234
|
+
dataset = load_dataset(
|
|
235
|
+
"mlabonne/FineTome-100k",
|
|
236
|
+
revision = dataset_revision,
|
|
237
|
+
split = "train[:5000]",
|
|
238
|
+
)
|
|
211
239
|
|
|
212
240
|
trainer = SFTTrainer(
|
|
213
241
|
model = model,
|
|
@@ -239,11 +267,21 @@ Load with `fast_inference=True` to route sampling through vLLM in the same proce
|
|
|
239
267
|
|
|
240
268
|
```python
|
|
241
269
|
import unsloth
|
|
270
|
+
import os
|
|
271
|
+
import re
|
|
242
272
|
from unsloth import FastLanguageModel
|
|
243
273
|
from trl import GRPOTrainer, GRPOConfig
|
|
244
274
|
|
|
275
|
+
def reviewed_revision(variable):
|
|
276
|
+
revision = os.environ.get(variable, "")
|
|
277
|
+
if re.fullmatch(r"[0-9a-fA-F]{40}", revision) is None:
|
|
278
|
+
raise RuntimeError(f"{variable} must be a reviewed full 40-character Hub commit SHA")
|
|
279
|
+
return revision.lower()
|
|
280
|
+
|
|
281
|
+
model_revision = reviewed_revision("UNSLOTH_MODEL_REVISION")
|
|
245
282
|
model, tokenizer = FastLanguageModel.from_pretrained(
|
|
246
283
|
model_name = "unsloth/Qwen3-4B",
|
|
284
|
+
revision = model_revision,
|
|
247
285
|
max_seq_length = 1024,
|
|
248
286
|
load_in_4bit = True,
|
|
249
287
|
fast_inference = True, # vLLM sampling backend
|
|
@@ -307,8 +345,13 @@ GRPO learning rates sit roughly two orders of magnitude below SFT. Reward functi
|
|
|
307
345
|
committed token grants write access to every model the account owns.
|
|
308
346
|
- Fine-tuning reproduces the training data's content and biases in the weights. Confirm the
|
|
309
347
|
dataset is licensed for training and free of secrets before starting.
|
|
348
|
+
- Pin every Hub model and dataset to a reviewed full commit SHA, obtain approval before changing
|
|
349
|
+
either revision, and record both values with the training artifact. Prefer the verified local
|
|
350
|
+
cache for repeat runs instead of re-resolving network defaults.
|
|
310
351
|
- GGUF export builds llama.cpp from source on first use, compiling third-party code and
|
|
311
|
-
requiring network access.
|
|
352
|
+
requiring network access. Before the first export, identify and review the exact llama.cpp
|
|
353
|
+
revision that will be built; do not permit an unattended moving-revision fetch. Prefer a
|
|
354
|
+
user-approved, full-commit-pinned local toolchain and cache.
|
|
312
355
|
- Unsloth is dual-licensed: the core package is Apache-2.0, while optional components such as
|
|
313
356
|
the Studio UI are AGPL-3.0. Check a component's license before redistributing it.
|
|
314
357
|
|
|
@@ -931,7 +931,7 @@ Workflow:
|
|
|
931
931
|
|
|
932
932
|
## Related Skills
|
|
933
933
|
|
|
934
|
-
Works well with: `vercel-deployment`, `nextjs-app-router`, `redis-specialist`, `email-systems`, `supabase-backend`, `cloudflare-workers`
|
|
934
|
+
Works well with: `upstash-redis`, `upstash-ratelimit`, `vercel-deployment`, `nextjs-app-router`, `redis-specialist`, `email-systems`, `supabase-backend`, `cloudflare-workers`
|
|
935
935
|
|
|
936
936
|
## When to Use
|
|
937
937
|
- User mentions or implies: qstash
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upstash-ratelimit
|
|
3
|
+
description: "Add rate limiting to API routes, middleware, and edge functions with @upstash/ratelimit: sliding window, fixed window, and token bucket backed by Upstash Redis."
|
|
4
|
+
category: backend
|
|
5
|
+
risk: critical
|
|
6
|
+
source: self
|
|
7
|
+
source_type: self
|
|
8
|
+
date_added: "2026-08-31"
|
|
9
|
+
author: CahidArda
|
|
10
|
+
tags: [upstash, rate-limiting, redis, serverless, edge, middleware, 429]
|
|
11
|
+
tools: [claude, codex, cursor, gemini]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Upstash Ratelimit
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
`@upstash/ratelimit` implements distributed rate limiting on top of Upstash
|
|
19
|
+
Redis. Because state lives in Redis, every instance of a serverless function
|
|
20
|
+
or edge worker shares the same counters, which an in-memory limiter cannot
|
|
21
|
+
do. It ships three algorithms (fixed window, sliding window, token bucket),
|
|
22
|
+
per-identifier keys, optional in-memory blocking of already-limited
|
|
23
|
+
identifiers, and optional analytics.
|
|
24
|
+
|
|
25
|
+
## When to Use This Skill
|
|
26
|
+
|
|
27
|
+
- Use when the user needs to limit requests per IP, user, API key, or tenant
|
|
28
|
+
across multiple serverless instances or regions.
|
|
29
|
+
- Use when protecting login, signup, form, webhook, or LLM endpoints from
|
|
30
|
+
abuse and returning `429 Too Many Requests`.
|
|
31
|
+
- Use when choosing between fixed window, sliding window, and token bucket.
|
|
32
|
+
- Do not use for client-side retry/backoff against a third-party API's limits;
|
|
33
|
+
see `api-rate-limit-handler`.
|
|
34
|
+
- Do not use for a single long-running process with no shared state; an
|
|
35
|
+
in-memory limiter is simpler there.
|
|
36
|
+
|
|
37
|
+
## How It Works
|
|
38
|
+
|
|
39
|
+
### Step 1: Install and configure
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install @upstash/ratelimit @upstash/redis
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Set `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` in the environment.
|
|
46
|
+
|
|
47
|
+
### Step 2: Create the limiter once, outside the handler
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { Ratelimit } from "@upstash/ratelimit";
|
|
51
|
+
import { Redis } from "@upstash/redis";
|
|
52
|
+
|
|
53
|
+
export const ratelimit = new Ratelimit({
|
|
54
|
+
redis: Redis.fromEnv(),
|
|
55
|
+
limiter: Ratelimit.slidingWindow(10, "10 s"), // 10 requests per 10 seconds
|
|
56
|
+
prefix: "rl:api",
|
|
57
|
+
analytics: true,
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Constructing the limiter at module scope lets the built-in ephemeral cache
|
|
62
|
+
short-circuit blocked identifiers without a Redis call.
|
|
63
|
+
|
|
64
|
+
### Step 3: Call `limit()` with a stable identifier
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const { success, limit, remaining, reset, pending } = await ratelimit.limit(userId);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`success` is `false` when the identifier is over its limit. `reset` is a Unix
|
|
71
|
+
timestamp in milliseconds. `pending` is a promise for background work
|
|
72
|
+
(analytics, multi-region sync); await it or pass it to `waitUntil` on edge
|
|
73
|
+
runtimes so the function is not frozen before it completes.
|
|
74
|
+
|
|
75
|
+
## Examples
|
|
76
|
+
|
|
77
|
+
### Example 1: Next.js middleware returning 429
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { Ratelimit } from "@upstash/ratelimit";
|
|
81
|
+
import { Redis } from "@upstash/redis";
|
|
82
|
+
import { NextResponse, type NextRequest } from "next/server";
|
|
83
|
+
|
|
84
|
+
const ratelimit = new Ratelimit({
|
|
85
|
+
redis: Redis.fromEnv(),
|
|
86
|
+
limiter: Ratelimit.slidingWindow(20, "1 m"),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export async function middleware(request: NextRequest) {
|
|
90
|
+
const ip = request.headers.get("x-forwarded-for") ?? "anonymous";
|
|
91
|
+
const { success, limit, remaining, reset } = await ratelimit.limit(ip);
|
|
92
|
+
|
|
93
|
+
if (!success) {
|
|
94
|
+
return new NextResponse("Too Many Requests", {
|
|
95
|
+
status: 429,
|
|
96
|
+
headers: {
|
|
97
|
+
"X-RateLimit-Limit": String(limit),
|
|
98
|
+
"X-RateLimit-Remaining": String(remaining),
|
|
99
|
+
"X-RateLimit-Reset": String(reset),
|
|
100
|
+
"Retry-After": String(Math.ceil((reset - Date.now()) / 1000)),
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return NextResponse.next();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const config = { matcher: "/api/:path*" };
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Example 2: Token bucket with per-plan limits
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
const limiters = {
|
|
114
|
+
free: new Ratelimit({
|
|
115
|
+
redis: Redis.fromEnv(),
|
|
116
|
+
prefix: "rl:free",
|
|
117
|
+
limiter: Ratelimit.tokenBucket(5, "10 s", 10), // refill 5 per 10 s, burst 10
|
|
118
|
+
}),
|
|
119
|
+
pro: new Ratelimit({
|
|
120
|
+
redis: Redis.fromEnv(),
|
|
121
|
+
prefix: "rl:pro",
|
|
122
|
+
limiter: Ratelimit.tokenBucket(50, "10 s", 100),
|
|
123
|
+
}),
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const { success } = await limiters[plan].limit(apiKey);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Best Practices
|
|
130
|
+
|
|
131
|
+
- ✅ Use a stable, low-cardinality identifier (user id, API key, tenant) where
|
|
132
|
+
possible; fall back to IP only for anonymous traffic.
|
|
133
|
+
- ✅ Set a distinct `prefix` per endpoint or plan so limits do not collide.
|
|
134
|
+
- ✅ Return `Retry-After` and `X-RateLimit-*` headers with 429 responses.
|
|
135
|
+
- ✅ Prefer `slidingWindow` for most APIs; use `tokenBucket` when short bursts
|
|
136
|
+
are acceptable; use `fixedWindow` when the lowest Redis cost matters.
|
|
137
|
+
- ❌ Don't construct a new `Ratelimit` inside the request handler.
|
|
138
|
+
- ❌ Don't rely on `pending` completing on its own in edge runtimes.
|
|
139
|
+
- ❌ Don't rate limit by `x-forwarded-for` without validating it is set by
|
|
140
|
+
your proxy; clients can spoof it otherwise.
|
|
141
|
+
|
|
142
|
+
## Limitations
|
|
143
|
+
|
|
144
|
+
- Requires an Upstash Redis database; it does not work with other Redis
|
|
145
|
+
servers or without network access.
|
|
146
|
+
- Each `limit()` call is at least one HTTP round trip to Redis, so it adds
|
|
147
|
+
latency to every request it guards.
|
|
148
|
+
- Sliding window is an approximation that assumes an even spread of requests
|
|
149
|
+
in the previous window; it is not an exact log.
|
|
150
|
+
- `MultiRegionRatelimit` trades strict accuracy for lower latency and does
|
|
151
|
+
not support the token bucket algorithm.
|
|
152
|
+
- If Redis is unreachable, the default `timeout` (5 s) lets requests through
|
|
153
|
+
(`reason: "timeout"`); this fails open, not closed.
|
|
154
|
+
- This skill does not replace environment-specific validation, testing, or
|
|
155
|
+
expert review.
|
|
156
|
+
|
|
157
|
+
## Security & Safety Notes
|
|
158
|
+
|
|
159
|
+
- Rate limiting is one layer of abuse protection, not authentication. Pair it
|
|
160
|
+
with auth and input validation.
|
|
161
|
+
- The Redis token grants full database access; keep it server-side.
|
|
162
|
+
- Changing limits in production can lock out legitimate users. Confirm the
|
|
163
|
+
numbers with the user before deploying stricter limits.
|
|
164
|
+
|
|
165
|
+
## Common Pitfalls
|
|
166
|
+
|
|
167
|
+
- **Problem:** Every request is allowed even after the limit.
|
|
168
|
+
**Solution:** Each identifier must be the same string across requests;
|
|
169
|
+
check that the identifier is not `undefined` or a fresh random value.
|
|
170
|
+
- **Problem:** Analytics are empty on Vercel Edge or Cloudflare Workers.
|
|
171
|
+
**Solution:** Pass `pending` to `waitUntil` (`ctx.waitUntil(pending)`) so
|
|
172
|
+
the background request is not cancelled when the response is sent.
|
|
173
|
+
|
|
174
|
+
## Related Skills
|
|
175
|
+
|
|
176
|
+
- `@upstash-redis` - The client this package uses for storage.
|
|
177
|
+
- `@api-rate-limit-handler` - Client-side backoff and retry when you are the
|
|
178
|
+
one being rate limited.
|
|
179
|
+
- `@upstash-qstash` - Queue and smooth traffic to downstream services instead
|
|
180
|
+
of rejecting it.
|
|
181
|
+
|
|
182
|
+
## Additional Resources
|
|
183
|
+
|
|
184
|
+
- [Upstash Ratelimit documentation](https://upstash.com/docs/redis/sdks/ratelimit-ts/overview)
|
|
185
|
+
- [@upstash/ratelimit on GitHub](https://github.com/upstash/ratelimit-js)
|
|
186
|
+
- [Algorithms guide](https://upstash.com/docs/redis/sdks/ratelimit-ts/algorithms)
|