agentsclimarketplace

Monorepo sync types

Skill lukedj78/dev-flow/monorepo-sync-types

A filesystem contract (.workflow/meta.json) + 37 agent skills that take a product from idea to production: web (Next.js 16) & mobile (Expo/RN), plus an eve agent engine and Linear/scrum. Runs on Claude Code, Codex, Copilot, Gemini, Cursor.

Install
npx -y skills add lukedj78/dev-flow --skill monorepo-sync-types

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

One thing to look at

  • 4 stars4 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

Regenerate backend types and re-export them via packages/shared/types/ in a turborepo monorepo so both apps/web/ and apps/mobile/ consume a single typed surface. Supports Supabase (via supabase gen types typescript), neon-drizzle (via drizzle-kit introspect/pull + InferSelectModel/InferInsertModel re-exports — the default DB stack), tRPC (via inference from the server router import), and custom REST (via a manual or zod-derived schema). Reads .workflow/meta.json with stack.framework="monorepo" and stack.{auth,db} populated. Use when "rigenera i tipi da Supabase", "sync DB schema to packages/shared/types", "il backend è cambiato, aggiorna i tipi", "sync types from tRPC", "sync Drizzle types after a schema change". Not for: creating shared packages from scratch (use monorepo-add-shared-package), wiring the backend client itself (use module-add or rn-module-add).

SKILL.md

9.2 KB, as published. Nobody here has run it

monorepo-sync-types — regenerate + propagate backend types across the monorepo

Contract

See references/contracts.md (vendored from dev-flow). Key facts:

  • Reads <project-root>/.workflow/meta.json#stack.framework — must be "monorepo".
  • Reads meta.json#stack.db and stack.auth to determine the source of truth:
    • "supabase" → run supabase gen types typescript against the linked project.
    • "neon-drizzle" → introspect/pull via drizzle-kit and re-export InferSelectModel/InferInsertModel from the Drizzle schema (this is the default DB for most projects — see module-add/references/module-db.md).
    • "trpc" → re-import the server router type and re-export it.
    • "firebase" → no auto-gen, but normalize fixed types from Firestore rules (limited).
    • "custom-rest" → ask the user where the schema lives (Zod / OpenAPI / TS source).
  • Writes generated types into packages/shared/src/types/ (NOT into packages/api/, because packages/shared is consumed by BOTH apps including for non-backend code).
  • Idempotent: regenerating overwrites; no-diff prints "already in sync".
  • Does NOT modify phase.

When this skill applies

  • User says: "rigenera i tipi", "sync DB schema", "supabase types update", "the backend changed, update types".
  • Orchestrator does NOT route here automatically — invoked on demand (e.g., after a backend migration).

Knowledge dependencies

  • monorepo-bootstrap/references/structure.md — for packages/shared/src/types/ location.
  • rn-backend/references/<provider>.md — for provider-specific gen commands.
  • module-add/references/module-db.md — for the web side conventions.

Workflow

Step 1 — Verify preconditions

Read .workflow/meta.json. Abort if:

  • stack.framework != "monorepo".
  • stack.db == null AND stack.auth == null → "No backend configured yet. Run module-add db or rn-module-add db first."

Read meta.json#stack_config.supabase_project_ref (or equivalent) for the gen command target.

Step 2 — Branch by provider

Supabase

Run the canonical gen command:

npx supabase gen types typescript --project-id <ref> > packages/shared/src/types/database.ts

(or --linked if supabase login + supabase link already done.)

Add to packages/shared/src/index.ts:

export type { Database } from './types/database';

Add a typed Supabase client wrapper in packages/api/src/client.ts:

import { createClient } from '@supabase/supabase-js';
import type { Database } from '@<slug>/shared/types/database';

export const supabase = createClient<Database>(
  process.env.EXPO_PUBLIC_SUPABASE_URL!, // or NEXT_PUBLIC_ on web side — see decision-tree
  process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY!,
);

Both web and mobile apps now import supabase from @<slug>/api/client and get full type safety: supabase.from('posts').select(...) is fully typed.

neon-drizzle

