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.
- 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/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/templates/.claude/settings.json +12 -0
- package/templates/.env.example +1 -1
- 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 +1 -0
- 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/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/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/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 -33
- 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
|
@@ -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 {
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
);
|
|
33
|
+
if (result.revoked === true) return [];
|
|
34
|
+
return Array.isArray(result.features) ? result.features : [];
|
|
30
35
|
} catch {
|
|
31
|
-
return
|
|
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
|
|
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,39 +1,40 @@
|
|
|
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",
|
|
@@ -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.
|
|
45
|
-
"@working-theory/ui-docs-blog": "0.23.
|
|
46
|
-
"@working-theory/ui-scheduling": "0.23.
|
|
47
|
-
"@working-theory/ui-signal-collectors": "0.24.
|
|
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.
|
|
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.
|