opencode-skills-collection 4.0.48 → 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 -1
- 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/find-matching-tenders/SKILL.md +100 -0
- 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 -21
|
@@ -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)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upstash-redis
|
|
3
|
+
description: "Use the @upstash/redis HTTP client for caching, sessions, counters, and Redis data structures from serverless and edge runtimes without connection pooling."
|
|
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, redis, cache, serverless, edge, key-value]
|
|
11
|
+
tools: [claude, codex, cursor, gemini]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Upstash Redis
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
`@upstash/redis` is a Redis client that talks to an Upstash Redis database over
|
|
19
|
+
HTTPS instead of a TCP connection. Because every command is a stateless HTTP
|
|
20
|
+
request, it works in environments where a pooled TCP client is awkward or
|
|
21
|
+
impossible: Vercel and Netlify functions, Cloudflare Workers, Deno, Bun, and
|
|
22
|
+
Next.js middleware. The client serializes and deserializes JavaScript values
|
|
23
|
+
automatically, so numbers and objects round-trip without manual `JSON.parse`.
|
|
24
|
+
|
|
25
|
+
## When to Use This Skill
|
|
26
|
+
|
|
27
|
+
- Use when the user needs a cache, session store, counter, leaderboard, or
|
|
28
|
+
simple queue from a serverless or edge function.
|
|
29
|
+
- Use when the user mentions Upstash Redis, `UPSTASH_REDIS_REST_URL`, or
|
|
30
|
+
`@upstash/redis`.
|
|
31
|
+
- Use when migrating an `ioredis` or `node-redis` call site to a runtime that
|
|
32
|
+
cannot hold a persistent TCP socket.
|
|
33
|
+
- Do not use for a self-hosted or non-Upstash Redis server; the client only
|
|
34
|
+
speaks the Upstash REST protocol. Use `ioredis` or `node-redis` there.
|
|
35
|
+
- Do not use for Redis administration or CLI work; see `redis-cli`.
|
|
36
|
+
|
|
37
|
+
## How It Works
|
|
38
|
+
|
|
39
|
+
### Step 1: Configure credentials
|
|
40
|
+
|
|
41
|
+
Create a database in the Upstash console and copy the REST URL and token into
|
|
42
|
+
environment variables. Never hardcode them.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
UPSTASH_REDIS_REST_URL=https://<your-db>.upstash.io
|
|
46
|
+
UPSTASH_REDIS_REST_TOKEN=<your-rest-token>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Step 2: Create one client per module
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { Redis } from "@upstash/redis";
|
|
53
|
+
|
|
54
|
+
export const redis = Redis.fromEnv();
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
On Cloudflare Workers, import from `@upstash/redis/cloudflare` and pass the
|
|
58
|
+
worker `env` object: `Redis.fromEnv(env)`.
|
|
59
|
+
|
|
60
|
+
### Step 3: Call Redis commands as methods
|
|
61
|
+
|
|
62
|
+
Command names are lowercase methods (`get`, `set`, `hset`, `zadd`, `incr`).
|
|
63
|
+
Values are auto-serialized; pass and receive native JavaScript types.
|
|
64
|
+
|
|
65
|
+
## Examples
|
|
66
|
+
|
|
67
|
+
### Example 1: Cache-aside with a TTL
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { Redis } from "@upstash/redis";
|
|
71
|
+
|
|
72
|
+
const redis = Redis.fromEnv();
|
|
73
|
+
|
|
74
|
+
export async function getUser(id: string) {
|
|
75
|
+
const cached = await redis.get<{ id: string; name: string }>(`user:${id}`);
|
|
76
|
+
if (cached) return cached;
|
|
77
|
+
|
|
78
|
+
const user = await db.users.findById(id);
|
|
79
|
+
await redis.set(`user:${id}`, user, { ex: 3600 }); // expires in 1 hour
|
|
80
|
+
return user;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function invalidateUser(id: string) {
|
|
84
|
+
await redis.del(`user:${id}`);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Example 2: Batch commands in a pipeline
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const pipeline = redis.pipeline();
|
|
92
|
+
pipeline.hset("user:1", { name: "Alice", plan: "pro" });
|
|
93
|
+
pipeline.incr("signups:total");
|
|
94
|
+
pipeline.zadd("leaderboard", { score: 120, member: "user:1" });
|
|
95
|
+
const [hsetResult, signups, zaddResult] = await pipeline.exec();
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`pipeline()` batches independent commands into one round trip. It is not
|
|
99
|
+
atomic; use `redis.multi()` for MULTI/EXEC or a reviewed server-side Lua script
|
|
100
|
+
when commands must run as one unit.
|
|
101
|
+
|
|
102
|
+
## Best Practices
|
|
103
|
+
|
|
104
|
+
- ✅ Read credentials with `Redis.fromEnv()` or from a secrets manager.
|
|
105
|
+
- ✅ Set a TTL (`{ ex: seconds }`) on cache entries so stale data expires.
|
|
106
|
+
- ✅ Namespace keys (`user:123`, `session:abc`) to keep the keyspace readable.
|
|
107
|
+
- ✅ Use `pipeline()` or `mget`/`mset` instead of many sequential awaits.
|
|
108
|
+
- ❌ Don't `JSON.stringify` values before `set`; the client already does it.
|
|
109
|
+
- ❌ Don't call `keys("*")` in request handlers; use `scan` for large keyspaces.
|
|
110
|
+
- ❌ Don't store secrets or PII in Redis without a retention plan and TTL.
|
|
111
|
+
|
|
112
|
+
## Limitations
|
|
113
|
+
|
|
114
|
+
- Requires an Upstash Redis database; it cannot connect to other Redis servers.
|
|
115
|
+
- Each command is an HTTP request, so latency is higher than a warm TCP
|
|
116
|
+
connection; batch with pipelines where it matters.
|
|
117
|
+
- Transactions (`multi()`) do not roll back on runtime errors, and `WATCH` is
|
|
118
|
+
not available over REST; use a Lua script for atomic check-and-set.
|
|
119
|
+
- Pub/Sub subscribe and blocking commands (`BLPOP`, `XREAD BLOCK`) are not
|
|
120
|
+
supported over the REST client.
|
|
121
|
+
- This skill covers the TypeScript client only. Python, Go, and other SDKs
|
|
122
|
+
differ in method names.
|
|
123
|
+
- This skill does not replace environment-specific validation, testing, or
|
|
124
|
+
expert review.
|
|
125
|
+
|
|
126
|
+
## Security & Safety Notes
|
|
127
|
+
|
|
128
|
+
- The REST token grants full read/write access to the database. Keep it in
|
|
129
|
+
server-side environment variables; never ship it to a browser bundle.
|
|
130
|
+
- Use a read-only token from the console for read-only workloads.
|
|
131
|
+
- Commands such as `flushdb` and `del` are destructive; confirm with the user
|
|
132
|
+
before running them against a production database.
|
|
133
|
+
|
|
134
|
+
## Common Pitfalls
|
|
135
|
+
|
|
136
|
+
- **Problem:** `get` returns `null` in production but works locally.
|
|
137
|
+
**Solution:** The deployment is missing `UPSTASH_REDIS_REST_URL` or
|
|
138
|
+
`UPSTASH_REDIS_REST_TOKEN`; check the platform's environment settings.
|
|
139
|
+
- **Problem:** A number comes back as a string after `incr` on a value set
|
|
140
|
+
with `JSON.stringify`.
|
|
141
|
+
**Solution:** Store the raw number (`redis.set("n", 1)`) and let the client
|
|
142
|
+
serialize it.
|
|
143
|
+
|
|
144
|
+
## Related Skills
|
|
145
|
+
|
|
146
|
+
- `@upstash-ratelimit` - Rate limiting built on this client.
|
|
147
|
+
- `@upstash-qstash` - HTTP message queue and schedules when you need delivery
|
|
148
|
+
guarantees rather than a data store.
|
|
149
|
+
- `@redis-cli` - Inspecting and administering Redis from the command line.
|
|
150
|
+
- `@bullmq-specialist` - Job queues on a TCP Redis you operate yourself.
|
|
151
|
+
|
|
152
|
+
## Additional Resources
|
|
153
|
+
|
|
154
|
+
- [Upstash Redis documentation](https://upstash.com/docs/redis)
|
|
155
|
+
- [@upstash/redis on GitHub](https://github.com/upstash/redis-js)
|
|
156
|
+
- [TypeScript SDK reference](https://upstash.com/docs/redis/sdks/ts/overview)
|