agentsclimarketplace

Jz tanstack ship

Skill jinzhedev/jz-tanstack-ship

Assemble and verify a TanStack Start SaaS from a PRD.

Install
npx -y skills add jinzhedev/jz-tanstack-ship

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 24 days oldThe repository was created 24 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Build a complete TanStack Start SaaS application from a product requirements document. 根据 PRD 组装并验证 TanStack Start SaaS;适用于“帮我搭一个 SaaS”、 “用 TanStack 建个站”、“做个带支付的网站”。Guides capability selection (Google OAuth / magic-link login, Stripe subscriptions and one-time purchases, credit ledger), assembles verified reference implementations from assets/, configures services via CLI (Stripe, Drizzle), and generates product pages from DESIGN.md. Use when the user wants to scaffold, bootstrap, or ship a new SaaS site on TanStack Start.

SKILL.md

10.9 KB, as published. Nobody here has run it

jz-tanstack-ship

Assemble a production-ready TanStack Start SaaS app. Money-handling and auth code is copied from assets/ (verified, tested), never generated from scratch. Product pages and UI are generated per project. Follow the phases in order.

Phase 0 — Read inputs

  1. Read the user's PRD and DESIGN.md if provided. If neither exists, ask for a one-paragraph product description before proceeding.
  2. Read assets/manifest.json — it is the source of truth for capabilities, file destinations, dependencies, tables, and env vars.

Phase 1 — Capability selection

Derive defaults from the PRD, then confirm with the user (AskUserQuestion or equivalent — one question per dimension, always offer the default):

DimensionOptionsDefault
Databasesqlite (libsql) / postgressqlite
Deploy profilecloudflare / vercel / node-vpsnode-vps
Loginauth-google, auth-magic-link (multi-select)both
Email providerresend / mailgunresend; only ask when magic-link is selected
Pricing modelsubscriptions, onetime (multi-select)from PRD
Creditscredits on/offon if PRD mentions usage metering
AI generationai on/off + providers: replicate / fal / gemini (multi-select)from PRD

Enforce constraints from the manifest (e.g. credits requires a payment capability). Record the selection as SELECTION.md in the project root — future skill runs read it to stay consistent.

Use this machine-readable shape (Markdown headings and comments are allowed):

database: postgres
deploy: cloudflare
login: auth-google, auth-magic-link
email: resend
pricing: subscriptions, onetime
credits: on
ai: replicate, gemini

Phase 2 — Scaffold

pnpm dlx gitpick TanStack/router/tree/main/examples/react/start-basic-react-query <app-dir>
cd <app-dir>

Then remove the example's demo content:

  • delete src/routes/{posts*,users*,deferred.tsx,redirect.tsx,_pathlessLayout*}, src/routes/api/users*, src/utils/{posts,users}.tsx, src/routeTree.gen.ts
  • edit src/routes/__root.tsx: replace demo nav links (Posts/Users/etc.) with links to the routes you will install (/pricing, /account) — stale links break tsc because route types are generated.

Phase 3 — Assemble from assets

Run the deterministic assembler from the directory containing this SKILL.md:

node "$SKILL_DIR/scripts/assemble.mjs" \
  --target "$APP_DIR" \
  --selection "$APP_DIR/SELECTION.md"
cd "$APP_DIR"
pnpm install

Resolve SKILL_DIR and APP_DIR to absolute paths before running the command. Do not manually copy assets or strip markers. If rerunning after generated files were intentionally edited, review the diff first and pass --force only when replacing those edits is intended.

The assembler performs these steps:

  1. Copy each files[] entry src → dest. Respect when conditions (db:sqlite / db:postgres / capability names).
  2. Strip marker blocks for unselected capabilities: remove everything between // [cap:X] and // [/cap:X] (and {/* [cap:X] */} in JSX, and # [cap:X] in env files) when X is not selected; when X is selected, remove only the marker lines. Same for [db:x] blocks — exactly one dialect survives, uncommented. Apply the same rule to [email:resend] / [email:mailgun] blocks — exactly one provider survives when magic-link is selected. Blocks nest; process inner blocks first.
  3. Merge deps / devDeps / dialectDeps[dialect] / dialectDevDeps[dialect] into package.json (scripts: add test: vitest run, db:push: drizzle-kit push, db:generate: drizzle-kit generate, db:migrate: drizzle-kit migrate).
  4. Generate .env from the assembled .env.example; create a cryptographically random BETTER_AUTH_SECRET. Never overwrite an existing .env.

It also writes .jz-tanstack-ship-files.json so a later selection change removes obsolete generated files without deleting files outside the manifest. See references/assembler.md for CLI and overwrite rules.

Files marked "ui": true are the replaceable presentation layer: if the project has a DESIGN.md, regenerate them to match it, keeping every authClient.* / fetch('/api/...') call and route path identical to the reference implementation.

Phase 4 — Service configuration (CLI-first)

Do what can be done via CLI; print precise manual steps for the rest.

Databasepnpm db:push (dev). For production hand the user db:generate + db:migrate and ask them to review SQL before applying. For Cloudflare/Vercel PostgreSQL, use Neon with @neondatabase/serverless WebSockets because the credit ledger uses interactive transactions. Node/VPS uses Postgres.js. Follow references/postgres-cloudflare-neon.md; use a dedicated database or branch in TEST_DATABASE_URL. Never run tests against production.

