@akanjs/cli 3.0.0-alpha.5 → 3.0.0-alpha.50

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 (110) hide show
  1. package/.build-stamp +1 -1
  2. package/README.ko.md +1 -1
  3. package/README.md +1 -1
  4. package/{abstractCompactor-e5yq60qz.js → abstractCompactor-20e0cwbs.js} +2 -2
  5. package/{agent.command-b0fcx3r2.js → agent.command-nqs3bw0g.js} +8 -7
  6. package/{application.command-cgmy00gd.js → application.command-k2v46pxf.js} +7 -6
  7. package/{applicationBuildRunner-n1svrwgt.js → applicationBuildRunner-t5v6tcrm.js} +6 -7
  8. package/{applicationReleasePackager-gg8ccbeh.js → applicationReleasePackager-93nzdfsy.js} +1 -1
  9. package/buildBatch.proc.js +6 -5
  10. package/{capacitorApp-yyw35vg4.js → capacitorApp-927ahhz2.js} +29 -26
  11. package/{cloud.command-1m7hfdpk.js → cloud.command-7jwmxtez.js} +11 -10
  12. package/{context.command-b5heat2a.js → context.command-6kn3tady.js} +16 -15
  13. package/{dependencyScanner-f6grrep3.js → dependencyScanner-rbs2407b.js} +1 -1
  14. package/getDirname-ka11hm7e.js +8 -0
  15. package/{guideline.command-9ew6aqhm.js → guideline.command-pjdrndqk.js} +7 -6
  16. package/guidelines/agentRule/agentRule.generate.json +31 -0
  17. package/guidelines/agentRule/agentRule.instruction.md +413 -0
  18. package/guidelines/conventions/conventions.generate.json +25 -0
  19. package/guidelines/conventions/conventions.instruction.md +752 -0
  20. package/guidelines/cssRule/cssRule.instruction.md +7 -0
  21. package/guidelines/framework/framework.instruction.md +1 -0
  22. package/guidelines/mcpRule/mcpRule.generate.json +25 -0
  23. package/guidelines/mcpRule/mcpRule.instruction.md +164 -0
  24. package/guidelines/workspaceOnboarding/workspaceOnboarding.generate.json +24 -0
  25. package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +638 -0
  26. package/incrementalBuilder.proc.js +7 -6
  27. package/index-1577bej2.js +7 -0
  28. package/{index-jn9vfpwz.js → index-24h5bqpj.js} +54 -15
  29. package/{index-hrwzz2wf.js → index-4z05haak.js} +1 -1
  30. package/{index-m4kpmmtj.js → index-5em412h5.js} +1 -1
  31. package/{index-6npsfpxr.js → index-5fe016p2.js} +3 -3
  32. package/{index-bjpxzr6s.js → index-5vy2jzar.js} +1 -1
  33. package/{index-exhpp31y.js → index-6bs24rb4.js} +15 -7
  34. package/{index-nx1vbtsc.js → index-7etegaht.js} +14 -14
  35. package/{index-wrrb8rxk.js → index-7w5vv8jk.js} +4 -4
  36. package/{index-meh7mmz9.js → index-7zd96bng.js} +8 -4
  37. package/{index-p69fcwe1.js → index-866yqz3y.js} +13 -13
  38. package/{index-jkynkyfk.js → index-8rr621r2.js} +5 -3
  39. package/{index-47qxw1tz.js → index-90zyjrby.js} +170 -65
  40. package/{index-pfrt7qwh.js → index-97cw8sg1.js} +4 -4
  41. package/{index-p6txr1ss.js → index-bwt525ne.js} +9 -11
  42. package/{index-qp089jp0.js → index-j37qq1f2.js} +1 -1
  43. package/{index-c94nq3c1.js → index-k7sm38v6.js} +3 -3
  44. package/{index-j9cxndc0.js → index-qnv0aqmm.js} +9 -6
  45. package/{index-0cj2zxbm.js → index-sh01r3pm.js} +1 -1
  46. package/{index-0fj4yg7k.js → index-wr999y03.js} +105 -227
  47. package/{index-nfxq39kj.js → index-xe3ychw5.js} +37 -6
  48. package/{index-60a3jfcz.js → index-xrdrttjb.js} +8 -8
  49. package/{index-mn96y1nk.js → index-y59x51f1.js} +6 -6
  50. package/{index-mterpj70.js → index-zgkey4d0.js} +86 -17
  51. package/index.js +20 -19
  52. package/{library.command-aa1g8nsa.js → library.command-7h3yezwc.js} +5 -4
  53. package/{localRegistry.command-kshva1ck.js → localRegistry.command-vnkc41gj.js} +10 -9
  54. package/{module.command-b1zzgqde.js → module.command-4g11k3xd.js} +9 -8
  55. package/{package.command-9qv6vr9s.js → package.command-z7dd6vy2.js} +5 -4
  56. package/package.json +3 -4
  57. package/{page.command-k16pw244.js → page.command-y996708m.js} +5 -4
  58. package/{primitive.command-s60dn1ae.js → primitive.command-4fcn8k83.js} +10 -9
  59. package/{quality.command-p3vaze52.js → quality.command-hx5af2yn.js} +193 -112
  60. package/{recipeScanner-tghkzrrt.js → recipeScanner-zrzev25x.js} +3 -3
  61. package/{repair.command-mfv749ha.js → repair.command-ysxd8pef.js} +7 -6
  62. package/{routeSourceValidator-cncd92fv.js → routeSourceValidator-7fycdqyv.js} +3 -39
  63. package/{scalar.command-hs3nwzv1.js → scalar.command-3chgxybs.js} +8 -7
  64. package/templates/app/env/env.server.debug.ts.template +1 -5
  65. package/templates/app/env/env.server.develop.ts.template +1 -5
  66. package/templates/app/env/env.server.local.ts.template +1 -5
  67. package/templates/app/env/env.server.main.ts.template +1 -5
  68. package/templates/app/page/styles.css.template +3 -3
  69. package/templates/appSample/srvkit/AuthGuard.ts +9 -5
  70. package/templates/appSample/srvkit/SessionInternalArg.ts +4 -3
  71. package/templates/crudPages/[__model__Id]/edit/page.tsx +1 -1
  72. package/templates/crudPages/new/page.tsx +1 -1
  73. package/templates/crudSinglePage/page.tsx +1 -1
  74. package/templates/env/_env.server.type.ts +7 -5
  75. package/templates/lib/db.ts +1 -1
  76. package/templates/lib/dict.ts +1 -1
  77. package/templates/lib/sig.ts +1 -1
  78. package/templates/lib/srv.ts +6 -2
  79. package/templates/module/__Model__.Util.tsx +1 -1
  80. package/templates/module/__Model__.Zone.tsx +3 -5
  81. package/templates/workspaceRoot/AGENTS.md.template +0 -967
  82. package/templates/workspaceRoot/CLAUDE.md.template +19 -0
  83. package/templates/workspaceRoot/biome.json.template +4 -220
  84. package/templates/workspaceRoot/docs/AI-DEVELOPMENT.md.template +15 -16
  85. package/templates/workspaceRoot/docs/GENERATED.md.template +12 -10
  86. package/{typeChecker-kravn7ns.js → typeChecker-g6bh0xfv.js} +1 -1
  87. package/typecheck.proc.js +1 -1
  88. package/{workflow.command-z0e0fk0z.js → workflow.command-endxfzv5.js} +15 -14
  89. package/{workspace.command-3q1d6kk4.js → workspace.command-5f2nmmd5.js} +70 -52
  90. package/templates/workspaceRoot/.cursor/rules/akan-page-routing.mdc.template +0 -43
  91. package/templates/workspaceRoot/.cursor/rules/akan-sync-conventions.mdc.template +0 -34
  92. package/templates/workspaceRoot/.cursor/rules/application-test-commands.mdc.template +0 -16
  93. package/templates/workspaceRoot/.cursor/rules/change-scope.mdc.template +0 -12
  94. package/templates/workspaceRoot/.cursor/rules/client-server-boundaries.mdc.template +0 -18
  95. package/templates/workspaceRoot/.cursor/rules/coding-comments.mdc.template +0 -25
  96. package/templates/workspaceRoot/.cursor/rules/coding-style.mdc.template +0 -44
  97. package/templates/workspaceRoot/.cursor/rules/domain-module-conventions.mdc.template +0 -17
  98. package/templates/workspaceRoot/.cursor/rules/format-lint.mdc.template +0 -12
  99. package/templates/workspaceRoot/.cursor/rules/lint-enforced-rules.mdc.template +0 -18
  100. package/templates/workspaceRoot/.cursor/rules/module-file-playbook.mdc.template +0 -61
  101. package/templates/workspaceRoot/.cursor/rules/naming-conventions.mdc.template +0 -15
  102. package/templates/workspaceRoot/.cursor/rules/react-component-style.mdc.template +0 -21
  103. package/templates/workspaceRoot/.cursor/rules/repo-overview.mdc.template +0 -17
  104. package/templates/workspaceRoot/.cursor/rules/scalar-modeling.mdc.template +0 -58
  105. package/templates/workspaceRoot/.cursor/rules/secrets-and-env.mdc.template +0 -13
  106. package/templates/workspaceRoot/.cursor/rules/service-signal-conventions.mdc.template +0 -83
  107. package/templates/workspaceRoot/.cursor/rules/srvkit-adapters-and-guards.mdc.template +0 -50
  108. package/templates/workspaceRoot/.cursor/rules/ssr-first.mdc.template +0 -59
  109. package/templates/workspaceRoot/.cursor/rules/typescript-imports.mdc.template +0 -16
  110. /package/{index-r24hmh0q.js → index-yb2wsc7f.js} +0 -0
