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

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/.build-stamp CHANGED
@@ -1 +1 @@
1
- a0eaadfb9b58c3aa21817d33fb8bc0ad7889c719fb293715682a39a426dc01b9
1
+ 474303da5b9e0b19ac13326096bbf0527aa6a2ed71915d1eb65c33af084338b4
@@ -4,7 +4,7 @@ import {
4
4
  } from "./index-0wae5ebk.js";
5
5
  import {
6
6
  ApplicationScript
7
- } from "./index-nx1vbtsc.js";
7
+ } from "./index-r7nqv8qj.js";
8
8
  import {
9
9
  getMobileTargetChoices
10
10
  } from "./index-76rn3g2c.js";
@@ -4,7 +4,7 @@ import {
4
4
  CsrArtifactBuilder,
5
5
  SsrBaseArtifactBuilder,
6
6
  precompressArtifacts
7
- } from "./index-0fj4yg7k.js";
7
+ } from "./index-qb61h0k7.js";
8
8
  import"./index-xjd3rqzj.js";
9
9
  import"./index-j9cxndc0.js";
10
10
  import"./index-bjpxzr6s.js";
@@ -5,7 +5,7 @@ import {
5
5
  FontOptimizer,
6
6
  PagesBundleBuilder,
7
7
  SsrBaseArtifactBuilder
8
- } from "./index-0fj4yg7k.js";
8
+ } from "./index-qb61h0k7.js";
9
9
  import"./index-xjd3rqzj.js";
10
10
  import"./index-j9cxndc0.js";
11
11
  import"./index-bjpxzr6s.js";
@@ -11,6 +11,7 @@ import {
11
11
  formatAndroidReleaseSigningError,
12
12
  formatIosRunFailureMessage,
13
13
  getAdbDeviceStateIssues,
14
+ getAndroidLocalServerHost,
14
15
  getMissingAndroidReleaseSigningKeys,
15
16
  isPlaceholderAppId,
16
17
  materializeCapacitorConfig,
@@ -24,7 +25,7 @@ import {
24
25
  selectLocalDevHost,
25
26
  sortIosRunTargets,
26
27
  writeRootCapacitorConfig
27
- } from "./index-mterpj70.js";
28
+ } from "./index-hk9rbcsz.js";
28
29
  import"./index-76rn3g2c.js";
29
30
  import"./index-47qxw1tz.js";
30
31
  import"./index-mxvakhsm.js";
@@ -44,6 +45,7 @@ export {
44
45
  materializeCapacitorConfig,
45
46
  isPlaceholderAppId,
46
47
  getMissingAndroidReleaseSigningKeys,
48
+ getAndroidLocalServerHost,
47
49
  getAdbDeviceStateIssues,
48
50
  formatIosRunFailureMessage,
49
51
  formatAndroidReleaseSigningError,
@@ -1,13 +1,13 @@
1
1
  // @bun
2
2
  import {
3
3
  CloudScript
4
- } from "./index-mn96y1nk.js";
5
- import"./index-p6txr1ss.js";
4
+ } from "./index-zr2wvx69.js";
5
+ import"./index-zbx0jheb.js";
6
6
  import"./index-meh7mmz9.js";
7
7
  import {
8
8
  GlobalConfig
9
9
  } from "./index-0cj2zxbm.js";
10
- import"./index-nx1vbtsc.js";
10
+ import"./index-r7nqv8qj.js";
11
11
  import"./index-76rn3g2c.js";
12
12
  import"./index-exhpp31y.js";
13
13
  import {
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  ContextScript
4
- } from "./index-p69fcwe1.js";
4
+ } from "./index-vkqxw0ek.js";
5
5
  import"./index-6npsfpxr.js";
6
6
  import"./index-c94nq3c1.js";
7
7
  import"./index-pfrt7qwh.js";
@@ -9,7 +9,7 @@ import"./index-60a3jfcz.js";
9
9
  import"./index-hrwzz2wf.js";
10
10
  import"./index-wrrb8rxk.js";
11
11
  import"./index-ss469dec.js";
12
- import"./index-mterpj70.js";
12
+ import"./index-hk9rbcsz.js";
13
13
  import"./index-0cj2zxbm.js";
