create-ailk 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +7 -2
  2. package/dist/cli.js +5 -1
  3. package/dist/content-payloads.d.ts +37 -0
  4. package/dist/content-payloads.js +64 -0
  5. package/dist/lib/fetch-module.d.ts +3 -3
  6. package/dist/lib/fetch-module.js +11 -5
  7. package/dist/lib/fetch-template.d.ts +1 -1
  8. package/dist/lib/fetch-template.js +7 -4
  9. package/dist/module-architecture.d.ts +9 -0
  10. package/dist/module-architecture.js +43 -12
  11. package/dist/parse-args.d.ts +2 -0
  12. package/dist/parse-args.js +7 -0
  13. package/dist/surfaces.js +2 -3
  14. package/package.json +3 -3
  15. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +4 -3
  16. package/templates/.claude/settings.json +12 -0
  17. package/templates/.env.example +1 -1
  18. package/templates/CLAUDE.md +38 -22
  19. package/templates/README.md +12 -12
  20. package/templates/apps/api/jest.config.cjs +1 -0
  21. package/templates/apps/api/package.json +1 -0
  22. package/templates/apps/mcp/CLAUDE.md +23 -10
  23. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +160 -17
  24. package/templates/apps/mcp/__tests__/index.test.ts +118 -1
  25. package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +425 -5
  26. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +67 -3
  27. package/templates/apps/mcp/__tests__/server.test.ts +162 -1
  28. package/templates/apps/mcp/src/index.ts +35 -3
  29. package/templates/apps/mcp/src/lib/commerce-claim.ts +193 -24
  30. package/templates/apps/mcp/src/server.ts +77 -5
  31. package/templates/apps/mcp/src/tools/purchase.ts +1 -1
  32. package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +15 -0
  33. package/templates/apps/web/app/.well-known/mcp.json/route.ts +10 -3
  34. package/templates/apps/web/src/lib/commerce-claim.ts +15 -6
  35. package/templates/content/_site.mdx +23 -4
  36. package/templates/database/CHANGELOG.md +7 -0
  37. package/templates/database/package.json +1 -1
  38. package/versions.json +34 -33
  39. package/templates/.claude/skills/README.md +0 -37
  40. package/templates/.claude/skills/add-auth/SKILL.md +0 -313
  41. package/templates/.claude/skills/add-auth/references/env-vars.md +0 -106
  42. package/templates/.claude/skills/add-auth/references/oauth-app-setup.md +0 -180
  43. package/templates/.claude/skills/add-page/SKILL.md +0 -260
  44. package/templates/.claude/skills/add-page/references/page-types.md +0 -123
  45. package/templates/.claude/skills/add-page/scripts/site-pages.mjs +0 -87
  46. package/templates/.claude/skills/add-page/templates/comparison-page.mdx.tmpl +0 -99
  47. package/templates/.claude/skills/add-page/templates/content.mdx.tmpl +0 -18
  48. package/templates/.claude/skills/add-page/templates/flow-landing-page.mdx.tmpl +0 -43
  49. package/templates/.claude/skills/add-page/templates/report-page.mdx.tmpl +0 -52
  50. package/templates/.claude/skills/add-page/templates/reports-list.mdx.tmpl +0 -38
  51. package/templates/.claude/skills/check-site/SKILL.md +0 -245
  52. package/templates/.claude/skills/check-site/references/common-errors.md +0 -116
  53. package/templates/.claude/skills/check-site/references/findings-glossary.md +0 -140
  54. package/templates/.claude/skills/check-site/references/severity-levels.md +0 -114
  55. package/templates/.claude/skills/fill-page/SKILL.md +0 -199
  56. package/templates/.claude/skills/fill-page/profiles/plumbing.yaml +0 -80
  57. package/templates/.claude/skills/fill-page/references/section-sources.md +0 -41
  58. package/templates/.claude/skills/setup-database/SKILL.md +0 -156
  59. package/templates/.claude/skills/setup-database/scripts/verify-inbox.ts +0 -154
  60. package/templates/.claude/skills/setup-deploy/SKILL.md +0 -312
  61. package/templates/.claude/skills/setup-deploy/references/runbook-template.md +0 -204
  62. package/templates/.claude/skills/setup-deploy/references/vercel-json-template.md +0 -98
  63. package/templates/.claude/skills/update-page/SKILL.md +0 -117
  64. package/templates/.claude/skills/update-page/scripts/update-page.ts +0 -640
  65. package/templates/.claude/skills/update-page/templates/redirects.mjs.tmpl +0 -31
  66. package/templates/.claude/skills/upgrade-plan/SKILL.md +0 -121
  67. package/templates/.claude/skills/upgrade-plan/references/options.md +0 -24
  68. package/templates/.claude/skills/upgrade-plan/scripts/upgrade-plan.ts +0 -527
  69. package/templates/apps/mcp/__tests__/lib/require-paid-tier.test.ts +0 -75
  70. package/templates/apps/mcp/src/lib/require-paid-tier.ts +0 -44