@@ -9,10 +9,10 @@ import {
9
9
  createTsconfigPackageResolver,
10
10
  rewriteBarrelImports,
11
11
  transformUseClient
12
- } from "./index-j9cxndc0.js";
12
+ } from "./index-qnv0aqmm.js";
13
13
  import {
14
14
  __require
15
- } from "./index-r24hmh0q.js";
15
+ } from "./index-yb2wsc7f.js";
16
16
 
17
17
  // pkgs/@akanjs/devkit/frontendBuild/allRoutesBuilder.ts
18
18
  import path4 from "path";
@@ -810,10 +810,10 @@ import { compile } from "tailwindcss";
810
810
 
811
811
  // pkgs/@akanjs/devkit/frontendBuild/cssCandidateCache.ts
812
812
  import { stat } from "fs/promises";
813
- var CANDIDATE_RE = /-?[\w@][\w:/.-]*(?:\[[^\]]+\][\w:/.-]*)*/g;
813
+ var CANDIDATE_RE = /-?(?:[\w@]|\[[^\]\s]+\])[\w:/.-]*(?:\[[^\]\s]+\][\w:/.-]*)*/g;
814
814
 
815
815
  class CssCandidateCache {
816
- static #version = 1;
816
+ static #version = 3;
817
817
  #path;
818
818
  #entries = new Map;
819
819
  #dirty = false;
@@ -973,8 +973,12 @@ class CssImportResolver {
973
973
  const pkg = await Bun.file(pkgPath).json();
974
974
  const subpath = id === pkgName ? "." : `.${id.slice(pkgName.length)}`;
975
975
  const exportValue = pkg.exports?.[subpath];
976
- const styleEntry = (typeof exportValue === "string" ? exportValue : exportValue?.style || exportValue?.import || exportValue?.default) || pkg.exports?.["."]?.style || pkg.style || "index.css";
977
- return await this.#firstExisting(path7.resolve(pkgDir, styleEntry));
976
+ const exportedEntry = typeof exportValue === "string" ? exportValue : exportValue?.style || exportValue?.import || exportValue?.default;
977
+ if (exportedEntry)
978
+ return await this.#firstExisting(path7.resolve(pkgDir, exportedEntry));
979
+ if (subpath !== ".")
980
+ return await this.#firstExisting(path7.resolve(pkgDir, subpath));
981
+ return await this.#firstExisting(path7.resolve(pkgDir, pkg.exports?.["."]?.style || pkg.style || "index.css"));
978
982
  } catch {
979
983
  return null;
980
984
  }
@@ -1031,6 +1035,8 @@ class CssCompiler {
1031
1035
  #fileExistsCache = new Map;
1032
1036
  #resolvedFileCache = new Map;
1033
1037
  #resolvedSpecifierCache = new Map;
1038
+ #discoveredCssPaths = new Set;
1039
+ importedStylesheetsByBasePath = {};
1034
1040
  #fileExists(absPath) {
1035
1041
  let cached = this.#fileExistsCache.get(absPath);
1036
1042
  if (!cached) {
@@ -1064,13 +1070,20 @@ class CssCompiler {
1064
1070
  async getCss({ refresh } = {}) {
1065
1071
  if (this.#cssText !== null && !refresh)
1066
1072
  return this.#cssText;
1073
+ this.#discoveredCssPaths.clear();
1074
+ this.importedStylesheetsByBasePath = {};
1067
1075
  const { cssPaths, sourcePaths } = await this.discoverCssAndSources({ refresh });
1068
- this.#cssText = await this.compileCss(cssPaths, sourcePaths);
1076
+ const { css, imported } = await this.#compileWithImports(cssPaths, sourcePaths);
1077
+ this.#cssText = css;
1078
+ this.importedStylesheetsByBasePath = { "": imported };
1079
+ await this.#warnUnreachableStylesheets();
1069
1080
  return this.#cssText;
1070
1081
  }
1071
1082
  async getCssByBasePath({ refresh } = {}) {
1072
1083
  if (this.#cssTextByBasePath !== null && !refresh)
1073
1084
  return this.#cssTextByBasePath;
1085
+ this.#discoveredCssPaths.clear();
1086
+ this.importedStylesheetsByBasePath = {};
1074
1087
  const akanConfig = await this.#app.getConfig({ refresh });
1075
1088
  const pageKeys = await this.#app.getPageKeys({ refresh });
1076
1089
  const basePaths = [...akanConfig.basePaths];
@@ -1081,7 +1094,8 @@ class CssCompiler {
1081
1094
  return ["", ""];
1082
1095
  const started = Date.now();
1083
1096
  const { cssPaths, sourcePaths } = await this.discoverCssAndSources({ refresh, pageKeys: rootPageKeys });
1084
- const css = await this.compileCss(cssPaths, sourcePaths);
1097
+ const { css, imported } = await this.#compileWithImports(cssPaths, sourcePaths);
1098
+ this.importedStylesheetsByBasePath[""] = imported;
1085
1099
  this.#logger.verbose(`css base=root paths=${cssPaths.length} sources=${sourcePaths.length} in ${Date.now() - started}ms`);
1086
1100
  return ["", css];
1087
1101
  })(),
@@ -1091,14 +1105,27 @@ class CssCompiler {
1091
1105
  return [basePath, ""];
1092
1106
  const started = Date.now();
1093
1107
  const { cssPaths, sourcePaths } = await this.discoverCssAndSources({ refresh, pageKeys: basePathPageKeys });
1094
- const css = await this.compileCss(cssPaths, sourcePaths);
1108
+ const { css, imported } = await this.#compileWithImports(cssPaths, sourcePaths);
1109
+ this.importedStylesheetsByBasePath[basePath] = imported;
1095
1110
  this.#logger.verbose(`css base=${basePath} paths=${cssPaths.length} sources=${sourcePaths.length} in ${Date.now() - started}ms`);
1096
1111
  return [basePath, css];
1097
1112
  })
1098
1113
  ]);
1099
1114
  this.#cssTextByBasePath = Object.fromEntries(cssEntries);
1115
+ await this.#warnUnreachableStylesheets();
1100
1116
  return this.#cssTextByBasePath;
1101
1117
  }
1118
+ async#warnUnreachableStylesheets() {
1119
+ const pageDir = path8.join(this.#app.cwdPath, "page");
1120
+ const glob = new Bun.Glob("**/*.css");
1121
+ for await (const cssPath of glob.scan({ cwd: pageDir, absolute: true })) {
1122
+ if (cssPath.includes(`${path8.sep}(libs)${path8.sep}`))
1123
+ continue;
1124
+ if (this.#discoveredCssPaths.has(cssPath))
1125
+ continue;
1126
+ this.#logger.warn(`css ${path8.relative(this.#app.cwdPath, cssPath)} is imported by no route and never compiled`);
1127
+ }
1128
+ }
1102
1129
  async discoverCss({ refresh } = {}) {
1103
1130
  const { cssPaths } = await this.discoverCssAndSources({ refresh });
1104
1131
  return cssPaths;
@@ -1158,21 +1185,47 @@ class CssCompiler {
1158
1185
  queue.push(resolved);
1159
1186
  }
1160
1187
  }
1161
- return { cssPaths: [...cssFiles], sourcePaths: [...sourceFiles] };
1188
+ const tokenPaths = await this.#libTokenStylesheets(sourceFiles);
1189
+ const cssPaths = [...new Set([...tokenPaths, ...cssFiles])];
1190
+ for (const cssPath of cssPaths)
1191
+ this.#discoveredCssPaths.add(cssPath);
1192
+ return { cssPaths, sourcePaths: [...sourceFiles] };
1193
+ }
1194
+ async#libTokenStylesheets(sourceFiles) {
1195
+ const libsRoot = path8.join(this.#app.workspace.workspaceRoot, "libs");
1196
+ const libNames = new Set;
1197
+ for (const filePath of sourceFiles) {
1198
+ const relPath = path8.relative(libsRoot, filePath);
1199
+ if (relPath.startsWith("..") || path8.isAbsolute(relPath))
1200
+ continue;
1201
+ const [libName] = relPath.split(path8.sep);
1202
+ if (libName)
1203
+ libNames.add(libName);
1204
+ }
1205
+ const tokenPaths = await Promise.all([...libNames].sort().map(async (libName) => {
1206
+ const tokensPath = path8.join(libsRoot, libName, "ui/tokens.css");
1207
+ return await this.#fileExists(tokensPath) ? tokensPath : null;
1208
+ }));
1209
+ return tokenPaths.filter((tokensPath) => !!tokensPath);
1162
1210
  }
1163
1211
  async compileCss(cssPaths, sourcePaths) {
1212
+ const { css } = await this.#compileWithImports(cssPaths, sourcePaths);
1213
+ return css;
1214
+ }
1215
+ async#compileWithImports(cssPaths, sourcePaths) {
1164
1216
  if (cssPaths.length === 0)
1165
- return "";
1217
+ return { css: "", imported: [] };
1166
1218
  const compileStarted = Date.now();
1167
1219
  const compilers = await Promise.all(cssPaths.map(async (cssPath) => {
1168
1220
  const css = await Bun.file(cssPath).text();
1169
1221
  const base = path8.dirname(cssPath);
1222
+ const imported2 = new Map;
1170
1223
  const compiler = await compile(css, {
1171
1224
  base,
1172
- loadStylesheet: (id, fromBase) => this.#loadStylesheet(id, fromBase),
1225
+ loadStylesheet: (id, fromBase) => this.#loadStylesheet(id, fromBase, imported2),
1173
1226
  loadModule: (id, fromBase) => this.#loadModule(id, fromBase)
1174
1227
  });
1175
- return { cssPath, compiler };
1228
+ return { cssPath, compiler, imported: imported2 };
1176
1229
  }));
1177
1230
  const sourceDirs = new Set;
1178
1231
  for (const entry of compilers) {
@@ -1185,23 +1238,39 @@ class CssCompiler {
1185
1238
  const candidates = await this.#scanCandidates(sourcePaths, [...sourceDirs]);
1186
1239
  this.#logger.verbose(`css candidates scanned count=${candidates.length} sources=${sourcePaths.length} dirs=${sourceDirs.size} in ${Date.now() - scanStarted}ms`);
1187
1240
  const parts = [];
1241
+ const imported = [];
1188
1242
  for (const entry of compilers) {
1189
1243
  if (!entry)
1190
1244
  continue;
1191
- parts.push(entry.compiler.build(candidates));
1245
+ const part = entry.compiler.build(candidates);
1246
+ parts.push(part);
1247
+ for (const [cssPath, content] of entry.imported) {
1248
+ const declaredNames = declaredCustomProperties(content);
1249
+ imported.push({ cssPath, declaredNames });
1250
+ if (declaredNames.length === 0 || declaredNames.some((name) => part.includes(`${name}:`)))
1251
+ continue;
1252
+ this.#logger.warn(`css @import ${cssPath} was loaded by ${entry.cssPath} but none of its ${declaredNames.length} declaration(s) reached the compiled CSS`);
1253
+ }
1192
1254
  }
1193
1255
  this.#logger.verbose(`css compiled paths=${cssPaths.length} candidates=${candidates.length} in ${Date.now() - compileStarted}ms`);
1194
- return parts.join(`
1195
- `);
1256
+ return { css: parts.join(`
1257
+ `), imported };
1196
1258
  }
1197
- async#loadStylesheet(id, fromBase) {
1259
+ async#loadStylesheet(id, fromBase, imported) {
1198
1260
  const p = await this.#resolveCssImport(id, fromBase);
1261
+ this.#discoveredCssPaths.add(p);
1199
1262
  const content = await Bun.file(p).text();
1263
+ imported?.set(p, content);
1264
+ this.#logger.verbose(`css import "${id}" from ${fromBase} -> ${p} (${content.length} bytes)`);
1200
1265
  return { path: p, base: path8.dirname(p), content };
1201
1266
  }
1202
1267
  async#resolveCssImport(id, fromBase) {
1203
- if (id.startsWith(".") || id.startsWith("/"))
1204
- return path8.resolve(fromBase, id);
1268
+ if (id.startsWith(".") || id.startsWith("/")) {
1269
+ const filePath = path8.resolve(fromBase, id);
1270
+ if (await this.#fileExists(filePath))
1271
+ return filePath;
1272
+ throw new Error(`[css] failed to resolve stylesheet import "${id}" from ${fromBase} (no file at ${filePath})`);
1273
+ }
1205
1274
  const resolver = await this.#getCssImportResolver();
1206
1275
  const resolved = await resolver.resolve(id, fromBase);
1207
1276
  if (resolved)
@@ -1286,6 +1355,10 @@ function isSourceFile(filePath) {
1286
1355
  function isIgnoredNodeModuleSource(filePath) {
1287
1356
  return NODE_MODULES_RE.test(filePath) && !AKANJS_NODE_MODULE_RE.test(filePath);
1288
1357
  }
1358
+ function declaredCustomProperties(css) {
1359
+ const withoutThemeBlocks = css.replace(/@theme[^{]*\{[^}]*\}/g, "");
1360
+ return [...new Set([...withoutThemeBlocks.matchAll(/(?:^|[\s;{])(--[\w-]+)\s*:/g)].map(([, name]) => name))].filter((name) => !!name);
1361
+ }
1289
1362
  function getPageKeyBasePath(pageKey, basePaths) {
1290
1363
  const normalized = pageKey.split(path8.sep).join("/").replace(/^\.\//, "");
1291
1364
  const segments = normalized.split("/");
@@ -1763,7 +1836,6 @@ var DEFAULT_EXCLUDE_PREFIX = ["@akanjs/cli/", "@akanjs/devkit/"];
1763
1836
  var OPTIONAL_BACKEND_EXTERNAL_EXACT = new Set([
1764
1837
  "@libsql/client",
1765
1838
  "bullmq",
1766
- "croner",
1767
1839
  "ioredis",
1768
1840
  "postgres",
1769
1841
  "protobufjs"
@@ -2180,7 +2252,7 @@ class SsrBaseArtifactBuilder {
2180
2252
  const ssrBundle = await new ClientEntriesBundler({
2181
2253
  app: this.#app,
2182
2254
  entries: [rscSegmentOutletEntry],
2183
- ...RouteClientBuilder.resolveSsrClientExternalOptions(this.#command),
2255
+ ...RouteClientBuilder.resolveSsrClientBundleOptions(this.#command),
2184
2256
  outputSubdir: "client-ssr",
2185
2257
  command: this.#command
2186
2258
  }).bundle();
@@ -2232,13 +2304,13 @@ class SsrBaseArtifactBuilder {
2232
2304
  `);
2233
2305
  if (!cssText)
2234
2306
  return [];
2235
- return [this.#writeCssAsset(basePath, cssText)];
2307
+ return [this.#writeCssAsset(basePath, cssText, cssCompiler.importedStylesheetsByBasePath[basePath] ?? [])];
2236
2308
  })));
2237
2309
  if (optimizedFonts.files.length > 0)
2238
2310
  this.#app.verbose(`[base-artifact] optimized ${optimizedFonts.files.length} font files`);
2239
2311
  return { cssCompiler, optimizedFonts, cssAssets };
2240
2312
  }
2241
- async#writeCssAsset(basePath, cssText) {
2313
+ async#writeCssAsset(basePath, cssText, imported) {
2242
2314
  const cssAssetName = basePath || "root";
2243
2315
  const preparedCssText = await prepareCssAsset(this.#command, basePath, cssText);
2244
2316
  const cssHash = Bun.hash(`${basePath}
@@ -2248,9 +2320,17 @@ ${preparedCssText}`).toString(36);
2248
2320
  `/_akan/styles/${cssAssetName}-${cssHash}.css`
2249
2321
  ];
2250
2322
  await Bun.write(path13.join(this.#absArtifactDir, cssRelPath), preparedCssText);
2323
+ SsrBaseArtifactBuilder.#warnDroppedImports(this.#app, cssRelPath, preparedCssText, imported);
2251
2324
  this.#app.verbose(`[base-artifact] wrote ${preparedCssText.length} bytes of CSS for ${basePath} -> ${cssRelPath}`);
2252
2325
  return [basePath, { cssUrl, cssRelPath }];
2253
2326
  }
2327
+ static #warnDroppedImports(app, cssRelPath, css, imported) {
2328
+ for (const { cssPath, declaredNames } of imported) {
2329
+ if (declaredNames.length === 0 || declaredNames.some((name) => css.includes(`${name}:`)))
2330
+ continue;
2331
+ app.logger.warn(`[base-artifact] @import ${cssPath} declares ${declaredNames.length} custom propert${declaredNames.length === 1 ? "y" : "ies"} and none of them are in ${cssRelPath}`);
2332
+ }
2333
+ }
2254
2334
  }
2255
2335
  // pkgs/@akanjs/devkit/frontendBuild/styleGuard.ts
2256
2336
  var PREFIX = "(?:bg|text|border(?:-[tblrxy])?(?:-[se])?|ring(?:-offset)?|fill|stroke|shadow|from|to|via|divide|outline|decoration|placeholder|caret|accent)";
@@ -2258,210 +2338,8 @@ var PALETTE = "(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|gre
2258
2338
  var LEAD = "(^|[\\s\"'`{(\\[:!])";
2259
2339
  var TAIL = "(?=[\\s\"'`})\\]:/!,]|$)";
2260
2340
  var RAW_PALETTE_RE = new RegExp(`${LEAD}((?:${PREFIX})-)${PALETTE}-\\d{2,3}${TAIL}`, "g");
2261
- var ARBITRARY_COLOR_RE = /\[(#[0-9a-fA-F]{3,8}|(?:rgb|rgba|hsl|hsla|oklch|oklab|lab|lch|hwb|color)\([^\]]*\))\]/g;
2262
2341
  var INTERPOLATED_ARBITRARY_RE = new RegExp(`${LEAD}[a-z][a-z0-9-]*-\\[[^\\]\`]*\\$\\{`, "g");
2263
- var STYLE_OBJECT_RE = /style=\{\{([\s\S]*?)\}\}/g;
2264
- var STYLE_TAG_RE = /<style[^>]*>([\s\S]*?)<\/style>/g;
2265
- var INLINE_COLOR_LITERAL_RE = /#[0-9a-fA-F]{3,8}\b|(?:rgb|rgba|hsl|hsla|oklch|oklab|lab|lch|hwb)\(/g;
2266
2342
  var DAISYUI_LEGACY_RE = new RegExp(`${LEAD}(?:` + "btn-(?:primary|secondary|accent|neutral|info|success|warning|error|ghost|link|outline|square|circle|wide|block|xs|sm|md|lg)|" + "badge-(?:primary|secondary|accent|neutral|info|success|warning|error|ghost|outline)|" + "alert-(?:info|success|warning|error)|" + "input-(?:bordered|primary|secondary|accent|ghost|error)|" + "select-(?:bordered|primary|ghost)|textarea-(?:bordered|primary|ghost)|" + "checkbox-(?:primary|secondary|accent)|toggle-(?:primary|secondary|accent)|" + "loading-(?:spinner|dots|ring|ball|bars|infinity)|" + "card-(?:body|title|actions)|modal-(?:box|action|backdrop)|" + "collapse-(?:title|content|arrow|plus)|dropdown-(?:content|end|start|hover)|" + "stat-(?:title|value|desc)|tabs-(?:boxed|lifted|bordered)|tab-active|" + "menu-(?:title|dropdown)|steps-(?:horizontal|vertical)|join-item|" + "mockup-(?:code|phone|browser|window)|drawer-(?:side|content|toggle)" + `)${TAIL}`, "g");
2267
- var ALL_RULES = [
2268
- "raw-palette",
2269
- "arbitrary-color",
2270
- "inline-color",
2271
- "daisyui-legacy",
2272
- "interpolated-arbitrary"
2273
- ];
2274
-
2275
- class StyleGuard {
2276
- run(files) {
2277
- const out = [];
2278
- for (const file of files) {
2279
- const directives = this.#parseDirectives(file.content);
2280
- const scan = this.#stripComments(file.content);
2281
- const local = [];
2282
- this.#scanClassLiterals(file, scan, local);
2283
- this.#scanInlineColors(file, scan, local);
2284
- for (const v of local)
2285
- if (!this.#isSuppressed(v, directives))
2286
- out.push(v);
2287
- }
2288
- return out;
2289
- }
2290
- #stripComments(src) {
2291
- const out = src.split("");
2292
- const blank = (i2) => {
2293
- if (src[i2] !== `
2294
- `)
2295
- out[i2] = " ";
2296
- };
2297
- let state = "code";
2298
- let i = 0;
2299
- while (i < src.length) {
2300
- const c = src[i];
2301
- const d = src[i + 1];
2302
- if (state === "code") {
2303
- if (c === "/" && d === "/") {
2304
- blank(i);
2305
- blank(i + 1);
2306
- state = "line";
2307
- i += 2;
2308
- } else if (c === "/" && d === "*") {
2309
- blank(i);
2310
- blank(i + 1);
2311
- state = "block";
2312
- i += 2;
2313
- } else {
2314
- if (c === "'")
2315
- state = "squote";
2316
- else if (c === '"')
2317
- state = "dquote";
2318
- else if (c === "`")
2319
- state = "template";
2320
- i++;
2321
- }
2322
- } else if (state === "line") {
2323
- if (c === `
2324
- `)
2325
- state = "code";
2326
- else
2327
- blank(i);
2328
- i++;
2329
- } else if (state === "block") {
2330
- if (c === "*" && d === "/") {
2331
- blank(i);
2332
- blank(i + 1);
2333
- state = "code";
2334
- i += 2;
2335
- } else {
2336
- blank(i);
2337
- i++;
2338
- }
2339
- } else {
2340
- const close = state === "squote" ? "'" : state === "dquote" ? '"' : "`";
2341
- if (c === "\\")
2342
- i += 2;
2343
- else {
2344
- if (c === close)
2345
- state = "code";
2346
- i++;
2347
- }
2348
- }
2349
- }
2350
- return out.join("");
2351
- }
2352
- #parseDirectives(content) {
2353
- const directives = { file: null, nextLine: new Map };
2354
- const lines = content.split(`
2355
- `);
2356
- for (let i = 0;i < lines.length; i++) {
2357
- const line = lines[i];
2358
- const nextLineMatch = line.match(/styleguard-disable-next-line\b(.*)$/);
2359
- if (nextLineMatch) {
2360
- directives.nextLine.set(i + 2, this.#extractRules(nextLineMatch[1]));
2361
- continue;
2362
- }
2363
- const fileMatch = line.match(/styleguard-disable\b(.*)$/);
2364
- if (fileMatch)
2365
- directives.file = this.#mergeFileScope(directives.file, this.#extractRules(fileMatch[1]));
2366
- }
2367
- return directives;
2368
- }
2369
- #extractRules(tail) {
2370
- const found = ALL_RULES.filter((rule) => tail.includes(rule));
2371
- return found.length === 0 ? "all" : new Set(found);
2372
- }
2373
- #mergeFileScope(prev, next) {
2374
- if (prev === "all" || next === "all")
2375
- return "all";
2376
- return new Set([...prev ?? [], ...next]);
2377
- }
2378
- #isSuppressed(v, directives) {
2379
- if (directives.file === "all" || directives.file?.has(v.rule))
2380
- return true;
2381
- const nextLine = directives.nextLine.get(v.line);
2382
- return nextLine === "all" || (nextLine?.has(v.rule) ?? false);
2383
- }
2384
- #scanClassLiterals(file, scan, out) {
2385
- for (const m of scan.matchAll(RAW_PALETTE_RE)) {
2386
- out.push(this.#violation(file, m.index ?? 0, {
2387
- rule: "raw-palette",
2388
- severity: "error",
2389
- suggestion: "\uC2DC\uB9E8\uD2F1 \uD1A0\uD070\uC73C\uB85C \uAD50\uCCB4\uD558\uC138\uC694 \u2014 \uC608: text-gray-500\u2192text-muted-foreground, bg-red-500\u2192bg-destructive, bg-blue-500\u2192bg-info. \uBE0C\uB79C\uB4DC \uC0C9\uC740 page/styles.css \uD1A0\uD070\uC5D0\uC11C \uC870\uC815\uD569\uB2C8\uB2E4."
2390
- }));
2391
- }
2392
- for (const m of scan.matchAll(ARBITRARY_COLOR_RE)) {
2393
- out.push(this.#violation(file, m.index ?? 0, {
2394
- rule: "arbitrary-color",
2395
- severity: "error",
2396
- suggestion: "\uC784\uC758 \uC0C9 \uB9AC\uD130\uB7F4 \uB300\uC2E0 \uD1A0\uD070\uC744 \uC4F0\uC138\uC694 \u2014 bg-[#3b82f6]\u2192bg-info, text-[rgb(...)]\u2192\uC2DC\uB9E8\uD2F1 \uD1A0\uD070. \uAF2D \uD544\uC694\uD558\uBA74 \uC2A4\uCF54\uD504 \uD1A0\uD070(.campaign-x { --primary: \u2026 })\uC744 \uC815\uC758\uD558\uC138\uC694."
2397
- }));
2398
- }
2399
- for (const m of scan.matchAll(DAISYUI_LEGACY_RE)) {
2400
- out.push(this.#violation(file, m.index ?? 0, {
2401
- rule: "daisyui-legacy",
2402
- severity: "error",
2403
- suggestion: "daisyUI \uD074\uB798\uC2A4\uB294 \uC81C\uAC70\uB410\uC2B5\uB2C8\uB2E4. akanjs/ui \uD504\uB9AC\uBBF8\uD2F0\uBE0C(Button/Badge \uB4F1)\uB098 buttonRecipe()/badgeRecipe() + \uC2DC\uB9E8\uD2F1 \uD1A0\uD070\uC73C\uB85C \uAD50\uCCB4\uD558\uC138\uC694."
2404
- }));
2405
- }
2406
- for (const m of scan.matchAll(INTERPOLATED_ARBITRARY_RE)) {
2407
- out.push(this.#violation(file, m.index ?? 0, {
2408
- rule: "interpolated-arbitrary",
2409
- severity: "error",
2410
- suggestion: "\uB7F0\uD0C0\uC784 \uAC12\uC73C\uB85C \uC784\uC758\uAC12 \uD074\uB798\uC2A4\uB97C \uC870\uB9BD\uD558\uBA74 CSS \uAC00 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uC2A4\uCE90\uB108\uB294 \uC18C\uC2A4 \uD14D\uC2A4\uD2B8\uB97C \uC77D\uC2B5\uB2C8\uB2E4). \uD06C\uAE30/\uC704\uCE58\uB294 style prop \uC73C\uB85C \uB118\uAE30\uC138\uC694 \u2014 style={{ minHeight }}. \uAC12\uC774 enum \uC774\uBA74 \uB9AC\uD130\uB7F4 \uD074\uB798\uC2A4 \uB9F5\uC73C\uB85C \uB450\uC138\uC694."
2411
- }));
2412
- }
2413
- }
2414
- #scanInlineColors(file, scan, out) {
2415
- const push = (index) => out.push(this.#violation(file, index, {
2416
- rule: "inline-color",
2417
- severity: "error",
2418
- suggestion: "\uC778\uB77C\uC778 \uC0C9 \uB9AC\uD130\uB7F4 \uB300\uC2E0 CSS \uBCC0\uC218/\uD1A0\uD070\uC744 \uCC38\uC870\uD558\uC138\uC694 \u2014 style={{ color: 'var(--primary)' }} \uB610\uB294 \uC2DC\uB9E8\uD2F1 \uD074\uB798\uC2A4(text-primary). \uD558\uB4DC\uCF54\uB529 hex/rgb \uB294 \uD14C\uB9C8 \uC804\uD658\uC744 \uAE68\uB728\uB9BD\uB2C8\uB2E4."
2419
- }));
2420
- for (const region of [STYLE_OBJECT_RE, STYLE_TAG_RE]) {
2421
- for (const m of scan.matchAll(region)) {
2422
- const inner = m[1] ?? "";
2423
- const innerStart = (m.index ?? 0) + m[0].indexOf(inner);
2424
- for (const lit of inner.matchAll(INLINE_COLOR_LITERAL_RE)) {
2425
- push(innerStart + (lit.index ?? 0));
2426
- }
2427
- }
2428
- }
2429
- }
2430
- #violation(file, index, base) {
2431
- const before = file.content.slice(0, index);
2432
- const line = before.length === 0 ? 1 : before.split(`
2433
- `).length;
2434
- const lineStart = before.lastIndexOf(`
2435
- `) + 1;
2436
- const lineEnd = file.content.indexOf(`
2437
- `, index);
2438
- const snippet = file.content.slice(lineStart, lineEnd === -1 ? undefined : lineEnd).trim();
2439
- return { ...base, path: file.path, line, snippet };
2440
- }
2441
- static countNamedComponentClasses(css) {
2442
- const names = new Set;
2443
- const layerRe = /@layer\s+components\s*\{/g;
2444
- for (const layer of css.matchAll(layerRe)) {
2445
- const bodyStart = (layer.index ?? 0) + layer[0].length;
2446
- const body = StyleGuard.#extractBalanced(css, bodyStart);
2447
- for (const sel of body.matchAll(/(^|[\s}])\.(-?[a-zA-Z_][\w-]*)/g))
2448
- names.add(sel[2]);
2449
- }
2450
- return { count: names.size, names: [...names].sort() };
2451
- }
2452
- static #extractBalanced(css, start) {
2453
- let depth = 1;
2454
- for (let i = start;i < css.length; i++) {
2455
- const ch = css[i];
2456
- if (ch === "{")
2457
- depth++;
2458
- else if (ch === "}") {
2459
- depth--;
2460
- if (depth === 0)
2461
- return css.slice(start, i);
2462
- }
2463
- }
2464
- return css.slice(start);
2465
- }
2466
- }
2343
+ var DAISYUI_TOKEN = "(?:base-(?:100|200|300|content)|(?:primary|secondary|accent|neutral|info|success|warning|error)-content|error)";
2344
+ var DAISYUI_TOKEN_RE = new RegExp(`${LEAD}(?:${PREFIX})-${DAISYUI_TOKEN}${TAIL}`, "g");
2467
2345
  export { AllRoutesBuilder, CsrArtifactBuilder, CssCompiler, FontOptimizer, PagesBundleBuilder, precompressArtifacts, SsrBaseArtifactBuilder };