14
14
  import"./index-jn9vfpwz.js";
15
15
  import"./index-m4kpmmtj.js";
@@ -514,6 +514,10 @@ function getAdbDeviceStateIssues(output) {
514
514
  return [];
515
515
  });
516
516
  }
517
+ function getAndroidLocalServerHost(adbDevicesOutput, fallbackHost) {
518
+ const onlineDeviceIds = adbDevicesOutput?.split(/\r?\n/).map((line) => line.trim().split(/\s+/)).filter(([id, state]) => id && state === "device").map(([id]) => id) ?? [];
519
+ return onlineDeviceIds.length === 1 && onlineDeviceIds[0]?.startsWith("emulator-") ? "10.0.2.2" : fallbackHost;
520
+ }
517
521
  var ANDROID_MIN_SDK_VERSION = 26;
518
522
  function raiseGradleMinSdkVersion(content, floor = ANDROID_MIN_SDK_VERSION) {
519
523
  const match = content.match(/minSdkVersion\s*=\s*(\d+)/);
@@ -691,7 +695,7 @@ class CapacitorApp {
691
695
  async save() {
692
696
  await this.project.commit();
693
697
  }
694
- async#prepareIos({ operation, env, regenerate = false }) {
698
+ async#prepareIos({ operation, env, regenerate = false, iosRunTargetKind }) {
695
699
  await this.init({ platform: "ios", operation, env, regenerate });
696
700
  await this.#prepareTargetAssets();
697
701
  await this.#prepareExternalFiles("ios");
@@ -700,9 +704,10 @@ class CapacitorApp {
700
704
  await this.#applyDeepLinks("ios", { operation, env });
701
705
  await this.#flushIosEntitlements();
702
706
  await this.project.commit();
707
+ await this.#setCodeSignEntitlementsInIosIfExists("App/App.entitlements");
703
708
  await this.#generateAssets({ operation, env });
704
709
  this.app.verbose(`syncing iOS`);
705
- await this.#spawnMobile("npx", ["cap", "sync", "ios"], { operation, env });
710
+ await this.#spawnMobile("npx", ["cap", "sync", "ios"], { operation, env }, { iosRunTargetKind });
706
711
  this.app.verbose(`sync completed.`);
707
712
  }
708
713
  async buildIos({ env = "debug", regenerate = false } = {}) {
@@ -721,10 +726,10 @@ class CapacitorApp {
721
726
  async runIos({ operation, env, regenerate = false, noAllowProvisioningUpdates = false, iosDeviceId }) {
722
727
  if (operation === "release")
723
728
  await this.prepareWww();
724
- await this.#prepareIos({ operation, env, regenerate });
725
729
  const runTarget = await this.#selectIosRunTarget(iosDeviceId);
730
+ await this.#prepareIos({ operation, env, regenerate, iosRunTargetKind: runTarget.kind });
726
731
  if (runTarget.kind === "simulator") {
727
- await this.#spawnMobile("npx", ["cap", "run", "ios", "--target", runTarget.id], { operation, env }, { stdio: "inherit" });
732
+ await this.#spawnMobile("npx", ["cap", "run", "ios", "--target", runTarget.id, "--no-sync"], { operation, env }, { stdio: "inherit", platform: "ios", iosRunTargetKind: runTarget.kind });
728
733
  return;
729
734
  }
730
735
  await this.#runIosPhysicalDevice({ operation, env, runTarget, noAllowProvisioningUpdates });
@@ -800,7 +805,7 @@ ${textError instanceof Error ? textError.message : ""}`);
800
805
  noAllowProvisioningUpdates
801
806
  }) {
802
807
  const mobileEnv = sanitizeIosNativeRunEnv(await this.#commandEnv(operation, env));
803
- const configContent = await this.#writeCapacitorConfig({ operation }, mobileEnv);
808
+ const configContent = await this.#writeCapacitorConfig({ operation, platform: "ios", iosRunTargetKind: runTarget.kind }, mobileEnv);
804
809
  await this.#writeRootCapacitorConfig(configContent);
805
810
  const scheme = this.#iosScheme();
806
811
  const command = buildIosNativeRunCommand({
@@ -857,6 +862,7 @@ ${error.message}`;
857
862
  await this.#applyAndroidMinSdkVersion();
858
863
  await this.#applyPermissions({ operation, env });
859
864
  await this.#applyDeepLinks("android", { operation, env });
865
+ await this.#disableNativeKeyboardResizeInAndroid();
860
866
  await this.project.commit();
861
867
  await this.#generateAssets({ operation, env });
862
868
  await this.#ensureAndroidAssetsDir();
@@ -916,6 +922,23 @@ ${error.message}`;
916
922
  async openAndroid() {
917
923
  await this.#spawnMobile("npx", ["cap", "open", "android"], { operation: "local", env: "local" });
918
924
  }
925
+ async#disableNativeKeyboardResizeInAndroid() {
926
+ const manifestPath = path.join(this.app.cwdPath, this.androidRootPath, "app/src/main/AndroidManifest.xml");
927
+ let manifest = await readFile(manifestPath, "utf8");
928
+ let changed = false;
929
+ manifest = manifest.replace(/<activity\b[^>]*android:name="\.MainActivity"[^>]*>/, (activityTag) => {
930
+ if (activityTag.includes("android:windowSoftInputMode=")) {
931
+ const nextTag = activityTag.replace(/android:windowSoftInputMode="[^"]*"/, 'android:windowSoftInputMode="adjustNothing"');
932
+ changed ||= nextTag !== activityTag;
933
+ return nextTag;
934
+ }
935
+ changed = true;
936
+ return activityTag.replace(/>$/, `
937
+ android:windowSoftInputMode="adjustNothing">`);
938
+ });
939
+ if (changed)
940
+ await writeFile(manifestPath, manifest);
941
+ }
919
942
  async#ensureAndroidAssetsDir() {
920
943
  await mkdir(path.join(this.app.cwdPath, this.androidAssetsPath), { recursive: true });
921
944
  }
@@ -1005,12 +1028,16 @@ ${error.message}`;
1005
1028
  return html.replace(/<\/head\s*>/i, `${script}
1006
1029
  </head>`);
1007
1030
  }
1008
- async#writeCapacitorConfig({ operation }, commandEnv) {
1031
+ async#writeCapacitorConfig({
1032
+ operation,
1033
+ platform,
1034
+ iosRunTargetKind
1035
+ }, commandEnv) {
1009
1036
  await mkdir(this.targetRoot, { recursive: true });
1010
1037
  let localIp;
1011
1038
  if (operation === "local") {
1012
1039
  const override = commandEnv.AKAN_PUBLIC_CLIENT_HOST ?? process.env.AKAN_PUBLIC_CLIENT_HOST;
1013
- const resolution = selectLocalDevHost(os.networkInterfaces(), { override });
1040
+ const resolution = await this.#resolveLocalDevHost({ override, platform, iosRunTargetKind });
1014
1041
  localIp = resolution.host;
1015
1042
  this.#logDevHostResolution(resolution, commandEnv);
1016
1043
  }
@@ -1024,9 +1051,29 @@ ${error.message}`;
1024
1051
  await Bun.write(path.join(this.targetRoot, "capacitor.config.json"), content);
1025
1052
  return content;
1026
1053
  }
1054
+ async#resolveLocalDevHost({
1055
+ override,
1056
+ platform,
1057
+ iosRunTargetKind
1058
+ }) {
1059
+ const resolution = selectLocalDevHost(os.networkInterfaces(), { override });
1060
+ if (resolution.source === "override")
1061
+ return resolution;
1062
+ if (platform === "ios" && iosRunTargetKind === "simulator")
1063
+ return { ...resolution, host: "localhost", source: "platform" };
1064
+ if (platform === "android") {
1065
+ try {
1066
+ const host = getAndroidLocalServerHost(await this.#spawn("adb", ["devices"]), resolution.host);
1067
+ return host === resolution.host ? resolution : { ...resolution, host, source: "platform" };
1068
+ } catch {
1069
+ return resolution;
1070
+ }
1071
+ }
1072
+ return resolution;
1073
+ }
1027
1074
  #logDevHostResolution(resolution, commandEnv) {
1028
1075
  this.app.log(`Mobile live-reload server: ${this.#localCsrUrl(resolution.host, commandEnv)}`);
1029
- if (resolution.source === "override")
1076
+ if (resolution.source === "override" || resolution.source === "platform")
1030
1077
  return;
1031
1078
  const suspicious = resolution.host === "127.0.0.1" || resolution.host.startsWith("169.254.");
1032
1079
  const alternatives = resolution.candidates.filter((candidate) => candidate.address !== resolution.host);
@@ -1226,18 +1273,26 @@ ${error.message}`;
1226
1273
  return await this.app.spawn(command, args, { cwd: this.app.cwdPath, ...options });
1227
1274
  }
1228
1275
  async#spawnMobile(command, args = [], { operation, env }, options = {}) {
1276
+ const { iosRunTargetKind, platform, ...spawnOptions } = options;
1229
1277
  const mobileEnv = { ...await this.#commandEnv(operation, env), ...options.env };
1230
- const configContent = await this.#writeCapacitorConfig({ operation }, mobileEnv);
1278
+ const configContent = await this.#writeCapacitorConfig({ operation, platform: platform ?? this.#inferMobilePlatform(args), iosRunTargetKind }, mobileEnv);
1231
1279
  await this.#writeRootCapacitorConfig(configContent);
1232
1280
  try {
1233
1281
  return await this.#spawn(command, args, {
1234
- ...options,
1282
+ ...spawnOptions,
1235
1283
  env: mobileEnv
1236
1284
  });
1237
1285
  } finally {
1238
1286
  await this.#clearRootCapacitorConfigs();
1239
1287
  }
1240
1288
  }
1289
+ #inferMobilePlatform(args) {
1290
+ if (args.includes("android"))
1291
+ return "android";
1292
+ if (args.includes("ios"))
1293
+ return "ios";
1294
+ return;
1295
+ }
1241
1296
  async addCamera() {
1242
1297
  await this.#setPermissionInIos({
1243
1298
  cameraUsageDescription: "$(PRODUCT_NAME) requires access to the camera to take photos.",
@@ -1339,17 +1394,31 @@ ${error.message}`;
1339
1394
  while (end < lines.length && !/^\s*\};\s*$/.test(lines[end] ?? ""))
1340
1395
  end++;
1341
1396
  const settings = lines.slice(start, end + 1);
1342
- if (settings.some((setting) => setting.includes("CODE_SIGN_ENTITLEMENTS")))
1343
- continue;
1397
+ const configName = lines.slice(end + 1, end + 8).find((setting) => setting.includes("name = ")) ?? "";
1344
1398
  const indent = line.match(/^\s*/)?.[0] ?? "";
1345
- lines.splice(index, 0, `${indent}CODE_SIGN_ENTITLEMENTS = ${entitlementsRelPath};`);
1346
- index++;
1347
- changed = true;
1399
+ const insertSettings = [];
1400
+ if (!settings.some((setting) => setting.includes("CODE_SIGN_ENTITLEMENTS"))) {
1401
+ insertSettings.push(`${indent}CODE_SIGN_ENTITLEMENTS = ${entitlementsRelPath};`);
1402
+ }
1403
+ if (configName.includes("name = Debug;") && !settings.some((setting) => setting.includes("CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION"))) {
1404
+ insertSettings.push(`${indent}CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION = YES;`);
1405
+ }
1406
+ if (insertSettings.length > 0) {
1407
+ lines.splice(index, 0, ...insertSettings);
1408
+ index += insertSettings.length;
1409
+ changed = true;
1410
+ }
1348
1411
  }
1349
1412
  if (changed)
1350
1413
  await writeFile(pbxprojPath, lines.join(`
1351
1414
  `));
1352
1415
  }
1416
+ async#setCodeSignEntitlementsInIosIfExists(entitlementsRelPath) {
1417
+ const entitlementsPath = path.join(this.app.cwdPath, this.iosProjectPath, entitlementsRelPath);
1418
+ if (!await Bun.file(entitlementsPath).exists())
1419
+ return;
1420
+ await this.#setCodeSignEntitlementsInIos(entitlementsRelPath);
1421
+ }
1353
1422
  async#setUrlSchemesInAndroid(schemes) {
