Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Sets up Alchemy (alchemy-run/alchemy, Infrastructure-as-TypeScript) in any codebase — new project scaffold OR add to existing app. Wires Cloudflare/AWS providers, state backend, secrets, and binding types end-to-end with strict secret hygiene. USE THIS SKILL whenever the user mentions "alchemy", "alchemy.run", "Infrastructure as TypeScript", or asks to deploy a Worker/Lambda/D1/R2/KV/Queue/DO via TS, add a state backend, configure ALCHEMY_PASSWORD, generate alchemy.run.ts, replace SST/Pulumi/CDK
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-08 | ✗→✓ | ▲ Improved | 40% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 55% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 119% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 75% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 42% | 0% |
You are configuring Alchemy — an ESM-only, TypeScript-native IaC library where resources are async functions you await. State lives in JSON files (default), Cloudflare DO, S3, or SQLite. Secrets are encrypted with ALCHEMY_PASSWORD.
This skill is interactive. Do not guess the user's situation. Run the Intake below first, then branch.
Ask the user these questions in a single batched prompt (use the AskUserQuestion tool if available; otherwise plain text with numbered options). Do not skip — the answers change every subsequent decision.
alchemy login), CF API token in env, AWS profile, or "I'll do it later".$USER), per-branch PR previews (pr-<n>), explicit prod/staging/dev.Follow up only if answers are contradictory or block progress. Don't over-interrogate.
Before touching anything, run:
bashls package.json tsconfig.json alchemy.run.ts wrangler.toml wrangler.jsonc sst.config.ts pulumi.yaml cdk.json 2>/dev/null
If alchemy.run.ts already exists, read it first and treat the task as a modification, not a fresh install. If wrangler.toml/sst.config.ts/pulumi.yaml exist, the user is migrating — confirm before deleting any of those.
Based on intake answer for runtime + start point:
New project from template (preferred when starting fresh):
bashbunx alchemy create <name> --template=<typescript|vite|nextjs|sveltekit|nuxt|astro|tanstack-start|react-router|redwood|bun-spa>
Add to existing project:
bash# pick the package manager that matches the repo bun add alchemy # or: pnpm add / npm install / yarn add bunx alchemy init # optional: --framework <name> --yes
Verify install:
bashnode -e "console.log(require('alchemy/package.json').version)"
Alchemy is ESM-only. If the repo is CommonJS, set "type": "module" or move IaC code to .mts / .ts files compiled with ESM target.
alchemy.run.tsAlways place at repo root. Skeleton (cloud-agnostic core, add provider blocks per intake):
tsimport alchemy from "alchemy"; // import per cloud — examples: // import { Worker, KVNamespace, R2Bucket, D1Database } from "alchemy/cloudflare"; // import { Function, Table, Bucket } from "alchemy/aws"; const app = await alchemy("APP_NAME_HERE", { phase: process.argv.includes("--destroy") ? "destroy" : "up", // stage: process.env.STAGE, // uncomment if multi-stage // password: process.env.ALCHEMY_PASSWORD, // already default, set for clarity }); // === resources go here === // export const worker = await Worker("api", { entrypoint: "./src/worker.ts" }); await app.finalize();
Required rules:
async function, not arrow, for any custom Resource.await app.finalize() must be the last statement in the up path. Without it, orphans are not GC'd.alchemy.secret(process.env.X). Never inline a literal token.See references/cloudflare.md and references/aws.md for full resource shapes.
Follow references/security.md in full. Quick checklist:
scripts/gen_password.sh (32+ bytes, base64). Write to .env. Never commit.openssl rand -base64 32..gitignore — must contain: .env .env.* !.env.example .alchemy/ node_modules/ .wrangler/ Run scripts/gitignore_check.sh to verify.
.env.example — list every variable name with empty value. Commit this file; never commit .env.alchemy.run.ts literally. Always pull from process.env and wrap with alchemy.secret().git status shows .env, .alchemy/, or any file with *.pem, *.key, credentials.json. Surface and stop.Default filesystem is fine for solo dev. For team/CI use Cloudflare or S3.
ts// Cloudflare DO-backed (recommended for shared state) import { CloudflareStateStore } from "alchemy/state"; const app = await alchemy("my-app", { stateStore: (scope) => new CloudflareStateStore(scope, { stateToken: alchemy.secret(process.env.ALCHEMY_STATE_TOKEN), }), }); // S3 import { S3StateStore } from "alchemy/aws"; new S3StateStore(scope, { bucketName: "my-app-alchemy-state", region: "us-east-1" }); // SQLite local import { SQLiteStateStore } from "alchemy/state"; new SQLiteStateStore(scope, { filename: ".alchemy/state.sqlite" });
If the user picks CloudflareStateStore, the alchemy-state-service Worker auto-deploys on first run.
Branch on intake:
bun alchemy configure && bun alchemy login. Tokens land in ~/.alchemy/credentials/<profile>/cloudflare.json. Tell user to keep that dir private.CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID to .env. Mint a token with minimal scopes (Workers Scripts:Edit, KV/R2/D1/Queue:Edit as needed, Account:Read). Show the scoped permissions list, don't reuse Global API Key unless they insist.AWS_PROFILE / AWS_ACCESS_KEY_ID / SSO. Never write long-lived AWS keys into the repo.See references/frameworks.md. One block per framework. Key invariants:
alchemy() plugin from alchemy/cloudflare/vite to vite.config.ts.@opennextjs/cloudflare, add open-next.config.ts, use Nextjs(...) resource.svelte.config.js adapter alchemy/cloudflare/sveltekit.astro.config.mjs adapter alchemy/cloudflare/astro with output: "server".Add these (preserve existing scripts):
json{ "scripts": { "deploy": "alchemy deploy", "destroy": "alchemy destroy", "dev": "alchemy dev", "run": "alchemy run" } }
If dev/deploy are already taken, prefix with alchemy: instead of overwriting.
bun tsc --noEmit (or tsc --noEmit) — must pass.bun alchemy run --stage local — should print outputs without mutating cloud.bun alchemy dev — Miniflare boots, hot reload works.bun alchemy deploy --stage <name>.If type-check fails on missing binding types, add to types/env.d.ts:
tsimport type { worker } from "../alchemy.run.ts"; declare module "cloudflare:workers" { namespace Cloudflare { export interface Env extends typeof worker.Env {} } }
See references/cicd.md for the canonical GitHub Actions setup with per-PR stages, secret injection, and a destroy job on PR close. Always include the safety check refusing to destroy prod.
await app.finalize() → orphans accumulate.phase: "destroy" halts the script — code after await alchemy(...) never runs.url: true Worker preview URLs are incompatible with Durable Objects in prod — use routes/domains.ALCHEMY_PASSWORD after secrets are stored corrupts decryption. Rotation procedure is not officially documented; treat the password as permanent..alchemy/ works only for single-developer projects. For teams, switch state backend before the first deploy.async function, not arrow.For custom Resource authoring, see references/custom-resources.md. For troubleshooting failed deploys, see references/troubleshooting.md.
Other measured skills in the registry, with their headline benchmark lift.