@@ -1,22 +1,25 @@
1
1
  // @bun
2
2
  import {
3
3
  AkanContextAnalyzer
4
- } from "./index-jn9vfpwz.js";
4
+ } from "./index-24h5bqpj.js";
5
5
  import {
6
6
  Prompter
7
- } from "./index-qp089jp0.js";
7
+ } from "./index-j37qq1f2.js";
8
8
  import {
9
9
  runner,
10
10
  script
11
- } from "./index-jkynkyfk.js";
11
+ } from "./index-8rr621r2.js";
12
12
  import {
13
13
  AGENT_BLOCK_END,
14
14
  AGENT_BLOCK_START,
15
15
  AppExecutor,
16
+ CLAUDE_COMMENT_RULE,
16
17
  LibExecutor,
18
+ readDevkitVersion,
17
19
  renderRecipeEntries,
20
+ stampBlockVersion,
18
21
  upsertAgentBlock
19
- } from "./index-47qxw1tz.js";
22
+ } from "./index-90zyjrby.js";
20
23
  import {
21
24
  collectRecipeSources,
22
25
  scanRecipes
@@ -103,19 +106,33 @@ ${scopeIndexPointer}
103
106
 
104
107
  `;
105
108
  };
109
+ var renderGuides = async (context) => {
110
+ const conventions = (await Prompter.getInstruction("conventions")).trim();
111
+ if (context.pkgs.some((pkg) => pkg.name === "akanjs"))
112
+ return conventions;
113
+ const onboarding = (await Prompter.getInstruction("workspaceOnboarding")).trim();
114
+ const appName = context.apps[0]?.name ?? "app";
115
+ return `${conventions}
116
+
117
+ ${onboarding.replaceAll("<%= appName %>", appName)}`;
118
+ };
106
119
  var renderManagedBlock = async (workspace) => {
107
120
  const context = await AkanContextAnalyzer.analyze(workspace);
108
121
  const frameworkGuide = await Prompter.getInstruction("framework");
109
122
  const appNames = context.apps.map((app) => app.name);
110
123
  const sampleCleanup = await renderSampleCleanup(workspace, appNames);
111
124
  const recipeIndex = await renderRecipeIndex(workspace);
112
- return `## Workspace
125
+ const guides = await renderGuides(context);
126
+ const version = await readDevkitVersion();
127
+ const block = `## Workspace
113
128
 
114
129
  - Repo: ${context.repoName}
115
130
  - Apps: ${context.apps.map((app) => app.name).join(", ") || "none"}
116
131
  - Libraries: ${context.libs.map((lib) => lib.name).join(", ") || "none"}
117
132
  - Packages: ${context.pkgs.map((pkg) => pkg.name).join(", ") || "none"}
118
133
 
134
+ ${guides}
135
+
119
136
  ${sampleCleanup}## Akan Module Abstracts
120
137
 
121
138
  - Before changing a domain, service, or scalar module, read its \`*.abstract.md\` file first.
@@ -148,7 +165,19 @@ ${context.validationCommands.map((command) => `- \`${command}\``).join(`
148
165
 
149
166
  ## Framework Guide
150
167
 
151
- ${frameworkGuide.trim()}`;
168
+ ${frameworkGuide.trim()}
169
+
170
+ ## Before You Finish
171
+
172
+ 1. \`bun run akan lint <appName>\` \u2014 Tailwind class order, \`Err\`, \`console\`, \`#private\` scope, unused imports.
173
+ 2. \`bun run akan typecheck <appName>\` \u2014 server/client boundary violations.
174
+ 3. \`bun run akan sync <appName>\` if you added, renamed, or deleted any file.
175
+ 4. Did you write or change a \`.tsx\` file? \`bun run akan quality ssr\` \u2014 did the server share hold, and did you add
176
+ a \`"use client"\` you cannot justify?
177
+ 5. Re-read the file you wrote against its section above.
178
+ 6. Did you add a comment? Delete it unless it documents an external constraint or a security decision.
179
+ 7. Did behavior or an invariant change? Update the module's \`*.abstract.md\`.`;
180
+ return version ? stampBlockVersion(block, version) : block;
152
181
  };