1354
1423
  const manifestPath = path.join(this.app.cwdPath, this.androidRootPath, "app/src/main/AndroidManifest.xml");
1355
1424
  let manifest = await readFile(manifestPath, "utf8");
@@ -1444,4 +1513,4 @@ ${filter}$1`);
1444
1513
  }
1445
1514
  }
1446
1515
 
1447
- export { SWIFTUICORE_MIN_IOS_MAJOR, rootCapacitorConfigFilenames, rootCapacitorConfigPaths, clearRootCapacitorConfigs, writeRootCapacitorConfig, selectLocalDevHost, parseIosRuntimeMajor, sortIosRunTargets, parseDevicectlDevices, parseSimctlDevices, buildIosNativeRunCommand, classifyIosRunFailure, formatIosRunFailureMessage, sanitizeIosNativeRunEnv, PLACEHOLDER_APP_IDS, isPlaceholderAppId, getMissingAndroidReleaseSigningKeys, formatAndroidReleaseSigningError, getAdbDeviceStateIssues, ANDROID_MIN_SDK_VERSION, raiseGradleMinSdkVersion, assertJsonSerializable, materializeCapacitorConfig, CapacitorApp };
1516
+ export { SWIFTUICORE_MIN_IOS_MAJOR, rootCapacitorConfigFilenames, rootCapacitorConfigPaths, clearRootCapacitorConfigs, writeRootCapacitorConfig, selectLocalDevHost, parseIosRuntimeMajor, sortIosRunTargets, parseDevicectlDevices, parseSimctlDevices, buildIosNativeRunCommand, classifyIosRunFailure, formatIosRunFailureMessage, sanitizeIosNativeRunEnv, PLACEHOLDER_APP_IDS, isPlaceholderAppId, getMissingAndroidReleaseSigningKeys, formatAndroidReleaseSigningError, getAdbDeviceStateIssues, getAndroidLocalServerHost, ANDROID_MIN_SDK_VERSION, raiseGradleMinSdkVersion, assertJsonSerializable, materializeCapacitorConfig, CapacitorApp };
@@ -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;
@@ -1898,9 +1898,9 @@ function openBrowser(url) {
1898
1898
  }
1899
1899
 
1900
1900
  // pkgs/@akanjs/cli/application/application.runner.ts
1901
- var loadBuildRunner = async () => (await import("./applicationBuildRunner-n1svrwgt.js")).ApplicationBuildRunner;
1901
+ var loadBuildRunner = async () => (await import("./applicationBuildRunner-ha3y8ew8.js")).ApplicationBuildRunner;
1902
1902
  var loadReleasePackager = async () => (await import("./applicationReleasePackager-gg8ccbeh.js")).ApplicationReleasePackager;
1903
- var loadCapacitorApp = async () => (await import("./capacitorApp-yyw35vg4.js")).CapacitorApp;
1903
+ var loadCapacitorApp = async () => (await import("./capacitorApp-jaksw4cy.js")).CapacitorApp;
1904
1904
  var loadAbstractCompactor = async () => (await import("./abstractCompactor-e5yq60qz.js")).AbstractCompactor;
1905
1905
  var loadPrompts = async () => await import("@inquirer/prompts");
1906
1906
 
@@ -16,7 +16,7 @@ import {
16
16
  } from "./index-wrrb8rxk.js";
17
17
  import {
18
18
  isPlaceholderAppId
19
- } from "./index-mterpj70.js";
19
+ } from "./index-hk9rbcsz.js";
20
20
  import {
21
21
  AkanContextAnalyzer,
22
22
  akanMcpInstallConfigPaths,
@@ -7,7 +7,7 @@ import {
7
7
  } from "./index-0cj2zxbm.js";
8
8
  import {
9
9
  openBrowser
10
- } from "./index-nx1vbtsc.js";
10
+ } from "./index-r7nqv8qj.js";
11
11
  import {
12
12
  runner
13
13
  } from "./index-jkynkyfk.js";
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  CloudRunner
4
- } from "./index-p6txr1ss.js";
4
+ } from "./index-zbx0jheb.js";
5
5
  import {
6
6
  PackageScript
7
7
  } from "./index-meh7mmz9.js";
@@ -12,7 +12,7 @@ import {
12
12
  } from "./index-0cj2zxbm.js";
13
13
  import {
14
14
  ApplicationScript
15
- } from "./index-nx1vbtsc.js";
15
+ } from "./index-r7nqv8qj.js";
16
16
  import {
17
17
  script
18
18
  } from "./index-jkynkyfk.js";
package/index.js CHANGED
@@ -17,16 +17,16 @@ import path from "path";
17
17
 
18
18
  // pkgs/@akanjs/cli/commandModules.ts
19
19
  var commandModules = {
20
- workspace: async () => (await import("./workspace.command-3q1d6kk4.js")).WorkspaceCommand,
20
+ workspace: async () => (await import("./workspace.command-3ydyy991.js")).WorkspaceCommand,
21
21
  agent: async () => (await import("./agent.command-b0fcx3r2.js")).AgentCommand,
22
- application: async () => (await import("./application.command-cgmy00gd.js")).ApplicationCommand,
22
+ application: async () => (await import("./application.command-cezdnvtq.js")).ApplicationCommand,
23
23
  library: async () => (await import("./library.command-aa1g8nsa.js")).LibraryCommand,
24
- localRegistry: async () => (await import("./localRegistry.command-kshva1ck.js")).LocalRegistryCommand,
24
+ localRegistry: async () => (await import("./localRegistry.command-jha3sn50.js")).LocalRegistryCommand,
25
25
  package: async () => (await import("./package.command-9qv6vr9s.js")).PackageCommand,
26
26
  module: async () => (await import("./module.command-b1zzgqde.js")).ModuleCommand,
27
27
  page: async () => (await import("./page.command-k16pw244.js")).PageCommand,
28
- context: async () => (await import("./context.command-b5heat2a.js")).ContextCommand,
29
- cloud: async () => (await import("./cloud.command-1m7hfdpk.js")).CloudCommand,
28
+ context: async () => (await import("./context.command-ajn1b683.js")).ContextCommand,
29
+ cloud: async () => (await import("./cloud.command-6xvsd487.js")).CloudCommand,
30
30
  guideline: async () => (await import("./guideline.command-9ew6aqhm.js")).GuidelineCommand,
31
31
  scalar: async () => (await import("./scalar.command-hs3nwzv1.js")).ScalarCommand,
32
32
  primitive: async () => (await import("./primitive.command-s60dn1ae.js")).PrimitiveCommand,
@@ -2,14 +2,14 @@
2
2
  import {
3
3
  CloudRunner,
4
4
  getNpmRegistryUrl
5
- } from "./index-p6txr1ss.js";
5
+ } from "./index-zbx0jheb.js";
6
6
  import {
7
7
  PackageScript
8
8
  } from "./index-meh7mmz9.js";
9
9
  import"./index-0cj2zxbm.js";
10
10
  import {
11
11
  ApplicationScript
12
- } from "./index-nx1vbtsc.js";
12
+ } from "./index-r7nqv8qj.js";
13
13
  import"./index-76rn3g2c.js";
14
14
  import"./index-exhpp31y.js";
15
15
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/cli",
3
- "version": "3.0.0-alpha.5",
3
+ "version": "3.0.0-alpha.6",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -34,7 +34,7 @@
34
34
  "@langchain/openai": "^1.4.6",
35
35
  "@tailwindcss/node": "^4.3.0",
36
36
  "@trapezedev/project": "^7.1.4",
37
- "akanjs": "3.0.0-alpha.5",
37
+ "akanjs": "3.0.0-alpha.6",
38
38
  "chalk": "^5.6.2",
39
39
  "commander": "^14.0.3",
40
40
  "dayjs": "^1.11.20",
@@ -262,7 +262,8 @@ final fallback when no CLI command covers the change.
262
262
  | Use `console.log()` | Biome lint forbids `console.log`. Only `console.error`, `console.info`, `console.warn` are allowed. | Use one of the three allowed console methods, or `this.logger.*` / `new Logger("ClassName")` on the server |
263
263
  | Write a `//!` marker in `ui/`, `webkit/`, `common/`, `page/**/*.tsx`, `*.constant.ts`, `*.store.ts`, or a module component file | `no-bang-comment-in-client.grit` bans it. Bun classifies `//!` and `/*!` as legal comments and keeps them through minification, so the note ships to every visitor. | `// FIXME:` in browser-reachable code; `//!` stays legal in server, `srvkit/`, and CLI files |
