New
Apps 1,895Makers 1,890This week 1,690
Promote your app−30%
next-expo-supabase-agent-harness screenshot 1
2views in
the last 30 days

next-expo-supabase-agent-harness

CLI that scaffolds a Next.js + Expo monorepo on Supabase with Claude Code hooks

Built with Claude Code

by bhodisea · Unclaimed · from AUAustralia · added Oct 29, 2026
no ratings0 comments

OpenSSF Baseline OpenSSF Scorecard

A CLI that scaffolds a pnpm monorepo with a Next.js 16 web app and an Expo 57 (React Native) mobile app on one Supabase backend, plus Claude Code hooks and CI gates that block an agent turn or a merge until validation passes.

A terminal recording: init writes a project, the Bash guard denies a force-push, the secrets gate goes red on a service-role key in a tracked file and green once it is removed

No model runs in the recording. The hooks are programs that read the JSON payload Claude Code hands them, and the gates are programs that exit 1, so what they print is what the agent is shown. It is generated from docs/media/harness-demo.tape, and a test runs that script's commands against each change, so a message that changes reds until the recording is regenerated.

The npm package is this CLI. npx downloads it and runs it in one step, from an empty folder for the new project:

npx --yes next-expo-supabase-agent-harness@latest init

Or install it globally once:

npm i -g next-expo-supabase-agent-harness

and then, from an empty folder for the new project, run:

next-expo-supabase-agent-harness init

Either way, init asks for the project's details before it writes anything: its name, its package name, the app identifier, the Supabase project ref, the GitHub owner and a few more. Each question shows a default in brackets that Enter accepts, and init --yes accepts them all without asking. The scaffold is written into the current folder.

Install with -g: a local npm i writes a package-lock.json into the folder, and init stops when it finds one. A global install stays at the version you installed, so run npm i -g next-expo-supabase-agent-harness@latest before update to move it to the newest release; update refuses to run from a CLI older than the project.

To pin a release, take a version number from the Releases page (the tag without its v) and use it in place of latest, or install next-expo-supabase-agent-harness@<version>. Once init has finished, in the same folder:

git init          # first: the prepare script (lefthook install) needs a repository
pnpm install
git add -A && git commit -m "chore: scaffold"   # the first commit must include pnpm-lock.yaml
pnpm validate

Status: stable (2.0.x). CI proves the scaffold on Linux, and on macOS without the database gates. The installer's unit tests run on Linux, Windows and macOS. pnpm validate has never run in CI on Windows. See Limitations.

Who this is for

The harness assumes one shape: a pnpm monorepo with a Next.js web app and an Expo mobile app on one Supabase backend, Node 22 or newer, Docker for the local database the database gates run against, and Claude Code as the agent, because Claude Code is what runs the hooks that block a turn. Enforcement comes in layers: the Claude Code hooks, the lefthook commit hooks, pnpm validate, and CI. Outside that shape, some layers still apply:

  • A web-only app. Not a fit today. init writes both apps, and the mobile gates judge apps/mobile. Without it, expo-policy and native-deps skip on a laptop but fail closed in CI, and parity goes red on the mobile paths its register names. CI runs the frozen step list, so a project cannot drop those gates.
  • An existing monorepo with its own CI. init retrofits a pnpm workspace that has apps/web or apps/mobile. It keeps your configs and parks the harness's beside them as <name>.harness.<ext> files, and gate-integrity stays red until each one is merged or accepted after review. It keeps a workflow file you already have, so your own ci.yml runs no harness gate until you add the chain to it, and an existing lefthook.yml is one of the parked configs. The Claude Code hooks are merged into your .claude/settings.json. A repository on npm, yarn or bun, or without pnpm-workspace.yaml, is refused.
  • An agent that is not Claude Code. The commit hooks, pnpm validate and CI apply as they do for a person. The .claude/ hooks never run, so nothing denies a command before it runs or keeps a turn from ending on a red tree: a violation is caught at the commit or at the pull request instead.

