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/README.md
CHANGED
|
@@ -52,9 +52,9 @@ pnpm create ailk@latest my-site --shape organization-service --license <key>
|
|
|
52
52
|
|
|
53
53
|
`--design minimalism` picks the other look and needs a Pro or Pro Plus key;
|
|
54
54
|
`standard` (the default) needs none. `--template <alias>` scaffolds one of the
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
three named aliases (`marketing-site-free`, `marketing-site`, `saas-product`) —
|
|
56
|
+
each fixes a shape, a design and its own module list — and cannot be combined
|
|
57
|
+
with `--shape` or `--design`.
|
|
58
58
|
|
|
59
59
|
### Add a purchased module to an existing site
|
|
60
60
|
|
|
@@ -275,8 +275,13 @@ gate runs before anything is copied (`scaffold()` in `src/programmatic.ts`), so
|
|
|
275
275
|
a refused run leaves no partial scaffold behind.
|
|
276
276
|
|
|
277
277
|
`AILK_MODULE_REGISTRY_URL` is optional: `fetchModule` uses an explicitly passed
|
|
278
|
-
`registryUrl`, then that variable, then
|
|
279
|
-
`DEFAULT_AILK_MODULE_REGISTRY_URL
|
|
278
|
+
`registryUrl`, then that variable, then `https://api.ailaunchkit.ai/v1/modules`
|
|
279
|
+
(`DEFAULT_AILK_MODULE_REGISTRY_URL`, `src/lib/fetch-module.ts`).
|
|
280
|
+
`AILK_TEMPLATE_REGISTRY_URL` is optional the same way: `fetchTemplate` uses an
|
|
281
|
+
explicitly passed `registryUrl`, then that variable, then
|
|
282
|
+
`https://api.ailaunchkit.ai/v1/templates` (`DEFAULT_AILK_TEMPLATE_REGISTRY_URL`,
|
|
283
|
+
`src/lib/fetch-template.ts`). Neither client follows a redirect: a 3xx from
|
|
284
|
+
either registry fails the fetch before any second request is sent.
|
|
280
285
|
|
|
281
286
|
```bash
|
|
282
287
|
# free-only — no key read, no network call
|
package/dist/cli.js
CHANGED
|
@@ -43,9 +43,10 @@ import { ADD_ROUTE_HELP, AddRouteRefusedError, addRouteToScaffold, formatAddRout
|
|
|
43
43
|
import { APPLY_DESIGN_HELP, applyDesign, formatApplyDesignReport, parseApplyDesignArgs, } from "./apply-design.js";
|
|
44
44
|
import { CLAUDE_MD_CHECK_PATH } from "./claude-md.js";
|
|
45
45
|
import { buildScaffoldMarkers } from "./copy.js";
|
|
46
|
+
import { assertReleased } from "./lib/module-license-gate.js";
|
|
46
47
|
import { FREE_PREREQUISITES_NOTE, LICENSED_PREREQUISITES_NOTE, isLicensedInstall, proCallout, } from "./lib/pro-callout.js";
|
|
47
48
|
import { LICENSE_SET_HELP, runLicenseSet } from "./license-set.js";
|
|
48
|
-
import { helpText, parseCliArgs, parseModules, resolveCliShape, toScaffoldOptions, } from "./parse-args.js";
|
|
49
|
+
import { PLUGIN_INSTALL_LINES, helpText, parseCliArgs, parseModules, resolveCliShape, toScaffoldOptions, } from "./parse-args.js";
|
|
49
50
|
import { addModule, formatSurfaceDrift, scaffold, syncArchitectureSync, syncCatalogSync, syncClaudeMdSync, syncRoutesSync, } from "./programmatic.js";
|
|
50
51
|
import { CORE_SURFACE_PATHS, SURFACE_ALIASES } from "./surfaces.js";
|
|
51
52
|
import { SYNC_ROUTES_HELP, formatSyncRoutesReport, parseSyncRoutesArgs, } from "./sync-routes.js";
|
|
@@ -207,7 +208,17 @@ async function main() {
|
|
|
207
208
|
}
|
|
208
209
|
// No silent default (#6754 D2/D3): a scaffold run naming neither --shape
|
|
209
210
|
// nor --template asks for a shape on a terminal and is refused without one.
|
|
210
|
-
|
|
211
|
+
// A --design or --modules outside the release scope is refused before that
|
|
212
|
+
// prompt asks anything (#6984); scaffold() checks the full run again.
|
|
213
|
+
const parsed = parseCliArgs(argv, { interactive: process.stdin.isTTY === true });
|
|
214
|
+
if (parsed.needsShapePrompt && !parsed.help && !parsed.version) {
|
|
215
|
+
assertReleased("designs", [parsed.design]);
|
|
216
|
+
assertReleased("modules", parsed.modules ?? []);
|
|
217
|
+
}
|
|
218
|
+
const opts = await resolveCliShape(parsed, {
|
|
219
|
+
input: process.stdin,
|
|
220
|
+
output: process.stdout,
|
|
221
|
+
});
|
|
211
222
|
// Deprecation notice for a retired `--surfaces` id (#5563 D2). `surfaces.ts`
|
|
212
223
|
// stays pure — `canonicalizeSurface` only resolves the alias — so this is
|
|
213
224
|
// the one place that prints: compare the raw `--surfaces` argv token
|
|
@@ -370,6 +381,10 @@ async function main() {
|
|
|
370
381
|
lines.push("Open http://localhost:3000 to see the home page.");
|
|
371
382
|
lines.push("Adding auth, saved flows, checkout or database-backed content later? README.md \u00a7 \"When you need a database\" has the one-command setup.");
|
|
372
383
|
lines.push("");
|
|
384
|
+
lines.push("Claude Code: the AI Launch Kit skills come from the ailk plugin. Trust this folder and Claude Code loads it, or run in a session:");
|
|
385
|
+
for (const line of PLUGIN_INSTALL_LINES)
|
|
386
|
+
lines.push(` ${line}`);
|
|
387
|
+
lines.push("");
|
|
373
388
|
lines.push(`Route surfaces: ${CORE_SURFACE_PATHS.join(", ")}` +
|
|
374
389
|
(surfaces.length > 0 ? ` + ${surfaces.join(", ")}` : ""));
|
|
375
390
|
lines.push("Add another with `create-ailk add-route <id>`, or re-run with --surfaces <id>.");
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** The issuer's content kinds (spec 7110 § R1), pinned equal to CONTENT_KINDS by the issuer's test. */
|
|
2
|
+
export declare const CONTENT_PAYLOAD_KINDS: readonly ["skill", "profile", "template-set", "instructions"];
|
|
3
|
+
export type ContentPayloadKind = (typeof CONTENT_PAYLOAD_KINDS)[number];
|
|
4
|
+
export interface ContentPayload {
|
|
5
|
+
readonly kind: ContentPayloadKind;
|
|
6
|
+
/** ^[a-z0-9][a-z0-9-]*$ — the route's :name and the store's path segment. */
|
|
7
|
+
readonly name: string;
|
|
8
|
+
/** The ONE paid feature the issuer's CONTENT_FEATURES row maps this name to. */
|
|
9
|
+
readonly feature: string;
|
|
10
|
+
/** SemVer — the content version, never create-ailk's (spec 7131 § D1). */
|
|
11
|
+
readonly version: string;
|
|
12
|
+
/**
|
|
13
|
+
* 64-hex `contentDigest` of `files` at `version` (§ D5). A change to any
|
|
14
|
+
* payload byte needs a new `version` AND a new `digest`; the build refuses
|
|
15
|
+
* until this agrees with the tree.
|
|
16
|
+
*/
|
|
17
|
+
readonly digest: string;
|
|
18
|
+
/** Repo-relative, POSIX; each file's source is `${sourceRoot}/${file}`. */
|
|
19
|
+
readonly sourceRoot: string;
|
|
20
|
+
/** Archive paths, POSIX, relative, sorted, unique — listed, never globbed. */
|
|
21
|
+
readonly files: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
export declare const CONTENT_PAYLOADS: readonly ContentPayload[];
|
|
24
|
+
/** The producer-side cap on one payload's archive (spec 7131 § D6). */
|
|
25
|
+
export declare const MAX_CONTENT_PAYLOAD_BYTES: number;
|
|
26
|
+
/** The keyed route's public base; `AILK_CONTENT_REGISTRY_URL` overrides it. */
|
|
27
|
+
export declare const DEFAULT_AILK_CONTENT_REGISTRY_URL = "https://api.ailaunchkit.ai/v1/content";
|
|
28
|
+
/**
|
|
29
|
+
* sha256 over the sorted `${path}\0${sha256hex(bytes)}\n` lines (§ D5).
|
|
30
|
+
* Covers archive paths and file bytes, never the archive's own bytes — gzip
|
|
31
|
+
* and tar headers differ between machines and must not decide whether the
|
|
32
|
+
* content changed.
|
|
33
|
+
*/
|
|
34
|
+
export declare function contentDigest(files: ReadonlyArray<{
|
|
35
|
+
path: string;
|
|
36
|
+
bytes: Uint8Array;
|
|
37
|
+
}>): string;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The content-payload manifest (spec 7131 § D2): every licence-gated content
|
|
3
|
+
* payload the issuer's keyed route `GET /v1/content/:kind/:name/:version`
|
|
4
|
+
* serves (spec 7110) — its kind, name, feature, content version, content
|
|
5
|
+
* digest and source files. The ONE place the builder
|
|
6
|
+
* (`scripts/build-content-payloads.mjs`), its `--verify` leg and the issuer's
|
|
7
|
+
* `CONTENT_FEATURES` agreement test read.
|
|
8
|
+
*
|
|
9
|
+
* A build-time registry, not programmatic API: deliberately not exported from
|
|
10
|
+
* `src/index.ts`. It carries names, a feature, a version and a digest — never
|
|
11
|
+
* a payload byte — so the public tarball's `dist/content-payloads.js` ships no
|
|
12
|
+
* paid content (spec 7131 § 7, AC-SPB-05).
|
|
13
|
+
*
|
|
14
|
+
* create-ailk is `oss`-tier and cannot import the issuer or the licence
|
|
15
|
+
* manifest, so the kind list is a pinned COPY of the issuer's `CONTENT_KINDS`
|
|
16
|
+
* and `feature` is a plain string; the issuer's `module-features.test.ts`
|
|
17
|
+
* fails when either drifts.
|
|
18
|
+
*/
|
|
19
|
+
import { createHash } from "node:crypto";
|
|
20
|
+
/** The issuer's content kinds (spec 7110 § R1), pinned equal to CONTENT_KINDS by the issuer's test. */
|
|
21
|
+
export const CONTENT_PAYLOAD_KINDS = [
|
|
22
|
+
"skill",
|
|
23
|
+
"profile",
|
|
24
|
+
"template-set",
|
|
25
|
+
"instructions",
|
|
26
|
+
];
|
|
27
|
+
export const CONTENT_PAYLOADS = Object.freeze([
|
|
28
|
+
Object.freeze({
|
|
29
|
+
kind: "template-set",
|
|
30
|
+
name: "scaffold-commerce",
|
|
31
|
+
feature: "commerce",
|
|
32
|
+
version: "1.0.0",
|
|
33
|
+
digest: "baa4ac6e86ec7e3b367eb67e77c3407da90c7ff9ef51d5f4e1185615194543ee",
|
|
34
|
+
sourceRoot: "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce",
|
|
35
|
+
files: Object.freeze([
|
|
36
|
+
"references/lookup-keys-template.md",
|
|
37
|
+
"references/marketplace-brand-pages.md",
|
|
38
|
+
"templates/checkout-route.template.ts",
|
|
39
|
+
"templates/invoice-route.template.ts",
|
|
40
|
+
"templates/marketplace-cta-route.tsx.tmpl",
|
|
41
|
+
"templates/payment-link-route.template.ts",
|
|
42
|
+
"templates/portal-route.template.ts",
|
|
43
|
+
"templates/stripe-config.template.ts",
|
|
44
|
+
"templates/subscription-route.template.ts",
|
|
45
|
+
]),
|
|
46
|
+
}),
|
|
47
|
+
]);
|
|
48
|
+
/** The producer-side cap on one payload's archive (spec 7131 § D6). */
|
|
49
|
+
export const MAX_CONTENT_PAYLOAD_BYTES = 256 * 1024;
|
|
50
|
+
/** The keyed route's public base; `AILK_CONTENT_REGISTRY_URL` overrides it. */
|
|
51
|
+
export const DEFAULT_AILK_CONTENT_REGISTRY_URL = "https://api.ailaunchkit.ai/v1/content";
|
|
52
|
+
const sha256Hex = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
53
|
+
/**
|
|
54
|
+
* sha256 over the sorted `${path}\0${sha256hex(bytes)}\n` lines (§ D5).
|
|
55
|
+
* Covers archive paths and file bytes, never the archive's own bytes — gzip
|
|
56
|
+
* and tar headers differ between machines and must not decide whether the
|
|
57
|
+
* content changed.
|
|
58
|
+
*/
|
|
59
|
+
export function contentDigest(files) {
|
|
60
|
+
const lines = files
|
|
61
|
+
.map(({ path, bytes }) => `${path}\0${sha256Hex(bytes)}\n`)
|
|
62
|
+
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
63
|
+
return sha256Hex(lines.join(""));
|
|
64
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const DEFAULT_AILK_MODULE_REGISTRY_URL = "https://ailaunchkit.ai/
|
|
1
|
+
export declare const DEFAULT_AILK_MODULE_REGISTRY_URL = "https://api.ailaunchkit.ai/v1/modules";
|
|
2
2
|
export interface FetchModuleOptions {
|
|
3
3
|
moduleName: string;
|
|
4
4
|
/** JWT used as `Authorization: Bearer <token>`. Must be non-empty. */
|
|
@@ -11,10 +11,10 @@ export interface FetchModuleOptions {
|
|
|
11
11
|
fetchImpl?: typeof fetch;
|
|
12
12
|
}
|
|
13
13
|
export declare class FetchModuleError extends Error {
|
|
14
|
-
/** True iff the failure was a license-gating signal (HTTP 401). */
|
|
14
|
+
/** True iff the failure was a license-gating signal (HTTP 401 or 403). */
|
|
15
15
|
readonly isLicenseError: boolean;
|
|
16
16
|
constructor(message: string,
|
|
17
|
-
/** True iff the failure was a license-gating signal (HTTP 401). */
|
|
17
|
+
/** True iff the failure was a license-gating signal (HTTP 401 or 403). */
|
|
18
18
|
isLicenseError?: boolean);
|
|
19
19
|
}
|
|
20
20
|
export declare function fetchModule(options: FetchModuleOptions): Promise<Uint8Array>;
|
package/dist/lib/fetch-module.js
CHANGED
|
@@ -9,8 +9,14 @@
|
|
|
9
9
|
* distribution only.
|
|
10
10
|
* - Empty-token guard before any network call (defense in depth — the
|
|
11
11
|
* caller should already have gated on an empty/absent key).
|
|
12
|
-
* - Status-code mapping: 401 → license invalid/revoked/lapsed,
|
|
13
|
-
*
|
|
12
|
+
* - Status-code mapping: 401 → license invalid/revoked/lapsed, 403 →
|
|
13
|
+
* not included / term ended, 404 → included, but that release's bundle
|
|
14
|
+
* not yet uploaded, other 4xx/5xx → upstream body. The issuer answers
|
|
15
|
+
* 403 before 404 (spec 7110 § D4), so a 404 reaches only a key that
|
|
16
|
+
* holds the module's feature.
|
|
17
|
+
* - Default registry: the product API gateway (`api.ailaunchkit.ai`, routed
|
|
18
|
+
* to the licence issuer's `/v1/modules`); `AILK_MODULE_REGISTRY_URL`
|
|
19
|
+
* overrides it.
|
|
14
20
|
*
|
|
15
21
|
* Deliberately DOES NOT depend on `@working-theory/license` or
|
|
16
22
|
* `@working-theory/cli` — both are `publishConfig.access: "restricted"` and
|
|
@@ -37,7 +43,7 @@
|
|
|
37
43
|
*/
|
|
38
44
|
import pkg from "../../package.json" with { type: "json" };
|
|
39
45
|
import { assertContentLengthWithinLimit, assertHttpsRegistryUrl, fetchTimeoutSignal, } from "./fetch-guard.js";
|
|
40
|
-
export const DEFAULT_AILK_MODULE_REGISTRY_URL = "https://ailaunchkit.ai/
|
|
46
|
+
export const DEFAULT_AILK_MODULE_REGISTRY_URL = "https://api.ailaunchkit.ai/v1/modules";
|
|
41
47
|
/**
|
|
42
48
|
* This package's own `package.json` `"version"`. Deliberately a static JSON
|
|
43
49
|
* import (`resolveJsonModule`), not `import.meta.url` + `fileURLToPath` (the
|
|
@@ -53,7 +59,7 @@ const CREATE_AILK_VERSION = pkg.version;
|
|
|
53
59
|
export class FetchModuleError extends Error {
|
|
54
60
|
isLicenseError;
|
|
55
61
|
constructor(message,
|
|
56
|
-
/** True iff the failure was a license-gating signal (HTTP 401). */
|
|
62
|
+
/** True iff the failure was a license-gating signal (HTTP 401 or 403). */
|
|
57
63
|
isLicenseError = false) {
|
|
58
64
|
super(message);
|
|
59
65
|
this.isLicenseError = isLicenseError;
|
|
@@ -108,7 +114,7 @@ export async function fetchModule(options) {
|
|
|
108
114
|
/* isLicenseError */ true);
|
|
109
115
|
}
|
|
110
116
|
if (response.status === 404) {
|
|
111
|
-
throw new FetchModuleError(`"${moduleName}" module content not yet available — ping us at hello@ailaunchkit.ai`);
|
|
117
|
+
throw new FetchModuleError(`"${moduleName}" module content not yet available for create-ailk ${version} — your licence includes it, but that release's bundle has not been uploaded yet; ping us at hello@ailaunchkit.ai`);
|
|
112
118
|
}
|
|
113
119
|
if (!response.ok) {
|
|
114
120
|
const body = await safeReadBody(response);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const DEFAULT_AILK_TEMPLATE_REGISTRY_URL = "https://ailaunchkit.ai/
|
|
1
|
+
export declare const DEFAULT_AILK_TEMPLATE_REGISTRY_URL = "https://api.ailaunchkit.ai/v1/templates";
|
|
2
2
|
export interface FetchTemplateOptions {
|
|
3
3
|
templateName: string;
|
|
4
4
|
/** JWT used as `Authorization: Bearer <token>`. Must be non-empty. */
|
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
* D1, § 6).
|
|
4
4
|
*
|
|
5
5
|
* The client half of `GET /v1/templates/:name/:version` (#5480's route),
|
|
6
|
-
* reached
|
|
6
|
+
* reached at `https://api.ailaunchkit.ai/v1/templates/:name/:version` through
|
|
7
|
+
* the product API gateway. Posture is
|
|
7
8
|
* `fetch-module.ts`'s, mirrored one-for-one rather than generalised into a
|
|
8
9
|
* shared helper: bearer-JWT auth, `redirect: "error"` so a redirected
|
|
9
10
|
* response cannot drop the bearer token onto an unintended host, an
|
|
10
11
|
* empty-token guard before any network call, and the same status mapping —
|
|
11
12
|
* 401 → licence invalid/revoked/lapsed, 403 → not included / term ended,
|
|
12
|
-
* 404 →
|
|
13
|
+
* 404 → included, but that release's bundle not yet uploaded, other non-2xx
|
|
14
|
+
* → the upstream body. The issuer answers 403 before 404 (spec 7110 § D4),
|
|
15
|
+
* so a 404 reaches only a key that holds the template's whole union.
|
|
13
16
|
*
|
|
14
17
|
* ONE word differs, and it is the whole reason this is a sibling rather
|
|
15
18
|
* than a second call site of `fetchModule` (spec 5481 § 3 DD5): the 404
|
|
@@ -27,7 +30,7 @@
|
|
|
27
30
|
*/
|
|
28
31
|
import pkg from "../../package.json" with { type: "json" };
|
|
29
32
|
import { assertContentLengthWithinLimit, assertHttpsRegistryUrl, fetchTimeoutSignal, } from "./fetch-guard.js";
|
|
30
|
-
export const DEFAULT_AILK_TEMPLATE_REGISTRY_URL = "https://ailaunchkit.ai/
|
|
33
|
+
export const DEFAULT_AILK_TEMPLATE_REGISTRY_URL = "https://api.ailaunchkit.ai/v1/templates";
|
|
31
34
|
/**
|
|
32
35
|
* This package's own `package.json` `"version"` — a static JSON import for
|
|
33
36
|
* the reason `fetch-module.ts`'s `CREATE_AILK_VERSION` doc gives: `import.meta`
|
|
@@ -95,7 +98,7 @@ export async function fetchTemplate(options) {
|
|
|
95
98
|
/* isLicenseError */ true);
|
|
96
99
|
}
|
|
97
100
|
if (response.status === 404) {
|
|
98
|
-
throw new FetchTemplateError(`"${templateName}" template content not yet available — ping us at hello@ailaunchkit.ai`);
|
|
101
|
+
throw new FetchTemplateError(`"${templateName}" template content not yet available for create-ailk ${version} — your licence includes it, but that release's bundle has not been uploaded yet; ping us at hello@ailaunchkit.ai`);
|
|
99
102
|
}
|
|
100
103
|
if (!response.ok) {
|
|
101
104
|
const body = await safeReadBody(response);
|
|
@@ -1,26 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Paid-module license gate (K1, #5089 — Flavor 3). Orchestrates
|
|
3
|
-
* `module-tier.ts`'s classification + `fetch-module.ts`'s authenticated
|
|
4
|
-
* fetch into the three honest states the CLI must surface:
|
|
5
|
-
*
|
|
6
|
-
* (a) no key + a paid module requested → conversion hint, non-zero, ZERO
|
|
7
|
-
* network (mirrors `install-skill.ts`'s free-tier short-circuit).
|
|
8
|
-
* (b) a lapsed/invalid key + a paid module → "your licence does not cover
|
|
9
|
-
* this", non-zero (the module distribution's own 401 — see
|
|
10
|
-
* `fetch-module.ts`'s file doc for why there is no local JWT
|
|
11
|
-
* pre-check here).
|
|
12
|
-
* (c) a valid key → every requested paid module's bundle bytes, ready for
|
|
13
|
-
* `extract-module-bundle.ts` to merge into the scaffold.
|
|
14
|
-
*
|
|
15
|
-
* A Free-only request (no paid modules) never reaches the key check at all —
|
|
16
|
-
* `resolvePaidModules` returns immediately, before touching `process.env` or
|
|
17
|
-
* the network, so a bare/all-Free scaffold is provably unaffected by this
|
|
18
|
-
* gate (K1's regression floor).
|
|
19
|
-
*
|
|
20
|
-
* Also carries the design gate (#6760): `assertDesignAllowed` refuses a
|
|
21
|
-
* non-`standard` design at the `free` level, naming the design and the
|
|
22
|
-
* run's level, before `scaffold()`'s first disk write.
|
|
23
|
-
*/
|
|
24
1
|
import type { ModuleName } from "../parse-args.js";
|
|
25
2
|
import { type Level } from "./module-tier.js";
|
|
26
3
|
/**
|
|
@@ -38,6 +15,35 @@ export declare const CONVERSION_HINT_URL = "https://ailaunchkit.ai/pricing";
|
|
|
38
15
|
export declare class LicenseRequiredError extends Error {
|
|
39
16
|
constructor(message: string);
|
|
40
17
|
}
|
|
18
|
+
/** The five axes `release-scope.json` declares (spec 6977 D3). */
|
|
19
|
+
export type ReleaseAxis = "templates" | "shapes" | "designs" | "modules" | "levels";
|
|
20
|
+
/** `gated` and `deferred` are both refused; only a `gated` one gets negative checks. */
|
|
21
|
+
export type ReleaseStatus = "released" | "gated" | "deferred";
|
|
22
|
+
export type ReleaseScope = Readonly<Record<ReleaseAxis, Readonly<Record<string, ReleaseStatus>>>>;
|
|
23
|
+
/**
|
|
24
|
+
* The package-root `release-scope.json` (shipped in `files`). A static JSON
|
|
25
|
+
* import, not an `import.meta.url` read, for `fetch-module.ts`'s reason: jest
|
|
26
|
+
* loads this module directly. `release-scope.test.ts` pins every registry
|
|
27
|
+
* name to an entry here (R5).
|
|
28
|
+
*/
|
|
29
|
+
export declare const RELEASE_SCOPE: ReleaseScope;
|
|
30
|
+
/** Thrown for a surface outside the release scope — a refusal, not a licence state. */
|
|
31
|
+
export declare class ReleaseScopeError extends Error {
|
|
32
|
+
constructor(message: string);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Refuse any of `names` that `scope[axis]` marks `gated` or `deferred`, with
|
|
36
|
+
* the not-available message `parse-args.ts` gives a reserved shape. A name
|
|
37
|
+
* the scope does not list (a customer's own design, say) is not refused.
|
|
38
|
+
* Bypassed when `AILK_RELEASE_SCOPE=ignore` — this package's jest setup and
|
|
39
|
+
* an operator's own run; a published CLI never sets it.
|
|
40
|
+
*
|
|
41
|
+
* Called on the scaffold path only (`scaffold()`, `cli.ts`, and the level
|
|
42
|
+
* check in `resolveModuleSet`) — never from `parseModules`, so `add-module`
|
|
43
|
+
* and `--sync-architecture` keep working on a site that already carries a
|
|
44
|
+
* gated module.
|
|
45
|
+
*/
|
|
46
|
+
export declare function assertReleased(axis: ReleaseAxis, names: readonly (string | undefined)[], scope?: ReleaseScope): void;
|
|
41
47
|
/**
|
|
42
48
|
* Design names every level renders (#6760 D1). Anything else in
|
|
43
49
|
* `STARTER_DESIGNS` is Pro and up. A path design's Free-ness is decided by
|
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paid-module license gate (K1, #5089 — Flavor 3). Orchestrates
|
|
3
|
+
* `module-tier.ts`'s classification + `fetch-module.ts`'s authenticated
|
|
4
|
+
* fetch into the three honest states the CLI must surface:
|
|
5
|
+
*
|
|
6
|
+
* (a) no key + a paid module requested → conversion hint, non-zero, ZERO
|
|
7
|
+
* network (mirrors `install-skill.ts`'s free-tier short-circuit).
|
|
8
|
+
* (b) a lapsed/invalid key + a paid module → "your licence does not cover
|
|
9
|
+
* this", non-zero (the module distribution's own 401 — see
|
|
10
|
+
* `fetch-module.ts`'s file doc for why there is no local JWT
|
|
11
|
+
* pre-check here).
|
|
12
|
+
* (c) a valid key → every requested paid module's bundle bytes, ready for
|
|
13
|
+
* `extract-module-bundle.ts` to merge into the scaffold.
|
|
14
|
+
*
|
|
15
|
+
* A Free-only request (no paid modules) never reaches the key check at all —
|
|
16
|
+
* `resolvePaidModules` returns immediately, before touching `process.env` or
|
|
17
|
+
* the network, so a bare/all-Free scaffold is provably unaffected by this
|
|
18
|
+
* gate (K1's regression floor).
|
|
19
|
+
*
|
|
20
|
+
* Also carries the design gate (#6760): `assertDesignAllowed` refuses a
|
|
21
|
+
* non-`standard` design at the `free` level, naming the design and the
|
|
22
|
+
* run's level, before `scaffold()`'s first disk write.
|
|
23
|
+
*
|
|
24
|
+
* And the release-scope gate (#6984, spec 6977 D3): `assertReleased` refuses
|
|
25
|
+
* a template, shape, design, module or level that `release-scope.json` marks
|
|
26
|
+
* `gated` or `deferred`, on the scaffold path only.
|
|
27
|
+
*/
|
|
28
|
+
import releaseScopeJson from "../../release-scope.json" with { type: "json" };
|
|
1
29
|
import { FetchModuleError, fetchModule } from "./fetch-module.js";
|
|
2
30
|
import { assertLicenseKeyShape } from "./license-key-files.js";
|
|
3
31
|
import { ADD_ONS, LEVEL_LABEL, LEVEL_MODULES, OPT_IN_MODULES, PAID_MODULES, classifyModules, composeModuleSet, levelOf, } from "./module-tier.js";
|
|
@@ -19,6 +47,74 @@ export class LicenseRequiredError extends Error {
|
|
|
19
47
|
this.name = "LicenseRequiredError";
|
|
20
48
|
}
|
|
21
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* The package-root `release-scope.json` (shipped in `files`). A static JSON
|
|
52
|
+
* import, not an `import.meta.url` read, for `fetch-module.ts`'s reason: jest
|
|
53
|
+
* loads this module directly. `release-scope.test.ts` pins every registry
|
|
54
|
+
* name to an entry here (R5).
|
|
55
|
+
*/
|
|
56
|
+
export const RELEASE_SCOPE = releaseScopeJson;
|
|
57
|
+
const AXIS_NOUN = {
|
|
58
|
+
templates: "template",
|
|
59
|
+
shapes: "shape",
|
|
60
|
+
designs: "design",
|
|
61
|
+
modules: "module",
|
|
62
|
+
levels: "level",
|
|
63
|
+
};
|
|
64
|
+
/** Thrown for a surface outside the release scope — a refusal, not a licence state. */
|
|
65
|
+
export class ReleaseScopeError extends Error {
|
|
66
|
+
constructor(message) {
|
|
67
|
+
super(message);
|
|
68
|
+
this.name = "ReleaseScopeError";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const RELEASE_STATUSES = ["released", "gated", "deferred"];
|
|
72
|
+
/**
|
|
73
|
+
* Refuse a scope carrying a status other than `released`, `gated` or
|
|
74
|
+
* `deferred` — fail closed: a misspelled status never reads as released.
|
|
75
|
+
*/
|
|
76
|
+
function assertValidScope(scope) {
|
|
77
|
+
for (const [axis, statuses] of Object.entries(scope)) {
|
|
78
|
+
for (const [name, status] of Object.entries(statuses)) {
|
|
79
|
+
if (!RELEASE_STATUSES.includes(status)) {
|
|
80
|
+
throw new ReleaseScopeError(`release-scope.json ${axis}."${name}" is "${String(status)}" — expected one of: ${RELEASE_STATUSES.join(", ")}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Refuse any of `names` that `scope[axis]` marks `gated` or `deferred`, with
|
|
87
|
+
* the not-available message `parse-args.ts` gives a reserved shape. A name
|
|
88
|
+
* the scope does not list (a customer's own design, say) is not refused.
|
|
89
|
+
* Bypassed when `AILK_RELEASE_SCOPE=ignore` — this package's jest setup and
|
|
90
|
+
* an operator's own run; a published CLI never sets it.
|
|
91
|
+
*
|
|
92
|
+
* Called on the scaffold path only (`scaffold()`, `cli.ts`, and the level
|
|
93
|
+
* check in `resolveModuleSet`) — never from `parseModules`, so `add-module`
|
|
94
|
+
* and `--sync-architecture` keep working on a site that already carries a
|
|
95
|
+
* gated module.
|
|
96
|
+
*/
|
|
97
|
+
export function assertReleased(axis, names, scope = RELEASE_SCOPE) {
|
|
98
|
+
if (process.env["AILK_RELEASE_SCOPE"] === "ignore")
|
|
99
|
+
return;
|
|
100
|
+
assertValidScope(scope);
|
|
101
|
+
const statuses = scope[axis];
|
|
102
|
+
for (const name of names) {
|
|
103
|
+
if (name === undefined)
|
|
104
|
+
continue;
|
|
105
|
+
const status = statuses[name];
|
|
106
|
+
if (status !== "gated" && status !== "deferred")
|
|
107
|
+
continue;
|
|
108
|
+
const released = Object.keys(statuses).filter((n) => statuses[n] === "released");
|
|
109
|
+
// A level comes from the key, never a flag — so it points at what the run CAN choose.
|
|
110
|
+
const choose = axis === "levels"
|
|
111
|
+
? `Your licence key's level is ${name}; name the modules you want with --modules (one of: ${Object.keys(scope.modules)
|
|
112
|
+
.filter((m) => scope.modules[m] === "released")
|
|
113
|
+
.join(", ")}).`
|
|
114
|
+
: `Choose one of: ${released.join(", ")}.`;
|
|
115
|
+
throw new ReleaseScopeError(`${AXIS_NOUN[axis]} "${name}" is not available yet. ${choose}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
22
118
|
/**
|
|
23
119
|
* Design names every level renders (#6760 D1). Anything else in
|
|
24
120
|
* `STARTER_DESIGNS` is Pro and up. A path design's Free-ness is decided by
|
|
@@ -223,6 +319,8 @@ export async function resolveModuleSet(options) {
|
|
|
223
319
|
return { modules, bundles };
|
|
224
320
|
}
|
|
225
321
|
const { level, bundles } = await deriveLevel(options);
|
|
322
|
+
// #6984 — a gated level (Pro Plus) is refused before its defaults compose.
|
|
323
|
+
assertReleased("levels", [level]);
|
|
226
324
|
const modules = composeModuleSet(namedList, level);
|
|
227
325
|
const { paid } = classifyModules(modules);
|
|
228
326
|
const missing = paid.filter((m) => !bundles.has(m));
|
|
@@ -212,6 +212,15 @@ export interface ModuleSlice {
|
|
|
212
212
|
* leak class `scripts/check-oss-strip.sh` exists to catch (#5644).
|
|
213
213
|
*/
|
|
214
214
|
installRoot?: string;
|
|
215
|
+
/**
|
|
216
|
+
* Paths this module's keyed content payloads occupy (src/content-payloads.ts):
|
|
217
|
+
* each payload file's monorepo source AND the scaffold path its skill's fetch
|
|
218
|
+
* writes it to. Never delivered — no build, extract or patch step reads this
|
|
219
|
+
* field. Declared so the paid-path leak guards, which derive from
|
|
220
|
+
* `ownedFiles`/`installMap`, still recognise a leaked copy after the files
|
|
221
|
+
* left `installMap` (#7133).
|
|
222
|
+
*/
|
|
223
|
+
contentPayloadPaths?: string[];
|
|
215
224
|
/**
|
|
216
225
|
* Plain text `extractModuleBundle` prints to stdout after a successful
|
|
217
226
|
* merge, with C0/C1 control characters (other than newline) stripped
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { existsSync, readFileSync } from "node:fs";
|
|
25
25
|
import { join, sep } from "node:path";
|
|
26
|
+
import { CONTENT_PAYLOADS, } from "./content-payloads.js";
|
|
26
27
|
import { importClosure } from "./lib/import-graph.js";
|
|
27
28
|
import { PAID_MODULES } from "./lib/module-tier.js";
|
|
28
29
|
import { RECOGNIZED_MODULES } from "./parse-args.js";
|
|
@@ -183,6 +184,21 @@ const ASSET_STORE_FILES = [
|
|
|
183
184
|
* tools/lint-architecture.ts fails on (#6649).
|
|
184
185
|
*/
|
|
185
186
|
const WEB_AUTH_ADAPTER_IMPORT = "database";
|
|
187
|
+
/**
|
|
188
|
+
* A content payload's paths for `ModuleSlice.contentPayloadPaths` (#7133):
|
|
189
|
+
* each file's `${sourceRoot}/${file}` and the `${destRoot}/${file}` its
|
|
190
|
+
* skill's keyed fetch writes it to — read off `CONTENT_PAYLOADS`, never
|
|
191
|
+
* hand-listed, so the leak guards cannot drift from the manifest.
|
|
192
|
+
*/
|
|
193
|
+
function payloadPaths(kind, name, destRoot) {
|
|
194
|
+
const payload = CONTENT_PAYLOADS.find((p) => p.kind === kind && p.name === name);
|
|
195
|
+
if (!payload)
|
|
196
|
+
throw new Error(`no content payload ${kind}/${name}`);
|
|
197
|
+
return payload.files.flatMap((file) => [
|
|
198
|
+
`${payload.sourceRoot}/${file}`,
|
|
199
|
+
`${destRoot}/${file}`,
|
|
200
|
+
]);
|
|
201
|
+
}
|
|
186
202
|
/**
|
|
187
203
|
* The per-module slices. Authored for every `RECOGNIZED_MODULES` value; only
|
|
188
204
|
* `marketing` (the default) is validated end-to-end against a golden.
|
|
@@ -1503,8 +1519,8 @@ export const MODULE_SLICES = {
|
|
|
1503
1519
|
// module's own `pageContributions`/`starter-grid.ts` rows respectively,
|
|
1504
1520
|
// so this module installs its own seeds for them directly, unlike the
|
|
1505
1521
|
// sample rows above. `/referral`'s seed and flow move here the same way,
|
|
1506
|
-
// moved out of
|
|
1507
|
-
//
|
|
1522
|
+
// moved out of a retired template tree (spec 6892 D3), which owned no
|
|
1523
|
+
// code of its own for either route.
|
|
1508
1524
|
// `generate-sample-report.py` keeps its root-level destination
|
|
1509
1525
|
// (finding 1 — AC7 forbids changing the Pro file set).
|
|
1510
1526
|
installMap: {
|
|
@@ -1661,30 +1677,31 @@ export const MODULE_SLICES = {
|
|
|
1661
1677
|
// it sits outside build-templates.mjs's SNAPSHOT_DIRS/overlay, so
|
|
1662
1678
|
// ownedFiles/paidModuleTemplateExclusions cannot reach it) to
|
|
1663
1679
|
// `module-templates/commerce/.claude/skills/scaffold-commerce/`, and
|
|
1664
|
-
// delivered into a keyed scaffold by `installMap` instead.
|
|
1680
|
+
// delivered into a keyed scaffold by `installMap` instead. Five literal
|
|
1665
1681
|
// entries, not one directory entry: `extractModuleBundle`'s
|
|
1666
1682
|
// `validateInstallMap` requires every source to be a FILE the bundle
|
|
1667
1683
|
// produced (`extract-module-bundle.ts`'s `!sourceStat.isFile()` check) —
|
|
1668
|
-
// a directory source is rejected outright.
|
|
1684
|
+
// a directory source is rejected outright. The skill ships as steps only
|
|
1685
|
+
// (#7133): its `templates/` and `references/` are the keyed content
|
|
1686
|
+
// payload `template-set/scaffold-commerce` (src/content-payloads.ts),
|
|
1687
|
+
// which `scripts/fetch-templates.ts` fetches with the site's licence key;
|
|
1688
|
+
// `link/` carries byte copies of the link path's two files, which need no
|
|
1689
|
+
// key.
|
|
1669
1690
|
installMap: {
|
|
1670
1691
|
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/SKILL.md": ".claude/skills/scaffold-commerce/SKILL.md",
|
|
1671
|
-
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/
|
|
1672
|
-
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/
|
|
1673
|
-
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/
|
|
1674
|
-
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/
|
|
1675
|
-
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl": ".claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl",
|
|
1676
|
-
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts": ".claude/skills/scaffold-commerce/templates/payment-link-route.template.ts",
|
|
1677
|
-
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/portal-route.template.ts": ".claude/skills/scaffold-commerce/templates/portal-route.template.ts",
|
|
1678
|
-
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts": ".claude/skills/scaffold-commerce/templates/stripe-config.template.ts",
|
|
1679
|
-
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts": ".claude/skills/scaffold-commerce/templates/subscription-route.template.ts",
|
|
1692
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/link/marketplace-brand-pages.md": ".claude/skills/scaffold-commerce/link/marketplace-brand-pages.md",
|
|
1693
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/link/marketplace-cta-route.tsx.tmpl": ".claude/skills/scaffold-commerce/link/marketplace-cta-route.tsx.tmpl",
|
|
1694
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/scripts/fetch-templates.ts": ".claude/skills/scaffold-commerce/scripts/fetch-templates.ts",
|
|
1695
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/scripts/stripe-catalog.ts": ".claude/skills/scaffold-commerce/scripts/stripe-catalog.ts",
|
|
1680
1696
|
// The commerce-boundary rule (#6039/#6070, this entry #6041): ships to
|
|
1681
1697
|
// a commerce-keyed scaffold's project root so an agent working there
|
|
1682
1698
|
// reads the money/live-mode/personal-data classification before it
|
|
1683
|
-
// acts. One more literal entry alongside the
|
|
1699
|
+
// acts. One more literal entry alongside the five scaffold-commerce
|
|
1684
1700
|
// skill entries above, for the same reason — a directory source is
|
|
1685
1701
|
// rejected by `validateInstallMap`.
|
|
1686
1702
|
"packages/create-ailk/module-templates/commerce/.claude/rules/commerce-boundary.md": ".claude/rules/commerce-boundary.md",
|
|
1687
1703
|
},
|
|
1704
|
+
contentPayloadPaths: payloadPaths("template-set", "scaffold-commerce", ".claude/skills/scaffold-commerce"),
|
|
1688
1705
|
// patches (#5581 U5, D5) — delivers the extracted component package to a
|
|
1689
1706
|
// keyed scaffold's apps/web, mirroring the workspace slice's identical
|
|
1690
1707
|
// merge-keys op above.
|
|
@@ -1832,12 +1849,27 @@ export function coOwnedModuleDestinations() {
|
|
|
1832
1849
|
// see that entry, and sync-routes' AC-5 coverage test, which reads this
|
|
1833
1850
|
// list so the resulting ROUTE_SINCE-vs-template absence is justified
|
|
1834
1851
|
// rather than reported stale.
|
|
1852
|
+
// - the three app-side suites of the API ↔ MCP parity guard (#7135, spec
|
|
1853
|
+
// docs/specs/7135-an-api-mcp-parity-guard-spec.md D11) — file entries,
|
|
1854
|
+
// matched exactly by `isExcludedTemplatePath`. Each imports the guard's
|
|
1855
|
+
// core from the monorepo's root `tests/parity/api-parity/` by relative
|
|
1856
|
+
// path, which a scaffold does not carry. A test that imports a slice file
|
|
1857
|
+
// is a dependent, not an import-graph companion, so
|
|
1858
|
+
// `paidModuleTemplateExclusions` never sweeps these along.
|
|
1859
|
+
// - apps/mcp/__tests__/pro-tool-call-gate.test.ts (#7315, spec
|
|
1860
|
+
// docs/specs/7315-a-gated-pro-tool-re-spec.md D4) — same reason as the
|
|
1861
|
+
// three suites above: it imports `SITE_ARGS` from the monorepo's root
|
|
1862
|
+
// `tests/parity/` tree, which no scaffold carries.
|
|
1835
1863
|
export const EXCLUDE_TEMPLATE_DIRS = [
|
|
1836
1864
|
"apps/web/__tests__",
|
|
1837
1865
|
"apps/web/app/__tests__",
|
|
1838
1866
|
"apps/web/app/[locale]/__tests__",
|
|
1839
1867
|
"apps/web/app/[locale]/(authed)/edit",
|
|
1840
1868
|
"apps/web/content",
|
|
1869
|
+
"apps/api/src/__tests__/api-parity.test.ts",
|
|
1870
|
+
"apps/api/src/__tests__/api-parity-paid.test.ts",
|
|
1871
|
+
"apps/mcp/__tests__/api-parity.test.ts",
|
|
1872
|
+
"apps/mcp/__tests__/pro-tool-call-gate.test.ts",
|
|
1841
1873
|
];
|
|
1842
1874
|
export function paidModuleTemplateExclusions(snapshotRoots, repoRoot) {
|
|
1843
1875
|
const roots = new Set(snapshotRoots);
|
package/dist/parse-args.d.ts
CHANGED
|
@@ -103,6 +103,8 @@ export interface ParseEnv {
|
|
|
103
103
|
/** A human can answer a prompt: `process.stdin.isTTY === true` in cli.ts. */
|
|
104
104
|
interactive: boolean;
|
|
105
105
|
}
|
|
106
|
+
/** The two lines that install the `ailk` plugin in a Claude Code session (#7298). */
|
|
107
|
+
export declare const PLUGIN_INSTALL_LINES: readonly ["/plugin marketplace add working-theory-labs/claude-plugins", "/plugin install ailk@working-theory"];
|
|
106
108
|
export declare const helpText: string;
|
|
107
109
|
/**
|
|
108
110
|
* Parse `create-ailk` argv. `env.interactive` defaults to false —
|
package/dist/parse-args.js
CHANGED
|
@@ -65,6 +65,11 @@ export class ParseError extends Error {
|
|
|
65
65
|
this.name = "ParseError";
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
+
/** The two lines that install the `ailk` plugin in a Claude Code session (#7298). */
|
|
69
|
+
export const PLUGIN_INSTALL_LINES = [
|
|
70
|
+
"/plugin marketplace add working-theory-labs/claude-plugins",
|
|
71
|
+
"/plugin install ailk@working-theory",
|
|
72
|
+
];
|
|
68
73
|
export const helpText = `create-ailk — scaffold a new AI Launch Kit site.
|
|
69
74
|
|
|
70
75
|
Usage:
|
|
@@ -166,6 +171,8 @@ Next steps after a successful scaffold:
|
|
|
166
171
|
cd <target-dir>
|
|
167
172
|
pnpm install
|
|
168
173
|
pnpm dev
|
|
174
|
+
Then, in Claude Code, install the ailk plugin:
|
|
175
|
+
${PLUGIN_INSTALL_LINES.map((line) => ` ${line}`).join("\n")}
|
|
169
176
|
|
|
170
177
|
Reference: ADR 0007 — \`pnpm create ailk@latest\` is the canonical install path.
|
|
171
178
|
`;
|