@@ -12,7 +12,9 @@
12
12
  * the deployments whose MCP server registers it. Without that, a KEYED site
13
13
  * would register `start_purchase` while hiding it from its own manifest —
14
14
  * the surface disagreement `apps/mcp/__tests__/catalog-drift.test.ts` exists
15
- * to make impossible.
15
+ * to make impossible. The same single read also yields the licence features
16
+ * the deployment holds (#7136), which gate the five `feature`-tagged tools
17
+ * here exactly as they gate registration on the MCP server.
16
18
  *
17
19
  * Mirrors /.well-known/ai-plugin.json conventions: Response.json with a
18
20
  * conservative cache-control header.
@@ -23,7 +25,10 @@
23
25
  */
24
26
 
25
27
  import packageJson from "../../../package.json";
26
- import { resolveCommerceClaim } from "../../../src/lib/commerce-claim";
28
+ import {
29
+ COMMERCE_FEATURE,
30
+ resolveLicenceFeatures,
31
+ } from "../../../src/lib/commerce-claim";
27
32
  import { resolveBaseUrl } from "../../../src/lib/routes";
28
33
 
29
34
  import { buildMcpManifest } from "./build-manifest";
@@ -46,11 +51,13 @@ export async function GET(): Promise<Response> {
46
51
  // (apps/mcp running in HTTP mode). When set, the manifest includes it
47
52
  // as an additional transport descriptor for discovering agents.
48
53
  const mcpHttpUrlEnv = process.env["MCP_PUBLIC_HTTP_URL"];
54
+ const heldFeatures = await resolveLicenceFeatures();
49
55
 
50
56
  const manifest = buildMcpManifest({
51
57
  baseUrl: resolveBaseUrl(),
52
58
  ailkVersion: packageJson.version,
53
- commerceClaimed: await resolveCommerceClaim(),
59
+ commerceClaimed: heldFeatures.includes(COMMERCE_FEATURE),
60
+ heldFeatures,
54
61
  // Spread conditional to satisfy exactOptionalPropertyTypes: omit the key
55
62
  // entirely when the env var is not set rather than passing undefined.
56
63
  ...(mcpHttpUrlEnv ? { mcpHttpUrl: mcpHttpUrlEnv } : {}),
@@ -15,19 +15,28 @@
15
15
  * unlike `apps/mcp/src/lib/commerce-claim.ts`'s boot-time memoisation — so a
16
16
  * site that buys commerce after its last deploy starts serving immediately
17
17
  * rather than waiting on a redeploy.
18
+ *
19
+ * {@link resolveLicenceFeatures} (#7136) is the same single read, returning
20
+ * every licence feature the deployment holds: `/.well-known/mcp.json` gates
21
+ * the five `feature`-tagged MCP tools on it, from one call per request.
18
22
  */
19
23
  import { checkLicense } from "@working-theory/license";
20
24
 
21
25
  /** The licence `features[]` claim that turns the commerce add-on on. */
22
- const COMMERCE_FEATURE = "commerce";
26
+ export const COMMERCE_FEATURE = "commerce";
23
27
 
24
- export async function resolveCommerceClaim(): Promise<boolean> {
28
+ /** The licence features this deployment positively holds — `[]` when the
29
+ * licence is revoked, carries no `features` array, or cannot be read. */
30
+ export async function resolveLicenceFeatures(): Promise<readonly string[]> {
25
31
  try {
26
32
  const result = await checkLicense();
27
- return (
28
- result.revoked !== true && result.features.includes(COMMERCE_FEATURE)
29
- );
33
+ if (result.revoked === true) return [];
34
+ return Array.isArray(result.features) ? result.features : [];
30
35
  } catch {
31
- return false;
36
+ return [];
32
37
  }
33
38
  }
39
+
40
+ export async function resolveCommerceClaim(): Promise<boolean> {
41
+ return (await resolveLicenceFeatures()).includes(COMMERCE_FEATURE);
42
+ }
@@ -49,11 +49,13 @@ theme:
49
49
  This file is your site's canonical configuration. Edit the frontmatter above to
50
50
  set your site name, base URL, default locale, and identity.
51
51
 
52
- `siteIdentity.kind` takes one of three values:
52
+ `siteIdentity.kind` takes one of four values:
53
53
 
54
- - Organization — a company, agency, nonprofit, or team
55
- - Person — a solo practitioner, consultant, or personal site
56
- - LocalBusiness — a business with a physical location customers visit
54
+ - Organization — a company, agency, nonprofit, or team
55
+ - Person — a solo practitioner, consultant, or personal site
56
+ - LocalBusiness — a business with a physical location customers visit
57
+ - SoftwareApplication — a product's own site, when its maker has a site of
58
+ its own (see below)
57
59
 
58
60
  `siteIdentity.secondary` (optional) names a second entity the site is about, and
59
61
  every page's structured data carries it:
@@ -73,6 +75,23 @@ For example, nested under `siteIdentity:`
73
75
  name: Acme App
74
76
  url: https://example.com/app
75
77
 
78
+ `kind: SoftwareApplication` is the inverse of that product secondary: the
79
+ product is the site's entity, and its maker — whose own site already declares
80
+ it — is named as `publisher` by the stable `@id` that site publishes. It takes
81
+ the product's `name` and `url` (optional `logo`, `description`,
82
+ `applicationCategory`, `operatingSystem`, `sameAs`) and a required `publisher`
83
+ with an `id` URL and an optional `name`; it takes no `secondary`. No second
84
+ Organization is declared here — every page's structured data points at the
85
+ maker's `@id`, and the home page's main entity is the product. For example:
86
+
87
+ siteIdentity:
88
+ kind: SoftwareApplication
89
+ name: Acme App
90
+ url: https://acme-app.example
91
+ publisher:
92
+ id: https://acme.example/#organization
93
+ name: Acme Inc.
94
+
76
95
  `sameAs` is the list of URLs that are unambiguously the same entity as you —
77
96
  your LinkedIn company page, your GitHub org, your X profile. Answer engines use
78
97
  it to reconcile your site with the rest of your presence, so it is worth
@@ -1,5 +1,12 @@
1
1
  # @working-theory/database
2
2
 
3
+ ## 0.30.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies []:
8
+ - @working-theory/validation@0.30.0
9
+
3
10
  ## 0.29.0
4
11
 
5
12
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@working-theory/database",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "license": "Apache-2.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/versions.json CHANGED
@@ -1,39 +1,40 @@
1
1
  {
2
- "@working-theory/admin-ui": "0.29.0",
3
- "@working-theory/aeo": "0.29.0",
4
- "@working-theory/agent-context": "0.29.0",
2
+ "@working-theory/admin-ui": "0.30.0",
3
+ "@working-theory/aeo": "0.30.0",
4
+ "@working-theory/agent-context": "0.30.0",
5
5
  "@working-theory/agent-contract": "0.1.0",
6
6
  "@working-theory/agent-memory": "0.0.0",
7
7
  "@working-theory/ai": "0.1.0",
8
- "@working-theory/analytics": "0.29.0",
9
- "@working-theory/api-client": "0.29.0",
8
+ "@working-theory/analytics": "0.30.0",
9
+ "@working-theory/api-client": "0.30.0",
10
10
  "@working-theory/cda-filesystem": "0.0.0",
11
- "@working-theory/cli": "0.29.0",
12
- "@working-theory/common": "0.29.0",
11
+ "@working-theory/cli": "0.30.0",
12
+ "@working-theory/common": "0.30.0",
13
13
  "@working-theory/consent": "0.1.0",
14
- "@working-theory/content-adapters": "0.29.0",
15
- "@working-theory/content-projection": "0.29.0",
16
- "@working-theory/email": "0.29.0",
14
+ "@working-theory/content-adapters": "0.30.0",
15
+ "@working-theory/content-projection": "0.30.0",
16
+ "@working-theory/email": "0.30.0",
17
17
  "@working-theory/env": "0.0.0",
18
- "@working-theory/eslint-config": "0.29.0",
19
- "@working-theory/fumadocs-schema": "0.29.0",
18
+ "@working-theory/eslint-config": "0.30.0",
19
+ "@working-theory/fumadocs-schema": "0.30.0",
20
20
  "@working-theory/ids": "0.8.0",
21
- "@working-theory/jsonld": "0.29.0",
22
- "@working-theory/license": "0.29.0",
23
- "@working-theory/metadata": "0.29.0",
24
- "@working-theory/observability": "0.29.0",
25
- "@working-theory/page-renderer": "0.29.0",
26
- "@working-theory/schema": "0.29.0",
27
- "@working-theory/schema-pack-registry": "0.29.0",
28
- "@working-theory/schema-validator": "0.29.0",
29
- "@working-theory/sections-registry": "0.29.0",
30
- "@working-theory/site-config": "0.29.0",
31
- "@working-theory/stripe-client": "0.29.0",
32
- "@working-theory/templates": "0.29.0",
33
- "@working-theory/theme": "0.29.0",
34
- "@working-theory/ui": "0.29.0",
35
- "@working-theory/validation": "0.29.0",
36
- "@working-theory/web-i18n": "0.29.0",
21
+ "@working-theory/jsonld": "0.30.0",
22
+ "@working-theory/license": "0.30.0",
23
+ "@working-theory/metadata": "0.30.0",
24
+ "@working-theory/observability": "0.30.0",
25
+ "@working-theory/page-renderer": "0.30.0",
26
+ "@working-theory/plugin-mcp": "0.1.1",
27
+ "@working-theory/schema": "0.30.0",
28
+ "@working-theory/schema-pack-registry": "0.30.0",
29
+ "@working-theory/schema-validator": "0.30.0",
30
+ "@working-theory/sections-registry": "0.30.0",
31
+ "@working-theory/site-config": "0.30.0",
32
+ "@working-theory/stripe-client": "0.30.0",
33
+ "@working-theory/templates": "0.30.0",
34
+ "@working-theory/theme": "0.30.0",
35
+ "@working-theory/ui": "0.30.0",
36
+ "@working-theory/validation": "0.30.0",
37
+ "@working-theory/web-i18n": "0.30.0",
37
38
  "@working-theory/billing-client": "0.0.0",
38
39
  "@working-theory/branding": "0.0.0",
39
40
  "@working-theory/license-client": "0.0.0",
@@ -41,12 +42,12 @@
41
42
  "@working-theory/marketing-api-client": "0.0.0",
42
43
  "@working-theory/partner-session": "0.0.0",
43
44
  "@working-theory/preview-protocol": "0.0.0",
44
- "@working-theory/ui-commerce": "0.23.6",
45
- "@working-theory/ui-docs-blog": "0.23.6",
46
- "@working-theory/ui-scheduling": "0.23.6",
47
- "@working-theory/ui-signal-collectors": "0.24.4",
45
+ "@working-theory/ui-commerce": "0.23.7",
46
+ "@working-theory/ui-docs-blog": "0.23.7",
47
+ "@working-theory/ui-scheduling": "0.23.7",
48
+ "@working-theory/ui-signal-collectors": "0.24.5",
48
49
  "@working-theory/web": "0.0.0",
49
50
  "@working-theory/api": "1.0.0",
50
51
  "@working-theory/mcp": "0.0.0",
51
- "@working-theory/database": "0.29.0"
52
+ "@working-theory/database": "0.30.0"
52
53
  }
@@ -1,37 +0,0 @@
1
- # Skills
2
-
3
- Skills are reusable Claude Code workflows scoped to this project. Each
4
- lives in its own subdirectory with a `SKILL.md` describing when to use
5
- it and how it works.
6
-
7
- The eight skills below ship with this project. Claude Code discovers
8
- them automatically at session start — nothing to install or enable.
9
-
10
- ## Available skills
11
-
12
- | Skill | Purpose |
13
- | -------------- | -------------------------------------------------------------------------------------------------------------- |
14
- | `add-page` | Add a page this site can serve — its content file, structured data and nav link — adding a missing route with `create-ailk add-route` first. Also documents the `schema:` frontmatter override for any page. |
15
- | `update-page` | Rename a page — give it a new URL and/or a new title — in one plan-then-confirm pass: its content files, its own route folder, exact internal links and the manifest row move together, and the old URL gets a permanent redirect. |
16
- | `add-auth` | Activate auth providers — magic-link via Resend, Google OAuth, GitHub OAuth. |
17
- | `setup-deploy` | Emit deploy configuration — `vercel.json`, `.env.example`, and `RUNBOOK.md` — for a Vercel deploy. Asks where leads go (email or a database) and warns when neither is set. |
18
- | `setup-database` | Take the site from no database to a working lead inbox: provision Postgres, create the tables, pin the owner, and verify one test message lands in `/leads`. |
19
- | `check-site` | Run `ailk-audit --json` and explain the score and findings in plain language. |
20
- | `fill-page` | Fill one page's section content from a category profile: labelled sample content (tier 1) or your own first draft from a short intake (tier 2). |
21
- | `upgrade-plan` | Compare Free, Pro, Pro Plus, Agency and the commerce add-on side by side, with the prices the public pricing page publishes right now, and recommend one option for this site with a reason. |
22
-
23
- `scaffold-commerce` is delivered with the commerce module (`--modules commerce`, licensed).
24
-
25
- ## How a skill works
26
-
27
- Each subdirectory holds:
28
-
29
- - `SKILL.md` — when to invoke, what it produces, what it requires.
30
- - Any supporting prompt fragments, code templates, or reference
31
- files the skill loads at runtime.
32
-
33
- ## Adding your own
34
-
35
- To add a project-specific skill, create a folder here with a
36
- `SKILL.md` describing it. Claude Code will discover it automatically
37
- on session start. See the skills above for examples of the format.
@@ -1,313 +0,0 @@
1
- ---
2
- name: add-auth
3
- description: Activate the site's sign-in providers (email-and-password, Google OAuth, GitHub OAuth) on an AILK consumer site. Use when the user says "wire up auth", "add login", "set up sign-in", "I need users to log in", or asks how to enable email / Google / GitHub sign-in on their AILK site. The skill writes env vars for whichever subset of providers the consumer activates and prints (plus saves) an OAuth-app-setup runbook. It does NOT modify `apps/web/auth.ts` and does NOT emit any pages.
4
- ---
5
-
6
- # add-auth
7
-
8
- Activate one or more sign-in providers on an AILK site by writing the
9
- env vars `getActiveProviderIds()` (`apps/web/lib/auth-env.ts`) reads
10
- to detect them, plus a `RUNBOOK.md` that documents the OAuth-app-creation
11
- steps for the consumer's records.
12
-
13
- The site's auth library is **Better Auth**, configured in
14
- `apps/web/auth.ts` and mounted at `apps/web/app/api/auth/[...all]/route.ts`.
15
-
16
- ## When to use
17
-
18
- The user has a working AILK site (post-`pnpm create ailk`) and wants
19
- to gate routes behind sign-in or accept email, Google or GitHub
20
- sign-in. Phrases that should trigger this skill:
21
-
22
- - "wire up auth" / "add login" / "set up sign-in"
23
- - "I want users to log in"
24
- - "enable Google sign-in" / "enable GitHub login"
25
- - "set up email login" / "set up magic-link auth"
26
- - "add OAuth"
27
-
28
- A magic link is not wired: Better Auth core ships no magic-link
29
- provider, so the `email` provider id switches on email-and-password
30
- sign-in instead. Say so when the consumer asks for a magic link, and
31
- offer `email` on those terms.
32
-
33
- Skip this skill when:
34
-
35
- - The user wants a new page. The site already ships its sign-in and
36
- sign-up pages (`apps/web/app/[locale]/(auth)/`), which list whichever
37
- providers are active. Any other page is `add-page`'s job.
38
- - The user is asking about a third-party provider outside the three
39
- provider ids below (Apple, Microsoft, Twitter, etc.). Adding one is a
40
- code change to `apps/web/auth.ts`, not env wiring. Acknowledge the
41
- request and stop.
42
- - The user is in production and needs production callback URLs. v1
43
- ships dev-mode callbacks (`http://localhost:3000`) only —
44
- production callback setup is deferred to `setup-deploy`
45
- or a future deploy skill. Note this and stop.
46
-
47
- ## The provider ids
48
-
49
- `getActiveProviderIds()` in `apps/web/lib/auth-env.ts` returns a
50
- provider id only when every one of its env vars is set to a non-empty
51
- value:
52
-
53
- | Provider id | Env vars | What it switches on |
54
- | ----------- | ------------------------------------------- | --------------------------------------------------------------- |
55
- | `email` | `AUTH_EMAIL_SERVER` + `AUTH_EMAIL_FROM` | Email-and-password sign-in (Better Auth's `emailAndPassword`) |
56
- | `google` | `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET` | Google OAuth (Better Auth's `socialProviders.google`) |
57
- | `github` | `GITHUB_CLIENT_ID` + `GITHUB_CLIENT_SECRET` | GitHub OAuth (Better Auth's `socialProviders.github`) |
58
-
59
- `references/env-vars.md` is the full contract, including the auxiliary
60
- vars (`BETTER_AUTH_URL`, `BETTER_AUTH_SECRET`, `AILK_AUTH_REQUIRED`,
61
- `AILK_AUTH_SIGNUP_OPEN`, `AILK_AUTH_PASSWORD_RESET`,
62
- `AUTH_EMAIL_WEBHOOK_SECRET`).
63
-
64
- ## What this skill does (and does not do)
65
-
66
- **Does:**
67
-
68
- - Reads `.env.local` to see which provider ids are already active.
69
- - Asks the consumer which of the three provider ids (`email`,
70
- `google`, `github`) they want to activate _this session_.
71
- - For each picked provider, walks the consumer through
72
- OAuth-app creation (`google` / `github`) or the Resend sender setup
73
- (`email`), then writes the resulting values to `.env.local`.
74
- - Checks that sign-in has a database to write to. Better Auth always
75
- runs its Prisma adapter with database sessions, so sign-in needs
76
- `DATABASE_URL` and the auth tables in place.
77
- - Writes (or merges) a `RUNBOOK.md` in the consumer's repo root
78
- with the same setup instructions the consumer just followed, so
79
- they can refer back later or hand to a collaborator.
80
-
81
- **Does not:**
82
-
83
- - Modify `apps/web/auth.ts`. The three provider ids are env-var-driven
84
- — `apps/web/auth.ts` reads `getActiveProviderIds()` at startup and
85
- builds only the providers it returns. No code changes are needed.
86
- - Create any new files under `app/`, `pages/`, or `content/`. The
87
- emit is limited to `.env.local` writes and a `RUNBOOK.md` write.
88
- - Set up production callback URLs. Dev-mode (`http://localhost:3000`)
89
- only at v1.
90
- - Add, remove, or modify any provider in `apps/web/auth.ts`. A
91
- provider the consumer added there by hand (e.g., Apple) is preserved
92
- as-is and never touched by this skill.
93
- - Accept production OAuth credentials. If the consumer pastes a
94
- production-looking secret, that's acceptable, but the runbook still
95
- documents only the dev callback URL.
96
-
97
- ## The mechanic
98
-
99
- ### Step 1 — Show the menu
100
-
101
- Greet the consumer and present the three provider ids, marking
102
- which are already active (env vars present in `.env.local`):
103
-
104
- ```
105
- Which sign-in providers do you want to activate?
106
-
107
- [ ] email (email-and-password sign-in)
108
- [ ] google (Google OAuth)
109
- [ ] github (GitHub OAuth)
110
-
111
- You can pick any subset, including "none of the above" (in which case
112
- this skill exits without writing anything). You can also re-run this
113
- skill later to activate more providers — already-active providers
114
- will be skipped automatically.
115
- ```
116
-
117
- If `.env.local` already has any of these active, mark them:
118
-
119
- ```
120
- [x] email (already active — AUTH_EMAIL_SERVER + AUTH_EMAIL_FROM are set)
121
- [ ] google (Google OAuth)
122
- [ ] github (GitHub OAuth)
123
- ```
124
-
125
- Wait for the consumer to pick a subset. Then, for each picked
126
- provider, run the matching sub-flow.
127
-
128
- ### Step 2 — Run per-provider sub-flows
129
-
130
- #### `email` sub-flow
131
-
132
- 1. Tell the consumer: _"The `email` provider turns on
133
- email-and-password sign-in. It switches on when `AUTH_EMAIL_SERVER`
134
- and `AUTH_EMAIL_FROM` are both set. You'll need a Resend API key
135
- (free tier is fine for development). Get one at
136
- <https://resend.com/api-keys>."_
137
- 2. Wait for the consumer to paste the API key.
138
- 3. Ask for the `AUTH_EMAIL_FROM` address. This must be a verified
139
- sender on the consumer's Resend account. For development it's
140
- common to use `onboarding@resend.dev` (Resend's shared dev
141
- sender). Mention that.
142
- 4. Construct `AUTH_EMAIL_SERVER` as
143
- `smtp://resend:<API_KEY>@smtp.resend.com:465`.
144
- 5. Write to `.env.local`:
145
- ```
146
- AUTH_EMAIL_SERVER=smtp://resend:re_...@smtp.resend.com:465
147
- AUTH_EMAIL_FROM=onboarding@resend.dev
148
- ```
149
- 6. Tell the consumer who actually sends the mail. `apps/web` sends
150
- none: `AUTH_EMAIL_SERVER` is read for its presence only, and
151
- `apps/web/lib/auth-email.ts` relays each auth email (verification,
152
- password reset) to `apps/api`, which sends it with `RESEND_API_KEY`
153
- from `AUTH_EMAIL_FROM`. For that mail to go out, `RESEND_API_KEY`
154
- and `AUTH_EMAIL_FROM` must be set for `apps/api`, and
155
- `AUTH_EMAIL_WEBHOOK_SECRET` (any long random string, the same value)
156
- for both apps. Password reset — and the verification mail with it —
157
- is off until `AILK_AUTH_PASSWORD_RESET=1`, and also needs
158
- `BETTER_AUTH_URL` and `BETTER_AUTH_SECRET` (`resetSendConfigured()`
159
- in `apps/web/lib/auth-env.ts`).
160
- 7. Tell the consumer that public sign-up stays closed until they set
161
- `AILK_AUTH_SIGNUP_OPEN=1` (read by `isSignUpOpen()` in
162
- `apps/web/lib/auth-env.ts`). With sign-up closed and `email` the only
163
- provider, nobody can create the first account — so either open
164
- sign-up long enough to create it, or activate `google` or `github`
165
- as well. Ask which they want; leave the flag unset otherwise.
166
-
167
- #### `google` sub-flow
168
-
169
- 1. Tell the consumer the steps in chat AND queue the same content for
170
- the runbook (see Step 3). Steps:
171
- - Go to <https://console.cloud.google.com/apis/credentials>.
172
- - Click **Create Credentials → OAuth client ID**. If prompted,
173
- configure the OAuth consent screen first (**External** → fill
174
- name, support email, developer contact email; Better Auth
175
- requests the scopes at runtime, so leave the scope list empty).
176
- - Application type: **Web application**.
177
- - Name: anything (e.g., "AILK dev").
178
- - **Authorized redirect URIs** — add exactly:
179
- `http://localhost:3000/api/auth/callback/google`
180
- - Click **Create**. Copy the **Client ID** and **Client secret**
181
- from the dialog.
182
- 2. Wait for the consumer to paste both values.
183
- 3. Write to `.env.local`:
184
- ```
185
- GOOGLE_CLIENT_ID=...
186
- GOOGLE_CLIENT_SECRET=...
187
- ```
188
-
189
- #### `github` sub-flow
190
-
191
- 1. Tell the consumer the steps in chat AND queue the same content for
192
- the runbook. Steps:
193
- - Go to <https://github.com/settings/developers>.
194
- - Click **New OAuth App**.
195
- - Application name: anything (e.g., "AILK dev").
196
- - Homepage URL: `http://localhost:3000`
197
- - **Authorization callback URL** — add exactly:
198
- `http://localhost:3000/api/auth/callback/github`
199
- - Click **Register application**. Generate a new client secret.
200
- Copy the **Client ID** and **Client secret**.
201
- 2. Wait for the consumer to paste both values.
202
- 3. Write to `.env.local`:
203
- ```
204
- GITHUB_CLIENT_ID=...
205
- GITHUB_CLIENT_SECRET=...
206
- ```
207
-
208
- ### Step 3 — Write the RUNBOOK
209
-
210
- After all picked providers are activated, write
211
- `RUNBOOK.md` (or merge into existing) at the consumer's repo root.
212
- The runbook captures the OAuth-app-creation steps and Resend setup
213
- the consumer just walked through, so they can hand it to a teammate
214
- or refer back later.
215
-
216
- Use `references/oauth-app-setup.md` as the template. Replace
217
- `<provider>` headings as appropriate, and only include sections for
218
- providers actually activated this session. If `RUNBOOK.md` already
219
- exists with sections for already-active providers, do not
220
- duplicate — add only the new sections beneath any existing content.
221
-
222
- ### Step 4 — Check the database
223
-
224
- Sign-in always reads and writes the database: `apps/web/auth.ts`
225
- runs Better Auth's Prisma adapter, and sessions are rows, not
226
- cookie-only tokens. There is no database-free mode. Before printing
227
- the closing summary, check:
228
-
229
- - `DATABASE_URL` (and `DATABASE_URL_NON_POOLED`, which the migrations
230
- use) is set in `.env.local`. If it is not, sign-in will fail on its
231
- first request — point the consumer at the `setup-database` skill,
232
- which sets both and creates the tables.
233
- - The auth tables exist. The Prisma schema is `database/schema.prisma`
234
- (Prisma reads the whole `database/` folder; the `User`, `Session`,
235
- `Account` and `Verification` models are in `database/auth/schema.prisma`).
236
- If the consumer has not applied the migrations yet, they run
237
- `pnpm db:migrate:deploy`.
238
-
239
- State what you found in the closing summary so the consumer is never
240
- surprised:
241
-
242
- > "Sign-in stores its users and sessions in your database (the
243
- > `User` and `Session` tables). Run `pnpm db:migrate:deploy` if you
244
- > haven't applied the migrations yet."
245
-
246
- or, when `DATABASE_URL` is unset:
247
-
248
- > "Sign-in needs a database and none is configured yet. Run the
249
- > `setup-database` skill before you try to sign in."
250
-
251
- ### Step 5 — Closing summary
252
-
253
- Print:
254
-
255
- - Which provider ids are now active (subset picked this session +
256
- already-active providers).
257
- - What Step 4 found about the database.
258
- - A reminder to set `AILK_AUTH_REQUIRED=1` in `.env.local` if they
259
- want protected routes (default is auth-disabled / public).
260
- - A pointer to `RUNBOOK.md` for future reference.
261
- - A pointer to `setup-deploy` for production callback URLs and
262
- `BETTER_AUTH_URL` / `BETTER_AUTH_SECRET` when the consumer is ready
263
- to deploy — a production deploy fails without `BETTER_AUTH_SECRET`.
264
-
265
- ## Re-invocation
266
-
267
- The skill is idempotent. On a second run, the menu in Step 1 marks
268
- already-active providers with `[x]` (read from `.env.local`), and
269
- the per-provider sub-flows skip those and only run for the new ones.
270
- The closing summary lists everything, not just what changed this
271
- session.
272
-
273
- If the consumer wants to _change_ a previously-activated provider's
274
- secrets (e.g., rotated their Google Client Secret), they should pick
275
- that provider in the menu and the sub-flow will overwrite the
276
- existing values in `.env.local`. Confirm before overwriting.
277
-
278
- ## Customization preservation
279
-
280
- If `apps/web/auth.ts` has been modified by the consumer to add a
281
- third-party provider (e.g., Apple), this skill leaves that file
282
- completely untouched. The skill never edits `apps/web/auth.ts`.
283
- Activation of the three provider ids happens entirely through env
284
- vars — `getActiveProviderIds()` in `apps/web/lib/auth-env.ts` is what
285
- reads them at startup.
286
-
287
- ## What "activation" means under the hood
288
-
289
- - **The provider list is computed at startup from env-var presence**
290
- by `getActiveProviderIds()` in `apps/web/lib/auth-env.ts`.
291
- - `apps/web/auth.ts` consumes that list: `google` and `github` become
292
- Better Auth `socialProviders` entries built from their env vars, and
293
- `email` switches `emailAndPassword` on.
294
- - A provider id is returned only when every one of its env vars is set
295
- (the table under § The provider ids).
296
- - A site with zero auth env vars boots cleanly with no active
297
- providers (`getActiveProviderIds()` returns an empty array).
298
-
299
- This means the skill's job is purely env-var wiring + documentation.
300
-
301
- ## References
302
-
303
- - `references/oauth-app-setup.md` — RUNBOOK template the skill instantiates per session.
304
- - `references/env-vars.md` — env-var contract (which vars activate which provider id).
305
-
306
- ## Coordination
307
-
308
- - **`add-page`** — any new page. This skill emits no pages; the
309
- sign-in and sign-up pages already ship.
310
- - **`setup-database`** — `DATABASE_URL` and the tables sign-in
311
- needs.
312
- - **`setup-deploy`** — production callback URLs and the production
313
- auth env vars. v1 of this skill is dev-only.