create-ailk 0.10.0 → 0.11.1
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.
- package/README.md +7 -2
- package/dist/cli.js +5 -1
- package/dist/content-payloads.d.ts +37 -0
- package/dist/content-payloads.js +64 -0
- package/dist/lib/fetch-module.d.ts +3 -3
- package/dist/lib/fetch-module.js +11 -5
- package/dist/lib/fetch-template.d.ts +1 -1
- package/dist/lib/fetch-template.js +7 -4
- package/dist/lib/scaffold-render-sweep.d.ts +16 -0
- package/dist/lib/scaffold-render-sweep.js +52 -3
- package/dist/module-architecture.d.ts +9 -0
- package/dist/module-architecture.js +43 -12
- package/dist/parse-args.d.ts +2 -0
- package/dist/parse-args.js +7 -0
- package/dist/surfaces.js +2 -3
- package/package.json +3 -3
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +4 -3
- package/site-templates/shapes/local-business-service/content/en/Service/first-job.mdx +8 -8
- package/site-templates/shapes/local-business-service/content/en/Service/second-job.mdx +8 -8
- package/site-templates/shapes/local-business-service/content/en/Service/third-job.mdx +8 -8
- package/site-templates/shapes/organization-service/content/en/Service/first-engagement.mdx +8 -8
- package/site-templates/shapes/organization-service/content/en/Service/second-engagement.mdx +8 -8
- package/site-templates/shapes/organization-service/content/en/Service/third-engagement.mdx +8 -8
- package/site-templates/shapes/person-service/content/en/Service/first-engagement.mdx +8 -8
- package/site-templates/shapes/person-service/content/en/Service/second-engagement.mdx +8 -8
- package/site-templates/shapes/person-service/content/en/Service/third-engagement.mdx +8 -8
- package/templates/.claude/settings.json +12 -0
- package/templates/.env.example +1 -5
- package/templates/CLAUDE.md +38 -22
- package/templates/README.md +12 -12
- package/templates/apps/api/jest.config.cjs +1 -0
- package/templates/apps/api/package.json +2 -1
- package/templates/apps/mcp/CLAUDE.md +23 -10
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +160 -17
- package/templates/apps/mcp/__tests__/index.test.ts +118 -1
- package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +425 -5
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +67 -3
- package/templates/apps/mcp/__tests__/server.test.ts +162 -1
- package/templates/apps/mcp/package.json +1 -1
- package/templates/apps/mcp/src/index.ts +35 -3
- package/templates/apps/mcp/src/lib/commerce-claim.ts +193 -24
- package/templates/apps/mcp/src/server.ts +77 -5
- package/templates/apps/mcp/src/tools/purchase.ts +1 -1
- package/templates/apps/web/CLAUDE.md +1 -1
- package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +15 -0
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +10 -3
- package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +1 -14
- package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/features/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/for/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +1 -14
- package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/portfolio/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/projects/[slug]/page.tsx +1 -15
- package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +1 -14
- package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +4 -16
- package/templates/apps/web/app/[locale]/vs/[slug]/__tests__/page.test.tsx +3 -15
- package/templates/apps/web/app/[locale]/vs/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +1 -14
- package/templates/apps/web/src/lib/commerce-claim.ts +15 -6
- package/templates/content/_site.mdx +23 -4
- package/templates/database/CHANGELOG.md +7 -0
- package/templates/database/package.json +1 -1
- package/versions.json +34 -34
- package/templates/.claude/skills/README.md +0 -37
- package/templates/.claude/skills/add-auth/SKILL.md +0 -313
- package/templates/.claude/skills/add-auth/references/env-vars.md +0 -106
- package/templates/.claude/skills/add-auth/references/oauth-app-setup.md +0 -180
- package/templates/.claude/skills/add-page/SKILL.md +0 -260
- package/templates/.claude/skills/add-page/references/page-types.md +0 -123
- package/templates/.claude/skills/add-page/scripts/site-pages.mjs +0 -87
- package/templates/.claude/skills/add-page/templates/comparison-page.mdx.tmpl +0 -99
- package/templates/.claude/skills/add-page/templates/content.mdx.tmpl +0 -18
- package/templates/.claude/skills/add-page/templates/flow-landing-page.mdx.tmpl +0 -43
- package/templates/.claude/skills/add-page/templates/report-page.mdx.tmpl +0 -52
- package/templates/.claude/skills/add-page/templates/reports-list.mdx.tmpl +0 -38
- package/templates/.claude/skills/check-site/SKILL.md +0 -245
- package/templates/.claude/skills/check-site/references/common-errors.md +0 -116
- package/templates/.claude/skills/check-site/references/findings-glossary.md +0 -140
- package/templates/.claude/skills/check-site/references/severity-levels.md +0 -114
- package/templates/.claude/skills/fill-page/SKILL.md +0 -199
- package/templates/.claude/skills/fill-page/profiles/plumbing.yaml +0 -80
- package/templates/.claude/skills/fill-page/references/section-sources.md +0 -41
- package/templates/.claude/skills/setup-database/SKILL.md +0 -156
- package/templates/.claude/skills/setup-database/scripts/verify-inbox.ts +0 -154
- package/templates/.claude/skills/setup-deploy/SKILL.md +0 -312
- package/templates/.claude/skills/setup-deploy/references/runbook-template.md +0 -204
- package/templates/.claude/skills/setup-deploy/references/vercel-json-template.md +0 -98
- package/templates/.claude/skills/update-page/SKILL.md +0 -117
- package/templates/.claude/skills/update-page/scripts/update-page.ts +0 -640
- package/templates/.claude/skills/update-page/templates/redirects.mjs.tmpl +0 -31
- package/templates/.claude/skills/upgrade-plan/SKILL.md +0 -121
- package/templates/.claude/skills/upgrade-plan/references/options.md +0 -24
- package/templates/.claude/skills/upgrade-plan/scripts/upgrade-plan.ts +0 -527
- package/templates/apps/mcp/__tests__/lib/require-paid-tier.test.ts +0 -75
- package/templates/apps/mcp/src/lib/require-paid-tier.ts +0 -44
|
@@ -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
|
|
52
|
+
`siteIdentity.kind` takes one of four values:
|
|
53
53
|
|
|
54
|
-
- Organization
|
|
55
|
-
- Person
|
|
56
|
-
- LocalBusiness
|
|
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
|
package/versions.json
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
1
|
{
|
|
2
|
-
"@working-theory/admin-ui": "0.
|
|
3
|
-
"@working-theory/aeo": "0.
|
|
4
|
-
"@working-theory/agent-context": "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.
|
|
9
|
-
"@working-theory/api-client": "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.
|
|
12
|
-
"@working-theory/common": "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.
|
|
15
|
-
"@working-theory/content-projection": "0.
|
|
16
|
-
"@working-theory/email": "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.
|
|
19
|
-
"@working-theory/fumadocs-schema": "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.
|
|
22
|
-
"@working-theory/license": "0.
|
|
23
|
-
"@working-theory/metadata": "0.
|
|
24
|
-
"@working-theory/observability": "0.
|
|
25
|
-
"@working-theory/page-renderer": "0.
|
|
26
|
-
"@working-theory/
|
|
27
|
-
"@working-theory/schema
|
|
28
|
-
"@working-theory/schema-
|
|
29
|
-
"@working-theory/
|
|
30
|
-
"@working-theory/
|
|
31
|
-
"@working-theory/
|
|
32
|
-
"@working-theory/
|
|
33
|
-
"@working-theory/
|
|
34
|
-
"@working-theory/
|
|
35
|
-
"@working-theory/
|
|
36
|
-
"@working-theory/
|
|
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",
|
|
40
41
|
"@working-theory/license-manifest": "0.0.0",
|
|
41
|
-
"@working-theory/marketing-api-client": "0.0.0",
|
|
42
42
|
"@working-theory/partner-session": "0.0.0",
|
|
43
43
|
"@working-theory/preview-protocol": "0.0.0",
|
|
44
|
-
"@working-theory/ui-commerce": "0.23.
|
|
45
|
-
"@working-theory/ui-docs-blog": "0.23.
|
|
46
|
-
"@working-theory/ui-scheduling": "0.23.
|
|
47
|
-
"@working-theory/ui-signal-collectors": "0.24.
|
|
44
|
+
"@working-theory/ui-commerce": "0.23.7",
|
|
45
|
+
"@working-theory/ui-docs-blog": "0.23.7",
|
|
46
|
+
"@working-theory/ui-scheduling": "0.23.7",
|
|
47
|
+
"@working-theory/ui-signal-collectors": "0.24.5",
|
|
48
48
|
"@working-theory/web": "0.0.0",
|
|
49
49
|
"@working-theory/api": "1.0.0",
|
|
50
50
|
"@working-theory/mcp": "0.0.0",
|
|
51
|
-
"@working-theory/database": "0.
|
|
51
|
+
"@working-theory/database": "0.30.0"
|
|
52
52
|
}
|
|
@@ -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.
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# Auth Env-Var Contract
|
|
2
|
-
|
|
3
|
-
Source of truth for which env vars activate which auth provider on
|
|
4
|
-
an AILK consumer site. Read by the `add-auth` skill before
|
|
5
|
-
asking the consumer to paste secrets.
|
|
6
|
-
|
|
7
|
-
The activation logic lives in `apps/web/lib/auth-env.ts` →
|
|
8
|
-
`getActiveProviderIds()`. A provider is active **only when every
|
|
9
|
-
required var is set to a non-empty value**. Partial wiring (e.g.,
|
|
10
|
-
`GOOGLE_CLIENT_ID` set but `GOOGLE_CLIENT_SECRET` missing) is treated
|
|
11
|
-
as inactive — `apps/web/auth.ts` never constructs that provider.
|
|
12
|
-
|
|
13
|
-
The site's auth library is **Better Auth** (`apps/web/auth.ts`,
|
|
14
|
-
`betterAuth({ secret, baseURL, socialProviders, … })`).
|
|
15
|
-
|
|
16
|
-
## Provider activation table
|
|
17
|
-
|
|
18
|
-
| Provider ID | Required env vars | Source |
|
|
19
|
-
| ----------- | -------------------------------------------- | ---------------------------- |
|
|
20
|
-
| `email` | `AUTH_EMAIL_SERVER` + `AUTH_EMAIL_FROM` | Email sign-in (Resend)¹ |
|
|
21
|
-
| `google` | `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET` | Google OAuth |
|
|
22
|
-
| `github` | `GITHUB_CLIENT_ID` + `GITHUB_CLIENT_SECRET` | GitHub OAuth |
|
|
23
|
-
|
|
24
|
-
¹ Better Auth core ships no magic-link provider, so `email` is an
|
|
25
|
-
env-presence signal: it switches on the password method
|
|
26
|
-
(`emailAndPassword`) in `apps/web/auth.ts`, not a wired magic link.
|
|
27
|
-
|
|
28
|
-
## Auxiliary auth env vars
|
|
29
|
-
|
|
30
|
-
| Var | Default | Effect |
|
|
31
|
-
| ---------------------- | ------- | ------------------------------------------------------------------------------------- |
|
|
32
|
-
| `AILK_AUTH_REQUIRED` | unset | Read by `isAuthRequired()` in `apps/web/lib/auth-env.ts` (`"1"` or `"true"`). Default is auth-disabled / public, so a fresh scaffold boots without requiring any auth wiring. |
|
|
33
|
-
| `BETTER_AUTH_URL` | unset | The site's public origin (e.g. `https://your-domain.com`). Better Auth builds OAuth callback URLs on it and it is the one trusted origin; unset, Better Auth derives both from each request. Set it in production. Local dev can leave it unset. |
|
|
34
|
-
| `BETTER_AUTH_SECRET` | unset | Signs session cookies and email-verification tokens. **Required in production** — `apps/web/auth.ts` throws when it is unset there, rather than fall back to the development secret published in the source. Generate with `openssl rand -base64 32`. Optional for `next dev`. |
|
|
35
|
-
| `AILK_AUTH_SIGNUP_OPEN` | unset | Read by `isSignUpOpen()` in `apps/web/lib/auth-env.ts` (`"1"` or `"true"`). Unset, public sign-up is closed: with `email` active, the password form signs existing accounts in but creates none. |
|
|
36
|
-
| `AILK_AUTH_PASSWORD_RESET` | unset | Read by `isResetAvailable()` (`"1"` or `"true"`). Password reset also needs `BETTER_AUTH_URL`, `BETTER_AUTH_SECRET` and `AUTH_EMAIL_WEBHOOK_SECRET` (`resetSendConfigured()`). |
|
|
37
|
-
| `AUTH_EMAIL_WEBHOOK_SECRET` | unset | Signs the auth-mail relay from `apps/web` to `apps/api`, which sends with `RESEND_API_KEY` from `AUTH_EMAIL_FROM`. Set the same value for both apps; unset, no auth mail is sent. |
|
|
38
|
-
|
|
39
|
-
The skill writes none of these auxiliary vars unasked. It mentions
|
|
40
|
-
them in the closing summary so the consumer is aware:
|
|
41
|
-
|
|
42
|
-
- `AILK_AUTH_REQUIRED` is the consumer's call (skill always defaults
|
|
43
|
-
to leaving it unset).
|
|
44
|
-
- `AILK_AUTH_SIGNUP_OPEN` is the consumer's call too; the skill asks
|
|
45
|
-
when `email` is active, because with sign-up closed and no OAuth
|
|
46
|
-
provider nobody can create the first account.
|
|
47
|
-
- `AILK_AUTH_PASSWORD_RESET` and `AUTH_EMAIL_WEBHOOK_SECRET` are the
|
|
48
|
-
auth-mail setup the `email` sub-flow describes.
|
|
49
|
-
- `BETTER_AUTH_URL` and `BETTER_AUTH_SECRET` are deferred to
|
|
50
|
-
production-time setup (`setup-deploy`), and a production deploy
|
|
51
|
-
fails without `BETTER_AUTH_SECRET`.
|
|
52
|
-
|
|
53
|
-
## Env-var formats — what valid values look like
|
|
54
|
-
|
|
55
|
-
### `AUTH_EMAIL_SERVER` (email sign-in via Resend)
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
smtp://resend:<RESEND_API_KEY>@smtp.resend.com:465
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
The Resend API key starts with `re_`. The literal username is the
|
|
62
|
-
string `resend` (Resend's SMTP convention). Port 465 is implicit-TLS
|
|
63
|
-
SMTP.
|
|
64
|
-
|
|
65
|
-
### `AUTH_EMAIL_FROM` (email sign-in via Resend)
|
|
66
|
-
|
|
67
|
-
A verified sender address on the consumer's Resend account. For
|
|
68
|
-
development, `onboarding@resend.dev` works without verification.
|
|
69
|
-
|
|
70
|
-
### `GOOGLE_CLIENT_ID` / `GITHUB_CLIENT_ID`
|
|
71
|
-
|
|
72
|
-
Public identifiers issued by Google / GitHub. Typically:
|
|
73
|
-
|
|
74
|
-
- Google: ends in `.apps.googleusercontent.com`
|
|
75
|
-
- GitHub: 20-character hex string
|
|
76
|
-
|
|
77
|
-
Either can be safely committed to a public template; production
|
|
78
|
-
secrets stay in `.env.local`.
|
|
79
|
-
|
|
80
|
-
### `GOOGLE_CLIENT_SECRET` / `GITHUB_CLIENT_SECRET`
|
|
81
|
-
|
|
82
|
-
Sensitive values. Treat as passwords. Never commit. The skill writes
|
|
83
|
-
these only to `.env.local` (which is in the consumer scaffold's
|
|
84
|
-
`.gitignore` by default).
|
|
85
|
-
|
|
86
|
-
## Detection of "already activated"
|
|
87
|
-
|
|
88
|
-
The skill's Step 1 menu marks providers as `[x]` when both required
|
|
89
|
-
vars are set in `.env.local`. The detection is presence-only — the
|
|
90
|
-
skill does not validate that the values actually work (no Google
|
|
91
|
-
API ping, no GitHub API ping, no test SMTP connection). A consumer
|
|
92
|
-
who pasted typos will get a runtime auth error from Better Auth on
|
|
93
|
-
their first sign-in attempt; the fix is to re-run the skill and
|
|
94
|
-
overwrite.
|
|
95
|
-
|
|
96
|
-
## Where these vars are read
|
|
97
|
-
|
|
98
|
-
- `apps/web/lib/auth-env.ts` — `getActiveProviderIds()` and
|
|
99
|
-
`isAuthRequired()`.
|
|
100
|
-
- `apps/web/auth.ts` — `buildSocialProviders()` consumes the
|
|
101
|
-
active-provider list and builds Better Auth's `socialProviders`
|
|
102
|
-
from the matching env vars; `resolveSecret()` reads
|
|
103
|
-
`BETTER_AUTH_SECRET`, and `baseURL` is `BETTER_AUTH_URL`.
|
|
104
|
-
|
|
105
|
-
The skill never modifies any of those files. All activation is
|
|
106
|
-
env-var-driven.
|