From 03fe583e3bb1e8a0c3fd563229afa76720d0d220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Galarowicz?= Date: Thu, 11 Jun 2026 21:58:03 +0200 Subject: [PATCH] feat!: extract vendor bridges to pharn-skills-* category modules (0.69.0) Move vendor-bridge skills out of pharn-stack-nextjs into stack-agnostic category modules so any stack pack can compose them, and add manifest schemaVersion 2 with the wizard catalog for selective installs. Co-authored-by: Cursor --- CHANGELOG.md | 22 ++ CLAUDE.md | 40 +++- README.md | 21 +- SKILLS_VERSION | 2 +- docs/architecture.md | 29 +++ docs/module-anatomy.md | 41 +++- docs/skill-authoring.md | 6 +- manifest.json | 213 +++++++++++++++++- pharn-skills-auth/module.json | 10 + pharn-skills-auth/skills/better-auth/SKILL.md | 58 +++++ .../skills/clerk/SKILL.md | 11 +- .../skills/supabase-auth/SKILL.md | 32 +++ pharn-skills-db/module.json | 10 + .../skills/neon/SKILL.md | 9 +- pharn-skills-email/module.json | 10 + .../skills/resend/SKILL.md | 9 +- pharn-skills-orm/module.json | 10 + .../skills/drizzle/SKILL.md | 9 +- .../skills/prisma/SKILL.md | 9 +- pharn-skills-payments/module.json | 10 + .../skills/stripe/SKILL.md | 9 +- .../combinations/planetscale.md | 2 +- pharn-stack-nextjs/module.json | 4 +- scripts/schemas/manifest.schema.json | 107 ++++++++- test/validate.test.mjs | 124 +++++++++- 25 files changed, 742 insertions(+), 65 deletions(-) create mode 100644 pharn-skills-auth/module.json create mode 100644 pharn-skills-auth/skills/better-auth/SKILL.md rename {pharn-stack-nextjs => pharn-skills-auth}/skills/clerk/SKILL.md (97%) create mode 100644 pharn-skills-auth/skills/supabase-auth/SKILL.md create mode 100644 pharn-skills-db/module.json rename {pharn-stack-nextjs => pharn-skills-db}/skills/neon/SKILL.md (98%) create mode 100644 pharn-skills-email/module.json rename {pharn-stack-nextjs => pharn-skills-email}/skills/resend/SKILL.md (98%) create mode 100644 pharn-skills-orm/module.json rename {pharn-stack-nextjs => pharn-skills-orm}/skills/drizzle/SKILL.md (98%) rename {pharn-stack-nextjs => pharn-skills-orm}/skills/prisma/SKILL.md (98%) create mode 100644 pharn-skills-payments/module.json rename {pharn-stack-nextjs => pharn-skills-payments}/skills/stripe/SKILL.md (98%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a3c128..023deb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,28 @@ Breaking changes for skills are tracked here so the CLI (`pharn-cli`) can warn u ## [Unreleased] +## [0.69.0] — 2026-06-11 + +### Changed (breaking for manifest consumers) + +Vendor-bridge skills extracted from `pharn-stack-nextjs` into standalone, stack-agnostic category modules, so any stack pack (the forthcoming `pharn-stack-tanstack-router`, etc.) can compose them without duplication: + +- `pharn-skills-db` — Neon (database hosts; Supabase uses its official vendor skill, no bridge). +- `pharn-skills-orm` — Drizzle, Prisma. +- `pharn-skills-auth` — Better Auth (stub), Clerk, Supabase Auth (stub). +- `pharn-skills-payments` — Stripe. +- `pharn-skills-email` — Resend. + +`feature-module-layers` stays in `pharn-stack-nextjs/skills/` — it is Next.js-specific. The six moved bridges were relocated with `git mv` (history preserved), patch-bumped, repointed to their new `module:`, and each gained `rule_files_provided_by: "stack-pack"` plus a body note clarifying that referenced rule files (`database.md`, …) ship with the installed stack pack and resolve at install time. + +`manifest.json` bumped to `schemaVersion: 2` — adds the `wizard` catalog (sections, questions, per-option `install` paths, `vendorSkill`/`comingSoon` flags, and dependency `rules`: hide / hideQuestion / relabel / warn) and `kind: "skill-category"` on the five new modules. The manifest JSON schema (`scripts/schemas/manifest.schema.json`) was extended — not loosened — with typed `$defs` for the wizard tree and the `kind` field; `schemaVersion` now accepts `1` or `2`. CLIs reading `schemaVersion: 1` manifests continue to work against pinned older commits. + +**Existing installations are unaffected** — skills already copied into your `.claude/` stay in place. New installs pick skills individually through the wizard (the CLI performs selective installation: it copies only the chosen skill subfolder, e.g. `pharn-skills-orm/skills/drizzle/`, never the whole category module). `SKILLS_VERSION` `0.68.1` → `0.69.0`; `pharn-stack-nextjs` module `0.30.0` → `0.31.0`. + +### Migration (for projects re-running the wizard) + +`npx pharn@latest update` — the CLI re-resolves your `stackAnswers` against the new layout. The wizard-execution, selective-install, and `pharn add :` work lives in the separate `pharn-cli` repo; this release ships the manifest data, schema, and the documented contract (see `docs/architecture.md`). + ## [0.68.1] — 2026-06-11 **`pharn-stack-react` review fixes (license drift + frontmatter convention).** Closes the actionable findings from the stack-react-module review. Both fix-level, no breaking changes, no behavioural delta. `SKILLS_VERSION` `0.68.0` → `0.68.1`; `pharn-stack-react` module `0.1.1` → `0.1.2`. diff --git a/CLAUDE.md b/CLAUDE.md index 6d7e4a4..13ee93f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -80,6 +80,21 @@ pharn-oss/ │ ├── module.json │ ├── commands/ ← slash-command entry points for each audit skill │ └── skills/ ← 8 audit skills: pharn-privacy-audit, pharn-security-review, pharn-a11y-audit, pharn-drift-check, pharn-posthog-masking-audit, pharn-logging-masking-audit, pharn-license-compliance-audit, pharn-supply-chain-audit +├── pharn-skills-db/ ← skill-category module: database-host vendor bridges (stack-agnostic) +│ ├── module.json +│ └── skills/ ← neon (db-host alt; Supabase uses its official vendor skill) +├── pharn-skills-orm/ ← skill-category module: ORM vendor bridges (stack-agnostic) +│ ├── module.json +│ └── skills/ ← drizzle (default), prisma (alt) +├── pharn-skills-auth/ ← skill-category module: auth vendor bridges (stack-agnostic) +│ ├── module.json +│ └── skills/ ← better-auth (default, stub), clerk (alt), supabase-auth (alt, stub) +├── pharn-skills-payments/ ← skill-category module: payments vendor bridges (stack-agnostic) +│ ├── module.json +│ └── skills/ ← stripe (default) +├── pharn-skills-email/ ← skill-category module: email vendor bridges (stack-agnostic) +│ ├── module.json +│ └── skills/ ← resend (default) ├── pharn-stack-react/ ← framework-agnostic React base (React 19 patterns, useEffect discipline) │ ├── module.json │ ├── skills/ ← avoid-react-use-effect @@ -102,7 +117,7 @@ pharn-oss/ │ ├── security.md ← SEC: OWASP Top 10, secrets, input validation, headers │ ├── testing.md ← TEST: Vitest units, Playwright E2E, tenant isolation │ └── accessibility.md ← A11Y: WCAG 2.2, keyboard operability, semantic HTML - ├── skills/ ← feature-module-layers; 6 vendor-bridge skills: drizzle (ORM default), prisma (ORM alt), stripe (payments default), resend (email default), neon (db-host alt), clerk (auth alt) + ├── skills/ ← feature-module-layers (Next.js-specific; the only skill here — vendor bridges now live in the pharn-skills-* category modules above) ├── phase-variants/ ← stack-specific generation patterns (build-phase consumed; see §6) │ 2 shipped: server-actions-pattern, next-safe-action-wrapping ├── grill-banks/ ← stack-specific grill question banks (grill-phase consumed; see §6) @@ -143,7 +158,7 @@ pharn-oss/ | A stack-specific generation pattern (the code shape a build phase emits) | `pharn-stack-*/phase-variants/` | | A stack-specific grill question bank (the questions the grill phase asks) | `pharn-stack-*/grill-banks/` | | An architecture-mapping pattern for a divergent backend (MySQL, reactive, NoSQL) | `pharn-stack-*/combinations/` (distinct from bridges — maps methodology onto a different architecture, not wires a vendor into the default one) | -| A vendor-bridge skill (config + wiring, not API how-to) | the relevant stack pack's `skills//` (there is no `vendor-bridges/` folder; pinned API docs live in `ai_docs/`) | +| A vendor-bridge skill (config + wiring, not API how-to) | the matching skill-category module's `skills//` (`pharn-skills-{db,orm,auth,payments,email}`; there is no `vendor-bridges/` folder; pinned API docs live in the stack pack's `ai_docs/`) | | A new `PreToolUse` / `UserPromptSubmit` hook (single-file `.cjs`, stdlib only) | `pharn-core/hooks//` | | A new `Stop` hook specific to a module (single-file `.cjs`, stdlib only) | `/hooks//` (e.g. `pharn-pipeline/hooks/`) | | Wizard logic, dependency rules, post-install steps | **NOT HERE** — that belongs in `pharn-cli` | @@ -159,6 +174,8 @@ pharn-oss/ - Supabase, Firebase, Better Auth, Convex, shadcn, Sentry, Vercel, Expo +**Exception — wiring-only coexistence.** A vendor that _has_ an official skill MAY still carry a thin `vendor-bridge` **stub** limited to PHARN-stack wiring the official skill does not cover (file locations, ORM-adapter choice, env wiring) — **never API how-to**. The wizard option for it must set `vendorSkill` to reference the official skill (so the official skill still does the teaching). This is why `pharn-skills-auth` ships `better-auth` and `supabase-auth` bridges alongside their official skills. A full how-to skill for these vendors is still forbidden; only the wiring stub is allowed. + **Vendors with no official skill yet (PHARN writes a _vendor-bridge_ skill):** - Stripe, Resend, Drizzle, Prisma, Neon, Clerk @@ -212,9 +229,9 @@ rule_files: [] --- ``` -### Vendor-bridge skill (inside the relevant stack pack's `skills//`) +### Vendor-bridge skill (inside a `pharn-skills-/skills//` category module) -Bridges are PHARN-written config + wiring guides for vendors without an official Claude skill. They are **reference/setup skills** consumed by the wizard during init and by `/pharn-build` when a feature needs the vendor. They are **not** interactive slash-commands. +Bridges are PHARN-written config + wiring guides for vendors without an official Claude skill. They are **reference/setup skills** consumed by the wizard during init and by `/pharn-build` when a feature needs the vendor. They are **not** interactive slash-commands. As of v0.69.0 they live in stack-agnostic **skill-category modules** (`pharn-skills-db`, `pharn-skills-orm`, `pharn-skills-auth`, `pharn-skills-payments`, `pharn-skills-email`) — not inside any one stack pack — so every stack pack can compose them. The `rules/` and `ai_docs/` a bridge references still ship with the installed **stack pack** and resolve in the user's `.claude/` at install time (the bridge frontmatter declares `rule_files_provided_by: "stack-pack"`). **Three-layer relationship:** @@ -243,7 +260,7 @@ version: "0.1.0" description: "Vendor-bridge for drizzle-orm. Config + wiring only. API how-to in ai_docs/drizzle-orm.md." kind: pharn-owned # in-repo PHARN bridge; community-scaffolded bridges use kind: community seal: "vendor-bridge" # NEVER "PHARN ✓ reviewed" on a bridge — that seal is forbidden here -module: pharn-stack-nextjs +module: pharn-skills-orm # the owning skill-category module (NOT a stack pack) library: "drizzle-orm" official_skill_available: false model_tier: sonnet @@ -253,9 +270,10 @@ category: "orm" # orm | payments | email | db-host | auth role: "default" # default | alternative alternative_to: null # set on alternative bridges (e.g. "drizzle" for prisma) rule_files: ["database.md", "architecture.md"] +rule_files_provided_by: "stack-pack" # the rule files ship with the installed stack pack, not this module reads: [".env.example", "drizzle.config.ts"] writes: ["shared/lib/db/client.ts", "shared/lib/db/schema/", "drizzle.config.ts"] -pharn_version: "0.52.0" +pharn_version: "0.69.0" --- ``` @@ -642,10 +660,10 @@ The Memory Bank is **markdown-canonical**. It is readable, diffable, lives in gi ## 8. Versioning -Single source of truth: `SKILLS_VERSION` file at repo root (semver). Current value: `0.68.0`. +Single source of truth: `SKILLS_VERSION` file at repo root (semver). Current value: `0.69.0`. ```text -SKILLS_VERSION → 0.68.0 +SKILLS_VERSION → 0.69.0 ``` **Rules:** @@ -747,8 +765,8 @@ Run through this every time a new skill, phase, lens, validator, command, audit, ### Step 2 — Vendor check (only for vendor-related additions) -- [ ] Does this vendor have an official Claude skill (Supabase, Firebase, Better Auth, Convex, shadcn, Sentry, Vercel, Expo)? → **Do not create a PHARN skill.** Reference the official one in `pharn.config.json` with a pinned commit hash. -- [ ] Is this a vendor without official skill (Stripe, Resend, Drizzle, Prisma, Neon, Clerk)? → Create a `vendor-bridge` skill in the relevant stack pack. Scope = wiring/config only. API how-to goes in `ai_docs/`. +- [ ] Does this vendor have an official Claude skill (Supabase, Firebase, Better Auth, Convex, shadcn, Sentry, Vercel, Expo)? → **Do not create a PHARN how-to skill.** Reference the official one in `pharn.config.json` with a pinned commit hash. (Exception: a thin wiring-only `vendor-bridge` stub may coexist with the official skill — see the §5 "wiring-only coexistence" note; the wizard option must set `vendorSkill`.) +- [ ] Is this a vendor without official skill (Stripe, Resend, Drizzle, Prisma, Neon, Clerk)? → Create a `vendor-bridge` skill in the matching skill-category module (`pharn-skills-{db,orm,auth,payments,email}/skills//`). Scope = wiring/config only. API how-to goes in the stack pack's `ai_docs/`. ### Step 3 — Banned-patterns check @@ -820,4 +838,4 @@ If a feature request fits one of those, redirect the user before writing anythin --- -_Last updated: June 2026 (v0.68.0). This file is the source of truth for working on pharn-oss. If something in it conflicts with another doc in this repo, this file wins._ +_Last updated: June 2026 (v0.69.0). This file is the source of truth for working on pharn-oss. If something in it conflicts with another doc in this repo, this file wins._ diff --git a/README.md b/README.md index 83af8d7..6a2cab1 100644 --- a/README.md +++ b/README.md @@ -92,14 +92,19 @@ Alongside the pipeline: `/pharn-drift`, `/pharn-cost`, `/pharn-eval`, `/pharn-pr Modules are **subfolders**, each fetched independently by the installer — not npm packages. `pharn-core` is required; everything else depends on it. -| Module | What it gives you | -| -------------------- | --------------------------------------------------------------------------------------------------- | -| `pharn-core` | Constitution, markdown memory bank, `privacy-shield` + `constitution-guard` hooks, 4 base skills | -| `pharn-pipeline` | The 6-stage pipeline plus `drift`, `cost`, `eval`, `pr` | -| `pharn-review` | `/pharn-review` with **13 context lenses** (architecture, security, privacy, a11y, …) | -| `pharn-audits` | **7 standalone audits** — privacy, security, a11y, PostHog + logging masking, license, supply-chain | -| `pharn-stack-react` | Framework-agnostic React 19 base (useEffect discipline, reused by every React stack pack) | -| `pharn-stack-nextjs` | Next.js + Supabase + Better Auth + Drizzle stack pack (19 rule files) | +| Module | What it gives you | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| `pharn-core` | Constitution, markdown memory bank, `privacy-shield` + `constitution-guard` hooks, 4 base skills | +| `pharn-pipeline` | The 6-stage pipeline plus `drift`, `cost`, `eval`, `pr` | +| `pharn-review` | `/pharn-review` with **13 context lenses** (architecture, security, privacy, a11y, …) | +| `pharn-audits` | **7 standalone audits** — privacy, security, a11y, PostHog + logging masking, license, supply-chain | +| `pharn-skills-db` | Database-host vendor bridges (Neon) — stack-agnostic, composed by the wizard | +| `pharn-skills-orm` | ORM vendor bridges (Drizzle, Prisma) — stack-agnostic | +| `pharn-skills-auth` | Auth vendor bridges (Better Auth, Clerk, Supabase Auth) — stack-agnostic | +| `pharn-skills-payments` | Payments vendor bridges (Stripe) — stack-agnostic | +| `pharn-skills-email` | Email vendor bridges (Resend) — stack-agnostic | +| `pharn-stack-react` | Framework-agnostic React 19 base (useEffect discipline, reused by every React stack pack) | +| `pharn-stack-nextjs` | Next.js App Router stack pack — 19 rule files, ai_docs, phase-variants, grill-banks; pair with `pharn-skills-*` for vendor bridges | --- diff --git a/SKILLS_VERSION b/SKILLS_VERSION index 7d530da..106d4ac 100644 --- a/SKILLS_VERSION +++ b/SKILLS_VERSION @@ -1 +1 @@ -0.68.1 +0.69.0 diff --git a/docs/architecture.md b/docs/architecture.md index 0cf7a34..04cfc2d 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -39,6 +39,35 @@ What `pharn-cli` does on `pharn init`: 6. Writes `pharn.config.json` to the adopter project, recording: `skillsVersion`, `commitSha`, `modules`, `stackAnswers`. 7. Runs `npm install` for any project dependencies the stack pack added. +## Manifest schemaVersion 2 — the wizard catalog and skill-category modules + +As of `SKILLS_VERSION` 0.69.0, `manifest.json` is `schemaVersion: 2`. Two things changed; the JSON schema (`scripts/schemas/manifest.schema.json`) was extended, not loosened, and `schemaVersion` now accepts `1` or `2` so CLIs pinned to an older commit keep working. + +### `kind: "skill-category"` modules + +Vendor-bridge skills no longer live inside a stack pack. They live in stack-agnostic **skill-category modules** — `pharn-skills-db`, `pharn-skills-orm`, `pharn-skills-auth`, `pharn-skills-payments`, `pharn-skills-email` — each marked `kind: "skill-category"` in its manifest entry (the field lives only in the manifest entry, not in `module.json`). This lets any stack pack compose the same bridges instead of duplicating them. A bridge's `rule_files` still ship with the installed **stack pack** and resolve in the user's `.claude/` at install time; the bridge declares `rule_files_provided_by: "stack-pack"` to make that dependency explicit. + +### The `wizard` block + +`manifest.wizard` is the single source the CLI reads to build the entire wizard flow: + +- `sections[]` → `questions[]` → `options[]`. Each option carries an `install` path (repo-relative path to a skill subfolder, or `null` for skip / coming-soon / vendor-official-only choices), an optional `vendorSkill` (names the official vendor skill the consent step offers), and optional `default` / `comingSoon` flags. +- A question's `rules[]` adjust later questions based on prior answers: `hide` (drop options), `hideQuestion` (drop the whole question), `relabel` (reword an option), `warn` (surface a message). The `if` condition matches answers by equality or `{ "not": "" }`. +- `wizard.defaults` are the answers used in default mode (the wizard skips all questions). + +The catalog only covers categories that have real skills today (CORE / AUTH & COMMS / API & DATA / PAYMENTS & INFRA). Further sections are added as their skills are built — never as empty placeholders. + +### Selective-install contract (implemented in `pharn-cli`) + +The CLI — not this repo — performs selective installation. The contract this repo's data assumes: + +1. Read `schemaVersion`. `2` → expect the `wizard` block; `1` → fall back to the legacy module-picker (backward-compat for pinned older manifests). +2. Build the wizard from `manifest.wizard.sections`, applying each question's `rules` against prior answers. +3. For each answered option with a non-null `install`, copy **only that subfolder** (e.g. `pharn-skills-orm/skills/drizzle/`) into `.claude/skills//` — never a sibling skill from the same category module. `module.json`'s `installs.skills → skills/` declares the mapping target, not the copy granularity. +4. Record `stackAnswers` and the installed skill paths in `pharn.config.json` alongside `skillsVersion` / `commitSha` / `modules`. +5. Options carrying a `vendorSkill` feed the existing SHA-pinned vendor-skills consent flow. +6. `pharn add :` (e.g. `pharn add orm:prisma`) adds one skill from a skill-category module without re-running the full wizard. + ## Versioning - `SKILLS_VERSION` at the repo root is the global version. diff --git a/docs/module-anatomy.md b/docs/module-anatomy.md index d1a1f1f..5fde6a3 100644 --- a/docs/module-anatomy.md +++ b/docs/module-anatomy.md @@ -146,6 +146,33 @@ Examples of the lens-vs-audit distinction: --- +## Skill-category modules (`pharn-skills-*`) + +### Purpose + +Stack-agnostic homes for **vendor-bridge skills**, introduced in v0.69.0. A bridge is config + wiring for a vendor without an official Claude skill — it is not tied to any one framework, so it lives in a category module that every stack pack can compose, rather than being duplicated inside each stack pack. Five modules, one per category (each `kind: "skill-category"`, `dependsOn: ["pharn-core"]`): + +- `pharn-skills-db` — database-host bridges. `skills/neon/` (`neon-bridge`, `category: "db-host"`, alternative to Supabase). Supabase as a host uses its official vendor skill — no bridge. +- `pharn-skills-orm` — ORM bridges. `skills/drizzle/` (`drizzle-bridge`, default) and `skills/prisma/` (`prisma-bridge`, alternative to Drizzle). +- `pharn-skills-auth` — auth bridges. `skills/better-auth/` (`better-auth-bridge`, default — **stub**), `skills/clerk/` (`clerk-bridge`, alternative), `skills/supabase-auth/` (`supabase-auth-bridge`, alternative, requires Supabase DB — **stub**). +- `pharn-skills-payments` — payments bridges. `skills/stripe/` (`stripe-bridge`, default). +- `pharn-skills-email` — email bridges. `skills/resend/` (`resend-bridge`, default). + +### Vendor-bridge skill convention + +Bridges live in `/skills//` (no `vendor-bridges/` folder). Each is `kind: pharn-owned` + `seal: "vendor-bridge"` — never `"PHARN ✓ reviewed"` (CLAUDE.md §5). Bridges are reference/setup skills (consumed by the wizard + `/pharn-build`), not interactive slash-commands. They cover install + env + config files + wiring only; API usage goes in the stack pack's `ai_docs/`. A bridge's `rule_files` (e.g. `database.md`) ship with the installed **stack pack**, not the category module, and resolve in the user's `.claude/` at install time — each bridge declares `rule_files_provided_by: "stack-pack"` to make that explicit. + +The CLI installs bridges **selectively**: choosing Drizzle in the wizard copies only `pharn-skills-orm/skills/drizzle/` into `.claude/skills/`, never Prisma's sibling bridge. See [`architecture.md`](./architecture.md) for the manifest `wizard` catalog and the selective-install contract. + +See `docs/skill-authoring.md § Vendor-bridge skills` for the full authoring guide, the default/alternative framing table, and the 10-section body shape. + +### What stays out of these modules + +- Rule files and `ai_docs/` snapshots — those ship with the stack pack a bridge references. +- Framework-specific skills (e.g. `feature-module-layers`) — those stay in the stack pack. + +--- + ## pharn-stack-react ### Purpose @@ -169,7 +196,7 @@ Framework-agnostic React skills and rules. Any React-based stack pack (Next.js t ### Purpose -The opinionated Next.js + Supabase + Better Auth + Vercel stack pack. Canonical-stack rule files, vendor-bridge skills, vendor ai_docs, and starter templates. +The opinionated Next.js + Supabase + Better Auth + Vercel stack pack. Canonical-stack rule files, vendor ai_docs, phase-variants, grill-banks, and starter templates. (Vendor-bridge skills no longer live here — as of v0.69.0 they live in the stack-agnostic `pharn-skills-*` category modules; see that section.) ### What lives here @@ -177,17 +204,9 @@ The opinionated Next.js + Supabase + Better Auth + Vercel stack pack. Canonical- - `rules/rules-index.md` — the index; installed into the user's project and `@import`ed by their `CLAUDE.md`; pointers only (never duplicates rule content). - `rules/architecture.md` — first rule file (ARCH-1..ARCH-12): layer model, Server/Client boundary, feature-slicing rules. Enforced by the `architecture-layers` lens and `layer-boundaries` validator. The exemplar that the remaining 18 files follow. - `rules/.md` (written in batches) — 7 additional rule files written to date: `database`, `auth`, `privacy`, `react-performance`, `saas-patterns`, `security`, `accessibility`; 12 forthcoming (stable prefixes reserved): `app-router`, `testing`, `observability`, `background-jobs`, `server-actions`, `data-fetching`, `i18n`, `feature-flags`, `animations`, `env-vars`, `proxy`, `feature-module-layers`. -- `skills/feature-module-layers/` — enforces the 4-layer feature-module architecture (schemas / actions / services / lib). -- `skills/drizzle/` (`drizzle-bridge`) — vendor-bridge for drizzle-orm. Config + wiring for Drizzle over Postgres (Supabase default; Neon alternative via the neon bridge). Cites DB-1, DB-5, DB-10, ARCH-4, SEC-1. API how-to lives in `ai_docs/drizzle-orm.md`. ✅ -- `skills/stripe/` (`stripe-bridge`) — vendor-bridge for Stripe. Config + wiring for SaaS subscriptions/billing: Stripe client singleton, webhook signature-verification wiring (SEC-1, JOBS-1), server-side plan gating stub (SAAS-5), customer portal wiring. API how-to lives in `ai_docs/stripe.md`. ✅ -- `skills/resend/` (`resend-bridge`) — vendor-bridge for Resend. Config + wiring for transactional and marketing email: shared `sendEmail` wrapper (MAIL-1/4/5), React Email render step (MAIL-3/8), marketing consent wiring (MAIL-6). API how-to lives in `ai_docs/resend/`; React Email component API in `ai_docs/react-email/`. ✅ -- `skills/neon/` (`neon-bridge`) — vendor-bridge for Neon. Alternative Postgres host to Supabase (`category: "db-host"`, `role: "alternative"`, `alternative_to: "supabase"`). Covers: `@neondatabase/serverless` install; `DATABASE_URL` (pooled) + `DATABASE_DIRECT_URL` (direct) env vars (server-only, SEC-1); serverless driver choice — HTTP (`neon-http`) for one-shot/edge queries vs WebSocket (`neon-serverless`) for interactive transactions (DB-10); module-level singleton constraint; Drizzle adapter wiring (primary ORM); Prisma adapter wiring (`@prisma/adapter-neon`); Neon branching — branch-per-environment/branch-per-preview (the host-level mechanism for preview-URL infra). API how-to lives in `ai_docs/neon.md` (not pre-populated; run `/pharn-docs` to populate). ✅ -- `skills/prisma/` (`prisma-bridge`) — vendor-bridge for Prisma. Alternative ORM to the default Drizzle (`category: "orm"`, `role: "alternative"`, `alternative_to: "drizzle"`). Covers: `@prisma/client` + `prisma` install + `prisma generate` step; `DATABASE_URL` (pooled) + `DIRECT_URL` (direct, declared in `schema.prisma` as `directUrl`) env vars (server-only, SEC-1); `prisma/schema.prisma` datasource/generator config; `shared/lib/db/client.ts` Prisma client **singleton** (the `globalThis` guard — the critical Prisma + serverless gotcha, DB-10); migration workflow (`prisma migrate dev`/`deploy`, append-only, DB-5); DB-1/3/4/5/10 rules apply regardless of ORM. API how-to lives in `ai_docs/prisma.md` (not pre-populated; MANIFEST entry added — run `/pharn-docs` to populate). ✅ -- `skills/clerk/` (`clerk-bridge`) — vendor-bridge for Clerk. Alternative auth provider to the default Better Auth (`category: "auth"`, `role: "alternative"`, `alternative_to: "better-auth"`). Covers: `@clerk/nextjs` install; env vars (`NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` client-safe, `CLERK_SECRET_KEY` + `CLERK_WEBHOOK_SECRET` server-only per SEC-1); `` root layout wiring; `clerkMiddleware()` + `createRouteMatcher` in `middleware.ts` (the auth gate, AUTH-1); server-side session via `auth()` (AUTH-1); authorization at the action/service layer using Clerk claims (AUTH-2); Clerk Organizations as the tenant context — `orgId` from the verified session (AUTH-3, SAAS-2, SAAS-4); user-sync webhook with Svix signature verification and idempotency (JOBS-1); redirect URL validation (AUTH-9). API how-to lives in `ai_docs/clerk.md` (not pre-populated; run `/pharn-docs` to populate). ✅ +- `skills/feature-module-layers/` — enforces the 4-layer feature-module architecture (schemas / actions / services / lib). **The only skill left in this stack pack** — it is genuinely Next.js-specific. The six vendor bridges that used to live here moved to the `pharn-skills-*` category modules in v0.69.0. -**Vendor-bridge skill convention.** Bridges live in `skills//` (no `vendor-bridges/` folder). Each bridge is `kind: pharn-owned` + `seal: "vendor-bridge"` — never `"PHARN ✓ reviewed"` (CLAUDE.md §5). Bridges are reference/setup skills (consumed by the wizard + `/pharn-build`), not interactive slash-commands. They cover install + env + config files + wiring only; API usage goes in `ai_docs/`. **All six vendor-bridge skills are now complete** (drizzle, stripe, resend, neon, prisma, clerk). - -See `docs/skill-authoring.md § Vendor-bridge skills` for the full authoring guide, the default/alternative framing table, and the 10-section body shape. +The `ai_docs/` snapshots a bridge references still ship with this stack pack (see below); the bridge skills themselves are documented under **Skill-category modules** further down. - `ai_docs/MANIFEST.json` — `/pharn-docs` manifest; 17 library entries (v0.50.0: 11 entries; v0.51.0: +stripe +react-email = 13; v0.57.0: +prisma = 14; v0.64.0: +planetscale = 15; v0.65.0: +convex +firebase = 17). Fetches and seals snapshots in adopter projects at run time. - `ai_docs/better-auth.md` ✅, `ai_docs/drizzle-orm.md` ✅, `ai_docs/tanstack-react-query.md` ✅, `ai_docs/stripe.md` ✅ (populated pre-committed snapshots); `ai_docs/next-safe-action/`, `ai_docs/next-intl/`, `ai_docs/resend/`, `ai_docs/react-email/`, `ai_docs/supabase-storage/`, `ai_docs/supabase-rls/`, `ai_docs/supabase-client/`, `ai_docs/supabase-realtime/`, `ai_docs/shadcn/` — declared in manifest; populated in adopter projects by `/pharn-docs`. diff --git a/docs/skill-authoring.md b/docs/skill-authoring.md index 4d62f1e..c458a06 100644 --- a/docs/skill-authoring.md +++ b/docs/skill-authoring.md @@ -36,7 +36,7 @@ The authoritative, per-artifact-type contract lives in [`../CLAUDE.md`](../CLAUD - **Griller** → `pharn-pipeline/skills/pharn-grill/grillers//GRILLER.md` - **Audit** → `pharn-audits/skills/` - **Standalone skill** → wherever it logically belongs; default `pharn-core/skills/` -- **Vendor-bridge** → the relevant stack pack's `skills//SKILL.md` (e.g. `pharn-stack-nextjs/skills/drizzle/`; no `vendor-bridges/` folder) +- **Vendor-bridge** → the matching skill-category module's `skills//SKILL.md` (e.g. `pharn-skills-orm/skills/drizzle/`; categories are `pharn-skills-{db,orm,auth,payments,email}`; no `vendor-bridges/` folder) ## The eval contract @@ -153,7 +153,7 @@ Required frontmatter fields specific to bridges (beyond the common skill fields) ### Location -`pharn-stack-nextjs/skills//SKILL.md`. There is **no** `vendor-bridges/` folder — bridges live under `skills//` in the relevant stack pack (CLAUDE.md §4). +`pharn-skills-/skills//SKILL.md` — the matching skill-category module (`pharn-skills-{db,orm,auth,payments,email}`), not a stack pack (changed in v0.69.0). There is **no** `vendor-bridges/` folder (CLAUDE.md §4). Add `rule_files_provided_by: "stack-pack"` so the cross-module rule-file dependency is explicit — the `rules/` a bridge cites ship with the installed stack pack, not the category module. ### Body shape @@ -168,7 +168,7 @@ Required frontmatter fields specific to bridges (beyond the common skill fields) 9. What this bridge does NOT do — explicit `ai_docs/` pointer. 10. Relationships table. -See `pharn-stack-nextjs/skills/drizzle/SKILL.md` as the reference exemplar. +See `pharn-skills-orm/skills/drizzle/SKILL.md` as the reference exemplar. ### Evals diff --git a/manifest.json b/manifest.json index 48de3c8..2f14369 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { - "schemaVersion": 1, - "skillsVersion": "0.68.1", + "schemaVersion": 2, + "skillsVersion": "0.69.0", "modules": [ { "name": "pharn-core", @@ -30,6 +30,46 @@ "dependsOn": ["pharn-core"], "description": "Privacy, security, accessibility, cross-feature consistency, telemetry masking, license-compliance, and supply-chain audits — Tier 1 complete (4 of 4), Tier 2 complete (2 of 2), Tier 3 complete (2 of 2); all three tiers complete (8 audits). Includes /pharn-privacy-audit (GDPR/CCPA, 9 auditors, opus-dominant), /pharn-security-review (OWASP Top 10 2021 + CWE, 9 auditors, CVSS-banded severity), /pharn-a11y-audit (WCAG 2.2 AA, 3-agent multi-modal: code-level sonnet + Storybook haiku + Playwright haiku, first audit with runners), /pharn-drift-check (cross-feature consistency, 9 auditors: 7 sonnet + 2 opus, 3 drift modes, codification suggestions), /pharn-posthog-masking-audit (PostHog PII masking, 3 parallel sonnet agents: config + events + dom, three leak surfaces, first Tier 2 audit), /pharn-logging-masking-audit (error-tracking + application-logging + request-logging PII masking, 3 parallel sonnet agents, second Tier 2 audit), /pharn-license-compliance-audit (SPDX license compliance, 5 sonnet auditors: dependency-licenses + compatibility + copyleft-risk + project-license-consistency + attribution-notices, stdlib-only node_modules inventory runner, AGPL-in-SaaS headline, first Tier 3 audit), and /pharn-supply-chain-audit (transitive CVEs + typosquatting + install-scripts + maintainer-health + lockfile-integrity + CycloneDX SBOM, 6 sonnet auditors, hybrid node_modules inventory + npm-audit runner, transitive-CVE headline, second Tier 3 audit — completes the module)." }, + { + "name": "pharn-skills-db", + "version": "0.1.0", + "required": false, + "dependsOn": ["pharn-core"], + "kind": "skill-category", + "description": "Database host vendor-bridge skills. Currently: Neon. Supabase uses its official vendor skill instead. Stack-agnostic." + }, + { + "name": "pharn-skills-orm", + "version": "0.1.0", + "required": false, + "dependsOn": ["pharn-core"], + "kind": "skill-category", + "description": "ORM vendor-bridge skills: Drizzle, Prisma. Stack-agnostic." + }, + { + "name": "pharn-skills-auth", + "version": "0.1.0", + "required": false, + "dependsOn": ["pharn-core"], + "kind": "skill-category", + "description": "Auth vendor-bridge skills: Better Auth, Clerk, Supabase Auth. Stack-agnostic." + }, + { + "name": "pharn-skills-payments", + "version": "0.1.0", + "required": false, + "dependsOn": ["pharn-core"], + "kind": "skill-category", + "description": "Payments vendor-bridge skills: Stripe. Stack-agnostic." + }, + { + "name": "pharn-skills-email", + "version": "0.1.0", + "required": false, + "dependsOn": ["pharn-core"], + "kind": "skill-category", + "description": "Email vendor-bridge skills: Resend. Stack-agnostic." + }, { "name": "pharn-stack-react", "version": "0.1.2", @@ -39,11 +79,174 @@ }, { "name": "pharn-stack-nextjs", - "version": "0.30.0", + "version": "0.31.0", "required": false, "dependsOn": ["pharn-core", "pharn-stack-react"], "exclusiveWith": ["pharn-stack-*"], - "description": "Next.js + Supabase + Better Auth + Vercel stack pack" + "description": "Next.js App Router stack pack — rules, ai_docs, phase-variants, grill-banks. Pair with pharn-skills-* modules for vendor bridges." + } + ], + "wizard": { + "sections": [ + { + "id": "core", + "title": "CORE", + "questions": [ + { + "id": "database", + "prompt": "Database", + "options": [ + { + "value": "supabase", + "label": "Supabase", + "default": true, + "install": null, + "vendorSkill": "supabase" + }, + { "value": "neon", "label": "Neon", "install": "pharn-skills-db/skills/neon" }, + { "value": "convex", "label": "Convex", "install": null, "comingSoon": true }, + { "value": "skip", "label": "skip", "install": null } + ] + } + ] + }, + { + "id": "auth-comms", + "title": "AUTH & COMMS", + "questions": [ + { + "id": "auth", + "prompt": "Auth", + "options": [ + { + "value": "better-auth", + "label": "Better Auth (requires ORM)", + "default": true, + "install": "pharn-skills-auth/skills/better-auth", + "vendorSkill": "better-auth" + }, + { + "value": "clerk", + "label": "Clerk", + "install": "pharn-skills-auth/skills/clerk" + }, + { + "value": "supabase-auth", + "label": "Supabase Auth", + "install": "pharn-skills-auth/skills/supabase-auth", + "vendorSkill": "supabase" + }, + { "value": "skip", "label": "skip", "install": null } + ], + "rules": [ + { + "type": "hide", + "if": { "database": "convex" }, + "options": ["better-auth"], + "reason": "Better Auth requires an ORM — not available with Convex" + }, + { + "type": "hide", + "if": { "database": { "not": "supabase" } }, + "options": ["supabase-auth"], + "reason": "Supabase Auth requires Supabase DB" + } + ] + }, + { + "id": "email", + "prompt": "Email", + "options": [ + { + "value": "resend", + "label": "Resend", + "default": true, + "install": "pharn-skills-email/skills/resend" + }, + { "value": "postmark", "label": "Postmark", "install": null, "comingSoon": true }, + { "value": "skip", "label": "skip", "install": null } + ], + "rules": [ + { + "type": "relabel", + "if": { "auth": "clerk" }, + "options": ["resend"], + "label": "Resend — transactional only (Clerk handles auth emails)" + } + ] + } + ] + }, + { + "id": "api-data", + "title": "API & DATA LAYER", + "questions": [ + { + "id": "orm", + "prompt": "ORM", + "options": [ + { + "value": "drizzle", + "label": "Drizzle", + "default": true, + "install": "pharn-skills-orm/skills/drizzle" + }, + { + "value": "prisma", + "label": "Prisma", + "install": "pharn-skills-orm/skills/prisma" + }, + { "value": "skip", "label": "skip", "install": null } + ], + "rules": [ + { + "type": "hideQuestion", + "if": { "database": "convex" }, + "reason": "Convex has its own query layer — ORM not needed" + }, + { + "type": "warn", + "if": { "auth": "better-auth", "orm": "skip" }, + "message": "Better Auth requires a DB access layer — add Drizzle or Prisma?" + } + ] + } + ] + }, + { + "id": "payments-infra", + "title": "PAYMENTS & INFRA", + "questions": [ + { + "id": "payments", + "prompt": "Payments", + "options": [ + { + "value": "stripe", + "label": "Stripe", + "install": "pharn-skills-payments/skills/stripe", + "vendorSkill": null + }, + { + "value": "lemon-squeezy", + "label": "Lemon Squeezy", + "install": null, + "comingSoon": true + }, + { "value": "polar", "label": "Polar", "install": null, "comingSoon": true }, + { "value": "skip", "label": "skip", "default": true, "install": null } + ] + } + ] + } + ], + "defaults": { + "comment": "Default mode answers — wizard skips all questions and uses these.", + "database": "supabase", + "auth": "better-auth", + "email": "resend", + "orm": "drizzle", + "payments": "skip" } - ] + } } diff --git a/pharn-skills-auth/module.json b/pharn-skills-auth/module.json new file mode 100644 index 0000000..94128df --- /dev/null +++ b/pharn-skills-auth/module.json @@ -0,0 +1,10 @@ +{ + "name": "pharn-skills-auth", + "version": "0.1.0", + "required": false, + "dependsOn": ["pharn-core"], + "description": "Auth vendor-bridge skills: Better Auth, Clerk, Supabase Auth. Stack-agnostic.", + "installs": { + "skills": "skills/" + } +} diff --git a/pharn-skills-auth/skills/better-auth/SKILL.md b/pharn-skills-auth/skills/better-auth/SKILL.md new file mode 100644 index 0000000..999e934 --- /dev/null +++ b/pharn-skills-auth/skills/better-auth/SKILL.md @@ -0,0 +1,58 @@ +--- +name: better-auth-bridge +skill: "better-auth-bridge" +version: "0.1.0" +description: "Vendor-bridge for Better Auth. Config + wiring only. API how-to lives in ai_docs/better-auth.md." +kind: pharn-owned +seal: "vendor-bridge" +module: pharn-skills-auth +library: "better-auth" +official_skill_available: true +model_tier: sonnet +scope: "config + wiring only" +priority: pharn-owned +category: "auth" +role: "default" +alternative_to: null +depends_on: [] +requires_any_orm: ["drizzle-bridge", "prisma-bridge"] +rule_files: ["auth.md", "security.md"] +rule_files_provided_by: "stack-pack" +reads: [".env.example"] +writes: ["shared/lib/auth/auth.ts", "shared/lib/auth/auth-client.ts"] +modes: + - default +pharn_version: "0.69.0" +--- + +# better-auth-bridge (vendor-bridge for Better Auth) + +> **Stub** — full content to be written during a dogfood session. +> Rule files referenced here are provided by your installed stack pack. + +Config + wiring for Better Auth. For API usage — sessions, organizations, magic links, 2FA — +see `ai_docs/better-auth.md`. + +## What this bridges + +Better Auth is the **default auth library** in the PHARN canonical stack. It lives at +`shared/lib/auth/` and connects to the database through an ORM adapter — Drizzle by default, +Prisma supported. One ORM bridge must be installed. + +## Prerequisites + +- An ORM bridge installed and configured (drizzle-bridge or prisma-bridge) +- `BETTER_AUTH_SECRET` env var (generate: `openssl rand -base64 32`) + +## Installation + +```bash +npm install better-auth +``` + +## Verification + +- [ ] `shared/lib/auth/auth.ts` compiles: `npx tsc --noEmit` passes +- [ ] `/api/auth/[...all]/route.ts` returns 200 on GET + +_Vendor-bridge (`seal: vendor-bridge`, `kind: pharn-owned`). Stub — expand during dogfood._ diff --git a/pharn-stack-nextjs/skills/clerk/SKILL.md b/pharn-skills-auth/skills/clerk/SKILL.md similarity index 97% rename from pharn-stack-nextjs/skills/clerk/SKILL.md rename to pharn-skills-auth/skills/clerk/SKILL.md index 4110dba..7c61527 100644 --- a/pharn-stack-nextjs/skills/clerk/SKILL.md +++ b/pharn-skills-auth/skills/clerk/SKILL.md @@ -1,11 +1,11 @@ --- name: clerk-bridge skill: "clerk-bridge" -version: "0.1.0" +version: "0.1.1" description: "Vendor-bridge for Clerk. Config + wiring for Clerk auth (alternative to Better Auth) in the PHARN Next.js stack. API how-to lives in ai_docs/clerk.md." kind: pharn-owned seal: "vendor-bridge" -module: pharn-stack-nextjs +module: pharn-skills-auth library: "clerk" official_skill_available: false model_tier: sonnet @@ -16,20 +16,23 @@ role: "alternative" alternative_to: "better-auth" depends_on: [] rule_files: ["auth.md", "saas-patterns.md", "security.md", "background-jobs.md"] +rule_files_provided_by: "stack-pack" reads: [".env.example", "middleware.ts", "app/layout.tsx"] writes: ["app/layout.tsx", "middleware.ts", "app/api/webhooks/clerk/route.ts", ".env.example"] modes: - default -pharn_version: "0.58.0" +pharn_version: "0.69.0" --- # clerk-bridge (vendor-bridge for Clerk) +> Rule files referenced here (`auth.md`, …) are provided by your installed stack pack. + Config + wiring for Clerk in the PHARN Next.js stack. For API usage — `` component props, the `auth()`/`currentUser()` helpers, the backend SDK, Organizations API, UI components — see `ai_docs/clerk.md`. ## What this bridges -> **Clerk is the alternative to the default auth provider, Better Auth. Use this bridge only if your project chose Clerk; otherwise use Better Auth, which has an official Claude skill — no bridge needed. One auth provider per project.** +> **Clerk is the alternative to the default auth provider, Better Auth. Use this bridge only if your project chose Clerk; otherwise use the `better-auth` bridge (a thin wiring stub — Better Auth's official Claude skill covers API usage). One auth provider per project.** Clerk is a fully-managed auth provider that handles sign-in/sign-up UI, JWTs, session management, and (via Clerk Organizations) multi-tenant membership. In the PHARN stack it replaces Better Auth as the auth layer: `` wraps the root layout, `clerkMiddleware()` in `middleware.ts` gates protected routes, and `auth()` in server components/actions provides the verified session. diff --git a/pharn-skills-auth/skills/supabase-auth/SKILL.md b/pharn-skills-auth/skills/supabase-auth/SKILL.md new file mode 100644 index 0000000..3ea6e52 --- /dev/null +++ b/pharn-skills-auth/skills/supabase-auth/SKILL.md @@ -0,0 +1,32 @@ +--- +name: supabase-auth-bridge +skill: "supabase-auth-bridge" +version: "0.1.0" +description: "Vendor-bridge for Supabase Auth. Requires Supabase as database host." +kind: pharn-owned +seal: "vendor-bridge" +module: pharn-skills-auth +library: "supabase-auth" +official_skill_available: true +model_tier: sonnet +scope: "config + wiring only" +priority: pharn-owned +category: "auth" +role: "alternative" +alternative_to: "better-auth" +depends_on: [] +requires_db: "supabase" +rule_files: ["auth.md"] +rule_files_provided_by: "stack-pack" +modes: + - default +pharn_version: "0.69.0" +--- + +# supabase-auth-bridge (vendor-bridge for Supabase Auth) + +> **Stub** — full content to be written when Supabase Auth ships in the wizard. +> Only available when Database = Supabase. +> Rule files referenced here are provided by your installed stack pack. + +_Vendor-bridge (`seal: vendor-bridge`, `kind: pharn-owned`). Stub._ diff --git a/pharn-skills-db/module.json b/pharn-skills-db/module.json new file mode 100644 index 0000000..3e2e8f2 --- /dev/null +++ b/pharn-skills-db/module.json @@ -0,0 +1,10 @@ +{ + "name": "pharn-skills-db", + "version": "0.1.0", + "required": false, + "dependsOn": ["pharn-core"], + "description": "Database host vendor-bridge skills. Currently: Neon. Supabase uses its official vendor skill instead. Stack-agnostic.", + "installs": { + "skills": "skills/" + } +} diff --git a/pharn-stack-nextjs/skills/neon/SKILL.md b/pharn-skills-db/skills/neon/SKILL.md similarity index 98% rename from pharn-stack-nextjs/skills/neon/SKILL.md rename to pharn-skills-db/skills/neon/SKILL.md index 1784512..cde06f2 100644 --- a/pharn-stack-nextjs/skills/neon/SKILL.md +++ b/pharn-skills-db/skills/neon/SKILL.md @@ -1,11 +1,11 @@ --- name: neon-bridge skill: "neon-bridge" -version: "0.1.0" +version: "0.1.1" description: "Vendor-bridge for Neon — alternative Postgres host to the default Supabase. Connection + serverless driver + branching wiring; API how-to lives in ai_docs/neon.md." kind: pharn-owned seal: "vendor-bridge" -module: pharn-stack-nextjs +module: pharn-skills-db library: "neon" official_skill_available: false model_tier: sonnet @@ -16,13 +16,16 @@ role: "alternative" alternative_to: "supabase" depends_on: [] rule_files: ["database.md", "architecture.md"] +rule_files_provided_by: "stack-pack" reads: [".env.example"] writes: ["shared/lib/db/client.ts", ".env.example"] -pharn_version: "0.55.0" +pharn_version: "0.69.0" --- # neon-bridge (vendor-bridge for Neon) +> Rule files referenced here (`database.md`, …) are provided by your installed stack pack. + Config + wiring for Neon as the Postgres host in the PHARN Next.js stack. For Neon-specific feature APIs — branch management, autoscaling, compute endpoints — see `ai_docs/neon.md` (populate via `/pharn-docs` if you chose Neon). ## What this bridges diff --git a/pharn-skills-email/module.json b/pharn-skills-email/module.json new file mode 100644 index 0000000..a8ebd69 --- /dev/null +++ b/pharn-skills-email/module.json @@ -0,0 +1,10 @@ +{ + "name": "pharn-skills-email", + "version": "0.1.0", + "required": false, + "dependsOn": ["pharn-core"], + "description": "Email vendor-bridge skills: Resend. Stack-agnostic.", + "installs": { + "skills": "skills/" + } +} diff --git a/pharn-stack-nextjs/skills/resend/SKILL.md b/pharn-skills-email/skills/resend/SKILL.md similarity index 98% rename from pharn-stack-nextjs/skills/resend/SKILL.md rename to pharn-skills-email/skills/resend/SKILL.md index 1339158..1fb4d7b 100644 --- a/pharn-stack-nextjs/skills/resend/SKILL.md +++ b/pharn-skills-email/skills/resend/SKILL.md @@ -1,11 +1,11 @@ --- name: resend-bridge skill: "resend-bridge" -version: "0.2.0" +version: "0.2.1" description: "Vendor-bridge for Resend. Config + wiring for transactional and marketing email in the PHARN Next.js stack. API how-to lives in ai_docs/resend/." kind: pharn-owned seal: "vendor-bridge" -module: pharn-stack-nextjs +module: pharn-skills-email library: "resend" official_skill_available: false model_tier: sonnet @@ -16,15 +16,18 @@ role: "default" alternative_to: null depends_on: [] rule_files: ["email.md"] +rule_files_provided_by: "stack-pack" reads: [".env.example", "env.ts"] writes: ["shared/email/send.ts", "shared/email/templates/", "emails/"] modes: - default -pharn_version: "0.56.0" +pharn_version: "0.69.0" --- # resend-bridge (vendor-bridge for resend) +> Rule files referenced here (`email.md`, …) are provided by your installed stack pack. + Config + wiring for Resend in the PHARN Next.js stack. For API usage — `emails.send()` parameters, batch sending, the idempotency header, domains API — see `ai_docs/resend/`. diff --git a/pharn-skills-orm/module.json b/pharn-skills-orm/module.json new file mode 100644 index 0000000..0c51ae1 --- /dev/null +++ b/pharn-skills-orm/module.json @@ -0,0 +1,10 @@ +{ + "name": "pharn-skills-orm", + "version": "0.1.0", + "required": false, + "dependsOn": ["pharn-core"], + "description": "ORM vendor-bridge skills: Drizzle, Prisma. Stack-agnostic.", + "installs": { + "skills": "skills/" + } +} diff --git a/pharn-stack-nextjs/skills/drizzle/SKILL.md b/pharn-skills-orm/skills/drizzle/SKILL.md similarity index 98% rename from pharn-stack-nextjs/skills/drizzle/SKILL.md rename to pharn-skills-orm/skills/drizzle/SKILL.md index 8b7fe83..e4aedc8 100644 --- a/pharn-stack-nextjs/skills/drizzle/SKILL.md +++ b/pharn-skills-orm/skills/drizzle/SKILL.md @@ -1,11 +1,11 @@ --- name: drizzle-bridge skill: "drizzle-bridge" -version: "0.1.0" +version: "0.1.1" description: "Vendor-bridge for drizzle-orm. Config + wiring for Drizzle in the PHARN Next.js stack. API how-to lives in ai_docs/drizzle-orm.md." kind: pharn-owned seal: "vendor-bridge" -module: pharn-stack-nextjs +module: pharn-skills-orm library: "drizzle-orm" official_skill_available: false model_tier: sonnet @@ -16,15 +16,18 @@ role: "default" alternative_to: null depends_on: [] rule_files: ["database.md", "architecture.md"] +rule_files_provided_by: "stack-pack" reads: [".env.example", "drizzle.config.ts"] writes: ["shared/lib/db/client.ts", "shared/lib/db/schema/", "drizzle.config.ts"] modes: - default -pharn_version: "0.52.0" +pharn_version: "0.69.0" --- # drizzle-bridge (vendor-bridge for drizzle-orm) +> Rule files referenced here (`database.md`, …) are provided by your installed stack pack. + Config + wiring for Drizzle in the PHARN Next.js stack. For API usage — queries, relations, transactions, prepared statements — see `ai_docs/drizzle-orm.md`. ## What this bridges diff --git a/pharn-stack-nextjs/skills/prisma/SKILL.md b/pharn-skills-orm/skills/prisma/SKILL.md similarity index 98% rename from pharn-stack-nextjs/skills/prisma/SKILL.md rename to pharn-skills-orm/skills/prisma/SKILL.md index 436a97b..bfc6089 100644 --- a/pharn-stack-nextjs/skills/prisma/SKILL.md +++ b/pharn-skills-orm/skills/prisma/SKILL.md @@ -1,11 +1,11 @@ --- name: prisma-bridge skill: "prisma-bridge" -version: "0.1.0" +version: "0.1.1" description: "Vendor-bridge for Prisma — the alternative ORM to the default Drizzle. Config + wiring (Prisma client singleton, schema datasource, DATABASE_URL/DIRECT_URL, migrations) over Postgres (Supabase or Neon). Use only if your project chose Prisma; otherwise use the Drizzle bridge. API how-to lives in ai_docs/prisma.md." kind: pharn-owned seal: "vendor-bridge" -module: pharn-stack-nextjs +module: pharn-skills-orm library: "prisma" official_skill_available: false model_tier: sonnet @@ -16,15 +16,18 @@ role: "alternative" alternative_to: "drizzle" depends_on: [] rule_files: ["database.md", "architecture.md"] +rule_files_provided_by: "stack-pack" reads: [".env.example", "prisma/schema.prisma"] writes: ["prisma/schema.prisma", "shared/lib/db/client.ts", ".env.example"] modes: - default -pharn_version: "0.57.0" +pharn_version: "0.69.0" --- # prisma-bridge (vendor-bridge for Prisma) +> Rule files referenced here (`database.md`, …) are provided by your installed stack pack. +> > **Prisma is an alternative to the default ORM (Drizzle). Use this bridge only if your project chose Prisma. If you took the default stack, use the Drizzle bridge and discard this one — one ORM per project.** Config + wiring for Prisma in the PHARN Next.js stack. For API usage — `findMany`, relations, transactions, `$queryRaw` — see `ai_docs/prisma.md` (not pre-populated; populate via `/pharn-docs` once you've added the manifest entry). diff --git a/pharn-skills-payments/module.json b/pharn-skills-payments/module.json new file mode 100644 index 0000000..317b04b --- /dev/null +++ b/pharn-skills-payments/module.json @@ -0,0 +1,10 @@ +{ + "name": "pharn-skills-payments", + "version": "0.1.0", + "required": false, + "dependsOn": ["pharn-core"], + "description": "Payments vendor-bridge skills: Stripe. Stack-agnostic.", + "installs": { + "skills": "skills/" + } +} diff --git a/pharn-stack-nextjs/skills/stripe/SKILL.md b/pharn-skills-payments/skills/stripe/SKILL.md similarity index 98% rename from pharn-stack-nextjs/skills/stripe/SKILL.md rename to pharn-skills-payments/skills/stripe/SKILL.md index 4799e60..bef88b8 100644 --- a/pharn-stack-nextjs/skills/stripe/SKILL.md +++ b/pharn-skills-payments/skills/stripe/SKILL.md @@ -1,11 +1,11 @@ --- name: stripe-bridge skill: "stripe-bridge" -version: "0.1.0" +version: "0.1.1" description: "Vendor-bridge for Stripe. Config + wiring for SaaS subscriptions/billing in the PHARN Next.js stack. API how-to lives in ai_docs/stripe.md." kind: pharn-owned seal: "vendor-bridge" -module: pharn-stack-nextjs +module: pharn-skills-payments library: "stripe" official_skill_available: false model_tier: sonnet @@ -16,15 +16,18 @@ role: "default" alternative_to: null depends_on: [] rule_files: ["saas-patterns.md", "security.md", "background-jobs.md", "architecture.md"] +rule_files_provided_by: "stack-pack" reads: [".env.example"] writes: ["shared/lib/stripe.ts", "app/api/webhooks/stripe/route.ts", "features/billing/services/billing.service.ts"] modes: - default -pharn_version: "0.53.0" +pharn_version: "0.69.0" --- # stripe-bridge (vendor-bridge for stripe) +> Rule files referenced here (`saas-patterns.md`, …) are provided by your installed stack pack. + Config + wiring for Stripe in the PHARN Next.js stack. For API usage — creating checkout sessions, managing subscriptions, the full webhook event surface — see `ai_docs/stripe.md`. diff --git a/pharn-stack-nextjs/combinations/planetscale.md b/pharn-stack-nextjs/combinations/planetscale.md index cfe75ac..55a99cf 100644 --- a/pharn-stack-nextjs/combinations/planetscale.md +++ b/pharn-stack-nextjs/combinations/planetscale.md @@ -184,7 +184,7 @@ Honest about what you give up versus the default: ## References - **Rules:** `database.md` (DB-1…DB-10 — DB-5 and DB-10 change; the rest apply), `saas-patterns.md` (SAAS-1 is the sole tenant-isolation control; **SAAS-7 is N/A — no MySQL RLS**), `testing.md` (**TEST-4 elevated — the only safety net**), `architecture.md ARCH-4`, `security.md SEC-1`, `auth.md` (Better Auth over MySQL). -- **Bridge:** `skills/drizzle/SKILL.md` — the Postgres-dialect default; this pattern is its MySQL-dialect variation (same data-access layer, different dialect + driver + migration workflow). +- **Bridge:** `pharn-skills-orm/skills/drizzle/SKILL.md` — the Postgres-dialect default; this pattern is its MySQL-dialect variation (same data-access layer, different dialect + driver + migration workflow). - **`ai_docs/planetscale`** — not populated; run `/pharn-docs` to fetch the Drizzle-MySQL + `@planetscale/database` API snapshot (the default `ai_docs/drizzle-orm.md` excludes MySQL). - **N/A on PlanetScale:** `phase-variants/supabase-rls-scaffolding.md` (planned, Postgres-only); the RLS-primary / session-variable answers in `grill-banks/supabase-rls-vs-row-level-patterns.md` (its app-layer questions still apply and are elevated). - **Official skill:** none (`official_skill_available: false`) — this pattern carries the wiring itself. diff --git a/pharn-stack-nextjs/module.json b/pharn-stack-nextjs/module.json index a2d4b70..bc53e75 100644 --- a/pharn-stack-nextjs/module.json +++ b/pharn-stack-nextjs/module.json @@ -1,10 +1,10 @@ { "name": "pharn-stack-nextjs", - "version": "0.30.0", + "version": "0.31.0", "required": false, "dependsOn": ["pharn-core", "pharn-stack-react"], "exclusiveWith": ["pharn-stack-*"], - "description": "Next.js + Supabase + Better Auth + Vercel stack pack", + "description": "Next.js App Router stack pack — rules, ai_docs, phase-variants, grill-banks. Pair with pharn-skills-* modules for vendor bridges.", "installs": { "skills": "skills/", "rules": "rules/", diff --git a/scripts/schemas/manifest.schema.json b/scripts/schemas/manifest.schema.json index 1cdce3d..90c974a 100644 --- a/scripts/schemas/manifest.schema.json +++ b/scripts/schemas/manifest.schema.json @@ -8,7 +8,7 @@ "properties": { "schemaVersion": { "type": "integer", - "const": 1 + "enum": [1, 2] }, "skillsVersion": { "type": "string", @@ -18,7 +18,14 @@ "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/ModuleEntry" } - } + }, + "wizard": { "$ref": "#/$defs/Wizard" } + }, + "if": { + "properties": { "schemaVersion": { "const": 2 } } + }, + "then": { + "required": ["wizard"] }, "$defs": { "ModuleEntry": { @@ -35,6 +42,10 @@ "pattern": "^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.-]+)?$" }, "required": { "type": "boolean" }, + "kind": { + "type": "string", + "enum": ["skill-category"] + }, "dependsOn": { "type": "array", "items": { "type": "string", "pattern": "^pharn-[a-z0-9-]+$" } @@ -45,6 +56,98 @@ }, "description": { "type": "string", "minLength": 1 } } + }, + "Wizard": { + "type": "object", + "additionalProperties": false, + "required": ["sections"], + "properties": { + "sections": { + "type": "array", + "items": { "$ref": "#/$defs/WizardSection" } + }, + "defaults": { + "type": "object", + "additionalProperties": { "type": "string" } + } + } + }, + "WizardSection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "title", "questions"], + "properties": { + "id": { "type": "string", "minLength": 1 }, + "title": { "type": "string", "minLength": 1 }, + "questions": { + "type": "array", + "items": { "$ref": "#/$defs/WizardQuestion" } + } + } + }, + "WizardQuestion": { + "type": "object", + "additionalProperties": false, + "required": ["id", "prompt", "options"], + "properties": { + "id": { "type": "string", "minLength": 1 }, + "prompt": { "type": "string", "minLength": 1 }, + "options": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/WizardOption" } + }, + "rules": { + "type": "array", + "items": { "$ref": "#/$defs/WizardRule" } + } + } + }, + "WizardOption": { + "type": "object", + "additionalProperties": false, + "required": ["value", "label", "install"], + "properties": { + "value": { "type": "string", "minLength": 1 }, + "label": { "type": "string", "minLength": 1 }, + "default": { "type": "boolean" }, + "comingSoon": { "type": "boolean" }, + "install": { "type": ["string", "null"] }, + "vendorSkill": { "type": ["string", "null"] } + } + }, + "WizardRule": { + "type": "object", + "additionalProperties": false, + "required": ["type", "if"], + "properties": { + "type": { + "type": "string", + "enum": ["hide", "hideQuestion", "relabel", "warn"] + }, + "if": { + "type": "object", + "minProperties": 1, + "additionalProperties": { + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "additionalProperties": false, + "required": ["not"], + "properties": { "not": { "type": "string" } } + } + ] + } + }, + "options": { + "type": "array", + "items": { "type": "string" } + }, + "reason": { "type": "string" }, + "label": { "type": "string" }, + "message": { "type": "string" } + } } } } diff --git a/test/validate.test.mjs b/test/validate.test.mjs index f10308c..97de234 100644 --- a/test/validate.test.mjs +++ b/test/validate.test.mjs @@ -24,16 +24,23 @@ function runValidate(cwd) { // Build a fixture repo on disk and return its root. `modules` maps folder name → // { manifestEntry, moduleJson }. A null moduleJson means "folder with no module.json". -function makeRepo({ skillsVersion = "1.0.0", manifestSkillsVersion, modules }) { +function makeRepo({ + skillsVersion = "1.0.0", + manifestSkillsVersion, + schemaVersion = 1, + wizard, + modules, +}) { const root = mkdtempSync(path.join(os.tmpdir(), "validate-test-")) writeFileSync(path.join(root, "SKILLS_VERSION"), skillsVersion + "\n") const manifest = { - schemaVersion: 1, + schemaVersion, skillsVersion: manifestSkillsVersion ?? skillsVersion, modules: Object.values(modules) .map((m) => m.manifestEntry) .filter(Boolean), } + if (wizard !== undefined) manifest.wizard = wizard writeFileSync(path.join(root, "manifest.json"), JSON.stringify(manifest, null, 2)) for (const [folder, m] of Object.entries(modules)) { const dir = path.join(root, folder) @@ -302,3 +309,116 @@ test("validate.mjs: SKILLS_VERSION mismatch with manifest is an error", () => { rmSync(root, { recursive: true, force: true }) } }) + +// A schemaVersion-2 fixture with a skill-category module and a valid wizard block. +function skillCategoryModuleSpec() { + const modules = coreModuleSpec() + const dbEntry = { + name: "pharn-skills-db", + version: "0.1.0", + required: false, + dependsOn: ["pharn-core"], + kind: "skill-category", + description: "Database host vendor-bridge skills. Stack-agnostic.", + } + modules["pharn-skills-db"] = { + manifestEntry: { ...dbEntry }, + // module.json carries no `kind` — that field lives only in the manifest entry. + moduleJson: { + name: "pharn-skills-db", + version: "0.1.0", + required: false, + dependsOn: ["pharn-core"], + description: "Database host vendor-bridge skills. Stack-agnostic.", + installs: { skills: "skills/" }, + }, + } + return modules +} + +const validWizard = { + sections: [ + { + id: "core", + title: "CORE", + questions: [ + { + id: "database", + prompt: "Database", + options: [ + { value: "neon", label: "Neon", install: "pharn-skills-db/skills/neon" }, + { value: "skip", label: "skip", install: null }, + ], + rules: [ + { + type: "hide", + if: { database: { not: "supabase" } }, + options: ["supabase-auth"], + reason: "needs supabase", + }, + ], + }, + ], + }, + ], + defaults: { comment: "defaults", database: "neon" }, +} + +test("validate.mjs: schemaVersion 2 with a wizard block and a skill-category module passes", () => { + const root = makeRepo({ + schemaVersion: 2, + wizard: validWizard, + modules: skillCategoryModuleSpec(), + }) + try { + const res = runValidate(root) + assert.equal(res.status, 0, res.stdout) + assert.match(res.stdout, /0 errors/) + } finally { + rmSync(root, { recursive: true, force: true }) + } +}) + +test("validate.mjs: a wizard option missing the required install field is a schema error", () => { + const badWizard = { + sections: [ + { + id: "core", + title: "CORE", + questions: [ + { + id: "database", + prompt: "Database", + // `install` is required on every option — omit it to trip the schema. + options: [{ value: "neon", label: "Neon" }], + }, + ], + }, + ], + } + const root = makeRepo({ + schemaVersion: 2, + wizard: badWizard, + modules: skillCategoryModuleSpec(), + }) + try { + const res = runValidate(root) + assert.equal(res.status, 1, res.stdout) + assert.match(res.stdout, /manifest\.json/) + } finally { + rmSync(root, { recursive: true, force: true }) + } +}) + +test("validate.mjs: an unknown module kind is a schema error", () => { + const modules = skillCategoryModuleSpec() + modules["pharn-skills-db"].manifestEntry.kind = "not-a-real-kind" + const root = makeRepo({ schemaVersion: 2, wizard: validWizard, modules }) + try { + const res = runValidate(root) + assert.equal(res.status, 1, res.stdout) + assert.match(res.stdout, /manifest\.json/) + } finally { + rmSync(root, { recursive: true, force: true }) + } +})