264
264
  | `throw new Error("...")` | `no-throw-raw-error.grit` bans raw errors outside tests, `*.constant.ts`, and `common/`. Raw errors carry no dictionary key, so they cannot be localized or toasted. | `throw new Err("task.error.<key>")` plus an `[en, ko]` entry in the module dictionary's `.error({})` |
265
- | Hand-order Tailwind classes, or reorder them to "fix" a diff | `useSortedClasses` is an error and also sorts the string arguments to `clsx()` and `cva()`. Sorter output like `font-bold text-2xl` looks wrong but is correct. | Write classes in any order and let `akan lint` sort them |
265
+ | Hand-order Tailwind classes, or reorder them to "fix" a diff | `useSortedClasses` is an error and also sorts the string arguments to `cn()`. Sorter output like `font-bold text-2xl` looks wrong but is correct. | Write classes in any order and let `akan lint` sort them |
266
+ | A colour outside the semantic vocabulary — `bg-red-500`, `bg-[#3b82f6]`, `btn-primary`, `text-base-content`, `style={{ color: "#fff" }}` | The theme closes the vocabulary, so these produce **no CSS at all** — the element renders unstyled, and without the lint rules there is no error and no warning either. Raw-palette, arbitrary-colour, daisyUI-legacy and inline-colour rules all catch this. | Semantic tokens: `bg-primary`, `text-foreground/70`, `border-border`. A genuinely fixed colour takes a `// biome-ignore lint/plugin: <reason>` |
266
267
  | `import` a third-party package inside a page, a barrel, or a module file | `no-import-external-library.grit` covers `page/**`, all barrels, and every `*.{constant,dictionary,document,service,signal,store}.ts` and `*.{Template,Unit,Util,View,Zone}.tsx`. | Re-export the symbol from a one-line shim in `base/`, `webkit/`, or `ui/` first, then import that |
