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
package/README.md
CHANGED
|
@@ -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
|
@@ -46,7 +46,7 @@ import { buildScaffoldMarkers } from "./copy.js";
|
|
|
46
46
|
import { assertReleased } from "./lib/module-license-gate.js";
|
|
47
47
|
import { FREE_PREREQUISITES_NOTE, LICENSED_PREREQUISITES_NOTE, isLicensedInstall, proCallout, } from "./lib/pro-callout.js";
|
|
48
48
|
import { LICENSE_SET_HELP, runLicenseSet } from "./license-set.js";
|
|
49
|
-
import { helpText, parseCliArgs, parseModules, resolveCliShape, toScaffoldOptions, } from "./parse-args.js";
|
|
49
|
+
import { PLUGIN_INSTALL_LINES, helpText, parseCliArgs, parseModules, resolveCliShape, toScaffoldOptions, } from "./parse-args.js";
|
|
50
50
|
import { addModule, formatSurfaceDrift, scaffold, syncArchitectureSync, syncCatalogSync, syncClaudeMdSync, syncRoutesSync, } from "./programmatic.js";
|
|
51
51
|
import { CORE_SURFACE_PATHS, SURFACE_ALIASES } from "./surfaces.js";
|
|
52
52
|
import { SYNC_ROUTES_HELP, formatSyncRoutesReport, parseSyncRoutesArgs, } from "./sync-routes.js";
|
|
@@ -381,6 +381,10 @@ async function main() {
|
|
|
381
381
|
lines.push("Open http://localhost:3000 to see the home page.");
|
|
382
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.");
|
|
383
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("");
|
|
384
388
|
lines.push(`Route surfaces: ${CORE_SURFACE_PATHS.join(", ")}` +
|
|
385
389
|
(surfaces.length > 0 ? ` + ${surfaces.join(", ")}` : ""));
|
|
386
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);
|
|
@@ -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.
|
|
@@ -1661,31 +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/templates/invoice-route.template.ts": ".claude/skills/scaffold-commerce/templates/invoice-route.template.ts",
|
|
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",
|
|
1680
1695
|
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/scripts/stripe-catalog.ts": ".claude/skills/scaffold-commerce/scripts/stripe-catalog.ts",
|
|
1681
1696
|
// The commerce-boundary rule (#6039/#6070, this entry #6041): ships to
|
|
1682
1697
|
// a commerce-keyed scaffold's project root so an agent working there
|
|
1683
1698
|
// reads the money/live-mode/personal-data classification before it
|
|
1684
|
-
// acts. One more literal entry alongside the
|
|
1699
|
+
// acts. One more literal entry alongside the five scaffold-commerce
|
|
1685
1700
|
// skill entries above, for the same reason — a directory source is
|
|
1686
1701
|
// rejected by `validateInstallMap`.
|
|
1687
1702
|
"packages/create-ailk/module-templates/commerce/.claude/rules/commerce-boundary.md": ".claude/rules/commerce-boundary.md",
|
|
1688
1703
|
},
|
|
1704
|
+
contentPayloadPaths: payloadPaths("template-set", "scaffold-commerce", ".claude/skills/scaffold-commerce"),
|
|
1689
1705
|
// patches (#5581 U5, D5) — delivers the extracted component package to a
|
|
1690
1706
|
// keyed scaffold's apps/web, mirroring the workspace slice's identical
|
|
1691
1707
|
// merge-keys op above.
|
|
@@ -1833,12 +1849,27 @@ export function coOwnedModuleDestinations() {
|
|
|
1833
1849
|
// see that entry, and sync-routes' AC-5 coverage test, which reads this
|
|
1834
1850
|
// list so the resulting ROUTE_SINCE-vs-template absence is justified
|
|
1835
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.
|
|
1836
1863
|
export const EXCLUDE_TEMPLATE_DIRS = [
|
|
1837
1864
|
"apps/web/__tests__",
|
|
1838
1865
|
"apps/web/app/__tests__",
|
|
1839
1866
|
"apps/web/app/[locale]/__tests__",
|
|
1840
1867
|
"apps/web/app/[locale]/(authed)/edit",
|
|
1841
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",
|
|
1842
1873
|
];
|
|
1843
1874
|
export function paidModuleTemplateExclusions(snapshotRoots, repoRoot) {
|
|
1844
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
|
`;
|
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.
|
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",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"tar": "^7.5.21"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@working-theory/schema": "0.
|
|
31
|
-
"@working-theory/templates": "0.
|
|
30
|
+
"@working-theory/schema": "0.30.0",
|
|
31
|
+
"@working-theory/templates": "0.30.0"
|
|
32
32
|
},
|
|
33
33
|
"repository": {
|
|
34
34
|
"type": "git",
|
|
@@ -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
|
|
|
@@ -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
|
@@ -314,7 +314,7 @@ CONTENT_ADAPTER=mdx
|
|
|
314
314
|
# AILK_LICENSE_KEY=
|
|
315
315
|
# Override the daily refresh upstream (self-hosters point at their own server
|
|
316
316
|
# implementing the @working-theory/validation refresh contract).
|
|
317
|
-
# Default: https://ailaunchkit.ai/
|
|
317
|
+
# Default: https://api.ailaunchkit.ai/v1/licenses/refresh
|
|
318
318
|
# AILK_LICENSE_REFRESH_URL=
|
|
319
319
|
# Self-host override — replaces the bundled public key with a PEM-encoded one.
|
|
320
320
|
# Only set this if you operate your own license-signing infrastructure.
|
package/templates/CLAUDE.md
CHANGED
|
@@ -21,7 +21,7 @@ apps/api/ Fastify HTTP API — lead capture, checkout, webhooks
|
|
|
21
21
|
apps/mcp/ MCP server — agent-callable tools
|
|
22
22
|
database/ Prisma schema + migrations
|
|
23
23
|
content/ Site settings (_site.mdx) and MDX pages (en/<TypeName>/)
|
|
24
|
-
.claude/ Rules,
|
|
24
|
+
.claude/ Rules, the agent definition and settings for Claude Code
|
|
25
25
|
tools/ This repo's own checks (architecture, CLAUDE.md drift)
|
|
26
26
|
```
|
|
27
27
|
|
|
@@ -67,7 +67,7 @@ your module set; `pnpm architecture` enforces it, and a hook in
|
|
|
67
67
|
| Diff hygiene when editing existing files | `.claude/rules/preserve-existing-style.md` |
|
|
68
68
|
| Code style, testing, security | `CONVENTIONS.md` |
|
|
69
69
|
| Environment variables and what they unlock | `.env.example` |
|
|
70
|
-
| Deploy configuration | `apps/api/vercel.json`; the
|
|
70
|
+
| Deploy configuration | `apps/api/vercel.json`; the `/ailk:setup-deploy` skill |
|
|
71
71
|
| Verification before push | `pnpm preflight` (see § Checks) |
|
|
72
72
|
|
|
73
73
|
<!-- /ailk:template id="resource-map" -->
|
|
@@ -76,18 +76,34 @@ your module set; `pnpm architecture` enforces it, and a hook in
|
|
|
76
76
|
|
|
77
77
|
## Skills
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
The AI Launch Kit skills come from the `ailk` Claude Code plugin, not from
|
|
80
|
+
this repo. `.claude/settings.json` names its marketplace and enables it, so
|
|
81
|
+
Claude Code loads it once you trust this folder. To install it by hand, run
|
|
82
|
+
these two lines in a Claude Code session:
|
|
80
83
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
84
|
+
```
|
|
85
|
+
/plugin marketplace add working-theory-labs/claude-plugins
|
|
86
|
+
/plugin install ailk@working-theory
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
| Skill | Use when |
|
|
90
|
+
| ---------------------- | ----------------------------------------------------------------- |
|
|
91
|
+
| `/ailk:add-page` | You need a new page, or extra JSON-LD on an existing one |
|
|
92
|
+
| `/ailk:update-page` | A page should live at a different URL, or needs a new title |
|
|
93
|
+
| `/ailk:fill-page` | A page still carries `{placeholder}` copy |
|
|
94
|
+
| `/ailk:check-site` | You want the site's citation-readiness score and findings |
|
|
95
|
+
| `/ailk:setup-database` | Leads should land in a database inbox at `/leads` |
|
|
96
|
+
| `/ailk:setup-deploy` | You are ready to deploy to Vercel |
|
|
97
|
+
| `/ailk:add-auth` | Visitors need to sign in (email, Google, GitHub) |
|
|
98
|
+
| `/ailk:upgrade-plan` | You are weighing Free against a paid level or the commerce add-on |
|
|
99
|
+
|
|
100
|
+
A repo scaffolded by an earlier create-ailk also carries its own older
|
|
101
|
+
copies of these skills. Once the plugin is installed, delete those copies;
|
|
102
|
+
keep scaffold-commerce, which the commerce module delivers.
|
|
103
|
+
|
|
104
|
+
For design work, the third-party impeccable plugin is optional:
|
|
105
|
+
`/plugin marketplace add pbakaus/impeccable`, then
|
|
106
|
+
`/plugin install impeccable@impeccable`.
|
|
91
107
|
|
|
92
108
|
<!-- /ailk:template id="skills" -->
|
|
93
109
|
|
|
@@ -95,15 +111,15 @@ In `.claude/skills/`; Claude Code finds them at session start.
|
|
|
95
111
|
|
|
96
112
|
## Checks
|
|
97
113
|
|
|
98
|
-
| Command
|
|
99
|
-
|
|
|
100
|
-
| `pnpm preflight`
|
|
101
|
-
| `pnpm architecture`
|
|
102
|
-
| `pnpm claude-md:check`
|
|
103
|
-
| `pnpm type-check`
|
|
104
|
-
| `pnpm lint`
|
|
105
|
-
| `pnpm test`
|
|
106
|
-
|
|
|
114
|
+
| Command | Catches |
|
|
115
|
+
| ------------------------ | -------------------------------------------------------- |
|
|
116
|
+
| `pnpm preflight` | The next four and `build`, not `test`. Run before a push |
|
|
117
|
+
| `pnpm architecture` | An import that crosses a boundary in `architecture.yaml` |
|
|
118
|
+
| `pnpm claude-md:check` | A hand-edit inside a template-owned section of this file |
|
|
119
|
+
| `pnpm type-check` | Type errors in every workspace |
|
|
120
|
+
| `pnpm lint` | Lint errors in every workspace |
|
|
121
|
+
| `pnpm test` | Failing unit tests |
|
|
122
|
+
| `/ailk:check-site` skill | Missing or invalid JSON-LD, weak citation-readiness |
|
|
107
123
|
|
|
108
124
|
<!-- /ailk:template id="checks" -->
|
|
109
125
|
|