Requirements

  • Node 22 or newer
  • pnpm 11 (corepack enable picks up the pinned version)
  • Docker, for the local Supabase stack that the database gates run against
  • Optional: the gitleaks binary. The pre-commit secrets scan prints a skip notice without it.

What you get

apps/web        Next.js 16 App Router. Serves the tRPC API for both clients.
apps/mobile     Expo 57, expo-router, React Native 0.86.
packages/api            framework-neutral tRPC v11 router
packages/contracts      zod DTOs shared by both apps
packages/platform/*     env, errors, events, observability, ratelimit, supabase
packages/verticals/*   your features (empty until you add one, or init --with-demo)
packages/design-tokens, design-system, design-system-native
supabase/       SQL-first schema, migrations, RLS policies, pgTAP tests, one Edge Function
tools/, .claude/, .github/   the gates, hooks and workflows described below

A default init writes no example feature. init --with-demo adds one worked feature end to end on both apps (the @app/notes vertical, its screens, routes and migrations, and its rows in the registers), and eject takes it back out; see docs/cli.md.

Postgres row-level security keyed on auth.uid() is the one authorization boundary for both apps. Versions are pinned in a single pnpm catalog (pnpm-workspace.yaml): Next 16.4, Expo 57, React 19.2, TypeScript 6, Biome 2.6, supabase-js 2, tRPC 11, zod 4.

How it works

  • PreToolUse guards. Hooks check every shell command, file write and MCP call against a data table of 151 guard-rule ids before the agent's tool runs. Nine hooks are wired: seven guards, a launcher that fails closed if a hook cannot load, and a session-start brief.
  • Stop hook. The agent cannot end a turn until pnpm validate, the RLS isolation tests against a real Postgres, both unit suites with coverage, per-file diff coverage, duplication, i18n, test quality, the mobile perf closure and the reviewer verdicts all pass.
  • pnpm validate. One chain of 37 gates, ordered cheap to expensive, driven by tools/harness.config.mjs. Each gate is documented with a proof that it can fail in the gates catalog.
  • CI. The same chain runs against a frozen copy of the step list (tools/validate.floor.json), so a project can add gates but cannot quietly remove one. Gates that need a database or a toolchain skip loudly on a laptop and fail closed in CI.
  • Supply chain. Every workflow the harness ships pins its actions by commit SHA, and zizmor audits them. OSV-Scanner checks the lockfile for known vulnerabilities, gitleaks scans for secrets, and the Renovate config lets a new npm release age (minimumReleaseAge) before proposing it. The ci-provenance module adds an SBOM and build provenance attestations.
  • Commit hooks. lefthook runs Biome and gitleaks before a commit and commitlint on the message.

The design is described in the harness doctrine, which ships into every scaffold. Its hook map lists each hook, the event it runs on and what it enforces.

Compared with other starters

Other projects also put a Next.js app and an Expo app in one repository:

  • create-t3-turbo is a Turborepo starter with an Expo app, Next.js and TanStack Start web apps (you pick one), tRPC, Better Auth, and Drizzle preconfigured for a Supabase Postgres database. You start from a copy of it, made with GitHub's "Use this template" button or with npx create-turbo@latest -e https://github.com/t3-oss/create-t3-turbo.
  • Solito is a library that unifies navigation between React Native and Next.js. npx create-solito-app@latest writes one of its example monorepos; in the default one, both apps render the same screens from a shared package.

In this harness:

  • Authorization is Postgres row-level security keyed on auth.uid(). Both apps meet the same policies, and pgTAP and supabase-js suites test them against a real database.
  • Claude Code hooks and CI gates block an agent turn or a merge until validation passes.
  • update applies a later harness release to a project that already exists.
  • The two apps share the API, the contracts and the design tokens, not their screens.

Commands

Command What it does
init Scaffold into --dir (default .). --tier core|standard|strict, --modules a,b, --set VAR=value, --dry-run.
update Apply a newer harness version to an existing install. --rollback restores the tree recorded before the last update.
doctor Report whether an install is healthy.
enable <module> / disable <module> Add or remove an opt-in module.
graduate Advance the install's base version once ramped checks are clean.
apply-proposal [<id>] Apply, as a human in a terminal, a register edit an agent staged in harness-proposals/: it shows the reason and the diff and writes the file once you type its path. No id lists them. --dry-run.

Full flag and placeholder reference: docs/cli.md, or --help.

Tiers choose which opt-in modules are installed. core installs none. standard (the default) installs ci-provenance, ci-mobile-release and ci-web-deploy. strict installs all twelve: those three plus device-e2e, eas-update, store-metadata, gate-a11y-deep, crash-reporting, push-notifications, eval-live, observability and e2ee. The Vercel deploy workflow, EAS Build/Submit and release-please come from modules, not the base.

Claude Code plugin

The agents, slash commands and skills also come as a Claude Code plugin, for a session outside a harness-installed project. Install it from this repository's marketplace:

claude plugin marketplace add BhodiSea/next-expo-supabase-agent-harness
claude plugin install next-expo-supabase-agent-harness@next-expo-supabase-agent-harness

or, inside a session on Claude Code 2.1.275 or later:

/plugin install next-expo-supabase-agent-harness --marketplace BhodiSea/next-expo-supabase-agent-harness

The plugin carries no hooks, gates or permissions; those come with init. Inside a harness-installed project you do not need it, and its reviewers' verdicts do not count toward the reviewer ledger. See plugin/README.md.

Limitations

  • The database gates are proven on Linux only. Every CI lane with a Supabase stack runs on ubuntu-latest. The macos-scaffold lane runs init and pnpm validate on a Mac without Docker, the way a laptop runs them, and fails if any gate skips except types-drift, which needs the database. No lane runs a scaffold on Windows.
  • Rate limiting covers the application layer only. It binds the tRPC router and Next.js Server Actions. It does not bound direct PostgREST calls made with a user's JWT, or sign-in and sign-up, which go to Supabase Auth. If the rate-limit backend is unreachable, it degrades to an in-process limiter, and it fails open if that also fails.
  • The e2ee module ships building blocks, not an encrypted app. No shipped feature is encrypted; the worked example init --with-demo adds stores plaintext. The module's own README lists what it does not solve.
  • Device tests are not per-PR. The Android emulator and Maestro lane runs on a schedule and on manual dispatch.
  • The guards are tamper-evident, not tamper-proof. See the scope notes in SECURITY.md.

The longer list is kept per release under "What stays open" in the CHANGELOG.

Measured cost

Recorded by CI on a GitHub-hosted Linux x64 runner on 2026-10-02 and committed in scripts/chain-budget.json. They are that runner's numbers, not a promise about your machine.

Run Wall time
pnpm validate, warm warm ≈ 27 s (26881 ms)
pnpm validate, cold cold ≈ 145 s (145363 ms)
Stop hook, full turn end 54.8 s (54815 ms)

Compliance mappings

The scaffold ships two registers that map published requirements to the gate, hook or test that addresses each one, including the requirements it does not meet. These are mappings. They are not certifications, and the project claims no conformance level. The gates print the current standing as:

149 ML3 requirements: 8 effective, 13 alternate-control, 11 not-implemented, 61 not-applicable, 56 organisation-boundary; 8 shared clauses
392 mapped requirements: 11 covered, 149 partial, 105 not-covered, 127 not-applicable — ASVS 5.0.0 (345), MASVS 2.1 (24), CRA Annex I (23)

The first line counts the requirements of ASD's Essential Eight (register). The second counts the requirements of three further standards (crosswalk).

Documentation

Contributing and feedback

To fork the harness itself into a sibling for a different stack, start from a clone with a fresh history, as docs/forking.md describes. This repository is descended from expo-postgres-agent-harness and tauri-postgres-agent-harness.

License

Apache-2.0 for the repository. Everything under template/** is "Apache-2.0 OR 0BSD": recipients choose either, so scaffolded code carries no attribution requirement.

Description taken from the repository README.

Ratings

–
0 ratings
5 0
4 0
3 0
2 0
1 0

Rate this app

Sign in to rate

Comments 0

Sign in to comment

No comments yet.

Related apps

#cli · #scaffolding · #nextjs