267
268
  | Import server APIs (`fs`, `Bun`, `process.env`) in `ui/`, `webkit/`, or `common/` | Server-only imports in client code cause build failures. | Keep server dependencies in `lib/`, `srvkit/`, or `private/` only |
268
269
  | Skip running `akan sync` after deleting a file | Deleted files remain referenced in barrel exports, causing import errors everywhere. | Run `akan sync <name>` after every file add, remove, or rename |
@@ -421,11 +422,14 @@ is convention that keeps hand-written code reading like generated code.
421
422
  `.Template.tsx` files contain zero `useState`: forms are store-driven with `Field.*`, `value={taskForm.x}`, and
422
423
  `onChange={st.do.setXOnTask}` passed by reference.
423
424
  - Read with `st.use.*` and write with `st.do.*`. Client components do not call `fetch.*`.
424
- - Static class strings stay plain strings. Use `clsx` only for a conditional or to merge an incoming `className`,
425
- and merge the caller last: `clsx("base", conditional, className)`. `clsx` comes from `akanjs/client`. No
426
- `twMerge`, no `cn()`.
427
- - Use daisyUI semantic tokens with opacity modifiers (`text-base-content/60`, `bg-base-100/70`). Never `dark:`
428
- theming is the daisyUI theme block in `page/*/styles.css`.
425
+ - Reach for a recipe before writing a look by hand: `buttonRecipe`, `badgeRecipe` and `inputRecipe` from
426
+ `akanjs/ui` carry the button, badge and field surfaces. Call them as `buttonRecipe(variants?, className?)` —
427
+ the second argument merges internally and takes an array, so never wrap it in `cn()`.
428
+ - Static class strings stay plain strings. Reach for `cn` only for a conditional or to merge an incoming
429
+ `className`, and merge the caller last: `cn("base", conditional, className)`. `cn` comes from `akanjs/client`
430
+ and is the only class-combining function — no `clsx`, no raw `twMerge`.
431
+ - Use semantic tokens with opacity modifiers (`text-foreground/60`, `bg-background/70`, `border-border`). Never
432
+ `dark:` — theming is the token block in `page/*/styles.css`, where each token has a `-foreground` pair.
429
433
  - Hoist enum→class lookups to a module-scope `as const` map typed `{ [key in cnst.TaskStatus["value"]]: string }`,