This is the default DB stack for most projects (see module-add/references/module-db.md). Drizzle's schema file is already the source of truth for types — there's no separate "gen types" command — so syncing means two things: (1) make sure the schema file matches the live DB, and (2) re-export row types to packages/shared/.

  1. If drift with the live database is suspected (someone ran DDL outside Drizzle, or this is the first sync), refresh the schema from the database:
    npx drizzle-kit pull
    
    This introspects the Neon database and (re)writes the schema file. Confirm the target path against module-add/references/module-db.md — by default apps/web/lib/db/schema.ts.
  2. Re-export typed rows via InferSelectModel / InferInsertModel (from drizzle-orm) into packages/shared/src/types/db.ts:
    import type { InferSelectModel, InferInsertModel } from 'drizzle-orm';
    import { users, posts } from '../../../../apps/web/lib/db/schema'; // adjust relative path to the real schema location
    
    export type User = InferSelectModel<typeof users>;
    export type NewUser = InferInsertModel<typeof users>;
    export type Post = InferSelectModel<typeof posts>;
    export type NewPost = InferInsertModel<typeof posts>;
    
  3. Add to packages/shared/src/index.ts:
    export * from './types/db';
    

If the schema file was hand-edited to add tables/columns that haven't reached the database yet, don't pull (it would overwrite the new columns) — instead run the migration first (drizzle-kit generate + drizzle-kit migrate; drizzle-kit push is dev-only and destructive on column drops, per module-add/references/module-db.md), then re-export types from the now-current schema.

tRPC

The types flow automatically via TS inference — no gen step. The skill verifies the wiring is correct:

  1. packages/api/src/client.ts imports the server's AppRouter type:
    import type { AppRouter } from '../../../server/src/router';
    export const trpc = createTRPCReact<AppRouter>();
    
  2. (Optional) Re-export Zod-derived input/output types from packages/shared/src/types/:
    import type { inferRouterOutputs } from '@trpc/server';
    import type { AppRouter } from '../../api/src/router-type'; // a re-export
    export type RouterOutputs = inferRouterOutputs<AppRouter>;
    

If the server lives outside the monorepo: import via a published @<scope>/types package OR copy the router types file into packages/shared/src/types/router.d.ts and tell the user to keep it in sync.

Firebase

No auto-gen. The skill creates skeleton types from Firestore rules + user input:

  1. Ask the user for the collections in use (or parse firestore.rules).
  2. For each collection, ask the user for the document shape OR generate a placeholder.
  3. Write packages/shared/src/types/firestore.ts with the shapes.

This is a manual-maintenance scenario — Firebase doesn't ship a type-gen tool the way Supabase does.

Custom REST

Ask the user where the schema lives:

  • Zod schemas in the server: ask for the path, generate packages/shared/src/types/api.ts via z.infer<typeof Schema> for each.
  • OpenAPI spec: ask for the YAML/JSON path, run npx openapi-typescript <spec> -o packages/shared/src/types/api.d.ts.
  • TS source in a separate repo: copy the relevant types into packages/shared/src/types/api.ts and instruct the user to keep in sync.

Step 3 — Verify

Run pnpm tsc --noEmit from apps/web, apps/mobile, and packages/shared. Must pass.

If types changed in ways that broke existing code, report each error — the user fixes the consumers (the skill doesn't auto-fix arbitrary call sites).

Step 4 — Update meta.json + commit

{
  "stack_config": {
    "types_last_synced_at": "<iso>",
    "types_source": "supabase" // or "neon-drizzle" / "trpc" / "firebase" / "custom-rest"
  },
  "history": [
    ...,
    { "skill": "monorepo-sync-types", "ran_at": "<iso>", "outputs": [...] }
  ]
}

If git repo: commit with chore(types): sync from <provider>.

Common anti-patterns (NEVER do)

  • ❌ Generate types into packages/api/ — types are general-purpose, both apps consume them; they live in packages/shared/.
  • ❌ Hand-edit generated type files — they will be overwritten next sync.
  • ❌ Skip the packages/shared/src/index.ts re-export — apps would need deep import paths.
  • ❌ Forget the --project-id flag on supabase gen types — it will fail or generate the wrong schema.
  • ❌ Run gen without verifying the user is logged into the right project (supabase status).
  • ❌ For tRPC: import the server router via a brittle relative path (../../../../server/src/router). Instead use a workspace import if the server is in the monorepo, or a .d.ts shim file if not.

Updating meta.json (recommended pattern)

python3 .../dev-flow/scripts/update_meta.py <project-root> record-artifact \
    --path packages/shared/src/types/database.ts --produced-by 'monorepo-sync-types'
python3 .../dev-flow/scripts/update_meta.py <project-root> append-history \
    --skill 'monorepo-sync-types' --inputs '{"source": "supabase"}' --outputs '{"types": "packages/shared/src/types/database.ts"}'

Sources

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.