153
182
  var renderAgentsMd = async (workspace, existing) => {
154
183
  const block = await renderManagedBlock(workspace);
@@ -171,6 +200,8 @@ var renderClaudeMd = async (workspace) => {
171
200
  return `# ${context.repoName} \u2014 Claude Code Guide
172
201
 
173
202
  @AGENTS.md
203
+
204
+ ${CLAUDE_COMMENT_RULE}
174
205
  `;
175
206
  };
176
207
  var renderCursorRule = () => `---
@@ -1,13 +1,13 @@
1
1
  // @bun
2
- import {
3
- PageScript
4
- } from "./index-hrwzz2wf.js";
5
2
  import {
6
3
  pluralizeName
7
4
  } from "./index-ss469dec.js";
5
+ import {
6
+ PageScript
7
+ } from "./index-4z05haak.js";
8
8
  import {
9
9
  AiSession
10
- } from "./index-0cj2zxbm.js";
10
+ } from "./index-sh01r3pm.js";
11
11
  import {
12
12
  bilingualDescriptionForField,
13
13
  bilingualLabelForField,
@@ -15,17 +15,17 @@ import {
15
15
  generatedFilesForSync,
16
16
  moduleSourcePaths,
17
17
  sourceFile
18
- } from "./index-m4kpmmtj.js";
18
+ } from "./index-5em412h5.js";
19
19
  import {
20
20
  Prompter
21
- } from "./index-qp089jp0.js";
21
+ } from "./index-j37qq1f2.js";
22
22
  import {
23
23
  runner,
24
24
  script
25
- } from "./index-jkynkyfk.js";
25
+ } from "./index-8rr621r2.js";
26
26
  import {
27
27
  ModuleExecutor
28
- } from "./index-47qxw1tz.js";
28
+ } from "./index-90zyjrby.js";
29
29
  import {
30
30
  FileSys
31
31
  } from "./index-67546d0j.js";
@@ -1,24 +1,24 @@
1
1
  // @bun
2
2
  import {
3
3
  CloudRunner
4
- } from "./index-p6txr1ss.js";
4
+ } from "./index-bwt525ne.js";
5
5
  import {
6
6
  PackageScript
7
- } from "./index-meh7mmz9.js";
7
+ } from "./index-7zd96bng.js";
8
8
  import {
9
9
  AiSession,
10
10
  CloudApi,
11
11
  GlobalConfig
12
- } from "./index-0cj2zxbm.js";
12
+ } from "./index-sh01r3pm.js";
13
13
  import {
14
14
  ApplicationScript
15
- } from "./index-nx1vbtsc.js";
15
+ } from "./index-7etegaht.js";
16
16
  import {
17
17
  script
18
- } from "./index-jkynkyfk.js";
18
+ } from "./index-8rr621r2.js";
19
19
  import {
20
20
  PkgExecutor
21
- } from "./index-47qxw1tz.js";
21
+ } from "./index-90zyjrby.js";
22
22
 
23
23
  // pkgs/@akanjs/cli/cloud/cloud.script.ts
24
24
  import { Logger } from "akanjs/common";