430
434
  not `Record<...>`.
431
435
 
@@ -232,6 +232,25 @@
232
232
  "**/*.Zone.tsx"
233
233
  ],
234
234
  "plugins": ["./node_modules/@akanjs/devkit/lint/no-import-external-library.grit"]
235
+ },
236
+ {
237
+ "includes": [
238
+ "apps/**/*.ts",
239
+ "apps/**/*.tsx",
240
+ "libs/**/*.ts",
241
+ "libs/**/*.tsx",
242
+ "!**/*.test.ts",
243
+ "!**/*.test.tsx",
244
+ "!**/*.spec.ts",
245
+ "!**/*.spec.tsx"
246
+ ],
247
+ "plugins": [
248
+ "./node_modules/@akanjs/devkit/lint/no-raw-palette-class.grit",
249
+ "./node_modules/@akanjs/devkit/lint/no-arbitrary-color.grit",
250
+ "./node_modules/@akanjs/devkit/lint/no-daisyui-legacy-class.grit",
251
+ "./node_modules/@akanjs/devkit/lint/no-inline-color.grit",
252
+ "./node_modules/@akanjs/devkit/lint/no-interpolated-arbitrary-class.grit"
253
+ ]
235
254
  }
236
255
  ]
237
256
  }
@@ -49,8 +49,11 @@ See `docs/GENERATED.md` for the generated file list.
49
49
 
