create-ailk 0.9.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 +10 -5
- package/dist/cli.js +17 -2
- 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/module-license-gate.d.ts +29 -23
- package/dist/lib/module-license-gate.js +98 -0
- package/dist/module-architecture.d.ts +9 -0
- package/dist/module-architecture.js +46 -14
- package/dist/parse-args.d.ts +2 -0
- package/dist/parse-args.js +7 -0
- package/dist/programmatic.js +12 -4
- package/dist/starter-grid.d.ts +4 -67
- package/dist/starter-grid.js +6 -84
- package/dist/surfaces.js +2 -3
- package/dist/template-manifest.d.ts +1 -1
- package/dist/template-manifest.js +5 -5
- package/package.json +6 -4
- package/release-scope.json +25 -0
- package/site-templates/designs/minimalism/sample/README.md +2 -2
- package/site-templates/designs/minimalism/sample/content/en/HomePage/home.mdx +1 -1
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +4 -3
- package/templates/.claude/agents/web.md +10 -8
- package/templates/.claude/rules/architecture.md +10 -7
- package/templates/.claude/settings.json +12 -0
- package/templates/.env.example +24 -3
- 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/templates/package.json +2 -0
- package/versions.json +34 -33
- package/dist/modules/commerce/0.6.0.tar.gz +0 -0
- package/dist/modules/commerce/0.6.1.tar.gz +0 -0
- package/dist/modules/docs-blog/0.6.0.tar.gz +0 -0
- package/dist/modules/docs-blog/0.6.1.tar.gz +0 -0
- package/dist/modules/marketing-site/0.6.1.tar.gz +0 -0
- package/dist/modules/minimalism-site/0.6.1.tar.gz +0 -0
- package/dist/modules/multi-tenant/0.6.0.tar.gz +0 -0
- package/dist/modules/multi-tenant/0.6.1.tar.gz +0 -0
- package/dist/modules/saas-product/0.6.1.tar.gz +0 -0
- package/dist/modules/scheduling/0.6.0.tar.gz +0 -0
- package/dist/modules/scheduling/0.6.1.tar.gz +0 -0
- package/dist/modules/signal-collectors/0.6.0.tar.gz +0 -0
- package/dist/modules/signal-collectors/0.6.1.tar.gz +0 -0
- package/dist/modules/simple-service/0.6.1.tar.gz +0 -0
- package/dist/modules/workspace/0.6.0.tar.gz +0 -0
- package/dist/modules/workspace/0.6.1.tar.gz +0 -0
- package/site-templates/aliases/minimalism-site.TEMPLATE-MANIFEST.json +0 -538
- package/templates/.claude/skills/README.md +0 -37
- package/templates/.claude/skills/add-auth/SKILL.md +0 -261
- package/templates/.claude/skills/add-auth/references/env-vars.md +0 -98
- package/templates/.claude/skills/add-auth/references/oauth-app-setup.md +0 -172
- package/templates/.claude/skills/add-page/SKILL.md +0 -260
- package/templates/.claude/skills/add-page/references/page-types.md +0 -124
- 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 -235
- package/templates/.claude/skills/check-site/references/common-errors.md +0 -113
- 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 -275
- package/templates/.claude/skills/setup-deploy/references/env-vars-template.md +0 -192
- 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/api/.ailk/inbox/2026-09-20T14-57-31.552Z-fallback-1789916251590-037e9005.json +0 -8
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.840Z-fallback-1789916253842-b52659e9.json +0 -12
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.844Z-fallback-1789916253845-6b266f8c.json +0 -12
- 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
package/dist/programmatic.js
CHANGED
|
@@ -31,7 +31,7 @@ import { writeAuthSecretFile } from "./lib/auth-secret-file.js";
|
|
|
31
31
|
import { extractModuleBundle, installBundleTree, } from "./lib/extract-module-bundle.js";
|
|
32
32
|
import { fetchTemplate } from "./lib/fetch-template.js";
|
|
33
33
|
import { assertLicenseKeyShape, writeLicenseKeyFiles, } from "./lib/license-key-files.js";
|
|
34
|
-
import { assertDesignAllowed, deriveLevel, resolveModuleSet, resolvePaidModules, } from "./lib/module-license-gate.js";
|
|
34
|
+
import { assertDesignAllowed, assertReleased, deriveLevel, resolveModuleSet, resolvePaidModules, } from "./lib/module-license-gate.js";
|
|
35
35
|
import { classifyModules } from "./lib/module-tier.js";
|
|
36
36
|
import { normalizeModules } from "./module-architecture.js";
|
|
37
37
|
import { RECOGNIZED_SHAPES, RESERVED_SHAPES, } from "./parse-args.js";
|
|
@@ -235,11 +235,16 @@ export async function scaffold(opts) {
|
|
|
235
235
|
if (opts.template !== undefined && opts.shape !== undefined) {
|
|
236
236
|
throw new TemplateError("pass a template or a shape, not both");
|
|
237
237
|
}
|
|
238
|
+
// #6984 — a surface `release-scope.json` does not release is refused here,
|
|
239
|
+
// after parsing and before the licence gate, fetch or write (spec 6977 D3).
|
|
240
|
+
assertReleased("templates", [opts.template]);
|
|
241
|
+
assertReleased("shapes", [opts.shape]);
|
|
238
242
|
const template = opts.template !== undefined
|
|
239
243
|
? resolveTemplate(opts.template)
|
|
240
244
|
: opts.shape !== undefined
|
|
241
245
|
? resolveShapeTemplate(opts.shape)
|
|
242
246
|
: undefined;
|
|
247
|
+
assertReleased("modules", [...(opts.modules ?? []), ...(template?.requires ?? [])]);
|
|
243
248
|
// #6813 D8 step 3 — the design this scaffold renders, resolved from its
|
|
244
249
|
// package ONCE, before any fetch or write: the caller's override, else the
|
|
245
250
|
// template's own. Unset for a scaffold naming neither. Replaces the
|
|
@@ -250,6 +255,8 @@ export async function scaffold(opts) {
|
|
|
250
255
|
const resolvedDesign = designRef !== undefined
|
|
251
256
|
? resolveDesign(designRef, { designsDir: DESIGNS_DIR })
|
|
252
257
|
: undefined;
|
|
258
|
+
// By registry name, so a byte-identical copy of a gated design is refused too.
|
|
259
|
+
assertReleased("designs", [resolvedDesign?.recorded]);
|
|
253
260
|
// #6816 D6 step 3b — a design's section presets are validated here, before
|
|
254
261
|
// any fetch or write, against the look groups the shipped catalog carries
|
|
255
262
|
// (D2): a non-look field or unknown variant throws `DesignPackageError`
|
|
@@ -313,8 +320,8 @@ export async function scaffold(opts) {
|
|
|
313
320
|
// answers, never a key claim (D2) — only when the caller CHOSE a
|
|
314
321
|
// non-Free design alongside the template (`opts.design`, #6813 D7). An
|
|
315
322
|
// alias's own declared design is never gated (#6778 option (c)), so a
|
|
316
|
-
// plain `--template
|
|
317
|
-
//
|
|
323
|
+
// plain `--template <alias>` run makes no extra network call and is
|
|
324
|
+
// never refused on its design.
|
|
318
325
|
if (chosenDesign !== undefined && !chosenDesign.free) {
|
|
319
326
|
({ level } = await deriveLevel({
|
|
320
327
|
licenseKey,
|
|
@@ -557,7 +564,8 @@ export async function scaffold(opts) {
|
|
|
557
564
|
writeFileSync(join(result.targetDir, NAV_CONFIG_PATH), renderNavConfig(result.surfaces, { serviceSlugs }));
|
|
558
565
|
pruneUnrosteredSeeds(result.targetDir, mergeRoster(template, template.requires), result.surfaces, ownership);
|
|
559
566
|
// spec 6892 D4 — the resolved design's own sample (the minimalism design's
|
|
560
|
-
// nine-slide deck home + its SVGs
|
|
567
|
+
// nine-slide deck home + its SVGs; no template declares `minimalism`
|
|
568
|
+
// since #6976, so none installs it today), installed AFTER the template
|
|
561
569
|
// layer so it wins over the shape's `HomePage/home.mdx`, and ONLY when
|
|
562
570
|
// the resolved design is the template's OWN declared design: a caller
|
|
563
571
|
// combination like `--shape organization-product --design minimalism`
|
package/dist/starter-grid.d.ts
CHANGED
|
@@ -31,9 +31,8 @@ export interface AliasDifferences {
|
|
|
31
31
|
/**
|
|
32
32
|
* Further positioned groups (#6757 D7): each group's rows go before its
|
|
33
33
|
* own `before` route, applied group by group in array order, AFTER `add`
|
|
34
|
-
* is placed
|
|
35
|
-
*
|
|
36
|
-
* shape's row order) uses this; every other alias leaves it unset.
|
|
34
|
+
* is placed — for an alias that interleaves rows at more than one point
|
|
35
|
+
* in the shape's row order. No alias in the table below needs it today.
|
|
37
36
|
*/
|
|
38
37
|
readonly insert?: readonly {
|
|
39
38
|
readonly before: string;
|
|
@@ -58,8 +57,8 @@ export interface TemplateAlias {
|
|
|
58
57
|
readonly differences?: AliasDifferences;
|
|
59
58
|
/**
|
|
60
59
|
* Whether this alias ships its own `site-templates/<name>/` tree (spec 6892
|
|
61
|
-
* D1). Absent means `true` — every alias
|
|
62
|
-
*
|
|
60
|
+
* D1). Absent means `true` — every alias today keeps its own directory.
|
|
61
|
+
* `false` declares that the alias resolves purely from the
|
|
63
62
|
* grid: its seed layer is its shape's tree (`templateTreeDir`), and its
|
|
64
63
|
* generated roster record lives under `site-templates/aliases/` rather
|
|
65
64
|
* than a directory of its own (`shippedManifestPath`).
|
|
@@ -152,68 +151,6 @@ export declare const ALIASES: Readonly<{
|
|
|
152
151
|
readonly before: "/portfolio";
|
|
153
152
|
};
|
|
154
153
|
};
|
|
155
|
-
/**
|
|
156
|
-
* The second Pro template (#5589) — the reports-first, scroll-snap deck
|
|
157
|
-
* site. NOT `marketing-site` plus a module: its `modules` omit `docs-blog`,
|
|
158
|
-
* so the two Pro templates are siblings under `_base` (#5618 D2), never a
|
|
159
|
-
* lineage. Level still derives from `modules`: `scheduling` and
|
|
160
|
-
* `signal-collectors` are paid, so `templateDelivery` returns `"paid"`
|
|
161
|
-
* exactly as it does for `marketing-site`.
|
|
162
|
-
*
|
|
163
|
-
* It composes the `organization-service` shape roster (#6757) rather than
|
|
164
|
-
* the organization-product one: the deck home replaces the document-scroll
|
|
165
|
-
* home, and `/how-it-works`, the product page, `/pricing` and `/sign-in`
|
|
166
|
-
* are not pages this site has. The roster is what the
|
|
167
|
-
* template IS, so an unrostered page has neither a route directory
|
|
168
|
-
* (`rosterSurfaces`) nor its `_base`/overlay seed (#6646 D1).
|
|
169
|
-
*
|
|
170
|
-
* It declares NO `layout` block (#6638): the deck home, mark-only nav and
|
|
171
|
-
* centered footer are the `minimalism` design's own `layout` (its
|
|
172
|
-
* `DESIGN.md`, resolved by `resolveDesign` off `design:` — #6813 D4) — the
|
|
173
|
-
* look any shape can wear. This alias ships no tree of its own (`ownTree:
|
|
174
|
-
* false`, spec 6892 D1); its GENERATED roster record (no narrative `.md`,
|
|
175
|
-
* D5) lives at `site-templates/aliases/minimalism-site.TEMPLATE-MANIFEST.json`.
|
|
176
|
-
*
|
|
177
|
-
* `/reports` (a `ReportsList` hub over `ReportPage`) and `/referral` (a
|
|
178
|
-
* flow landing page) are signal-collectors module samples (spec 6892 D3);
|
|
179
|
-
* neither carries a `GATEABLE_SURFACES` entry: `/referral` is a
|
|
180
|
-
* single-segment `LandingPage` slug served by the generic `[slug]` route
|
|
181
|
-
* (#5469 D2), the same mechanism Free's `/how-it-works` uses, and `/reports`
|
|
182
|
-
* is its own routes-map row. `/reports/[slug]` (the reader) is the
|
|
183
|
-
* signal-collectors module's own contributed row.
|
|
184
|
-
*
|
|
185
|
-
* `/services/[slug]` declares its seed DIRECTORY as `contentDir`
|
|
186
|
-
* (`content/en/Service`, as `routes-map.ts` records it). The three
|
|
187
|
-
* samples ride the template tree unmapped, landing at that directory.
|
|
188
|
-
*
|
|
189
|
-
* Since #6757, `organization-service` carries a real standard roster
|
|
190
|
-
* (D1), so this alias's `differences` is additions and drops only: it
|
|
191
|
-
* drops the `/blog` teaser (a Pro alias does not upsell its own level,
|
|
192
|
-
* D8), the portfolio pair (the reports are the proof, not a
|
|
193
|
-
* portfolio) and the two auth code routes the shape rosters (#6797 —
|
|
194
|
-
* this site has no `/sign-in`, above), adds the signal-collectors
|
|
195
|
-
* samples, and INSERTS its two own
|
|
196
|
-
* rows — `/reports` before `/about`, `/referral` before `/faq` (D7) — to
|
|
197
|
-
* reproduce its pre-#6757 row order (§ 1.2) exactly.
|
|
198
|
-
*/
|
|
199
|
-
readonly "minimalism-site": {
|
|
200
|
-
readonly shape: "organization-service";
|
|
201
|
-
readonly design: "minimalism";
|
|
202
|
-
readonly modules: readonly ["marketing", "scheduling", "signal-collectors"];
|
|
203
|
-
readonly categories: readonly ["real-estate", "technology", "legal"];
|
|
204
|
-
readonly ownTree: false;
|
|
205
|
-
readonly differences: {
|
|
206
|
-
readonly drop: readonly ["/sign-in", "/sign-up", "/blog", "/meet", "/meet/[slug]", "/portfolio", "/portfolio/[slug]"];
|
|
207
|
-
readonly add: readonly import("./module-architecture.js").PageRosterRow[];
|
|
208
|
-
readonly insert: readonly [{
|
|
209
|
-
readonly before: "/about";
|
|
210
|
-
readonly rows: readonly [import("./module-architecture.js").PageRosterRow];
|
|
211
|
-
}, {
|
|
212
|
-
readonly before: "/faq";
|
|
213
|
-
readonly rows: readonly [import("./module-architecture.js").PageRosterRow];
|
|
214
|
-
}];
|
|
215
|
-
};
|
|
216
|
-
};
|
|
217
154
|
}>;
|
|
218
155
|
/**
|
|
219
156
|
* The coordinates a keyless `--shape S` builds (#6757 D5) — the composed
|
package/dist/starter-grid.js
CHANGED
|
@@ -171,14 +171,10 @@ const PORTFOLIO_ROWS = [
|
|
|
171
171
|
{ route: "/portfolio/[slug]", contentDir: "content/en/PortfolioItem" },
|
|
172
172
|
];
|
|
173
173
|
/**
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* (§ 1.2) — `/reports` before `/about`, `/referral` before `/faq`. Neither
|
|
179
|
-
* carries a `GATEABLE_SURFACES` entry: both are single-segment slugs served
|
|
180
|
-
* by the generic `[slug]` route (#5469 D2), the same mechanism Free's
|
|
181
|
-
* `/how-it-works` used.
|
|
174
|
+
* The two report/referral sample rows. Neither carries a `GATEABLE_SURFACES`
|
|
175
|
+
* entry: `/referral` is a single-segment `LandingPage` slug served by the
|
|
176
|
+
* generic `[slug]` route (#5469 D2), the same mechanism Free's
|
|
177
|
+
* `/how-it-works` used, and `/reports` is its own routes-map row.
|
|
182
178
|
*/
|
|
183
179
|
const REPORTS_ROW = {
|
|
184
180
|
route: "/reports",
|
|
@@ -193,8 +189,7 @@ const REFERRAL_ROW = {
|
|
|
193
189
|
* `/reports`, a `ReportsList` hub over the module's four `ReportPage` seeds,
|
|
194
190
|
* and `/referral`, a sample flow landing page. `marketing-site` and
|
|
195
191
|
* `saas-product` add these before `/portfolio`, alongside
|
|
196
|
-
* `SIGNAL_COLLECTORS_SAMPLE_ROWS
|
|
197
|
-
* `insert` groups instead (its pre-existing row order, D1).
|
|
192
|
+
* `SIGNAL_COLLECTORS_SAMPLE_ROWS`.
|
|
198
193
|
*/
|
|
199
194
|
const SIGNAL_COLLECTORS_REPORT_ROWS = [
|
|
200
195
|
REPORTS_ROW,
|
|
@@ -206,8 +201,7 @@ const SIGNAL_COLLECTORS_REPORT_ROWS = [
|
|
|
206
201
|
* --registry packages/schema/src/registry/entries.ts` at harness commit
|
|
207
202
|
* `7bdaf148e1b0e639cd90a80008955e55cc4aa2b5`, edited only by the deviations
|
|
208
203
|
* `site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md` § 3a
|
|
209
|
-
* records with their reasons.
|
|
210
|
-
* that alias's `differences` are additions and drops only.
|
|
204
|
+
* records with their reasons.
|
|
211
205
|
*/
|
|
212
206
|
const ORGANIZATION_SERVICE_ROWS = [
|
|
213
207
|
{ route: "/", content: ["content/en/HomePage/home.mdx"] },
|
|
@@ -429,78 +423,6 @@ export const ALIASES = Object.freeze({
|
|
|
429
423
|
before: "/portfolio",
|
|
430
424
|
},
|
|
431
425
|
},
|
|
432
|
-
/**
|
|
433
|
-
* The second Pro template (#5589) — the reports-first, scroll-snap deck
|
|
434
|
-
* site. NOT `marketing-site` plus a module: its `modules` omit `docs-blog`,
|
|
435
|
-
* so the two Pro templates are siblings under `_base` (#5618 D2), never a
|
|
436
|
-
* lineage. Level still derives from `modules`: `scheduling` and
|
|
437
|
-
* `signal-collectors` are paid, so `templateDelivery` returns `"paid"`
|
|
438
|
-
* exactly as it does for `marketing-site`.
|
|
439
|
-
*
|
|
440
|
-
* It composes the `organization-service` shape roster (#6757) rather than
|
|
441
|
-
* the organization-product one: the deck home replaces the document-scroll
|
|
442
|
-
* home, and `/how-it-works`, the product page, `/pricing` and `/sign-in`
|
|
443
|
-
* are not pages this site has. The roster is what the
|
|
444
|
-
* template IS, so an unrostered page has neither a route directory
|
|
445
|
-
* (`rosterSurfaces`) nor its `_base`/overlay seed (#6646 D1).
|
|
446
|
-
*
|
|
447
|
-
* It declares NO `layout` block (#6638): the deck home, mark-only nav and
|
|
448
|
-
* centered footer are the `minimalism` design's own `layout` (its
|
|
449
|
-
* `DESIGN.md`, resolved by `resolveDesign` off `design:` — #6813 D4) — the
|
|
450
|
-
* look any shape can wear. This alias ships no tree of its own (`ownTree:
|
|
451
|
-
* false`, spec 6892 D1); its GENERATED roster record (no narrative `.md`,
|
|
452
|
-
* D5) lives at `site-templates/aliases/minimalism-site.TEMPLATE-MANIFEST.json`.
|
|
453
|
-
*
|
|
454
|
-
* `/reports` (a `ReportsList` hub over `ReportPage`) and `/referral` (a
|
|
455
|
-
* flow landing page) are signal-collectors module samples (spec 6892 D3);
|
|
456
|
-
* neither carries a `GATEABLE_SURFACES` entry: `/referral` is a
|
|
457
|
-
* single-segment `LandingPage` slug served by the generic `[slug]` route
|
|
458
|
-
* (#5469 D2), the same mechanism Free's `/how-it-works` uses, and `/reports`
|
|
459
|
-
* is its own routes-map row. `/reports/[slug]` (the reader) is the
|
|
460
|
-
* signal-collectors module's own contributed row.
|
|
461
|
-
*
|
|
462
|
-
* `/services/[slug]` declares its seed DIRECTORY as `contentDir`
|
|
463
|
-
* (`content/en/Service`, as `routes-map.ts` records it). The three
|
|
464
|
-
* samples ride the template tree unmapped, landing at that directory.
|
|
465
|
-
*
|
|
466
|
-
* Since #6757, `organization-service` carries a real standard roster
|
|
467
|
-
* (D1), so this alias's `differences` is additions and drops only: it
|
|
468
|
-
* drops the `/blog` teaser (a Pro alias does not upsell its own level,
|
|
469
|
-
* D8), the portfolio pair (the reports are the proof, not a
|
|
470
|
-
* portfolio) and the two auth code routes the shape rosters (#6797 —
|
|
471
|
-
* this site has no `/sign-in`, above), adds the signal-collectors
|
|
472
|
-
* samples, and INSERTS its two own
|
|
473
|
-
* rows — `/reports` before `/about`, `/referral` before `/faq` (D7) — to
|
|
474
|
-
* reproduce its pre-#6757 row order (§ 1.2) exactly.
|
|
475
|
-
*/
|
|
476
|
-
"minimalism-site": {
|
|
477
|
-
shape: "organization-service",
|
|
478
|
-
design: "minimalism",
|
|
479
|
-
modules: ["marketing", "scheduling", "signal-collectors"],
|
|
480
|
-
categories: ["real-estate", "technology", "legal"],
|
|
481
|
-
// Spec 6892 D1 — this alias ships no `site-templates/minimalism-site/`
|
|
482
|
-
// tree of its own: it resolves purely from the grid. Its seed layer is
|
|
483
|
-
// its shape's tree (`templateTreeDir`), its deck home + SVGs are a
|
|
484
|
-
// sample the `minimalism` design installs (D4), and its `/reports` +
|
|
485
|
-
// `/referral` rows are signal-collectors module samples (D3).
|
|
486
|
-
ownTree: false,
|
|
487
|
-
differences: {
|
|
488
|
-
drop: [
|
|
489
|
-
"/sign-in",
|
|
490
|
-
"/sign-up",
|
|
491
|
-
"/blog",
|
|
492
|
-
"/meet",
|
|
493
|
-
"/meet/[slug]",
|
|
494
|
-
"/portfolio",
|
|
495
|
-
"/portfolio/[slug]",
|
|
496
|
-
],
|
|
497
|
-
add: SIGNAL_COLLECTORS_SAMPLE_ROWS,
|
|
498
|
-
insert: [
|
|
499
|
-
{ before: "/about", rows: [REPORTS_ROW] },
|
|
500
|
-
{ before: "/faq", rows: [REFERRAL_ROW] },
|
|
501
|
-
],
|
|
502
|
-
},
|
|
503
|
-
},
|
|
504
426
|
});
|
|
505
427
|
/**
|
|
506
428
|
* The coordinates a keyless `--shape S` builds (#6757 D5) — the composed
|
package/dist/surfaces.js
CHANGED
|
@@ -20,8 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
* The default (`--modules marketing`) enables NO gateable surface: every one of
|
|
22
22
|
* them is a claim about the business that the scaffolder cannot make. The path
|
|
23
|
-
* from there is the
|
|
24
|
-
* `templates/.claude/skills/`.
|
|
23
|
+
* from there is the `/ailk:add-page` skill in the `ailk` Claude Code plugin.
|
|
25
24
|
*
|
|
26
25
|
* Zero runtime dependencies — create-ailk is a published, standalone CLI.
|
|
27
26
|
*
|
|
@@ -1047,7 +1046,7 @@ export function renderNavConfig(enabled, options) {
|
|
|
1047
1046
|
// below it, the nav is yours and add-module reports the change it wanted
|
|
1048
1047
|
// instead.
|
|
1049
1048
|
//
|
|
1050
|
-
// Adding a link: scaffold the page (the
|
|
1049
|
+
// Adding a link: scaffold the page (the \`/ailk:add-page\` skill), then add an
|
|
1051
1050
|
// entry below with a \`labelKey\` that exists in the \`nav\` namespace of
|
|
1052
1051
|
// \`apps/web/messages/<locale>.json\`. Paths are locale-agnostic — Nav prefixes
|
|
1053
1052
|
// them with the active locale at render.
|
|
@@ -183,7 +183,7 @@ export declare function templateTreeDir(name: string): string;
|
|
|
183
183
|
/**
|
|
184
184
|
* Whether `name` ships its own `site-templates/<name>/` directory (spec 6892
|
|
185
185
|
* D1) — `false` only for a declared `--template` alias whose `ownTree` is
|
|
186
|
-
* `false` (
|
|
186
|
+
* `false` (none today). A `SHAPE_STARTERS` key owns its
|
|
187
187
|
* `shapes/<name>` tree outright, so it is never treeless by this predicate
|
|
188
188
|
* even though `templateTreeDir` also renames its directory.
|
|
189
189
|
*
|
|
@@ -60,10 +60,10 @@ export class TemplateError extends Error {
|
|
|
60
60
|
* shape's standard roster, minus the alias's `drop` routes, with its `add`
|
|
61
61
|
* rows inserted before the `before` route (appended when there is none),
|
|
62
62
|
* then each `insert` group's rows spliced before ITS OWN `before` route, in
|
|
63
|
-
* array order —
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
63
|
+
* array order — for an alias that interleaves its own rows into the shape's
|
|
64
|
+
* row order at more than one anchor point. Rows are pushed by reference,
|
|
65
|
+
* never cloned, so `manifest.roster.includes(row)` identity checks
|
|
66
|
+
* downstream keep working.
|
|
67
67
|
* Every refusal names the alias, so a malformed table fails at load — the
|
|
68
68
|
* shipped one — or in the test that injected it.
|
|
69
69
|
*
|
|
@@ -178,7 +178,7 @@ export function templateTreeDir(name) {
|
|
|
178
178
|
/**
|
|
179
179
|
* Whether `name` ships its own `site-templates/<name>/` directory (spec 6892
|
|
180
180
|
* D1) — `false` only for a declared `--template` alias whose `ownTree` is
|
|
181
|
-
* `false` (
|
|
181
|
+
* `false` (none today). A `SHAPE_STARTERS` key owns its
|
|
182
182
|
* `shapes/<name>` tree outright, so it is never treeless by this predicate
|
|
183
183
|
* even though `templateTreeDir` also renames its directory.
|
|
184
184
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-ailk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Scaffolder for AI Launch Kit — `pnpm create ailk@latest <dir>` produces a starter repo consuming @working-theory/* from npm (per ADR 0007).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -10,9 +10,11 @@
|
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
|
+
"!dist/modules",
|
|
13
14
|
"templates",
|
|
14
15
|
"site-templates",
|
|
15
16
|
"versions.json",
|
|
17
|
+
"release-scope.json",
|
|
16
18
|
"component-catalog.json",
|
|
17
19
|
"component-catalog.md",
|
|
18
20
|
"README.md",
|
|
@@ -25,8 +27,8 @@
|
|
|
25
27
|
"tar": "^7.5.21"
|
|
26
28
|
},
|
|
27
29
|
"devDependencies": {
|
|
28
|
-
"@working-theory/schema": "0.
|
|
29
|
-
"@working-theory/templates": "0.
|
|
30
|
+
"@working-theory/schema": "0.30.0",
|
|
31
|
+
"@working-theory/templates": "0.30.0"
|
|
30
32
|
},
|
|
31
33
|
"repository": {
|
|
32
34
|
"type": "git",
|
|
@@ -58,7 +60,7 @@
|
|
|
58
60
|
"build-catalog": "node scripts/build-component-catalog.mjs",
|
|
59
61
|
"check-catalog": "node scripts/build-component-catalog.mjs --check",
|
|
60
62
|
"manifest:build": "node scripts/build-template-manifest.mjs --template marketing-site-free",
|
|
61
|
-
"manifest:check": "node scripts/build-template-manifest.mjs --template marketing-site-free --check && node scripts/build-template-manifest.mjs --template marketing-site --check
|
|
63
|
+
"manifest:check": "node scripts/build-template-manifest.mjs --template marketing-site-free --check && node scripts/build-template-manifest.mjs --template marketing-site --check",
|
|
62
64
|
"design:gen": "tsc -b && node scripts/build-design-md.mjs",
|
|
63
65
|
"page-types:build": "tsx scripts/generate-page-types-reference.ts",
|
|
64
66
|
"page-types:check": "tsx scripts/generate-page-types-reference.ts --check"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"templates": {
|
|
3
|
+
"marketing-site-free": "released",
|
|
4
|
+
"marketing-site": "released",
|
|
5
|
+
"saas-product": "gated"
|
|
6
|
+
},
|
|
7
|
+
"shapes": {
|
|
8
|
+
"organization-product": "released",
|
|
9
|
+
"organization-service": "released",
|
|
10
|
+
"local-business-service": "released",
|
|
11
|
+
"person-service": "released",
|
|
12
|
+
"local-business-product": "deferred"
|
|
13
|
+
},
|
|
14
|
+
"designs": { "standard": "released", "minimalism": "gated" },
|
|
15
|
+
"modules": {
|
|
16
|
+
"marketing": "released",
|
|
17
|
+
"docs-blog": "released",
|
|
18
|
+
"scheduling": "released",
|
|
19
|
+
"signal-collectors": "released",
|
|
20
|
+
"workspace": "gated",
|
|
21
|
+
"multi-tenant": "gated",
|
|
22
|
+
"commerce": "gated"
|
|
23
|
+
},
|
|
24
|
+
"levels": { "free": "released", "pro": "released", "pro-plus": "gated" }
|
|
25
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# The `minimalism` design's home-deck sample
|
|
2
2
|
|
|
3
|
-
> Narrative moved here from the retired `
|
|
4
|
-
>
|
|
3
|
+
> Narrative moved here from the retired minimalism template's `TEMPLATE-MANIFEST.md` when
|
|
4
|
+
> that tree retired (spec 6892 D4/D5) — `minimalism` is a design, not a template, so it
|
|
5
5
|
> carries no generated manifest of its own. This file is NOT installed onto a scaffold
|
|
6
6
|
> (`installDesignSample` copies only `content/**` and `public/**`); it documents the sample for
|
|
7
7
|
> maintainers of this design.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: "HomePage"
|
|
3
3
|
slug: "home"
|
|
4
4
|
title: "Your Company"
|
|
5
|
-
description: "Placeholder home for the minimalism
|
|
5
|
+
description: "Placeholder home for the minimalism design — a nine-slide scroll-snap deck. Replace every line before you launch."
|
|
6
6
|
sections:
|
|
7
7
|
[
|
|
8
8
|
{
|
|
@@ -366,10 +366,11 @@ Every content route emits one `application/ld+json` script whose `@graph` carrie
|
|
|
366
366
|
| Node | `@id` | Built from | Links |
|
|
367
367
|
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
368
368
|
| Identity (`Organization` · `Person` · `LocalBusiness`) | `<origin>/#organization` (`#person` for a Person) | `siteIdentity` `name` · `url` · `logo`/`image` · `description` · `sameAs` | — |
|
|
369
|
+
| Identity (`SoftwareApplication`) | `<origin>/#product` | `siteIdentity` fields; `logo` emitted as `image` | `publisher` → the maker's own `@id` (`siteIdentity.publisher`), never a node here; takes no secondary |
|
|
369
370
|
| Secondary (optional) | `<origin>/#product` (a `SoftwareApplication`) · `<origin>/#organization` (a Person's company) | `siteIdentity.secondary` | product `publisher` → `#organization`, `featureList` from the product page's (`/software/product`) feature items; a Person `worksFor` its company, which has the Person as `founder` |
|
|
370
|
-
| `WebSite` | `<origin>/#website` | `siteName` · `baseUrl` · the render locale | `publisher` → the identity node
|
|
371
|
+
| `WebSite` | `<origin>/#website` | `siteName` · `baseUrl` · the render locale | `publisher` → the identity node (the maker, for a `SoftwareApplication` identity) |
|
|
371
372
|
|
|
372
|
-
Each page's primary node is `isPartOf` the `WebSite`; the home page's `mainEntity` is the secondary when declared, else the identity. A testimonials `Review` points its `itemReviewed` at the identity node, declared in the same graph. Free declares no secondary, so a fresh site emits the Organization and the WebSite. `/sign-in` emits no JSON-LD.
|
|
373
|
+
Each page's primary node is `isPartOf` the `WebSite`; the home page's `mainEntity` is the secondary when declared, else the identity (`#product` for a `SoftwareApplication` identity, #7103). A testimonials `Review` points its `itemReviewed` at the identity node, declared in the same graph. Free declares no secondary, so a fresh site emits the Organization and the WebSite. `/sign-in` emits no JSON-LD.
|
|
373
374
|
|
|
374
375
|
## 2a. State and persistence (every interactive element)
|
|
375
376
|
|
|
@@ -466,7 +467,7 @@ reversed (`docs/decisions/starter-roster-page-set.md` D4, D7).
|
|
|
466
467
|
|
|
467
468
|
### 3b. Site entity (operator-disposed 2026-09-24)
|
|
468
469
|
|
|
469
|
-
`derive-roster.py` names `SoftwareApplication` as the main entity for this template's inputs. The template deliberately declares the **Organization** as the site's primary node and the software as its **secondary** node (`siteIdentity.secondary`, kind `SoftwareApplication`, #6597): the Free template is a company's own site about its product, so the entity the site is _about_ is the maker. The inverse — `SoftwareApplication` primary — is for a product whose
|
|
470
|
+
`derive-roster.py` names `SoftwareApplication` as the main entity for this template's inputs. The template deliberately declares the **Organization** as the site's primary node and the software as its **secondary** node (`siteIdentity.secondary`, kind `SoftwareApplication`, #6597): the Free template is a company's own site about its product, so the entity the site is _about_ is the maker. The inverse — `SoftwareApplication` primary — is for a product whose maker has its own site (Positionary): express it as `siteIdentity.kind: SoftwareApplication` with a required `publisher` naming the maker's stable `@id` on that site (e.g. `https://salessmyth.com/#organization`, #7103), so the product is `<origin>/#product` and the maker is a reference, never a second Organization node here. A fresh Free scaffold declares no `secondary` until the owner names the product; the overlay `_site.mdx` documents the field.
|
|
470
471
|
|
|
471
472
|
## 4. Component inventory (Free, by tier)
|
|
472
473
|
|
|
@@ -22,8 +22,8 @@ upstream packages; don't try to edit them locally.
|
|
|
22
22
|
## Read before edit
|
|
23
23
|
|
|
24
24
|
1. `.claude/rules/architecture.md` — confirm what `apps/web` may
|
|
25
|
-
import
|
|
26
|
-
|
|
25
|
+
import, including its one database exception (the Better Auth
|
|
26
|
+
adapter). Everything else server-side goes through `apps/api`.
|
|
27
27
|
2. The page or component you're modifying, plus any sibling files in
|
|
28
28
|
the same directory.
|
|
29
29
|
3. Any project-local theme override file before touching styles —
|
|
@@ -46,7 +46,8 @@ Run `pnpm preflight` before opening a PR.
|
|
|
46
46
|
- Need a new content type? Add it under `content/<TypeName>/` and
|
|
47
47
|
reference the schema from `@working-theory/schema`.
|
|
48
48
|
- Need a database read? Wrong layer — `apps/web` does not read the
|
|
49
|
-
database
|
|
49
|
+
database outside the one exception `.claude/rules/architecture.md`
|
|
50
|
+
names. Route through `apps/api`.
|
|
50
51
|
|
|
51
52
|
## Domain conventions
|
|
52
53
|
|
|
@@ -55,23 +56,24 @@ Run `pnpm preflight` before opening a PR.
|
|
|
55
56
|
Adding `'use client'` to a file that just renders JSX is the
|
|
56
57
|
default failure mode — resist it.
|
|
57
58
|
- **Tailwind tokens only.** No raw hex, rgb, or arbitrary colors.
|
|
58
|
-
Use semantic tokens from `@working-theory/theme` (`bg-
|
|
59
|
+
Use semantic tokens from `@working-theory/theme` (`bg-surface`, `text-fg`,
|
|
59
60
|
`border-border`, etc.). If a token doesn't exist, extend the theme;
|
|
60
61
|
don't bypass.
|
|
61
62
|
- **Mobile-first responsive.** Default styles target mobile;
|
|
62
63
|
`md:`/`lg:` modifiers for larger viewports.
|
|
63
64
|
- **MDX rendering** uses `next-mdx-remote/rsc`, never `@next/mdx`
|
|
64
65
|
(the latter requires runtime config that fights Server Components).
|
|
65
|
-
- **
|
|
66
|
-
—
|
|
66
|
+
- **Better Auth's route handler** is the only API-shaped code in
|
|
67
|
+
`apps/web` — it lives at `apps/web/app/api/auth/[...all]/route.ts`.
|
|
67
68
|
Everything else goes to `apps/api`.
|
|
68
69
|
|
|
69
70
|
## Forbidden
|
|
70
71
|
|
|
71
72
|
- Importing from `apps/api` or `apps/mcp` — these are peer apps;
|
|
72
73
|
speak to the API via `@working-theory/api-client`.
|
|
73
|
-
- Importing `@working-theory/database`
|
|
74
|
-
route through
|
|
74
|
+
- Importing `@working-theory/database` outside the Better Auth adapter
|
|
75
|
+
(the exception `.claude/rules/architecture.md` names) — route through
|
|
76
|
+
`apps/api`.
|
|
75
77
|
- Importing `resend`, `@react-email/*` directly — wrap through
|
|
76
78
|
`@working-theory/email`, server-side via `apps/api`.
|
|
77
79
|
- Adding a `utils.ts`, `helpers.ts`, `common.ts`, or `misc.ts` file
|
|
@@ -25,13 +25,16 @@ other.
|
|
|
25
25
|
or `@working-theory/schema` (schema.org types).
|
|
26
26
|
- Need an HTTP call from `apps/web` to `apps/api`? Use
|
|
27
27
|
`@working-theory/api-client`. Don't import API route handlers directly.
|
|
28
|
-
- Need database access?
|
|
29
|
-
`@working-theory/database`. `apps/web`
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
- Need database access? `apps/api` and `apps/mcp` import
|
|
29
|
+
`@working-theory/database`. `apps/web` does not read the database — it goes
|
|
30
|
+
through `apps/api` via the published HTTP client. The one exception is the
|
|
31
|
+
Better Auth adapter (`apps/web/lib/prisma-for-auth.ts`, typed in
|
|
32
|
+
`apps/web/auth.ts`).
|
|
33
|
+
- Cross-cutting integrations (email, analytics) go through their
|
|
34
|
+
wrapper packages: `@working-theory/email`, `@working-theory/analytics`.
|
|
35
|
+
Never import `resend`, etc. directly. Auth has no wrapper package: it is
|
|
36
|
+
Better Auth, configured in `apps/web/auth.ts` and mounted at
|
|
37
|
+
`apps/web/app/api/auth/[...all]/route.ts`.
|
|
35
38
|
|
|
36
39
|
## What you can verify, what you can't
|
|
37
40
|
|
|
@@ -11,5 +11,17 @@
|
|
|
11
11
|
]
|
|
12
12
|
}
|
|
13
13
|
]
|
|
14
|
+
},
|
|
15
|
+
"extraKnownMarketplaces": {
|
|
16
|
+
"working-theory": {
|
|
17
|
+
"source": {
|
|
18
|
+
"source": "github",
|
|
19
|
+
"repo": "working-theory-labs/claude-plugins"
|
|
20
|
+
},
|
|
21
|
+
"autoUpdate": true
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"enabledPlugins": {
|
|
25
|
+
"ailk@working-theory": true
|
|
14
26
|
}
|
|
15
27
|
}
|
package/templates/.env.example
CHANGED
|
@@ -40,6 +40,8 @@ CONTENT_ADAPTER=mdx
|
|
|
40
40
|
|
|
41
41
|
# ── Database (optional in standalone mode) ───────────────────────────────────
|
|
42
42
|
# DATABASE_URL=postgres://user:pass@host:5432/db
|
|
43
|
+
# Direct (non-pooled) URL that `prisma migrate` uses (database/prisma.config.ts).
|
|
44
|
+
# DATABASE_URL_NON_POOLED=
|
|
43
45
|
|
|
44
46
|
# ── Stripe (sandbox keys only — never sk_live_/pk_live_) ─────────────────────
|
|
45
47
|
# STRIPE_SECRET_KEY=sk_test_...
|
|
@@ -57,6 +59,23 @@ CONTENT_ADAPTER=mdx
|
|
|
57
59
|
# BETTER_AUTH_URL=http://localhost:3000
|
|
58
60
|
# BETTER_AUTH_SECRET=
|
|
59
61
|
|
|
62
|
+
# ── API origin checks + checkout gate (apps/api) ─────────────────────────────
|
|
63
|
+
# Unset in production, either one makes the API log a startup warning; the
|
|
64
|
+
# API keeps running.
|
|
65
|
+
# FRONTEND_URL is the site the browser loads (no trailing slash). Set it on the
|
|
66
|
+
# API project: unset in production, CORS, cookie origin checks and the
|
|
67
|
+
# billing-portal return URLs fall back to http://localhost:3000, and checkout
|
|
68
|
+
# answers 500 to a caller without X-Internal-Auth.
|
|
69
|
+
# FRONTEND_URL=https://your-domain.com
|
|
70
|
+
# OSS_API_INTERNAL_SECRET gates POST /v1/checkout/sessions. Unset, the route
|
|
71
|
+
# admits every caller. Set, it answers 401 to any caller that does not send it
|
|
72
|
+
# as X-Internal-Auth — including the MCP start_purchase tool and a browser
|
|
73
|
+
# calling the API directly — so set it only when every checkout caller is a
|
|
74
|
+
# server that sends the header. Use the same value on the API and on each such
|
|
75
|
+
# caller.
|
|
76
|
+
# Generate with: openssl rand -hex 32
|
|
77
|
+
# OSS_API_INTERNAL_SECRET=
|
|
78
|
+
|
|
60
79
|
# ── Single-tenant operator pin (apps/api) ────────────────────────────────────
|
|
61
80
|
# REQUIRED on a build WITHOUT the multi-tenant module — for the submissions
|
|
62
81
|
# review dashboard AND for the `workspace` module's settings surface.
|
|
@@ -295,7 +314,7 @@ CONTENT_ADAPTER=mdx
|
|
|
295
314
|
# AILK_LICENSE_KEY=
|
|
296
315
|
# Override the daily refresh upstream (self-hosters point at their own server
|
|
297
316
|
# implementing the @working-theory/validation refresh contract).
|
|
298
|
-
# Default: https://ailaunchkit.ai/
|
|
317
|
+
# Default: https://api.ailaunchkit.ai/v1/licenses/refresh
|
|
299
318
|
# AILK_LICENSE_REFRESH_URL=
|
|
300
319
|
# Self-host override — replaces the bundled public key with a PEM-encoded one.
|
|
301
320
|
# Only set this if you operate your own license-signing infrastructure.
|
|
@@ -351,8 +370,10 @@ CONTENT_ADAPTER=mdx
|
|
|
351
370
|
# AILK_TEST_AUTH_USER_ALLOWLIST=
|
|
352
371
|
|
|
353
372
|
# ── Lead capture file store ───────────────────────────────────────────────────
|
|
354
|
-
#
|
|
355
|
-
#
|
|
373
|
+
# Outside production, every successful submission appends one JSONL line here,
|
|
374
|
+
# whichever destination delivered it. In production the write is skipped unless
|
|
375
|
+
# LEAD_CAPTURE_FILE is set, and then it is a side copy, never the delivery.
|
|
376
|
+
# Default: apps/api/leads.jsonl (relative to process cwd).
|
|
356
377
|
# LEAD_CAPTURE_FILE=apps/api/leads.jsonl
|
|
357
378
|
# Set to "true" to disable file writes (leads go to DB only; requires DATABASE_URL).
|
|
358
379
|
# LEAD_CAPTURE_FILE_DISABLED=false
|