Scaffold a new app in apps/<name> wired to the shared @civitai/* packages. Cherry-picks only the packages the app imports and wires each one's dependency, bundler transpile entry, env vars, and a server shim. Use when creating a new monorepo app or *.civitai.com spoke (SvelteKit by default; Next.js supported). Defers to docs/packages/new-app-integration.md and each package README as the source of truth.
90%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Stands up a new apps/<name> on the shared @civitai/* packages, adding only the packages the app
actually needs. Each app cherry-picks; there is no base bundle.
Source of truth (read these, don't restate them from memory — they may have changed):
packages/civitai-*/README.md — per-package reference (exports, env, gotchas)A new monorepo app: a moderator/admin tool, a *.civitai.com spoke, an internal dashboard. Not for
adding a feature to an existing app.
Ask only what can't be inferred:
apps/<name>, package name @civitai/<name>-app.apps/moderator (SvelteKit) or the
old Next shape.@civitai/auth, @civitai/db, @civitai/db-schema, @civitai/brand.require predicate (e.g. (u) => u.isModerator === true) and what happens to an
authenticated-but-unauthorized user (403, or redirect to https://civitai.com).Copy the shape of apps/moderator — read those files and adapt names. Base files (no packages yet):
package.json ("type": "module", vite/svelte-kit scripts), svelte.config.js (adapter-node),
vite.config.ts (process.env shim + empty ssr.noExternal to fill in §3), tsconfig.json,
postcss.config.cjs (module.exports = {}), .gitignore, src/app.html, src/app.d.ts,
src/global.css, src/routes/+layout.svelte, a landing +page.svelte.
Key bootstrap rules (full detail in the integration guide §3–4):
ssr.noExternal (or Next
transpilePackages), plus workspace peers (@civitai/db ⇒ also @civitai/db-schema).loadEnv(mode, process.cwd(), '') → process.env[k] ??= …, because
the packages read process.env directly and SvelteKit doesn't populate it.For each package the user picked, apply its row: add the dep(s), add to the transpile list, add the
env vars (to both .env and .env.example), and create the shim under src/lib/server/.
| Package | Deps to add | Transpile list adds | Env (→ .env & .env.example) | Shim |
|---|---|---|---|---|
@civitai/auth | @civitai/auth | @civitai/auth | AUTH_JWT_ISSUER, AUTH_JWKS_URI, AUTH_INTERNAL_TOKEN | lib/server/auth.ts (createSpokeGuard) + hooks.server.ts |
@civitai/db (Kysely) | @civitai/db, @civitai/db-schema | both | (none — explicit conn strings) DATABASE_URL, DATABASE_REPLICA_URL | lib/server/db.ts (createKyselyClients) |
@civitai/db (Prisma) | @civitai/db, @civitai/db-schema | both | DATABASE_URL, DATABASE_REPLICA_URL, NOTIFICATION_DB_URL, NOTIFICATION_DB_REPLICA_URL | lib/server/db.ts (createPrismaClients) |
@civitai/redis | @civitai/redis | @civitai/redis | REDIS_URL and REDIS_SYS_URL (both!) | lib/server/redis.ts (createRedisClients) |
@civitai/clickhouse | @civitai/clickhouse | @civitai/clickhouse | CLICKHOUSE_HOST, CLICKHOUSE_USERNAME, CLICKHOUSE_PASSWORD | lib/server/clickhouse.ts (createClickhouseClient) |
@civitai/email | @civitai/email | @civitai/email | EMAIL_HOST, EMAIL_PORT, EMAIL_USER, EMAIL_PASS, EMAIL_FROM (all optional) | lib/server/email.ts (re-export sendEmail) |
@civitai/axiom | @civitai/axiom | @civitai/axiom | AXIOM_TOKEN, AXIOM_ORG_ID, AXIOM_DATASTREAM (all optional) | lib/server/logger.ts (createAxiomLogger) |
@civitai/telemetry | @civitai/telemetry | @civitai/telemetry | (none) | lib/server/metrics.ts + a /metrics route |
@civitai/brand | @civitai/brand | @civitai/brand | (none) | routes/favicon.svg/+server.ts (buildFaviconSvg) |
@civitai/db-schema | (peer of db; rarely alone) | @civitai/db-schema | (none) | import DB / enums directly |
Shim snippets live in the package READMEs and the integration guide — read and adapt, don't invent.
Do not add transitive deps (pg, kysely, jose) — they resolve through the workspace packages.
Add them only if the app imports them directly (e.g. import { sql } from 'kysely').
REDIS_URL (e.g. just for the auth session cache) without
REDIS_SYS_URL makes loadRedisEnv throw — caught as fail-open, so the cache is silently lost.@civitai/db/kysely is env-free and Prisma-free (use for light apps); the
@civitai/db Prisma entry requires the full 4-var DB env set.sslNoVerify: true to createKyselyClients for the cnpg pooler's self-signed cert.@civitai/redis into an auth-only app if you want real-time revocation (isRevoked) or the cache.pnpm install
pnpm --filter @civitai/<name>-app run typecheck
pnpm --filter @civitai/<name>-app run buildBoth must pass. Then trim-check every dependency you're unsure about: remove it, reinstall, re-run typecheck and build — typecheck can pass on transitive types while runtime still needs the dep (or vice-versa). Report the final dependency list and which packages were wired.
Surface: the package list chosen, env vars the user must fill in .env (real secrets come from the main
app's repo-root .env), and the run command (/dev-server skill). Don't run release/install of secrets
without asking.
civitai/civitai
a74d6fe
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.