50
50
  `AGENTS.md` holds the full style guide. The rules agents break most often:
51
51
 
52
- - **Never hand-order Tailwind classes** — the linter sorts them, including inside `clsx()`, and its output looks
52
+ - **Never hand-order Tailwind classes** — the linter sorts them, including inside `cn()`, and its output looks
53
53
  unnatural on purpose.
54
+ - **Never use a colour outside the semantic vocabulary** — `bg-red-500`, `bg-[#3b82f6]` and `text-base-content`
55
+ compile to no CSS at all, so the element renders unstyled with no error. Use `bg-primary`, `text-foreground/70`
56
+ and the other tokens defined in `page/*/styles.css`.
54
57
  - **Never `throw new Error`** — throw `new Err("<module>.error.<key>")` with an `[en, ko]` entry in the module
55
58
  dictionary. `common/` cannot import `Err`, so keep throwing code out of it.
56
59
  - **Never import a third-party package** from a page, a barrel, or a module file. Re-export it through a one-line
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  ContextScript
4
- } from "./index-p69fcwe1.js";
4
+ } from "./index-vkqxw0ek.js";
5
5
  import"./index-6npsfpxr.js";
6
6
  import"./index-c94nq3c1.js";
7
7
  import"./index-pfrt7qwh.js";
@@ -9,17 +9,17 @@ import"./index-60a3jfcz.js";
9
9
  import"./index-hrwzz2wf.js";
10
10
  import {
11
11
  CloudScript
12
- } from "./index-mn96y1nk.js";
12
+ } from "./index-zr2wvx69.js";
13
13
  import {
14
14
  getLatestPackageVersion,
15
15
  getNpmRegistryUrl
16
- } from "./index-p6txr1ss.js";
16
+ } from "./index-zbx0jheb.js";
17
17
  import {
18
18
  PackageScript
19
19
  } from "./index-meh7mmz9.js";
20
20
  import"./index-wrrb8rxk.js";
21
21
  import"./index-ss469dec.js";
22
- import"./index-mterpj70.js";
22
+ import"./index-hk9rbcsz.js";
23
23
  import {
24
24
  GlobalConfig
25
25
  } from "./index-0cj2zxbm.js";
@@ -31,7 +31,7 @@ import"./index-m4kpmmtj.js";
31
31
  import"./index-qp089jp0.js";
32
32
  import {
33
33
  ApplicationScript
34
- } from "./index-nx1vbtsc.js";
34
+ } from "./index-r7nqv8qj.js";
35
35
  import"./index-76rn3g2c.js";
36
36
  import {
37
37
  LibraryScript