create-ailk 0.25.1 → 0.27.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.
Files changed (75) hide show
  1. package/README.md +14 -5
  2. package/dist/lib/fetch-template-snapshot.d.ts +2 -1
  3. package/dist/lib/fetch-template-snapshot.js +6 -5
  4. package/dist/lib/module-license-gate.js +2 -3
  5. package/dist/lib/module-tier.d.ts +2 -8
  6. package/dist/lib/module-tier.js +13 -12
  7. package/dist/module-architecture.js +14 -2
  8. package/dist/starter-grid.d.ts +6 -6
  9. package/dist/starter-grid.js +12 -5
  10. package/dist/surfaces.js +36 -2
  11. package/dist/template-record.d.ts +11 -3
  12. package/dist/template-record.js +20 -7
  13. package/dist/topology.js +23 -4
  14. package/dist/update.d.ts +1 -1
  15. package/dist/update.js +240 -20
  16. package/package.json +3 -3
  17. package/release-scope.json +1 -1
  18. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +8 -2
  19. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +2 -2
  20. package/site-templates/marketing-site/ailk-template.json +7 -1
  21. package/site-templates/marketing-site/provenance.json +7 -1
  22. package/site-templates/saas-product/ailk-template.json +2 -1
  23. package/site-templates/saas-product/provenance.json +1 -0
  24. package/templates/.env.example +22 -0
  25. package/templates/CLAUDE.md +20 -20
  26. package/templates/README.md +19 -19
  27. package/templates/apps/api/.env.example +28 -0
  28. package/templates/apps/api/CLAUDE.md +35 -9
  29. package/templates/apps/api/src/lib/__mocks__/prisma-client.js +16 -0
  30. package/templates/apps/api/vercel.json +7 -1
  31. package/templates/apps/mcp/CLAUDE.md +27 -10
  32. package/templates/apps/web/.env.example +13 -0
  33. package/templates/apps/web/CLAUDE.md +34 -6
  34. package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +43 -4
  35. package/templates/apps/web/app/[locale]/(auth)/legal-footnote.tsx +35 -0
  36. package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/next-path.test.ts +24 -1
  37. package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/page.test.tsx +168 -8
  38. package/templates/apps/web/app/[locale]/(auth)/sign-in/next-path.ts +13 -0
  39. package/templates/apps/web/app/[locale]/(auth)/sign-in/page.tsx +22 -39
  40. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +6 -6
  41. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-text.ts +20 -35
  42. package/templates/apps/web/app/[locale]/(auth)/sign-up/__tests__/page.test.tsx +301 -0
  43. package/templates/apps/web/app/[locale]/(auth)/sign-up/__tests__/sign-up-card.test.tsx +111 -5
  44. package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +96 -18
  45. package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +132 -12
  46. package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +53 -1
  47. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/billing.test.tsx +38 -0
  48. package/templates/apps/web/app/[locale]/(authed)/account/billing/page.tsx +4 -2
  49. package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +47 -24
  50. package/templates/apps/web/app/[locale]/(authed)/signed-out-gate.generated.tsx +23 -4
  51. package/templates/apps/web/app/[locale]/layout.tsx +48 -13
  52. package/templates/apps/web/app/[locale]/meet/confirmed/BookingHandOff.tsx +128 -0
  53. package/templates/apps/web/app/[locale]/meet/confirmed/__tests__/BookingHandOff.test.tsx +126 -0
  54. package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +118 -0
  55. package/templates/apps/web/app/api/auth/hub/authorize/route.ts +14 -0
  56. package/templates/apps/web/app/api/hub-session/__tests__/routes.test.ts +51 -0
  57. package/templates/apps/web/app/api/hub-session/sign-in/route.ts +13 -0
  58. package/templates/apps/web/auth.ts +8 -4
  59. package/templates/apps/web/i18n/request.ts +49 -1
  60. package/templates/apps/web/lib/__tests__/account-config.test.ts +7 -0
  61. package/templates/apps/web/lib/__tests__/auth-email.test.ts +276 -0
  62. package/templates/apps/web/lib/__tests__/relay.test.ts +289 -1
  63. package/templates/apps/web/lib/auth-email.ts +102 -20
  64. package/templates/apps/web/lib/hub-session/__tests__/sign-in-gate.test.tsx +92 -23
  65. package/templates/apps/web/lib/hub-session/sign-in-gate.tsx +97 -70
  66. package/templates/apps/web/lib/prisma-for-auth.ts +5 -2
  67. package/templates/apps/web/lib/relay.ts +116 -1
  68. package/templates/apps/web/lib/sign-up-agreements.ts +302 -28
  69. package/templates/apps/web/lib/site-contributions.ts +18 -0
  70. package/templates/apps/web/messages/en.json +271 -20
  71. package/templates/apps/web/messages/site/en.json +1 -0
  72. package/templates/apps/web/middleware.ts +8 -1
  73. package/templates/apps/web/next.config.mjs +9 -1
  74. package/templates/apps/web/package.json +2 -1
  75. package/versions.json +48 -48
