@ericrisco/rsc 0.1.52 → 0.1.54
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/manifest.json +15 -15
- package/package.json +1 -1
- package/skills/gamedev-shaders/SKILL.md +23 -43
- package/skills/gamedev-shaders/evals/cases.yaml +2 -2
- package/skills/gcp-essentials/SKILL.md +16 -19
- package/skills/hetzner/SKILL.md +18 -36
- package/skills/knowledge-ops/SKILL.md +14 -17
- package/skills/lead-gen/SKILL.md +2 -6
- package/skills/lead-gen/evals/cases.yaml +2 -2
- package/skills/linkedin-carousels/SKILL.md +31 -42
- package/skills/llm-pipeline/SKILL.md +4 -9
- package/skills/medium-publishing/SKILL.md +5 -13
- package/skills/modal/SKILL.md +31 -49
- package/skills/n8n/SKILL.md +37 -65
- package/skills/newsletter/SKILL.md +10 -20
- package/skills/newsletter/references/growth-loops.md +3 -3
- package/skills/notion-connector/SKILL.md +13 -26
- package/skills/planetscale/SKILL.md +18 -43
- package/skills/planetscale/references/no-foreign-keys.md +2 -2
- package/skills/prompt-engineering/SKILL.md +5 -16
- package/skills/prompt-engineering/evals/cases.yaml +1 -1
|
@@ -10,9 +10,9 @@ should_trigger:
|
|
|
10
10
|
- prompt: "Is this cold prospect list legal to email under GDPR before we send?"
|
|
11
11
|
why: "The compliance gate (GDPR LIA, no scraped basis) is part of making a list ship-ready — non-obvious that lead-gen owns it, not a generic legal skill."
|
|
12
12
|
- prompt: "Necesito una lista de clientes potenciales para nuestro SaaS de logística."
|
|
13
|
-
why: "Spanish core
|
|
13
|
+
why: "Spanish-language phrasing of the core case — build a target list of prospects."
|
|
14
14
|
- prompt: "A qui de la nostra base truquem primer, tenim 300 comptes sense prioritzar?"
|
|
15
|
-
why: "Catalan
|
|
15
|
+
why: "Catalan-language phrasing — scoring/tiering an unprioritized account list."
|
|
16
16
|
|
|
17
17
|
should_not_trigger:
|
|
18
18
|
- prompt: "Write the cold email sequence for these prospects."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: linkedin-carousels
|
|
3
|
-
description: "Use when
|
|
3
|
+
description: "Use when building a LinkedIn carousel / document post — the swipeable multi-page PDF — slide by slide: cover hook, narrative arc, one idea per slide, CTA closer, PDF export spec. NOT the account/format plan (that is `linkedin-strategy`), NOT a text or single-image feed post (that is `linkedin-content`), NOT a 16:9 talk deck (that is `presentations`)."
|
|
4
4
|
tags: [linkedin, carousel, document-post, slide-design, visual-hierarchy, hook, b2b, social-content]
|
|
5
5
|
recommends: [linkedin-content, linkedin-strategy, brand-identity, brand-voice, presentations, social-publisher]
|
|
6
6
|
origin: risco
|
|
@@ -8,16 +8,10 @@ origin: risco
|
|
|
8
8
|
|
|
9
9
|
# LinkedIn carousels
|
|
10
10
|
|
|
11
|
-
Design **one swipeable LinkedIn document post
|
|
12
|
-
|
|
13
|
-
Canva/Figma/Slides
|
|
14
|
-
|
|
15
|
-
You do not own the account plan (which formats, cadence, niche — that is
|
|
16
|
-
[`../linkedin-strategy/SKILL.md`](../linkedin-strategy/SKILL.md)), the words of a text or
|
|
17
|
-
single-image feed post ([`../linkedin-content/SKILL.md`](../linkedin-content/SKILL.md)), the
|
|
18
|
-
programmatic upload ([`../linkedin-api/SKILL.md`](../linkedin-api/SKILL.md)), or a spoken-to
|
|
19
|
-
16:9 talk deck ([`../presentations/SKILL.md`](../presentations/SKILL.md)). Your artifact is the
|
|
20
|
-
*slides of a phone-read PDF* — sequence and visual hierarchy are the product.
|
|
11
|
+
Design **one swipeable LinkedIn document post**. Your artifact is the *slides of a phone-read
|
|
12
|
+
PDF* — sequence and visual hierarchy are the product — handed back as a slide-by-slide build
|
|
13
|
+
ready to assemble in Canva/Figma/Slides plus a one-page design+export checklist. Everything
|
|
14
|
+
adjacent (the account plan, the feed copy, the upload, a talk deck) routes out; see Handoffs.
|
|
21
15
|
|
|
22
16
|
## What you produce
|
|
23
17
|
|
|
@@ -27,34 +21,30 @@ programmatic upload ([`../linkedin-api/SKILL.md`](../linkedin-api/SKILL.md)), or
|
|
|
27
21
|
- **A design + export spec** — canvas, safe area, type floor, PDF checklist (one page).
|
|
28
22
|
- **A one-line caption starter** — the first feed line; full caption tuning routes out.
|
|
29
23
|
|
|
30
|
-
Precondition:
|
|
31
|
-
|
|
24
|
+
Precondition: a topic, an angle, or a source (post/article). If the ask is the account plan or
|
|
25
|
+
a block of feed copy, you are in the wrong skill — see the handoffs table.
|
|
32
26
|
|
|
33
27
|
## Ground first
|
|
34
28
|
|
|
35
|
-
A carousel inherits an identity; it does not invent one.
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
[`../linkedin-strategy/SKILL.md`](../linkedin-strategy/SKILL.md) or
|
|
43
|
-
[`../linkedin-content/SKILL.md`](../linkedin-content/SKILL.md). Reuse the hook, don't reopen it.
|
|
44
|
-
|
|
45
|
-
If none exist, name your assumptions on slide 0 of the spec and proceed — don't stall.
|
|
29
|
+
A carousel inherits an identity; it does not invent one. Read the brand kit (colors, type,
|
|
30
|
+
logo — owned by [`../brand-identity/SKILL.md`](../brand-identity/SKILL.md)) and the voice
|
|
31
|
+
([`../brand-voice/SKILL.md`](../brand-voice/SKILL.md)) from `02-DOCS/` if they exist; don't pick
|
|
32
|
+
arbitrary fonts. Pull the angle from any plan or post already produced by
|
|
33
|
+
[`../linkedin-strategy/SKILL.md`](../linkedin-strategy/SKILL.md) or
|
|
34
|
+
[`../linkedin-content/SKILL.md`](../linkedin-content/SKILL.md) — reuse the hook, don't reopen
|
|
35
|
+
it. If none exist, name your assumptions on slide 0 of the spec and proceed; don't stall.
|
|
46
36
|
|
|
47
37
|
## The swipe economics (why every rule below exists)
|
|
48
38
|
|
|
49
39
|
Document/carousel posts are the **highest-engagement native format on LinkedIn right now** —
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
40
|
+
roughly 6.6% average engagement, ahead of every other format, and top-performing pages post far
|
|
41
|
+
more of them than median pages. The reason is **dwell time**: a swipeable deck holds the thumb
|
|
42
|
+
on screen, and dwell — not likes — is the algorithm's main distribution signal.
|
|
53
43
|
|
|
54
44
|
So the whole bet is the **cover slide and the swipe-to-slide-2 bounce**. If the cover doesn't
|
|
55
|
-
earn the first swipe, your slide count, your arc, and your CTA never get seen
|
|
56
|
-
|
|
57
|
-
|
|
45
|
+
earn the first swipe, your slide count, your arc, and your CTA never get seen; every swipe after
|
|
46
|
+
that extends dwell and signals quality, which is why each slide must *pull to the next*. Design
|
|
47
|
+
backwards from that: stop the thumb, then keep it moving.
|
|
58
48
|
|
|
59
49
|
## The cover slide
|
|
60
50
|
|
|
@@ -150,11 +140,10 @@ Good — one idea, one dominant element, breathing room
|
|
|
150
140
|
## Slide count
|
|
151
141
|
|
|
152
142
|
Aim for the **6–12 band, ~8 as the sweet spot** (cover + ~6 content + CTA). Reported peaks
|
|
153
|
-
cluster around 7–8 slides; 8–12 is where the highest dwell/save/share rates show up. Fewer
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
resolves in 7 slides, ship 7 — don't pad to hit a number.
|
|
143
|
+
cluster around 7–8 slides; 8–12 is where the highest dwell/save/share rates show up. Fewer than
|
|
144
|
+
5 underdelivers (not worth the swipe); more than ~15 loses people before the payoff. Treat the
|
|
145
|
+
band as a guide, not a law: **length follows the arc, not a quota** — if the arc resolves in 7
|
|
146
|
+
slides, ship 7, don't pad to hit a number.
|
|
158
147
|
|
|
159
148
|
## The CTA closer
|
|
160
149
|
|
|
@@ -184,17 +173,17 @@ Gotchas: open the exported PDF and verify fonts — free Canva silently substi
|
|
|
184
173
|
```
|
|
185
174
|
|
|
186
175
|
Portrait 4:5 takes the most vertical space in a mobile feed, which raises dwell and the chance
|
|
187
|
-
of a scroll-stop
|
|
188
|
-
devices
|
|
176
|
+
of a scroll-stop; PDF is the only export that preserves fonts and layout reliably across
|
|
177
|
+
devices, since slides exported as images lose crispness and break selectable text. The full
|
|
189
178
|
checklist in prose is in [`references/carousel-patterns.md`](references/carousel-patterns.md).
|
|
190
179
|
|
|
191
180
|
## The feed caption handoff
|
|
192
181
|
|
|
193
182
|
A document post still has a **text caption above it**, and its first 1–2 lines (before the
|
|
194
183
|
"…more" fold) must restate the cover hook — that line is what people read before deciding to
|
|
195
|
-
expand and swipe. Give a one-line starter that echoes the cover
|
|
196
|
-
|
|
197
|
-
|
|
184
|
+
expand and swipe. Give a one-line starter that echoes the cover; you own the slides, and
|
|
185
|
+
[`../linkedin-content/SKILL.md`](../linkedin-content/SKILL.md) owns the caption's full
|
|
186
|
+
length/tone tuning.
|
|
198
187
|
|
|
199
188
|
## Handoffs
|
|
200
189
|
|
|
@@ -202,7 +191,7 @@ the slides; that sibling owns the caption.
|
|
|
202
191
|
|---|---|
|
|
203
192
|
| The account/format plan, cadence, niche, who to target | [`../linkedin-strategy/SKILL.md`](../linkedin-strategy/SKILL.md) |
|
|
204
193
|
| A text or single-image feed post (just the copy) | [`../linkedin-content/SKILL.md`](../linkedin-content/SKILL.md) |
|
|
205
|
-
| 1:1 DMs / connection-request outreach sequences | `linkedin-outreach
|
|
194
|
+
| 1:1 DMs / connection-request outreach sequences | [`../linkedin-outreach/SKILL.md`](../linkedin-outreach/SKILL.md) |
|
|
206
195
|
| To publish/schedule the deck via the LinkedIn API | [`../linkedin-api/SKILL.md`](../linkedin-api/SKILL.md) |
|
|
207
196
|
| A spoken-to 16:9 talk deck (speaker support) | [`../presentations/SKILL.md`](../presentations/SKILL.md) |
|
|
208
197
|
| The color / type / logo brand kit the deck is styled in | [`../brand-identity/SKILL.md`](../brand-identity/SKILL.md) |
|
|
@@ -236,4 +225,4 @@ scripts/verify.sh path/to/carousel-spec.md
|
|
|
236
225
|
It checks (read-only) for a labeled cover slide, a slide count in 5–15 (warns outside 6–12), a
|
|
237
226
|
CTA slide with an explicit ask, a portrait/square canvas, a ≥28 px type floor, "PDF" as the
|
|
238
227
|
export target, and warns on any crammed (3+ bullet) interior slide. An empty or clean target
|
|
239
|
-
passes.
|
|
228
|
+
passes.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: llm-pipeline
|
|
3
|
-
description: "Use when wiring several LLM calls into
|
|
3
|
+
description: "Use when wiring several LLM calls into a production flow: typed contracts between steps, a router/gateway so 429s, timeouts and outages fail over instead of taking you down, and cost control via caching, model tiers and abort caps. NOT single-prompt wording (that is `prompt-engineering`), NOT a model-driven tool loop (that is `building-agents`)."
|
|
4
4
|
tags: [llm-orchestration, llm-gateway, fallbacks, prompt-caching, cost-control, litellm, reliability]
|
|
5
5
|
recommends: [prompt-engineering, structured-extraction, building-agents, cost-tracking, agent-eval, rag, observability, parallel]
|
|
6
6
|
origin: risco
|
|
@@ -10,14 +10,7 @@ origin: risco
|
|
|
10
10
|
|
|
11
11
|
Wire multiple LLM calls into a reliable, controllable production pipeline. You chain steps where one call's validated output feeds the next, put a router in front of providers so an outage fails over instead of taking you down, and engineer the cross-cutting concerns: timeouts, bounded retries, fallbacks, caching, and cost caps.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Treat the LLM as an unreliable network dependency, not a local function call. Everything below follows from that.
|
|
16
|
-
|
|
17
|
-
- **Every call gets a hard timeout and bounded retries.** Providers have outages, rate limits, and latency tails; a naked call can hang your whole request behind one slow upstream.
|
|
18
|
-
- **Steps pass validated structured objects, not free text.** The output of step N is a contract you assert on before it becomes step N+1's input — a parse failure is caught at the seam, not three steps later.
|
|
19
|
-
- **A router sits in front of every provider.** One model's 429/500/timeout fails over to another; no single provider is a single point of failure.
|
|
20
|
-
- **Cache and tier before you tune prompts.** The cheapest, fastest, most reliable call is the one you never made. Prompt wording is the last lever, not the first.
|
|
13
|
+
Treat the LLM as an unreliable network dependency, not a local function call. Every rule below follows from that: providers have outages, rate limits, and latency tails, so no single provider is a single point of failure and no call is allowed to run unbounded.
|
|
21
14
|
|
|
22
15
|
## Do you even need a pipeline?
|
|
23
16
|
|
|
@@ -116,6 +109,8 @@ except Exception:
|
|
|
116
109
|
|
|
117
110
|
## Caching: two distinct layers
|
|
118
111
|
|
|
112
|
+
The cheapest, fastest, most reliable call is the one you never made — so cache and tier before you tune prompts. Wording is the last lever, not the first.
|
|
113
|
+
|
|
119
114
|
| Layer | What it matches | Safety | Enable when |
|
|
120
115
|
| --- | --- | --- | --- |
|
|
121
116
|
| Prefix / prompt cache (provider-native) | Exact prefix of the prompt | Always safe (same input → same cached compute) | Always; put the stable prefix first |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: medium-publishing
|
|
3
|
-
description: "Use when
|
|
3
|
+
description: "Use when a finished article has to land on Medium correctly — profile vs publication, cross-posting under a canonical link so your own site keeps the SEO credit, import-tool failures, the 5-tag cap, pseudo-Markdown gotchas. NOT writing or headlining the piece (that is `medium-writing`), NOT cadence or earnings (that is `medium-strategy`)."
|
|
4
4
|
tags: [medium, publishing, cross-posting, canonical-url, seo, import-tool]
|
|
5
5
|
recommends: [medium-writing, medium-strategy, seo-geo, social-publisher]
|
|
6
6
|
origin: risco
|
|
@@ -10,13 +10,11 @@ origin: risco
|
|
|
10
10
|
|
|
11
11
|
Land a finished article on Medium with correct metadata: right destination, no duplicate-content penalty, valid tags, intact formatting. Writing the article is `../medium-writing/SKILL.md`; deciding when/where/how-often to publish and how it grows is `../medium-strategy/SKILL.md`. This skill is only the mechanics of getting bytes onto the platform with the right metadata attached.
|
|
12
12
|
|
|
13
|
-
## Reality check
|
|
13
|
+
## Reality check (2025-2026)
|
|
14
14
|
|
|
15
15
|
**The Medium API is closed to new integrations.** As of 2025-01-01 Medium issues no new integration tokens and accepts no new integrations; only tokens minted before that date still work. Why it matters: any plan that starts with "register a Medium API token and automate it" is dead on arrival for anyone who didn't already have one. Integration platforms confirm this — the Medium app in Make is marked legacy, and n8n's Medium credentials can no longer be newly configured.
|
|
16
16
|
|
|
17
|
-
So the default publishing path is **web editor + import tool + canonical link**, done through the UI. Treat the API as a legacy escape hatch (last section), not the plan.
|
|
18
|
-
|
|
19
|
-
Do not promise API automation. If the user asks "can I get a token and automate this?", the honest answer is almost always no — explain why and route them to the import path below.
|
|
17
|
+
So the default publishing path is **web editor + import tool + canonical link**, done through the UI. Treat the API as a legacy escape hatch (last section), not the plan — when asked "can I get a token and automate this?", the honest answer is almost always no, so say why and route to the import path below rather than promising automation.
|
|
20
18
|
|
|
21
19
|
## Decision table — pick your path
|
|
22
20
|
|
|
@@ -30,11 +28,9 @@ Branch on where the article already lives and what you control:
|
|
|
30
28
|
| You hold a pre-2025 API token and want automation | Legacy `POST /v1/users/{id}/posts` | `canonicalUrl` field in the request body |
|
|
31
29
|
| Destination is a publication you write for | Submit draft to the publication | Same as above; submission is separate from canonical |
|
|
32
30
|
|
|
33
|
-
When in doubt and the piece exists elsewhere, prefer the import tool — it gets canonical *and* backdate right in one step.
|
|
34
|
-
|
|
35
31
|
## Cross-post without an SEO penalty (the core job)
|
|
36
32
|
|
|
37
|
-
When the article already lives on your own site, you must tell search engines the
|
|
33
|
+
When the article already lives on your own site, you must tell search engines that the **origin** — your page, never the Medium URL — is canonical, or Google sees two copies and may credit Medium or neither. Two mechanisms, in order of preference:
|
|
38
34
|
|
|
39
35
|
**1. Import tool (preferred).** Pasting the original URL into Medium's import field does three things at once:
|
|
40
36
|
- imports the rendered content into a new draft,
|
|
@@ -54,8 +50,6 @@ Good: Use the import tool (canonical + backdate set automatically), or if
|
|
|
54
50
|
BEFORE publishing.
|
|
55
51
|
```
|
|
56
52
|
|
|
57
|
-
The canonical must point to the **origin** (your site), not to the Medium URL. The whole point is that your site keeps the SEO credit.
|
|
58
|
-
|
|
59
53
|
## When the import fails
|
|
60
54
|
|
|
61
55
|
The importer is an HTTP crawler hitting your URL; failures are crawl failures, not Medium bugs. Map the symptom to the cause:
|
|
@@ -98,9 +92,7 @@ Good: Convert the table to a labeled list, or screenshot/export it as an
|
|
|
98
92
|
Two outcomes, often confused:
|
|
99
93
|
|
|
100
94
|
- **Self-publish** puts the story on your own profile immediately.
|
|
101
|
-
- **Submit to a publication** routes the draft to that publication's editors
|
|
102
|
-
|
|
103
|
-
Why it sits pending: a publication is editorially controlled; submission is a request, not a publish action. If you're not yet a writer there, request access first; submitting otherwise goes nowhere.
|
|
95
|
+
- **Submit to a publication** routes the draft to that publication's editors, where it stays **pending** — not live — until one of them publishes it. A publication is editorially controlled, so submission is a request, not a publish action, and it only works if you are already an accepted writer there. If you're not, request access first; submitting otherwise goes nowhere.
|
|
104
96
|
|
|
105
97
|
## Legacy API — only if you hold a pre-2025 token
|
|
106
98
|
|
package/skills/modal/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: modal
|
|
3
|
-
description: "Use when running Python
|
|
3
|
+
description: "Use when running Python or GPU workloads serverlessly on Modal — modal.App, inline container Images, gpu= on @app.function, Volumes for weight caching, Cron schedules, ASGI endpoints, modal run vs serve vs deploy. NOT managed prediction APIs with no container of your own (that is replicate); NOT SSH-able GPU boxes rented by the hour (that is runpod)."
|
|
4
4
|
tags: [modal, serverless, gpu, python, ai-infra, deployment, cron, web-endpoint]
|
|
5
5
|
recommends: [replicate, runpod, fastapi, docker, python, llm-pipeline]
|
|
6
6
|
origin: risco
|
|
@@ -19,27 +19,19 @@ Pinned stack: **modal 1.4.3** (released 2026-05-18), Python **3.10–3.14** (`>=
|
|
|
19
19
|
Install with `pip install modal` then `modal setup` to authenticate. Everything below uses
|
|
20
20
|
the Modal 1.0+ API; several pre-1.0 forms were removed and are called out as Bad→Good.
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## Not this skill
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- Building a container image inline (`Image.debian_slim().uv_pip_install(...)`) for a remote job.
|
|
27
|
-
- Persisting model weights or datasets across runs with a Modal `Volume`.
|
|
28
|
-
- Scheduled jobs (nightly report, periodic scrape) via `modal.Cron` / `modal.Period`.
|
|
29
|
-
- Exposing a function over HTTP (`fastapi_endpoint`, `asgi_app`, `wsgi_app`, `web_server`).
|
|
30
|
-
- Fan-out parallelism with `.map()` / `.starmap()` / `.spawn()`.
|
|
24
|
+
Modal owns the serverless-container-as-decorators surface and its CLI lifecycle; the *contents*
|
|
25
|
+
of your function belong elsewhere.
|
|
31
26
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
Modal owns the serverless-container-as-decorators surface and its CLI lifecycle; the
|
|
42
|
-
*contents* of your function (FastAPI app, RAG chain) belong to the siblings above.
|
|
27
|
+
| The job | Goes to |
|
|
28
|
+
|---|---|
|
|
29
|
+
| Calling a **managed prediction API** with no container of your own | [`replicate`](../replicate/SKILL.md) / [`together-fireworks`](../together-fireworks/SKILL.md) / [`fal`](../fal/SKILL.md) |
|
|
30
|
+
| Renting a **persistent, SSH-able GPU box** by the hour/week | [`runpod`](../runpod/SKILL.md) |
|
|
31
|
+
| **FastAPI design** (routing, Pydantic, deps) independent of host | [`fastapi`](../fastapi/SKILL.md) |
|
|
32
|
+
| Writing a **Dockerfile** for a registry / k8s / Compose | [`docker`](../docker/SKILL.md) |
|
|
33
|
+
| General **Python language/runtime** questions | [`python`](../python/SKILL.md) |
|
|
34
|
+
| **RAG / LLM pipeline** orchestration logic itself | [`llm-pipeline`](../llm-pipeline/SKILL.md) |
|
|
43
35
|
|
|
44
36
|
## Decision: which entrypoint?
|
|
45
37
|
|
|
@@ -109,8 +101,8 @@ image = (
|
|
|
109
101
|
)
|
|
110
102
|
```
|
|
111
103
|
|
|
112
|
-
|
|
113
|
-
download-once weight-cache pattern.
|
|
104
|
+
→ [`references/images-gpu-cookbook.md`](references/images-gpu-cookbook.md) for vLLM / torch+CUDA
|
|
105
|
+
/ diffusers recipes and the download-once weight-cache pattern.
|
|
114
106
|
|
|
115
107
|
## GPU — it's a string now
|
|
116
108
|
|
|
@@ -193,8 +185,7 @@ def pull():
|
|
|
193
185
|
```
|
|
194
186
|
|
|
195
187
|
Never bake a token into the image (`.run_commands("export TOKEN=...")`) — it's recorded in
|
|
196
|
-
layer history. Use a Secret.
|
|
197
|
-
HF/OpenAI secret patterns.
|
|
188
|
+
layer history. Use a Secret. The cookbook above also carries the HF/OpenAI secret patterns.
|
|
198
189
|
|
|
199
190
|
## Web endpoints
|
|
200
191
|
|
|
@@ -228,8 +219,8 @@ def web():
|
|
|
228
219
|
|
|
229
220
|
Develop with `modal serve app.py` (hot-reload); ship with `modal deploy app.py` (stable URL).
|
|
230
221
|
For custom domains, proxy-auth tokens, batching (`@modal.batched`), and concurrency tuning →
|
|
231
|
-
`references/web-and-scaling.md
|
|
232
|
-
that's
|
|
222
|
+
[`references/web-and-scaling.md`](references/web-and-scaling.md). For the FastAPI app's *own*
|
|
223
|
+
design (routes, Pydantic, deps), that's [`fastapi`](../fastapi/SKILL.md) — this skill only mounts it.
|
|
233
224
|
|
|
234
225
|
## Scheduled jobs
|
|
235
226
|
|
|
@@ -268,9 +259,9 @@ def main():
|
|
|
268
259
|
they complete (faster when latencies vary). Combine with `retries=` on the function so a
|
|
269
260
|
single bad input doesn't sink the batch.
|
|
270
261
|
|
|
271
|
-
## Anti-patterns
|
|
262
|
+
## Anti-patterns
|
|
272
263
|
|
|
273
|
-
|
|
|
264
|
+
| Anti-pattern | Do instead |
|
|
274
265
|
|---|---|
|
|
275
266
|
| "I'll use `gpu=modal.gpu.A100()` like the old docs" | Removed in 1.0. Use the string `gpu="A100-80GB"`. |
|
|
276
267
|
| "Attach a GPU, it might speed up this CPU job" | GPU is billed per second alive. CPU-only job → no `gpu=`. |
|
|
@@ -279,30 +270,21 @@ single bad input doesn't sink the batch.
|
|
|
279
270
|
| "`modal run` it, the endpoint/schedule will stay up" | `run` is ephemeral; it exits. Use `modal deploy` for anything persistent. |
|
|
280
271
|
| "Order the decorators however — Modal figures it out" | `@app.function` outermost, web decorator innermost. Wrong order errors. |
|
|
281
272
|
| "Bake the HF token into the image with `run_commands`" | Leaks into layer history. Use `modal.Secret.from_name(...)`. |
|
|
282
|
-
| "Just call the model via a managed API through Modal" | If you write no container, that's a managed-API job → `replicate
|
|
283
|
-
| "I need a box to SSH into for a week" | That's a persistent rental → `runpod
|
|
273
|
+
| "Just call the model via a managed API through Modal" | If you write no container, that's a managed-API job → [`replicate`](../replicate/SKILL.md). |
|
|
274
|
+
| "I need a box to SSH into for a week" | That's a persistent rental → [`runpod`](../runpod/SKILL.md), not Modal's scale-to-zero. |
|
|
284
275
|
| "Set `min_containers` high so it's always fast" | Idle warm containers cost money 24/7. Tune `scaledown_window` first. |
|
|
285
276
|
|
|
286
|
-
##
|
|
277
|
+
## Verify
|
|
287
278
|
|
|
288
|
-
`scripts/verify.sh [TARGET]` statically lints the nearest emitted Modal
|
|
289
|
-
`modal.App(`, **fails** if the removed `modal.gpu.` object form appears,
|
|
290
|
-
decorator sits under an `@app.function`, and that any `Volume` uses
|
|
279
|
+
[`scripts/verify.sh`](scripts/verify.sh) `[TARGET]` statically lints the nearest emitted Modal
|
|
280
|
+
`*.py`: it requires a `modal.App(`, **fails** if the removed `modal.gpu.` object form appears,
|
|
281
|
+
checks that any web decorator sits under an `@app.function`, and that any `Volume` uses
|
|
291
282
|
`from_name(..., create_if_missing=...)`. It runs `python -c "import modal"` only if modal is
|
|
292
|
-
installed (skip-pass otherwise)
|
|
293
|
-
exits 0.
|
|
294
|
-
|
|
295
|
-
## Project grounding (02-DOCS + CLAUDE.md)
|
|
296
|
-
|
|
297
|
-
In a project with a `02-DOCS/` layer (the [`harness`](../harness/SKILL.md) wiki), record this
|
|
298
|
-
app's real Modal choices — GPU types, image base, Volume names, schedule, endpoint shape — in
|
|
299
|
-
`02-DOCS/wiki/stack/modal.md` and index it in `02-DOCS/wiki/index.md` (the Knowledge map; root
|
|
300
|
-
`CLAUDE.md` keeps only a short pointer to it). Read it
|
|
301
|
-
first on every use; create/update it with the real decisions. No `02-DOCS/`? Skip silently.
|
|
283
|
+
installed (skip-pass otherwise), needs **no Modal credentials**, and exits 0 on an empty target.
|
|
302
284
|
|
|
303
|
-
##
|
|
285
|
+
## Project grounding (02-DOCS)
|
|
304
286
|
|
|
305
|
-
- [`
|
|
306
|
-
-
|
|
307
|
-
|
|
308
|
-
|
|
287
|
+
In a project with a `02-DOCS/` layer (the [`harness`](../harness/SKILL.md) wiki), read
|
|
288
|
+
`02-DOCS/wiki/stack/modal.md` first, then record this app's real Modal choices there — GPU types,
|
|
289
|
+
image base, Volume names, schedule, endpoint shape — and index it in `02-DOCS/wiki/index.md`. No
|
|
290
|
+
`02-DOCS/`? Skip silently.
|
package/skills/n8n/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: n8n
|
|
3
|
-
description: "Use when operating a live n8n instance programmatically —
|
|
3
|
+
description: "Use when operating a live n8n instance programmatically — its REST API (`{host}/api/v1`, `X-N8N-API-KEY`) or the n8n-mcp server — to create, validate, test, activate, update, delete workflows and read executions on self-host or n8n.cloud. NOT designing the flow or its importable JSON (that is `automation-flows`), NOT another platform (`make`, `zapier`)."
|
|
4
4
|
tags: [n8n, automation, workflows, mcp, rest-api, no-code, integrations]
|
|
5
5
|
recommends: [automation-flows, automation-strategy, api-connector-builder, webhooks]
|
|
6
6
|
profiles: [full]
|
|
@@ -9,15 +9,11 @@ origin: risco
|
|
|
9
9
|
|
|
10
10
|
# n8n — operate a live instance over its REST API and MCP
|
|
11
11
|
|
|
12
|
-
You already have a workflow *design*
|
|
13
|
-
This skill is the **operate** half: push
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
server, both hitting the same instance.
|
|
17
|
-
|
|
18
|
-
Designing the flow itself (what fires it, how errors are handled, dedup) is **automation-flows** —
|
|
19
|
-
this skill *consumes* that skill's `references/n8n-workflow-json.md` payload; it does not re-teach
|
|
20
|
-
design.
|
|
12
|
+
You already have a workflow *design* — the trigger→steps→error shape and the importable JSON, which is
|
|
13
|
+
`automation-flows`' job and this skill does not re-teach it. This skill is the **operate** half: push
|
|
14
|
+
that JSON into a running n8n, validate it, test it, flip it live, watch its executions, and tear it
|
|
15
|
+
down safely — all without touching the UI. n8n has the richest programmatic story of the no-code
|
|
16
|
+
platforms: a first-party REST API **plus** a mature MCP server, both hitting the same instance.
|
|
21
17
|
|
|
22
18
|
## Connect first — REST API vs MCP
|
|
23
19
|
|
|
@@ -31,10 +27,10 @@ Two ways to drive the same instance. Pick by who is at the wheel.
|
|
|
31
27
|
| Surface | identical on self-host **and** `*.app.n8n.cloud` | same underlying API; hosted trial at `dashboard.n8n-mcp.com` |
|
|
32
28
|
|
|
33
29
|
**Version reality (verify at author time — n8n moves fast):**
|
|
34
|
-
- API base is `{host}/api/v1`. Self-host ships interactive Swagger at `/api/v1/docs`; **cloud does not
|
|
30
|
+
- API base is `{host}/api/v1`. Self-host ships interactive Swagger at `/api/v1/docs`; **cloud does not**
|
|
31
|
+
— on cloud, work from the docs.
|
|
35
32
|
- `POST /workflows/{id}/execute` (run-by-id) is **recent** (n8n PR #20234). Older instances lack it —
|
|
36
|
-
there, the only programmatic run is POSTing to a Webhook node's URL.
|
|
37
|
-
- The API surface is the SAME whether you self-host or run on `*.app.n8n.cloud`; only the host differs.
|
|
33
|
+
there, the only programmatic run is POSTing to a Webhook node's URL.
|
|
38
34
|
|
|
39
35
|
### `.env`
|
|
40
36
|
|
|
@@ -66,7 +62,8 @@ shapes rather than guessing — that is the single biggest reason to drive via M
|
|
|
66
62
|
## Dynamic lifecycle
|
|
67
63
|
|
|
68
64
|
create → **validate** → test → **activate** → manage → delete. A concrete sketch per step, curl and
|
|
69
|
-
n8n-mcp side by side
|
|
65
|
+
n8n-mcp side by side; every endpoint with pagination and scopes is in
|
|
66
|
+
`references/rest-api-cheatsheet.md`.
|
|
70
67
|
|
|
71
68
|
### 1. Validate (do this BEFORE create)
|
|
72
69
|
|
|
@@ -107,8 +104,9 @@ n8n_test_workflow { "id": "$ID" }
|
|
|
107
104
|
|
|
108
105
|
### 4. Activate — the step people miss
|
|
109
106
|
|
|
110
|
-
`active` in the create/update body
|
|
111
|
-
does nothing. You MUST call
|
|
107
|
+
`active` (and `tags`) in the create/update body are **read-only and silently ignored**. Setting
|
|
108
|
+
`"active": true` does nothing, which is why the workflow you just created never fires. You MUST call
|
|
109
|
+
the separate endpoint:
|
|
112
110
|
|
|
113
111
|
```bash
|
|
114
112
|
curl -sS -X POST "$N8N_API_URL/api/v1/workflows/$ID/activate" -H "X-N8N-API-KEY: $N8N_API_KEY"
|
|
@@ -137,7 +135,8 @@ omitted. n8n-mcp's `n8n_update_partial_workflow` applies a targeted, atomic batc
|
|
|
137
135
|
|
|
138
136
|
### 6. Delete — irreversible
|
|
139
137
|
|
|
140
|
-
`DELETE` is a **hard delete: no trash, no undo
|
|
138
|
+
`DELETE` is a **hard delete: no trash, no undo**, so export the current JSON first, every time — this
|
|
139
|
+
one is absolute because nothing downstream can recover the workflow if you skip it.
|
|
141
140
|
|
|
142
141
|
```bash
|
|
143
142
|
curl -sS "$N8N_API_URL/api/v1/workflows/$ID" -H "X-N8N-API-KEY: $N8N_API_KEY" > backup-$ID.json # export FIRST
|
|
@@ -147,7 +146,7 @@ curl -sS -X DELETE "$N8N_API_URL/api/v1/workflows/$ID" -H "X-N8N-API-KEY: $N8N_A
|
|
|
147
146
|
n8n_get_workflow { "id": "$ID" } → save output → n8n_delete_workflow { "id": "$ID" }
|
|
148
147
|
```
|
|
149
148
|
|
|
150
|
-
|
|
149
|
+
The same applies to a full replace: export before `PUT`.
|
|
151
150
|
|
|
152
151
|
## Governance
|
|
153
152
|
|
|
@@ -156,50 +155,23 @@ If you expose n8n-mcp to an agent, lock down destructive tools with the `DISABLE
|
|
|
156
155
|
key itself too — n8n supports scoped keys (e.g. grant `workflow:read` without `workflow:execute`).
|
|
157
156
|
Details in `references/n8n-mcp-tools.md`.
|
|
158
157
|
|
|
159
|
-
##
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
vs operate boundary: they decide the trigger/steps/error path; you push it live. Start there if the
|
|
180
|
-
flow's logic isn't settled yet.
|
|
181
|
-
- **automation-strategy** — whether n8n is even the right platform (cost model, self-host vs cloud) vs
|
|
182
|
-
Make/Zapier/Power Automate.
|
|
183
|
-
- **make / zapier / power-automate** — the other platforms' operate stories (Zapier and Power Automate
|
|
184
|
-
can't create flows via API — n8n's edge).
|
|
185
|
-
- **api-connector-builder** — a generic typed HTTP client with auth/pagination/backoff, when the answer
|
|
186
|
-
is real code, not a workflow.
|
|
187
|
-
- **webhooks** — building the inbound receiver *in your own app*; here webhooks are just n8n triggers.
|
|
188
|
-
|
|
189
|
-
## Checklist
|
|
190
|
-
|
|
191
|
-
- [ ] `.env` has `N8N_API_URL`/`N8N_HOST` + `N8N_API_KEY` (key from Settings → n8n API); key not logged.
|
|
192
|
-
- [ ] Payload has `name`, `nodes` (one trigger), `connections`, `settings` per automation-flows' schema;
|
|
193
|
-
node credentials referenced by store, never inlined.
|
|
194
|
-
- [ ] Validated (`n8n_validate_workflow` / `n8n_autofix_workflow`) before create when MCP is available.
|
|
195
|
-
- [ ] Created via `POST /workflows` or `n8n_create_workflow`; captured the returned `id`.
|
|
196
|
-
- [ ] Tested — `execute`-by-id (no input) or POST to the webhook URL (with input) — and the run succeeded.
|
|
197
|
-
- [ ] Activated via `POST /workflows/{id}/activate` (NOT `active:true` in the body).
|
|
198
|
-
- [ ] Before any DELETE or full-replace: exported the current JSON (hard delete, no trash).
|
|
199
|
-
- [ ] If MCP is exposed to an agent: `DISABLED_TOOLS` set and/or the API key scoped.
|
|
200
|
-
|
|
201
|
-
## References
|
|
202
|
-
|
|
203
|
-
- `references/rest-api-cheatsheet.md` — every workflow/execution endpoint with curl, pagination, scopes.
|
|
204
|
-
- `references/n8n-mcp-tools.md` — the management + node-knowledge tool list, the auth split, `DISABLED_TOOLS`.
|
|
205
|
-
- `references/auth-and-selfhost.md` — API-key creation, self-host vs cloud, Swagger, installing/pointing n8n-mcp.
|
|
158
|
+
## Anti-patterns
|
|
159
|
+
|
|
160
|
+
| Anti-pattern | Why it bites | Do instead |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| `"active": true` (or `tags`) in the create/update body | Read-only fields, silently ignored — the #1 "why is my workflow inactive" bug | `POST /workflows/{id}/activate`; set tags via their own endpoint |
|
|
163
|
+
| Passing runtime input to `/workflows/{id}/execute` | It accepts no custom input, and older instances 404 the endpoint entirely | Trigger a Webhook workflow by POSTing to its URL |
|
|
164
|
+
| `PUT` with a partial body, or `DELETE` on a live workflow | Full replace drops every node you omitted; delete is a hard delete with no trash | GET and export first; prefer `n8n_update_partial_workflow` for edits |
|
|
165
|
+
| A `typeVersion` the target instance doesn't have | Create succeeds; it fails only at activate/execute time | Validate via MCP, or test, before activating |
|
|
166
|
+
| Inlining node credential secrets, or expecting to read them back | Credentials are not fully manageable over the public API — it can create some, but never returns a secret | Provision in the UI or via `n8n_manage_credentials`, then reference existing credentials by id/name |
|
|
167
|
+
| Treating a successful API activation as proof it works | The public API historically skips a few UI-side activation validations, so a workflow can be "active" yet non-functional | Run it live after activating and check the execution |
|
|
168
|
+
|
|
169
|
+
## Route elsewhere
|
|
170
|
+
|
|
171
|
+
| If the ask is | Skill |
|
|
172
|
+
|---|---|
|
|
173
|
+
| Designing the flow (trigger/steps/error path, dedup) and producing the importable JSON | `automation-flows` — start there if the logic isn't settled |
|
|
174
|
+
| Whether n8n is the right platform at all — cost model, self-host vs cloud | `automation-strategy` |
|
|
175
|
+
| Operating a different platform | `make`, `zapier`, `power-automate` (neither Zapier nor Power Automate can create flows via API — n8n's edge) |
|
|
176
|
+
| A generic typed HTTP client with auth/pagination/backoff — real code, not a workflow | `api-connector-builder` |
|
|
177
|
+
| The inbound webhook receiver in your own app (here webhooks are only n8n triggers) | `webhooks` |
|