Stripe (if payments selected) — see references/stripe-setup.md. Summary: check stripe --version and login state; create products/prices for subscription plans with stripe products create / stripe prices create; write returned price IDs into .env (STRIPE_PRICE_*); for local dev run stripe listen --forward-to localhost:3000/api/auth/stripe/webhook and copy the whsec_ into .env. Production webhook endpoint: {origin}/api/auth/stripe/webhook, events per stripeWebhookEvents in the manifest.

Google OAuth (if selected) — use scripts/setup-google-oauth.sh to prepare the Google Cloud project and enable required APIs, then follow references/google-oauth-setup.md for the short manual section in Google Auth Platform. Derive origins and redirect URIs from the actual local and production URLs; do not guess them. Never print or commit the client secret.

Email (if magic-link selected) — dev works with no credentials (links print to the server console). For production, configure the selected provider:

  • Resend: verified sender in EMAIL_FROM and API key in RESEND_API_KEY.
  • Mailgun: verified sender in EMAIL_FROM, sending domain in MAILGUN_DOMAIN, and private API key in MAILGUN_API_KEY. Mailgun uses the US API region.

Phase 5 — Product pages

Only now generate the product-specific pages (landing, feature pages, app pages) from the PRD/DESIGN.md. Rules:

  • Server logic goes through the assembled modules (~/server/credits/ledger for spending credits, ~/catalog for prices) — never inline new money/credit logic in routes.
  • To charge for a feature, call spendCredits() in the feature's server handler and surface InsufficientCreditsError as HTTP 402.
  • Update src/catalog.ts with the real products from the PRD (and recreate Stripe prices to match).

Phase 6 — Verify (mandatory, in this order)

Run the selection-aware validator from the skill directory:

node "$SKILL_DIR/scripts/validate.mjs" \
  --target "$APP_DIR" \
  --selection "$APP_DIR/SELECTION.md"

The validator checks generated-file integrity and profile requirements, then:

  1. Cloudflare only: generate Worker binding types with wrangler types.
  2. Build first so TanStack generates routeTree.gen.ts.
  3. Run tsc --noEmit.
  4. Run database-backed Vitest tests. PostgreSQL requires TEST_DATABASE_URL.

Use --profile-build before deployment to add wrangler deploy --dry-run for Cloudflare or docker build for Node/VPS. Vercel's local build is already the Nitro build above; the Vercel deployment itself requires a linked project.

Then a runtime smoke (dev server on a spare port):

  1. GET / and GET /sign-in → 200
  2. GET /api/credits unauthenticated → 401
  3. Magic-link flow (if selected): POST /api/auth/sign-in/magic-link, extract the verify URL from the server console, GET it, confirm the session cookie then GET /api/credits{"balance":0}
  4. If Stripe is configured with real test keys: stripe trigger checkout.session.completed and confirm no error in the server log.

For the common unauthenticated checks, pass the running URL to the validator:

node "$SKILL_DIR/scripts/validate.mjs" \
  --target "$APP_DIR" \
  --selection "$APP_DIR/SELECTION.md" \
  --structure-only \
  --runtime-url http://localhost:3000

If any step fails, fix and re-run the whole phase. Do not hand off a project whose verification did not pass.

Known version pins & pitfalls

  • better-auth and @better-auth/stripe must be the same version; better-auth ≥1.6 requires drizzle-orm@^0.45.2.
  • Stripe SDK v22 / API 2025+: invoice.subscription moved to invoice.parent.subscription_details.subscription (already handled in renewal-credits.ts — don't "fix" it back).
  • The better-auth Stripe plugin handles subscriptions only; one-time payments intentionally go through assets/billing/server/checkout.ts + the plugin's onEvent. Do not try authClient.subscription.upgrade for one-time SKUs.
  • Schema files for the two dialects must stay column-identical; when adding a table, edit both (or delete the unused one and note it in SELECTION.md).
  • AI: models.ts is the server-authoritative model catalog (alias → provider, model id, credit cost) — clients may only send aliases. Every create request returns status=queued without calling a provider. The client polls GET /api/ai/tasks/$id; the first poll submits work and later polls advance it. Provider-native synchronous work such as Gemini therefore runs on the first poll, not the create request. fal and replicate adapters still need one live smoke with real keys before launch (references/ai-setup.md).
  • better-auth owns user/session/account/verification/subscription table shapes; after a better-auth major upgrade run pnpm dlx @better-auth/cli generate and diff against src/db/schema.*.ts.

References

  • references/architecture.md — design rationale and data model
  • references/stripe-setup.md — exact Stripe CLI commands
  • references/google-oauth-setup.md — automated project setup and manual client checklist
  • references/postgres-cloudflare-neon.md — Neon PostgreSQL on Cloudflare Workers, including tests
  • references/assembler.md — deterministic assembly CLI, selection format, and overwrite rules
  • references/deploy-profiles.md — Cloudflare, Vercel, and Node/VPS runtime profiles
  • references/validation.md — selection-aware validation levels and commands
  • references/extending.md — adding a new capability directory + manifest entry

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.