package/dist/update.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * `create-ailk update [<target-dir>]` — bring template fixes into a site's
3
- * already-copied `apps/web`, `apps/api` and `apps/mcp` code (#8522).
3
+ * already-copied `apps/web`, `apps/api` and `apps/mcp` code (#8522), and
4
+ * the root `tools/` files their jest configs require (#9654).
4
5
  *
5
6
  * Reads `.ailk-template-files.json` (`template-record.ts`) to tell an
6
7
  * untouched copy from an owner's edit. An untouched file whose template
@@ -44,7 +45,7 @@ import { ParseError } from "./parse-args.js";
44
45
  import { SITE_ROSTER_PATH } from "./site-roster.js";
45
46
  import { SPOKE_CHECKOUT_TEMPLATE_PATHS } from "./spoke-checkout.js";
46
47
  import { excludedRoutePaths } from "./surfaces.js";
47
- import { DATABASE_BOOT_FILES, TEMPLATE_RECORD_PATH, isRecordable, parseTemplateRecord, serializeTemplateRecord, sha256Hex, } from "./template-record.js";
48
+ import { DATABASE_BOOT_FILES, ROOT_TOOL_FILES, TEMPLATE_RECORD_PATH, isRecordable, parseTemplateRecord, serializeTemplateRecord, sha256Hex, } from "./template-record.js";
48
49
  import { MCP_SEAM_PATH, MCP_SERVER_HUB, TopologyError, isSpokeExcluded, readSpokeShape, spokeExclusions, } from "./topology.js";
49
50
  /** A refusal the CLI exits 2 on, thrown before anything is written. */
50
51
  export class UpdateRefusedError extends Error {
@@ -77,12 +78,12 @@ function versionOlder(a, b) {
77
78
  const DATABASE_PACKAGE_GUARD = "database/ is still a package here — convert it to the thin consumer first";
78
79
  /**
79
80
  * Every file under `templatesDir`'s three app trees, plus each of the
80
- * `DATABASE_BOOT_FILES` present there (#9569 DD8), raw bytes keyed by
81
- * destination path (D3).
81
+ * `DATABASE_BOOT_FILES` (#9569 DD8) and `ROOT_TOOL_FILES` (#9654) present
82
+ * there, raw bytes keyed by destination path (D3).
82
83
  */
83
84
  function walkLocalTemplates(templatesDir) {
84
85
  const out = new Map();
85
- for (const boot of DATABASE_BOOT_FILES) {
86
+ for (const boot of [...DATABASE_BOOT_FILES, ...ROOT_TOOL_FILES]) {
86
87
  const abs = join(templatesDir, ...boot.split("/"));
87
88
  if (existsSync(abs))
88
89
  out.set(boot, readFileSync(abs));
@@ -236,6 +237,155 @@ const BUNDLED_MCP_TOOLS = new Set([
236
237
  "proposals",
237
238
  "submit_flow_step",
238
239
  ].map((name) => `${MCP_SRC}/tools/${name}.ts`));
240
+ /**
241
+ * The sha256 of every `apps/mcp/src` file a create-ailk template shipped
242
+ * before #9460 — the published tarballs 0.1.0 to 0.24.0, each one a blob in
243
+ * git history before the #9460 merge (#9654). A file under `apps/mcp/src`
244
+ * whose bytes hash into this set is a template copy, never a site edit, so
245
+ * the move removes it even where the record names another copy or none.
246
+ * Frozen as of #9460; never extend: no template ships a server file under
247
+ * `apps/mcp/src` any more.
248
+ */
249
+ const PRIOR_MCP_TEMPLATE_HASHES = new Set([
250
+ "02789f25139dd78952a77c2d6f7a7057d73d18cdf45845913ad4d35964ad0e16",
251
+ "035a4f5eda5d7a00046f6f3446100dd1e34f85f894f000b04a931e094799c03e",
252
+ "06fe9518a954091284571ec253abea979c738ee3556b73f8993c759607eb3c4b",
253
+ "0702b6d5012dab15d4030be01923ed8da8b0a237028e6c88053c8fdcee63e155",
254
+ "09fedce92b189911964733f6a2535d0d3b0f0104ac18a1ff76abf5fd96261922",
255
+ "0d42d4c17a35867245fc86c48efa5b6dd2c0f89a03c564f68b7661cde98dddf4",
256
+ "0dc43a72c6d2fd1a5f36f037d9b01abcb9a11d1653a04fb033b5b6b223fcfbd8",
257
+ "0fb7cd858e7cd3c1f8757824184519f2494f9c12ec49290d7b97ee7656a7f405",
258
+ "115ac6b648a45e5e1b22193097e92a8ee4da844be5853ca202cb624651ef4c56",
259
+ "16cefcfebbcadb2505b8d8253c853b2d2098209fa4bac84fe39dc7f6b38c02c3",
260
+ "16eaefbceb5edcc3a8039d8ef55776d1a1e317521ecca17386673b87852c4099",
261
+ "17c6e5115ba947d53dcbb632990f6b66d570ea1657d022c51bf62e09c0b58194",
262
+ "17cc5d3eed8849a33f2eebd0d12ffa72c9ffebcccf5a4818aaf8f94fca05aa0f",
263
+ "18185d6f4e4777a7b0856a971c509ce0e094f8ccb8206eb6a1e78d38030c3763",
264
+ "1c5f0f0cdd304b3aff3934e6f0878adb18fc6555be030cb81e3d4a0764d65c43",
265
+ "1d328a242845e31426f28c33d58bf5be7be76f526e066858f77916ef2b28af41",
266
+ "1d5810272869e6a2f66941f862ec7c39a54af82e13ff33d4bff4a19e0d52f1a4",
267
+ "1ddc5b3a79b4a6453a0ac1f6e60ef3463b58221fb5ee313d52c57e9928ea8cdc",
268
+ "2131309a986b964e99a9a9589a0c236d5d830b14d66653e2008289eae2e2cf94",
269
+ "21cd9825d4b7ae057c10b5ca7d01c83387be20be66bf8c38814e7db592a7517e",
270
+ "21d28634fc19f15389ec99a3e6005490faff1c1d364bc7bd3a1f387edab09434",
271
+ "22c245c92b973d86f425b934bd65229c0017801e738a80db19aa0a174d7142c0",
272
+ "23d1318bb8b9aaf489c62ce667d8ba56110f48c8b7c401713a3ac4849c0978bb",
273
+ "2d901fec3ca30024d39274b9dc27156b4d366c86a5c3fb2897a8ca9b97b6d2d9",
274
+ "2dfe47a0e1d6904b5b183fc4aeb067e492127f3ee39058677895a9cc5e270b9e",
275
+ "2eeb3c272e60e1dbf87f868fecac8526f265a4f5327afbeb5ee4c3ecebb848db",
276
+ "3110e5b85f764e39e0e9c7164290cd62a1be51b90e49ed38611a004143a6b130",
277
+ "31afce85a1982d17c9e1d8069e54913b0bdb295010104541ea04b061084b314e",
278
+ "36bc7bea57167851ae81b4baa57daee2fad4f2db9b96b730a72e2ca0c69d75b7",
279
+ "38fab15821f26449c149c12851ff7a857db89d55c4722f797b15274e31a4a4bf",
280
+ "39ef3340a53926a8e8dd77f78924d1975fa5284ab7ae2b99bb8602b20bb7e7a8",
281
+ "3bb46c8ea91349d7551a66c13462f99910ead5b813f0aa51a1b7ab1671f3576a",
282
+ "3cc84368f3eaaf7e875a145fabf7ac644ee4ef1fa785cf548c9639a69985661e",
283
+ "40a538c6a84a95690dc810a5249b74e67c234fc5cf4f6cc6476d7961f67ecbdd",
284
+ "426e33baaeed2bb51a720fce286dba4da00d94418d3554c628c87b3d14e03116",
285
+ "428c570d20f07c9069578c8729c2dcf7eb6eab3b956f8c20eccbabae2f518272",
286
+ "436fc6ed239cf5b4612ee4dbf102debaf5384a3872a30a834d1042e521e318f3",
287
+ "443a31b8af758901d5685134d5046cf4880b534268a9e9cc2ea85185cb5f54ee",
288
+ "443c60fd527c7703b5f3af9ad9b6efd49c359fdde717228254961ea9e7815117",
289
+ "4b28c6e6a6799eb9548720a9f16bafd93a15baa04cacbec950c23e2e5be0f1aa",
290
+ "4b8e654da8220df69a4c4457c1527c2bea9f5aed73d67458c4ce2a14f1b87e8f",
291
+ "4d01e1fcdda1f941789894a6a54d2940f47096c39f37609a5e908c0aa84789db",
292
+ "52583caf9f8946c36133949278449dceeb0ac8f74360534ca91b96938d80446c",
293
+ "5364243db73f8687c1c0ec9cc2516e95efb43a756e295ae8b6469fd55f940649",
294
+ "578dd3161d2dc283011a069a4d426037ba665f0f787a317779115b570a74269f",
295
+ "59882d160c82e5be765102c13dd3c05fbd7e48673408f89c194c8e021ae29797",
296
+ "5ae7a51f8d439d331c5a02229b4109daa5299de6c12d821b78bbc14feda8413f",
297
+ "5ed07e4eed1e6cc7d19185669a14b8df974ea640d283676f33f4db8566fb8cdc",
298
+ "5f60e2bd4fb529c02e195b1aef3bf2ebfa55db05212ae9888e265f3f2f9fade8",
299
+ "6022594cc246c8f3453935781607fa3750b7c8e5b29fab9c841f13364cc9d08a",
300
+ "60b1ed4e37ecc2e045726f2fd93ef90cc9dea33718532a90e365d6d64d803047",
301
+ "659248c6e0c85075142c6a86395fb2b6b2bdf36895dfb2aeb5ab5e044528f11b",
302
+ "66d95e135f59ff1ccdcf3480bd22eac81a559ef0a4439d9a34ce6c25a6885ada",
303
+ "68976a1295acc8ed025a8a3ab30d78492c7e40e4320ac5acecc53f9e7f2aa04f",
304
+ "69cebf99ae494a0fd06b4b9408587f230def919dceb4fbcf3ef950f243bbf486",
305
+ "6b919aa697e9785f175201193e498b17b6c64ffe08c34a9a3ff728d660aa68cf",
306
+ "6da43872f0bee03f3028183795d41ea257c6102928175efe162ffb0fd37849c8",
307
+ "6e86925727a045268249248e9dffd7e8869f2604af43fda27c8acf75982988de",
308
+ "6e8f01a9c1f20f580e2dec7fe8a4c811ffd88621612aac03604aedeaeb9b7c90",
309
+ "715ed89d03445103ca05c0686c8f6fc7cef0c61b18767c285777bb65122fa81a",
310
+ "74a2c97adba8e0390a281fddbc5dce3ec7f9e97577b2fcb9abcf0bdcee01b63c",
311
+ "75abc78c74f28259d817cf159b3969c548395131de6255978b9df4d275aa1544",
312
+ "77d1aacb454904eded6d9a043de7f95922d53bae9693df48b106a74de7405e98",
313
+ "78826164442493682a624ba492f6e39b3479208d93a7aa2fdfca70005333255f",
314
+ "792e6b2cf89272fe9860d585a23687e14881533f3644dd5b1e8e30ea89e6f4a2",
315
+ "7afb50314471ab4e8f31099db49e29a96e56efd83ba960a42732e177654c874d",
316
+ "7b7d5053aaa6358eb66042cfca9e2fe6280d354e23a303c129f08a413bf94c4c",
317
+ "7ed1787f0d71fceb92fb687a8d4e6a5ffc49896ee586303b4311035ba60ba2d2",
318
+ "81dd15a6b1479896458106b946edc0ac76f6c0acf4d525307b092ed11d2f4596",
319
+ "83eaa71438882dde8edf0d7978644a93c802a08a50804f5b2c62d01eaeb4edc9",
320
+ "8441a52d587584cccd6f1fbe3ba5204c9c49a9c6d452d9f844d34dcc0448ad76",
321
+ "847d648346ea4bd0d2f349399527f5a907e05367c6764696b724f3382090089e",
322
+ "85df33660e2291ffe1e77b7103e4705c31b93dd7a294872b6c0fca52a3bd3079",
323
+ "8a9eea9c23a94ff9df729a4aa423e62b02c0632e398bf01524f1caeed61944b9",
324
+ "8e7d112d48976c7ae66203b6c45e8c20708f60a4486411a30af7857e214b8d27",
325
+ "91945c6e325758d09d7f70586f63bda0758f187247e4a8cfab05d9ed45c1e25b",
326
+ "960723d00c54a88af4142cde66b77c5e70cd3b39f59dce462fdf8d153846a9cf",
327
+ "9623a2f691777ea1fea280782240837b24b8202082bf98dbaa2ea48c670774b8",
328
+ "9ebfb38044ad35af8156803be6bb975a7c6b1624fa511037d56beb7b9e0d3579",
329
+ "9ede34ab75c67dc0fe134d7bf64d040e250cec85111ef9fb11be745af5e90277",
330
+ "a03fd2d78e6e4982209e193ad27f0732035c2c2eb5edfcdd8e3093a42e5334a4",
331
+ "a14cf80e8cfc09105cd856bc5723a4b1a231f6ad8f08e5a443360c480a6b483b",
332
+ "a17f89416b31fb883dc9a5c389b021332b2b40e5052fe344e1aec96872475d5f",
333
+ "a20b034a648c2b9daaf3ca817a2229ce7e0136b0fc7487c7fed5fe0cdf1e9e7b",
334
+ "a4495346b79124d1b17f6082f0c1f1f99d3845f4f98cfde83543f2eadf600666",
335
+ "a6d6d6be76cff255bbf77d529dee8775817dae75725bf24964febbbc7cf8d508",
336
+ "a7421e1949c4f3671cfb0d758fecb3b87aab3df03d0feaed0259443ebb2a4353",
337
+ "ac7cd310467561330d3cc2914fd29c9a16ecd27d7f8413dd1120e28ce9ee3ad7",
338
+ "add424a2fbb1027d67270dca6e5e1e0e384dd37a729ba5c091022bc30b3d85c1",
339
+ "af5c4f3113285a137019f7bcbb905c04f0de3b6917d709a0fc8df3f0f39beda9",
340
+ "b0c8bc973e3b9f7fdaf95cb02ca111cb685db2ac98559c859071b4f26f600bf4",
341
+ "b455cd835c1769af463b4ebd91fa6716effbad6f8210df12d47637977b34c6e2",
342
+ "b7140dd15cac5f55309991e7ce1b438a9514a4b289c9ef807f75ed576246fc26",
343
+ "b7198546881c491e233018dadd3fbdb6378694e8a15b762b56c17a1c61d10c91",
344
+ "b7d1e33111a13249d6bc4d0a9334074430bc94f1a0c79d94f5c6f5907b76ae30",
345
+ "b9bd1f23c0e84f91cf7d46db46cab4cb1232574b0f6c9d6f5926fa0bab110570",
346
+ "ba7e5b4db4fd020b0aadace2083aacfb9ffafa63465d2026e4a79804eb8cd493",
347
+ "bb321afe40c6d7495676f9157bc80bbab9d5241fa84340133770b1ba3006d166",
348
+ "bced6c526cd757d87b4d725e247497b6e1286192b758421786c645719c3b4dfd",
349
+ "bf398a05bdbdb85a8034ecfd9f4d865587b7d25352a06f3c9f3ca1c0deed6787",
350
+ "bf4a2222b34a4fdabccd696341f6168104d3d7d74df036187347650ecf350d32",
351
+ "c1513e2317857959145d66659c3b147cc885ca46f4c239ed85f52a833d058971",
352
+ "c371bdde5f7ace2448c46a0e009d799bdb90d3351d42131234181d1efd2b486f",
353
+ "c57ccbaddda8c7d538d103c2b2d96889a815cbcc4469f5f6780b0883d99b4006",
354
+ "c5a448c17e6bba3379ee938b17309c02eb851cbf5fb54dcd6ec490503b70b121",
355
+ "c5d35895a261227c6cf537daeac4b24a98d91ea65e8322d7ca5c47d05c83570c",
356
+ "c64f4b1a9dba4d1fa76f8aea664c81e35961dca2e3f44f63a6783f5164951560",
357
+ "c676077b914411e737376f9604087aab67ba2f300aedb99e76a5839f1798827b",
358
+ "ca076f76130bd3e35488bfe23d7f55f1829164a4e09035459f3af2aee1d91ace",
359
+ "ca842e6ec1748a84fa30adde6f93a84e7b98b6211afbb3e7907e9b6b9b65fed2",
360
+ "ce7ada5214b23c595422399fbd10a8e95bb1b9b30a345c2117bf1954cc2163a6",
361
+ "d2b460eee83c40d26db73bded9ad30666fe970eb991c925727eed5e45d1a5cb4",
362
+ "d4cba3c71603c40bf0a8b6e1643a157e2028d3743b238fb932e059381a17deb9",
363
+ "d5fcd1015582870231c52f490b8daac7a89dbaa2d387f285e118fff98bf7ada2",
364
+ "d96ff716399aca50bd6f5c43bb37721e2d4cbb43793a453119f6e15e6eeca34b",
365
+ "da75ba145c8142544d6211b4eaba8485549511766e2c0d248ac2308b502cf986",
366
+ "dfbcfe0f30c6079153b5ab51096ef106716a713a7d6169b2ec00840423640560",
367
+ "e1945e3ff2b6d806fc459c90d0253ca71f2adac5bedc6c578ca21506f9dc094a",
368
+ "e43789a019a1c487b42cc785175f0b08b36a7e4f5ae72e40a6c1835f9d02b2d6",
369
+ "e482750dfb358f78b1142a8707cb007cc7a5e5c863614b05d0289f4843d10b6f",
370
+ "e4948f0dadc17dcae583e2d8732a1e95c8268d4e5bf68bc38e97e05a251b9fcb",
371
+ "e4aff38f30ca76f6da6ec4bf98c71a51d8fc41877ddcf791aeb61fcf34bb44ea",
372
+ "e59294cc6a47cabd5df6ab635dd3748f42610e1691b248d24b8962fc3661df43",
373
+ "e7c7711f3512fdd51bdd175098ffe97c1c4483b4369b2627bbf00eafd65c00af",
374
+ "e7feca03ec0681bee9bba97b9ee5024215899f95aad72cca08c0902b969bee5c",
375
+ "e97b05627ca65f8656167b5ffefb8236cce711173a98430d7ac7f063dc598223",
376
+ "e9ba0a0e497e5d12662a473b81e0cb09f81a094c2763266769e08f5556718f15",
377
+ "e9c9f8d30528a30084e58d41d93d7e4d58d07a4bd8f1e2c1d132c2525669e8a5",
378
+ "ec28dcc626ae59a3c5208f342ba8090c3e8640c6e4930a4b518dfdaa4ccdb773",
379
+ "ef7f59acd7f5de7287681786bb96f1907ac72a9cca7d49732c33320359725ef9",
380
+ "f37c56da121f8cba9c0a55bc69ef701e3d81c63a15f1a9ff354efc4fed58ff39",
381
+ "f562f262c2b26f3e613a50aea737bf7b021d8c4ea9e769ce6b4728675fd68b9e",
382
+ "f6f9e117ab5d1e68b7b99baa30ab822c0126467b8941195a187bf3af20f7faa5",
383
+ "f8d26912ffbc7c1a7b73a0916111aa5c969d98a60b211ff7ca75d8e7828d86c1",
384
+ "f984b95c8a9759cdd988722b7de5be6654c69e0ceb12abc4847115f964087fc0",
385
+ "fc43fb068d3816a65a00144f32ebd3fb077f474090de8c1c99dd77f542c35c43",
386
+ "fdbdd75dc3b798c548ddf28a3c730fa61f64dc1b48d30ff65e190bbc7bb097ba",
387
+ "ffe3db23d21ce93dbb8b832a04318cf23aaa8b1d6db11185ebcf30a634e86e6d",
388
+ ]);
239
389
  /** The old hub seam's two relative imports; each is now `MCP_SERVER_HUB` (DD7). */
240
390
  const OLD_HUB_SEAM_IMPORTS = ['"./public-workspace.js"', '"./license-key-store.js"'];
241
391
  /** Every file under `root`, as `/`-separated paths relative to `targetDir`. */
@@ -280,18 +430,26 @@ function resolveMoveVersions(app, pkg, packages, versions) {
280
430
  * what it lists for review: a `bundled` file (bundle-delivered, never
281
431
  * recorded) is removed even at a boot-set path (#9566: the #9564→#9565
282
432
  * window's in-app module objects sit where the template's re-exports land);
283
- * any other boot-set file is left to the record; the rest is reviewed with
284
- * `reason`, never deleted.
433
+ * any other boot-set file is left to the record; a file whose bytes hash
434
+ * into `templateCopies` is a prior template copy and is removed (#9654); the
435
+ * rest is reviewed with `reason`, never deleted.
285
436
  */
286
- function classifyUnrecorded(targetDir, src, bootSet, recorded, bundled, reason) {
437
+ function classifyUnrecorded(targetDir, src, bootSet, recorded, bundled, reason, templateCopies = new Set()) {
287
438
  const deletes = [];
288
439
  const review = [];
289
440
  for (const path of filesUnder(targetDir, src)) {
290
441
  if (recorded.has(path))
291
442
  continue;
292
- if (bundled.has(path))
443
+ if (bundled.has(path)) {
444
+ deletes.push(path);
445
+ continue;
446
+ }
447
+ if (bootSet.has(path))
448
+ continue;
449
+ const hash = sha256Hex(readFileSync(join(targetDir, ...path.split("/"))));
450
+ if (templateCopies.has(hash))
293
451
  deletes.push(path);
294
- else if (!bootSet.has(path))
452
+ else
295
453
  review.push({ path, reason });
296
454
  }
297
455
  return { deletes, review };
@@ -335,7 +493,7 @@ function planMcpMove(targetDir, modules, hub, recorded, versions) {
335
493
  if (!existsSync(join(targetDir, ...MCP_SRC.split("/")))) {
336
494
  throw new UpdateRefusedError(`${MCP_PACKAGE_JSON} is present but ${MCP_SRC}/ is not, so apps/mcp cannot be moved to ${MCP_SERVER_PACKAGE}`);
337
495
  }
338
- const unrecorded = classifyUnrecorded(targetDir, MCP_SRC, MCP_BOOT_SET, recorded, BUNDLED_MCP_TOOLS, `not in the boot set apps/mcp/src is since ${MCP_SERVER_PACKAGE} (#9460); move it into a package or delete it`);
496
+ const unrecorded = classifyUnrecorded(targetDir, MCP_SRC, MCP_BOOT_SET, recorded, BUNDLED_MCP_TOOLS, `not in the boot set apps/mcp/src is since ${MCP_SERVER_PACKAGE} (#9460); move it into a package or delete it`, PRIOR_MCP_TEMPLATE_HASHES);
339
497
  move.deletes = unrecorded.deletes;
340
498
  move.review = unrecorded.review;
341
499
  return move;
@@ -578,6 +736,47 @@ function planApiMove(targetDir, templatesDir, bootSet, recorded, versions) {
578
736
  move.review = unrecorded.review;
579
737
  return move;
580
738
  }
739
+ // ── The web app's carried dependencies (#9889 D9) ─────────────────────────
740
+ const WEB_PACKAGE_JSON = "apps/web/package.json";
741
+ /**
742
+ * The third-party packages `update` adds to a site's `apps/web/package.json`
743
+ * because template-owned web code imports them: `package.json` is never
744
+ * recorded, so without this a site updated onto that code would not build.
745
+ * Frozen; a name joins only with a spec.
746
+ */
747
+ const WEB_CARRIED_DEPENDENCIES = Object.freeze(["botid"]);
748
+ /**
749
+ * The site's `apps/web/package.json` with each carried dependency the running
750
+ * template declares and the site does not (in `dependencies` or
751
+ * `devDependencies`, at any range) added at the template's range, or
752
+ * `undefined` when there is nothing to add or no web app. Throws before any
753
+ * write when the site's file is not valid JSON.
754
+ */
755
+ function planWebDependencies(targetDir, templatesDir) {
756
+ const sitePath = join(targetDir, ...WEB_PACKAGE_JSON.split("/"));
757
+ const templatePath = join(templatesDir, ...WEB_PACKAGE_JSON.split("/"));
758
+ if (!existsSync(sitePath) || !existsSync(templatePath))
759
+ return undefined;
760
+ let pkg;
761
+ try {
762
+ pkg = JSON.parse(readFileSync(sitePath, "utf8"));
763
+ }
764
+ catch (err) {
765
+ throw new UpdateRefusedError(`${WEB_PACKAGE_JSON} is not valid JSON: ${err.message}`);
766
+ }
767
+ const templateDeps = JSON.parse(readFileSync(templatePath, "utf8")).dependencies ?? {};
768
+ const entries = {};
769
+ for (const name of WEB_CARRIED_DEPENDENCIES) {
770
+ const range = templateDeps[name];
771
+ if (range === undefined)
772
+ continue;
773
+ if (Object.hasOwn(pkg.dependencies ?? {}, name) || Object.hasOwn(pkg.devDependencies ?? {}, name)) {
774
+ continue;
775
+ }
776
+ entries[name] = range;
777
+ }
778
+ return Object.keys(entries).length > 0 ? withDependencies(pkg, entries) : undefined;
779
+ }
581
780
  export async function updateSite(opts) {
582
781
  const targetDir = resolve(opts.targetDir);
583
782
  const version = opts.version;
@@ -747,6 +946,9 @@ export async function updateSite(opts) {
747
946
  const apiMove = existsSync(apiPkgPath)
748
947
  ? planApiMove(targetDir, opts.templatesDir, new Set([...newMap.keys()].filter((path) => path.startsWith(`${API_SRC}/`))), new Set(Object.keys(record.files)), opts.versions ?? {})
749
948
  : undefined;
949
+ // ── The web app's carried dependencies (#9889 D9), planned before the
950
+ // first write so an unparseable apps/web/package.json refuses the run.
951
+ const webPackageJson = planWebDependencies(targetDir, opts.templatesDir);
750
952
  // ── Every recorded row (D4) ────────────────────────────────────────────
751
953
  const updated = [];
752
954
  const merged = [];
@@ -807,6 +1009,15 @@ export async function updateSite(opts) {
807
1009
  }
808
1010
  // Edited.
809
1011
  if (newBytes === undefined) {
1012
+ // #9654 — the bytes are a pre-#9460 template copy, just not the one
1013
+ // the row names: not an edit, so it is retired as an untouched copy
1014
+ // is, on the move's run or any later one.
1015
+ if (path.startsWith(`${MCP_SRC}/`) && PRIOR_MCP_TEMPLATE_HASHES.has(diskHash)) {
1016
+ deletes.push(path);
1017
+ delete nextFiles[path];
1018
+ removed.push(path);
1019
+ continue;
1020
+ }
810
1021
  review.push({ path, reason: `edited here, removed from the template in ${version}` });
811
1022
  continue;
812
1023
  }
@@ -898,11 +1109,13 @@ export async function updateSite(opts) {
898
1109
  if (sha256Hex(diskBytes) !== newHash) {
899
1110
  skipped.push({ path, reason: "exists, not in the record" });
900
1111
  }
901
- else if (DATABASE_BOOT_FILES.includes(path)) {
1112
+ else if (DATABASE_BOOT_FILES.includes(path) || ROOT_TOOL_FILES.includes(path)) {
902
1113
  // Adoption (#9569 DD8): a thin site converted by hand, or scaffolded
903
1114
  // before the boot files were recorded, gains the row for a copy that
904
- // already equals the running template's. App paths keep today's
905
- // behaviour: an unrecorded, identical copy stays unrecorded.
1115
+ // already equals the running template's — and so (#9654) does a root
1116
+ // tools/ file a site scaffolded before tools/ was recorded, or copied
1117
+ // by hand. App paths keep today's behaviour: an unrecorded, identical
1118
+ // copy stays unrecorded.
906
1119
  nextFiles[path] = { sha256: newHash, version };
907
1120
  current.push(path);
908
1121
  }
@@ -1032,6 +1245,11 @@ export async function updateSite(opts) {
1032
1245
  moved.updated.push(API_PACKAGE_JSON);
1033
1246
  pruneEmptyDirs(join(targetDir, ...API_SRC.split("/")));
1034
1247
  }
1248
+ // #9889 D9 — the web app's carried dependencies, after the record's writes.
1249
+ if (webPackageJson !== undefined) {
1250
+ writeFileSync(join(targetDir, ...WEB_PACKAGE_JSON.split("/")), webPackageJson);
1251
+ moved.updated.push(WEB_PACKAGE_JSON);
1252
+ }
1035
1253
  return {
1036
1254
  targetDir,
1037
1255
  version,
@@ -1085,8 +1303,9 @@ export function formatUpdateReport(result) {
1085
1303
  return `[create-ailk] Already up to date (create-ailk ${result.version}).\n`;
1086
1304
  }
1087
1305
  // #9567 / #9566 — the MCP and API moves add dependencies (the mcp README
1088
- // § Move, step 7, names the install; the API half says the same).
1089
- for (const pkgJson of [MCP_PACKAGE_JSON, API_PACKAGE_JSON]) {
1306
+ // § Move, step 7, names the install; the API half says the same), and so
1307
+ // does the web app's carried-dependency step (#9889 D9).
1308
+ for (const pkgJson of [MCP_PACKAGE_JSON, API_PACKAGE_JSON, WEB_PACKAGE_JSON]) {
1090
1309
  if (result.updated.includes(pkgJson)) {
1091
1310
  lines.push(`${pkgJson} gained dependencies: run \`pnpm install\`.`);
1092
1311
  }
@@ -1106,10 +1325,11 @@ Options:
1106
1325
 
1107
1326
  Reads .ailk-template-files.json (written beside .ailk-site-manifest.json by
1108
1327
  every template scaffold) to tell an untouched copy from an edit. An
1109
- untouched apps/web, apps/api or apps/mcp file whose template copy changed is
1110
- overwritten. An edited file whose template copy also changed is three-way
1111
- merged with \`git\` (git must be on PATH); a conflict leaves the file alone
1112
- and is listed under "Needs review". Every template test that imports a file
1328
+ untouched apps/web, apps/api or apps/mcp file, or a root tools/jest file the
1329
+ app jest configs require, whose template copy changed is overwritten. An
1330
+ edited file whose template copy also changed is three-way merged with
1331
+ \`git\` (git must be on PATH); a conflict leaves the file alone and is
1332
+ listed under "Needs review". Every template test that imports a file
1113
1333
  left under "Needs review" or "Skipped" (by a relative import) is held at the
1114
1334
  site's version with it, its record entry is not advanced, and it is listed
1115
1335
  under "Held back with <file>" — reconcile the file, then run update again to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ailk",
3
- "version": "0.25.1",
3
+ "version": "0.27.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",
@@ -28,8 +28,8 @@
28
28
  "tar": "^7.5.21"
29
29
  },
30
30
  "devDependencies": {
31
- "@working-theory/schema": "0.46.0",
32
- "@working-theory/templates": "0.46.0"
31
+ "@working-theory/schema": "0.49.0",
32
+ "@working-theory/templates": "0.49.0"
33
33
  },
34
34
  "repository": {
35
35
  "type": "git",
@@ -17,7 +17,7 @@
17
17
  "docs-blog": "released",
18
18
  "scheduling": "released",
19
19
  "signal-collectors": "released",
20
- "workspace": "gated",
20
+ "workspace": "released",
21
21
  "multi-tenant": "gated",
22
22
  "commerce": "gated"
23
23
  },
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "template": "marketing-site",
3
- "requires": ["marketing", "docs-blog", "scheduling", "signal-collectors"],
3
+ "requires": [
4
+ "marketing",
5
+ "docs-blog",
6
+ "scheduling",
7
+ "signal-collectors",
8
+ "workspace"
9
+ ],
4
10
  "categories": [
5
11
  "accounting",
6
12
  "legal",
@@ -17,7 +23,7 @@
17
23
  ],
18
24
  "shape": "organization-product",
19
25
  "design": "standard",
20
- "generatedHash": "f4a8f11aba42a071794585c4563015bd1c99cbe6917a36ee0629d37c6f5f476c",
26
+ "generatedHash": "d58492d416674a21b1358858d285b7050d14618f5e2bed2f0ae20bdbc5267d5a",
21
27
  "layout": {
22
28
  "nav": "full",
23
29
  "footer": "columns",
@@ -6,7 +6,7 @@
6
6
  | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7
7
  | Template | `marketing-site` |
8
8
  | Level | Pro |
9
- | `requires` | `[marketing, docs-blog, scheduling, signal-collectors]` (spec 5462 D12/D15, settled by Sam 2026-09-14) |
9
+ | `requires` | `[marketing, docs-blog, scheduling, signal-collectors, workspace]` (spec 5462 D12/D15, settled by Sam 2026-09-14; `workspace` since #9696) |
10
10
  | Delivery | licence-checked module bundle (#5441); never in the Free tarball. Composed base → modules → template (#5618): `/pricing`, `/privacy`, `/terms` arrive from the `_base` layer it `extends`, every module sample from the docs-blog and signal-collectors bundles its `requires` names, and this tree ships one copy override (blog post 05 onto `launching-ailk.mdx`) |
11
11
  | Sample content posture | every sample report, post and flow in the demo is **public** |
12
12
 
@@ -513,7 +513,7 @@ Everything in the Free manifest § 2a applies. Pro adds:
513
513
 
514
514
  ```
515
515
  marketing-site:
516
- requires: [marketing, docs-blog, scheduling, signal-collectors]
516
+ requires: [marketing, docs-blog, scheduling, signal-collectors, workspace]
517
517
  categories: [accounting, legal, consulting, hvac, roofing, plumbing, electrical, landscaping, auto-repair, real-estate, technology, medical-specialty]
518
518
  shape: organization-product
519
519
  design: standard
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "name": "marketing-site",
3
- "requires": ["marketing", "docs-blog", "scheduling", "signal-collectors"],
3
+ "requires": [
4
+ "marketing",
5
+ "docs-blog",
6
+ "scheduling",
7
+ "signal-collectors",
8
+ "workspace"
9
+ ],
4
10
  "extends": "_base",
5
11
  "$comment": "Pro template (#5464). installMap: bundle-relative source -> scaffold-relative destination; the stager reads only installMap/postInstall/patches. After #5618 D6 this tree holds compositions and copy overrides only: the base pages (/pricing, /privacy, /terms) arrive from the _base layer it extends, and every module sample (the blog listing and posts 01-04, the three docs pages, the projects, thank-you, flow, waitlist and report samples) ships in the docs-blog and signal-collectors bundles its requires names. Its one content file, post 05, is deliberately mapped ONTO apps/web/content/en/blog/launching-ailk.mdx (D6.2a): the template lands last and can overwrite but never remove the module's own seed post, so this is Rule B's copy-override clause with a real instance (a template-owned replacement of a seed row is a docs-blog gap on #5467, spec 5618 s7 finding 9). Unmapped content/en/** would land at the scaffold root's content/, exactly as marketing-site-free's does; today nothing is unmapped.",
6
12
  "installMap": {
@@ -1,4 +1,10 @@
1
1
  {
2
2
  "template": "marketing-site",
3
- "requires": ["marketing", "docs-blog", "scheduling", "signal-collectors"]
3
+ "requires": [
4
+ "marketing",
5
+ "docs-blog",
6
+ "scheduling",
7
+ "signal-collectors",
8
+ "workspace"
9
+ ]
4
10
  }
@@ -5,6 +5,7 @@
5
5
  "docs-blog",
6
6
  "scheduling",
7
7
  "signal-collectors",
8
+ "workspace",
8
9
  "multi-tenant"
9
10
  ],
10
11
  "extends": "_base",
@@ -12,5 +13,5 @@
12
13
  "installMap": {
13
14
  "provenance.json": ".ailk-template.json"
14
15
  },
15
- "postInstall": "Template saas-product installed. Its Pro Plus modules - docs-blog, scheduling, signal-collectors and multi-tenant (the authed apps/admin app) - are delivered by their own module bundles. This template's own page compositions, copy and demo data are pending under #5470; until they land it contributes the provenance record and its requires cross-check only, and the scaffold renders each module's own seed content."
16
+ "postInstall": "Template saas-product installed. Its Pro Plus modules - docs-blog, scheduling, signal-collectors, workspace (the /admin settings pages) and multi-tenant (the authed apps/admin app) - are delivered by their own module bundles. This template's own page compositions, copy and demo data are pending under #5470; until they land it contributes the provenance record and its requires cross-check only, and the scaffold renders each module's own seed content."
16
17
  }
@@ -5,6 +5,7 @@
5
5
  "docs-blog",
6
6
  "scheduling",
7
7
  "signal-collectors",
8
+ "workspace",
8
9
  "multi-tenant"
9
10
  ]
10
11
  }
@@ -188,6 +188,13 @@ CONTENT_ADAPTER=mdx
188
188
  # config declares a `followUps` block.
189
189
  # AILK_FOLLOWUP_EMAIL_FROM=
190
190
  #
191
+ # The inbox's sender (#9690): an Admin reply emailed to the client, and the
192
+ # Invite to sign up email. Optional — falls back to
193
+ # AILK_LEAD_NOTIFICATION_FROM when unset; with neither, a reply is still saved
194
+ # (its email is `not_configured`) and an invitation is refused. The links
195
+ # start at FRONTEND_URL (https only outside development).
196
+ # AILK_INBOX_FROM=
197
+ #
191
198
  # The Resend Contacts audience the #4732 follow-up sweep checks before
192
199
  # sending — a lead found unsubscribed in this audience is suppressed.
193
200
  # Optional: unset degrades to "cannot verify — proceed" (never to "block all
@@ -446,6 +453,14 @@ CONTENT_ADAPTER=mdx
446
453
  # that database. Rotation and the rekey bin: apps/api/.env.example.
447
454
  # STRIPE_CONNECTION_KEY=
448
455
  # STRIPE_CONNECTION_KEY_PREVIOUS=
456
+ # The key each Organization's Slack connection is encrypted under (#9748),
457
+ # and the key it replaced during a rotation: 32 random bytes each, `openssl
458
+ # rand -base64 32`, distinct from each other and from SCHEDULING_CREDENTIAL_KEY
459
+ # and both Stripe connection keys. Unset = a connection cannot be written and
460
+ # reads as not connected, but can be removed; any other shape stops the API.
461
+ # No rekey walk: rotation and the leaked-key rule in apps/api/.env.example.
462
+ # SLACK_CONNECTION_KEY=
463
+ # SLACK_CONNECTION_KEY_PREVIOUS=
449
464
  # The Organization that owns STRIPE_SECRET_KEY (#9285): with the multi-tenant
450
465
  # module only the Organization whose id is named here sells through it; unset
451
466
  # means nobody. Without the module the single Organization owns it.
@@ -454,6 +469,13 @@ CONTENT_ADAPTER=mdx
454
469
  # header against (#6085). Unset makes the receiver answer 503 and accept
455
470
  # nothing — the same fail-closed posture an unset STRIPE_WEBHOOK_SECRET has.
456
471
  # CAL_WEBHOOK_SECRET=
472
+ # The prospect-channel archive cron's secret (#9855). Vercel Cron sends it as
473
+ # `Authorization: Bearer <CRON_SECRET>` to GET /v1/cron/prospect-channel-archive
474
+ # (the scheduling module; the scaffold's apps/api/vercel.json schedules it
475
+ # daily). Generate it with `openssl rand -hex 32`. Unset, or shorter than 32
476
+ # characters, makes the route answer 503 and archive nothing. The bin
477
+ # (`prospect-channel-archive:sweep`) needs no secret.
478
+ # CRON_SECRET=
457
479
  # SCHEDULING_PUBLIC_BASE_URL=https://cal.com
458
480
  # SCHEDULING_PROVIDER=cal
459
481
 
@@ -73,7 +73,7 @@ your module set; `pnpm architecture` enforces it, and a hook in
73
73
  | Diff hygiene when editing existing files | `CONVENTIONS.md` § Commit + diff hygiene |
74
74
  | Code style, testing, security | `CONVENTIONS.md` |
75
75
  | Environment variables and what they unlock | `.env.example` |
76
- | Deploy configuration | `apps/api/vercel.json`; the `/ailk:setup-deploy` skill |
76
+ | Deploy configuration | `apps/api/vercel.json`; `/ailk:growth-ops deploy` |
77
77
  | Verification before push | `pnpm preflight` (see § Checks) |
78
78
 
79
79
  <!-- /ailk:template id="resource-map" -->
@@ -82,30 +82,30 @@ your module set; `pnpm architecture` enforces it, and a hook in
82
82
 
83
83
  ## Skills
84
84
 
85
- The AI Launch Kit skills come from the `ailk` Claude Code plugin, not from
86
- this repo. `.claude/settings.json` names its marketplace and enables it, so
87
- Claude Code loads it once you trust this folder. To install it by hand, run
88
- these two lines in a Claude Code session:
85
+ The AI Launch Kit skill, `growth-ops`, comes from the `ailk` Claude Code
86
+ plugin, not from this repo. `.claude/settings.json` names its marketplace and
87
+ enables it, so Claude Code loads it once you trust this folder. To install it
88
+ by hand, run these two lines in a Claude Code session:
89
89
 
90
90
  ```
91
91
  /plugin marketplace add working-theory-labs/claude-plugins
92
92
  /plugin install ailk@working-theory
93
93
  ```
94
94
 
95
- | Skill | Use when |
96
- | ---------------------- | ----------------------------------------------------------------- |
97
- | `/ailk:add-page` | You need a new page, or extra JSON-LD on an existing one |
98
- | `/ailk:update-page` | A page should live at a different URL, or needs a new title |
99
- | `/ailk:fill-page` | A page still carries `{placeholder}` copy |
100
- | `/ailk:check-site` | You want the site's citation-readiness score and findings |
101
- | `/ailk:setup-database` | Leads should land in a database inbox at `/leads` |
102
- | `/ailk:setup-deploy` | You are ready to deploy to Vercel |
103
- | `/ailk:add-auth` | Visitors need to sign in (email, Google, GitHub) |
104
- | `/ailk:upgrade-plan` | You are weighing Free against a paid level or the commerce add-on |
105
-
106
- A repo scaffolded by an earlier create-ailk also carries its own older
107
- copies of these skills. Once the plugin is installed, delete those copies;
108
- keep scaffold-commerce, which the commerce module delivers.
95
+ | Command | Use when |
96
+ | ------------------------------ | ----------------------------------------------------------------- |
97
+ | `/ailk:growth-ops page add` | You need a new page, or extra JSON-LD on an existing one |
98
+ | `/ailk:growth-ops page rename` | A page should live at a different URL, or needs a new title |
99
+ | `/ailk:growth-ops page fill` | A page still carries `{placeholder}` copy |
100
+ | `/ailk:growth-ops audit` | You want the site's citation-readiness score and findings |
101
+ | `/ailk:growth-ops database` | Leads should land in a database inbox at `/leads` |
102
+ | `/ailk:growth-ops deploy` | You are ready to deploy to Vercel |
103
+ | `/ailk:growth-ops auth` | Visitors need to sign in (email, Google, GitHub) |
104
+ | `/ailk:growth-ops plan` | You are weighing Free against a paid level or the commerce add-on |
105
+
106
+ Ask in plain words and `growth-ops` picks the verb. An older scaffold also
107
+ carries its own copies of these jobs as skills: once the plugin is installed,
108
+ delete them; keep scaffold-commerce, which the commerce module delivers.
109
109
 
110
110
  For design work, the third-party impeccable plugin is optional:
111
111
  `/plugin marketplace add pbakaus/impeccable`, then
@@ -125,7 +125,7 @@ For design work, the third-party impeccable plugin is optional:
125
125
  | `pnpm type-check` | Type errors in every workspace |
126
126
  | `pnpm lint` | Lint errors in every workspace |
127
127
  | `pnpm test` | Failing unit tests |
128
- | `/ailk:check-site` skill | Missing or invalid JSON-LD, weak citation-readiness |
128
+ | `/ailk:growth-ops audit` | Missing or invalid JSON-LD, weak citation-readiness |
129
129
 
130
130
  `.github/workflows/ci.yml` runs the preflight checks and `pnpm test` on every
131
131
  pull request as the `preflight` status check. One step is yours: make