@akanjs/cli 3.0.0-alpha.1 → 3.0.0-alpha.100

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 (162) 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-byytxs22.js} +3 -5
  5. package/{agent.command-yv00p0s0.js → agent.command-v1a4746v.js} +11 -12
  6. package/{application.command-0qsczf4k.js → application.command-fzdcnzhg.js} +65 -37
  7. package/{applicationBuildRunner-ct2pxyvw.js → applicationBuildRunner-s4gsk728.js} +28 -24
  8. package/{applicationReleasePackager-gg8ccbeh.js → applicationReleasePackager-28v9vbzk.js} +8 -9
  9. package/buildBatch.proc.js +12 -13
  10. package/capacitorApp-8d6d4zkm.js +12 -0
  11. package/{cloud.command-dxfv18sa.js → cloud.command-wygy2z05.js} +15 -17
  12. package/commandManifest.json +1 -1
  13. package/{context.command-8d9e3k6b.js → context.command-4vdt1aqq.js} +19 -22
  14. package/dependencyScanner-shs82ynp.js +8 -0
  15. package/devTui-qnf1txdz.js +553 -0
  16. package/devTuiView-yzct2cq6.js +9 -0
  17. package/getDirname-dr3jz1xg.js +7 -0
  18. package/{guideline.command-3jbkxzh6.js → guideline.command-p92dq6ts.js} +16 -17
  19. package/guidelines/agentRule/agentRule.generate.json +31 -0
  20. package/guidelines/agentRule/agentRule.instruction.md +533 -0
  21. package/guidelines/conventions/conventions.generate.json +25 -0
  22. package/guidelines/conventions/conventions.instruction.md +908 -0
  23. package/guidelines/cssRule/cssRule.instruction.md +7 -0
  24. package/guidelines/framework/framework.generate.json +3 -2
  25. package/guidelines/framework/framework.instruction.md +1 -9
  26. package/guidelines/mcpRule/mcpRule.generate.json +25 -0
  27. package/guidelines/mcpRule/mcpRule.instruction.md +217 -0
  28. package/guidelines/modelStore/modelStore.instruction.md +12 -0
  29. package/guidelines/modelTemplate/modelTemplate.instruction.md +5 -0
  30. package/guidelines/queryRule/queryRule.generate.json +31 -0
  31. package/guidelines/queryRule/queryRule.instruction.md +175 -0
  32. package/guidelines/runtimeRule/runtimeRule.generate.json +25 -0
  33. package/guidelines/runtimeRule/runtimeRule.instruction.md +220 -0
  34. package/guidelines/ssrRule/ssrRule.instruction.md +49 -5
  35. package/guidelines/transportRule/transportRule.generate.json +31 -0
  36. package/guidelines/transportRule/transportRule.instruction.md +60 -0
  37. package/guidelines/workspaceOnboarding/workspaceOnboarding.generate.json +24 -0
  38. package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +263 -0
  39. package/guidelines/workspaceRecipes/workspaceRecipes.generate.json +33 -0
  40. package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +402 -0
  41. package/incrementalBuilder.proc.js +26 -24
  42. package/{index-mr5s7mqd.js → index-0amdxycf.js} +8 -8
  43. package/{index-t3723f99.js → index-1yxtbmbj.js} +111 -42
  44. package/{index-ej426mcr.js → index-29sxqfaw.js} +65 -38
  45. package/index-3d9y066c.js +76 -0
  46. package/{index-76rn3g2c.js → index-47pj3aee.js} +4 -4
  47. package/index-5svjba73.js +159 -0
  48. package/index-6wwzs1bc.js +7 -0
  49. package/{index-k95xkh4p.js → index-7d6vny25.js} +16 -12
  50. package/{index-ec09pws3.js → index-8p8p0cf8.js} +14 -14
  51. package/{index-wnp7hwq7.js → index-92zztqy1.js} +7 -7
  52. package/{index-fgc8r6dj.js → index-a5vmm6x4.js} +3 -1
  53. package/{index-xys926f2.js → index-by5gpp8x.js} +4 -4
  54. package/{index-0cj2zxbm.js → index-c69j78kk.js} +12 -11
  55. package/{index-rzwcpcc3.js → index-fc1bh0a7.js} +4 -4
  56. package/{index-7c6k4yvg.js → index-gq44bqjt.js} +58 -25
  57. package/{index-a00jg8x8.js → index-gtmz74tj.js} +79 -55
  58. package/{index-tdbas9r0.js → index-h7yw2z9n.js} +7 -7
  59. package/{index-bjpxzr6s.js → index-hwkygn9s.js} +51 -3
  60. package/{index-bbkek1bp.js → index-hyywke0f.js} +1 -1
  61. package/{index-46tjzh6s.js → index-kcxmv48c.js} +29 -29
  62. package/{index-xjd3rqzj.js → index-n68jx79w.js} +6 -15
  63. package/{index-pyt2b4nw.js → index-p5neav9b.js} +1129 -1356
  64. package/{index-67546d0j.js → index-q0y2jq9j.js} +14 -2
  65. package/{index-bekddz9q.js → index-qkk27bk6.js} +32 -32
  66. package/{index-hgna5rjy.js → index-qsq3q8fy.js} +1034 -257
  67. package/{index-2q1f5j9k.js → index-srdssqj7.js} +18 -18
  68. package/{index-rh16j3c7.js → index-w8kmx1b6.js} +23 -13
  69. package/{index-nm6wghb6.js → index-yks023mb.js} +13 -13
  70. package/{index-1pvex4az.js → index-yza7zkpt.js} +94 -61
  71. package/{index-yzmhasdb.js → index-zmjppp25.js} +57 -57
  72. package/{index-8hsbnyes.js → index-znynzt5w.js} +100 -30
  73. package/{index-zasydjxd.js → index-zxgr3s8s.js} +31 -30
  74. package/{index-e16gnn26.js → index-zz5jyd94.js} +1002 -128
  75. package/index.js +22 -24
  76. package/{library.command-ebdj1vnm.js → library.command-s4fszngs.js} +11 -9
  77. package/{localRegistry.command-gb178ne8.js → localRegistry.command-gyw4da9z.js} +20 -22
  78. package/{module.command-9c1bnsx4.js → module.command-6n7dwvav.js} +13 -15
  79. package/{package.command-t93p4jkj.js → package.command-gr82ypnr.js} +8 -9
  80. package/package.json +5 -7
  81. package/{page.command-k3k0meh2.js → page.command-7gvgb3jb.js} +7 -8
  82. package/{primitive.command-de6nktdt.js → primitive.command-tk5ckw38.js} +13 -15
  83. package/{quality.command-z5kth2wa.js → quality.command-38v81baf.js} +229 -138
  84. package/recipeScanner-fpagtgc1.js +11 -0
  85. package/{repair.command-ehf2g112.js → repair.command-5xbwvsxp.js} +12 -13
  86. package/{routeSourceValidator-cncd92fv.js → routeSourceValidator-ze6x894c.js} +2 -40
  87. package/{scalar.command-7a2jtv33.js → scalar.command-0epwcr5k.js} +11 -13
  88. package/subspace.command-e2t36xb2.js +755 -0
  89. package/templates/app/env/env.client.debug.ts.template +1 -5
  90. package/templates/app/env/env.client.develop.ts.template +1 -5
  91. package/templates/app/env/env.client.local.ts.template +1 -5
  92. package/templates/app/env/env.client.main.ts.template +1 -5
  93. package/templates/app/env/env.client.testing.ts.template +1 -5
  94. package/templates/app/env/env.client.type.ts +2 -6
  95. package/templates/app/env/env.server.debug.ts.template +1 -5
  96. package/templates/app/env/env.server.develop.ts.template +1 -5
  97. package/templates/app/env/env.server.local.ts.template +1 -5
  98. package/templates/app/env/env.server.main.ts.template +1 -5
  99. package/templates/app/main.ts +3 -1
  100. package/templates/app/page/styles.css.template +3 -3
  101. package/templates/appSample/lib/task/task.signal.ts +11 -6
  102. package/templates/appSample/srvkit/AuthGuard.ts +9 -5
  103. package/templates/appSample/srvkit/SessionInternalArg.ts +4 -3
  104. package/templates/crudPages/[__model__Id]/edit/page.tsx +6 -6
  105. package/templates/crudPages/[__model__Id]/page.tsx +10 -13
  106. package/templates/crudPages/new/page.tsx +6 -6
  107. package/templates/crudPages/page.tsx +9 -9
  108. package/templates/crudSinglePage/page.tsx +11 -7
  109. package/templates/env/_env.server.type.ts +7 -5
  110. package/templates/lib/db.ts +4 -2
  111. package/templates/lib/dict.ts +1 -1
  112. package/templates/lib/sig.ts +5 -1
  113. package/templates/lib/srv.ts +6 -2
  114. package/templates/lib/useClient.ts +9 -3
  115. package/templates/module/__Model__.Util.tsx +1 -1
  116. package/templates/module/__Model__.Zone.tsx +3 -5
  117. package/templates/module/__model__.dictionary.ts +4 -1
  118. package/templates/module/__model__.signal.ts +2 -3
  119. package/templates/module/__model__.store.ts +1 -2
  120. package/templates/workspaceRoot/.gitignore.template +0 -1
  121. package/templates/workspaceRoot/.husky/pre-commit.template +16 -0
  122. package/templates/workspaceRoot/.vscode/settings.json.template +1 -1
  123. package/templates/workspaceRoot/AGENTS.md.template +4 -948
  124. package/templates/workspaceRoot/CLAUDE.md.template +19 -0
  125. package/templates/workspaceRoot/biome.json.template +4 -198
  126. package/templates/workspaceRoot/docs/AI-DEVELOPMENT.md.template +15 -16
  127. package/templates/workspaceRoot/docs/GENERATED.md.template +12 -10
  128. package/templates/workspaceRoot/package.json.template +2 -4
  129. package/typeChecker-3xgrzc2v.js +7 -0
  130. package/typecheck.proc.js +4 -5
  131. package/{workflow.command-e0tt0vk2.js → workflow.command-kphbr7eh.js} +21 -23
  132. package/{workspace.command-fpda0evj.js → workspace.command-j1gatz3d.js} +108 -91
  133. package/capacitorApp-4r9sm3qg.js +0 -58
  134. package/dependencyScanner-f6grrep3.js +0 -9
  135. package/index-mxvakhsm.js +0 -66
  136. package/index-r24hmh0q.js +0 -4
  137. package/recipeScanner-tghkzrrt.js +0 -12
  138. package/templates/libRoot/base/baseLogic.ts +0 -14
  139. package/templates/libRoot/base/index.ts +0 -8
  140. package/templates/workspaceRoot/.cursor/rules/akan-page-routing.mdc.template +0 -43
  141. package/templates/workspaceRoot/.cursor/rules/akan-sync-conventions.mdc.template +0 -33
  142. package/templates/workspaceRoot/.cursor/rules/application-test-commands.mdc.template +0 -16
  143. package/templates/workspaceRoot/.cursor/rules/change-scope.mdc.template +0 -12
  144. package/templates/workspaceRoot/.cursor/rules/client-server-boundaries.mdc.template +0 -18
  145. package/templates/workspaceRoot/.cursor/rules/coding-comments.mdc.template +0 -25
  146. package/templates/workspaceRoot/.cursor/rules/coding-style.mdc.template +0 -44
  147. package/templates/workspaceRoot/.cursor/rules/domain-module-conventions.mdc.template +0 -17
  148. package/templates/workspaceRoot/.cursor/rules/format-lint.mdc.template +0 -12
  149. package/templates/workspaceRoot/.cursor/rules/lint-enforced-rules.mdc.template +0 -18
  150. package/templates/workspaceRoot/.cursor/rules/module-file-playbook.mdc.template +0 -61
  151. package/templates/workspaceRoot/.cursor/rules/naming-conventions.mdc.template +0 -15
  152. package/templates/workspaceRoot/.cursor/rules/react-component-style.mdc.template +0 -21
  153. package/templates/workspaceRoot/.cursor/rules/repo-overview.mdc.template +0 -17
  154. package/templates/workspaceRoot/.cursor/rules/scalar-modeling.mdc.template +0 -58
  155. package/templates/workspaceRoot/.cursor/rules/secrets-and-env.mdc.template +0 -13
  156. package/templates/workspaceRoot/.cursor/rules/service-signal-conventions.mdc.template +0 -83
  157. package/templates/workspaceRoot/.cursor/rules/srvkit-adapters-and-guards.mdc.template +0 -50
  158. package/templates/workspaceRoot/.cursor/rules/ssr-first.mdc.template +0 -59
  159. package/templates/workspaceRoot/.cursor/rules/typescript-imports.mdc.template +0 -16
  160. package/typeChecker-kravn7ns.js +0 -8
  161. /package/{index-0wae5ebk.js → index-meh558jy.js} +0 -0
  162. /package/{index-ss469dec.js → index-nz8prx9g.js} +0 -0
@@ -1,11 +1,11 @@
1
1
  // @bun
2
+ import {
3
+ CommandExecutionError
4
+ } from "./index-qsq3q8fy.js";
2
5
  import {
3
6
  resolveMobilePath,
4
7
  targetHtmlFilename
5
- } from "./index-76rn3g2c.js";
6
- import {
7
- CommandExecutionError
8
- } from "./index-hgna5rjy.js";
8
+ } from "./index-47pj3aee.js";
9
9
 
10
10
  // pkgs/@akanjs/devkit/capacitorApp.ts
11
11
  import { cp, mkdir, readFile, rm, writeFile } from "fs/promises";
@@ -17,22 +17,22 @@ import { capitalize } from "akanjs/common";
17
17
 
18
18
  // pkgs/@akanjs/devkit/fileEditor.ts
19
19
  class FileEditor {
20
- filePath;
21
- content;
20
+ #filePath;
21
+ #content;
22
22
  constructor(filePath, content) {
23
- this.filePath = filePath;
24
- this.content = content;
23
+ this.#filePath = filePath;
24
+ this.#content = content;
25
25
  }
26
26
  static async create(filePath) {
27
27
  try {
28
28
  const content = await Bun.file(filePath).text();
29
29
  return new FileEditor(filePath, content);
30
- } catch (error) {
30
+ } catch (_error) {
31
31
  throw new Error(`Failed to read file: ${filePath}`);
32
32
  }
33
33
  }
34
34
  find(pattern) {
35
- const lines = this.content.split(`
35
+ const lines = this.#content.split(`
36
36
  `);
37
37
  const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
38
38
  for (let i = 0;i < lines.length; i++) {
@@ -45,7 +45,7 @@ class FileEditor {
45
45
  return -1;
46
46
  }
47
47
  findAll(pattern) {
48
- const lines = this.content.split(`
48
+ const lines = this.#content.split(`
49
49
  `);
50
50
  const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
51
51
  const matches = [];
@@ -63,10 +63,10 @@ class FileEditor {
63
63
  if (lineIndex === -1) {
64
64
  throw new Error(`Pattern not found: ${pattern}`);
65
65
  }
66
- const lines = this.content.split(`
66
+ const lines = this.#content.split(`
67
67
  `);
68
68
  lines.splice(lineIndex + 1, 0, data);
69
- this.content = lines.join(`
69
+ this.#content = lines.join(`
70
70
  `);
71
71
  return this;
72
72
  }
@@ -75,40 +75,40 @@ class FileEditor {
75
75
  if (lineIndex === -1) {
76
76
  throw new Error(`Pattern not found: ${pattern}`);
77
77
  }
78
- const lines = this.content.split(`
78
+ const lines = this.#content.split(`
79
79
  `);
80
80
  lines.splice(lineIndex, 0, data);
81
- this.content = lines.join(`
81
+ this.#content = lines.join(`
82
82
  `);
83
83
  return this;
84
84
  }
85
85
  replace(pattern, replacement) {
86
86
  const regex = typeof pattern === "string" ? new RegExp(pattern, "g") : pattern;
87
- this.content = this.content.replace(regex, replacement);
87
+ this.#content = this.#content.replace(regex, replacement);
88
88
  return this;
89
89
  }
90
90
  append(data) {
91
- this.content += `
91
+ this.#content += `
92
92
  ${data}`;
93
93
  return this;
94
94
  }
95
95
  prepend(data) {
96
- this.content = `${data}
97
- ${this.content}`;
96
+ this.#content = `${data}
97
+ ${this.#content}`;
98
98
  return this;
99
99
  }
100
100
  async save() {
101
101
  try {
102
- await Bun.write(this.filePath, this.content);
103
- } catch (error) {
104
- throw new Error(`Failed to save file: ${this.filePath}`);
102
+ await Bun.write(this.#filePath, this.#content);
103
+ } catch (_error) {
104
+ throw new Error(`Failed to save file: ${this.#filePath}`);
105
105
  }
106
106
  }
107
107
  getContent() {
108
- return this.content;
108
+ return this.#content;
109
109
  }
110
110
  setContent(content) {
111
- this.content = content;
111
+ this.#content = content;
112
112
  return this;
113
113
  }
114
114
  }
@@ -474,7 +474,7 @@ var PLACEHOLDER_APP_IDS = [
474
474
  "com.example.myapp"
475
475
  ];
476
476
  var placeholderAppIdSegment = /^(example|examples|myorg|myapp|mycompany|myorganization|changeme|todo|sample|test)$/;
477
- var isPlaceholderAppId = (appId) => {
477
+ var isPlaceholderAppId2 = (appId) => {
478
478
  const normalized = appId?.trim().toLowerCase() ?? "";
479
479
  if (!normalized)
480
480
  return true;
@@ -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+)/);
@@ -637,7 +641,7 @@ function materializeCapacitorConfig(target, { operation, localServerUrl, localIp
637
641
  return config;
638
642
  }
639
643
 
640
- class CapacitorApp {
644
+ class CapacitorApp2 {
641
645
  app;
642
646
  target;
643
647
  project;
@@ -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 { isPlaceholderAppId2, CapacitorApp2 };
@@ -1,26 +1,29 @@
1
1
  // @bun
2
- import {
3
- AkanContextAnalyzer
4
- } from "./index-a00jg8x8.js";
5
- import {
6
- Prompter
7
- } from "./index-tdbas9r0.js";
8
2
  import {
9
3
  runner,
10
4
  script
11
- } from "./index-8hsbnyes.js";
5
+ } from "./index-znynzt5w.js";
12
6
  import {
13
- AGENT_BLOCK_END,
14
7
  AGENT_BLOCK_START,
15
- AppExecutor,
16
- LibExecutor,
8
+ AGENT_BLOCK_END,
9
+ stampBlockVersion,
10
+ readDevkitVersion,
11
+ upsertAgentBlock,
17
12
  renderRecipeEntries,
18
- upsertAgentBlock
19
- } from "./index-hgna5rjy.js";
13
+ CLAUDE_COMMENT_RULE,
14
+ AppExecutor,
15
+ LibExecutor
16
+ } from "./index-qsq3q8fy.js";
17
+ import {
18
+ collectRecipeSources2,
19
+ scanRecipes2
20
+ } from "./index-by5gpp8x.js";
20
21
  import {
21
- collectRecipeSources,
22
- scanRecipes
23
- } from "./index-xys926f2.js";
22
+ AkanContextAnalyzer
23
+ } from "./index-gtmz74tj.js";
24
+ import {
25
+ Prompter
26
+ } from "./index-h7yw2z9n.js";
24
27
 
25
28
  // pkgs/@akanjs/cli/agent/agent.script.ts
26
29
  import { Logger as Logger2 } from "akanjs/common";
@@ -74,8 +77,8 @@ Keep a sample only while you are still learning its pattern; delete it once your
74
77
  `;
75
78
  };
76
79
  var renderRecipeIndex = async (workspace) => {
77
- const sources = await collectRecipeSources(`${workspace.workspaceRoot}/pkgs/akanjs/ui`, "akanjs/ui", "recipe");
78
- const recipes = scanRecipes(sources);
80
+ const sources = await collectRecipeSources2(`${workspace.workspaceRoot}/pkgs/akanjs/ui`, "akanjs/ui", "recipe");
81
+ const recipes = scanRecipes2(sources);
79
82
  const scopeIndexPointer = `App and lib recipes are **not** listed here. Each app/lib carries its own generated index \u2014
80
83
  \`apps/<app>/AGENTS.md\` / \`libs/<lib>/AGENTS.md\` (\`## Recipes In Scope\`) \u2014 regenerated by \`akan sync\` and
81
84
  verified by \`akan lint\`. When working inside an app or lib, consult that file before consuming or authoring a look.`;
@@ -87,15 +90,14 @@ ${scopeIndexPointer}
87
90
  `;
88
91
  return `## Recipes
89
92
 
90
- Framework UI recipes (Tailwind-variant look factories), importable from every app and lib. Consume by exact name \u2014
91
- \`import { <name> } from "akanjs/ui"\`, then \`<name>(variants?, className?)\`. The second arg merges internally and
92
- takes **an array too**, so never wrap it in \`cn()\`: \`<name>({}, ["h-full", isWide && "w-full", className])\`.
93
- Do not guess recipe names, import paths, or variant values; the list below carries
94
- the full contract (\`*\` marks the default, \`key?\` is a boolean flag), so there is no need to open the recipe file to
95
- consume one. tsc still reports variant mistakes. **Before inlining a repeated surface (card, box,
96
- tile, \u2026): reuse a recipe, or add one as \`apps/<app>/ui/Recipe/<name>.ts\` (one recipe per file, re-exported from
97
- that folder's \`index.ts\`) \u2014 never re-implement the same look inline in several places, and never author a
98
- near-duplicate.** Full authoring/consumption policy: the \`recipeRule\` guideline.
93
+ Framework UI recipes (Tailwind-variant look factories), importable from every app and lib: \`import { <name> } from
94
+ "akanjs/ui"\`, then \`<name>(variants?, className?)\`. The second arg merges internally and takes **an array too**, so
95
+ never wrap it in \`cn()\`: \`<name>({}, ["h-full", isWide && "w-full", className])\`. The list below is the full
96
+ contract (\`*\` marks the default, \`key?\` is a boolean flag) \u2014 do not guess a name, path, or variant value, and do
97
+ not open the recipe file to consume one. **Before inlining a repeated surface (card, box, tile, \u2026): reuse a recipe,
98
+ or add one as \`apps/<app>/ui/Recipe/<name>.ts\`, one per file, re-exported from that folder's \`index.ts\` \u2014 never
99
+ re-implement the same look inline in several places, and never author a near-duplicate.** Full policy: the
100
+ \`recipeRule\` guideline.
99
101
 
100
102
  ${renderRecipeEntries(recipes)}
101
103
 
@@ -103,19 +105,33 @@ ${scopeIndexPointer}
103
105
 
104
106
  `;
105
107
  };
108
+ var renderGuides = async (context) => {
109
+ const conventions = (await Prompter.getInstruction("conventions")).trim();
110
+ if (context.pkgs.some((pkg) => pkg.name === "akanjs"))
111
+ return conventions;
112
+ const onboarding = (await Prompter.getInstruction("workspaceOnboarding")).trim();
113
+ const appName = context.apps[0]?.name ?? "app";
114
+ return `${conventions}
115
+
116
+ ${onboarding.replaceAll("<%= appName %>", appName)}`;
117
+ };
106
118
  var renderManagedBlock = async (workspace) => {
107
119
  const context = await AkanContextAnalyzer.analyze(workspace);
108
120
  const frameworkGuide = await Prompter.getInstruction("framework");
109
121
  const appNames = context.apps.map((app) => app.name);
110
122
  const sampleCleanup = await renderSampleCleanup(workspace, appNames);
111
123
  const recipeIndex = await renderRecipeIndex(workspace);
112
- return `## Workspace
124
+ const guides = await renderGuides(context);
125
+ const version = await readDevkitVersion();
126
+ const block = `## Workspace
113
127
 
114
128
  - Repo: ${context.repoName}
115
129
  - Apps: ${context.apps.map((app) => app.name).join(", ") || "none"}
116
130
  - Libraries: ${context.libs.map((lib) => lib.name).join(", ") || "none"}
117
131
  - Packages: ${context.pkgs.map((pkg) => pkg.name).join(", ") || "none"}
118
132
 
133
+ ${guides}
134
+
119
135
  ${sampleCleanup}## Akan Module Abstracts
120
136
 
121
137
  - Before changing a domain, service, or scalar module, read its \`*.abstract.md\` file first.
@@ -131,15 +147,12 @@ If generated output is stale or broken, update the owning source file and run \`
131
147
 
132
148
  ${recipeIndex}## MCP Workflow Policy
133
149
 
134
- - Prefer Akan MCP workflows before direct source edits.
135
- - Direct source edits are denied when an allowlisted Akan workflow or repair tool can perform the change.
136
- - Use \`akan mcp --mode plan\` to inspect \`list_workflows\`, \`explain_workflow\`, and \`plan_workflow\`.
137
- - If \`plan_workflow\` returns \`planPath\` or \`next.tool=apply_workflow\`, call \`apply_workflow({ planPath })\` before editing source files directly.
138
- - Use \`akan mcp --mode apply\` only for allowlisted \`apply_workflow\`, \`run_validation\`, and repair tools.
139
- - After \`apply_workflow\`, run \`run_validation\` with \`validationTarget\` when present; otherwise use \`applyReportPath\`.
140
- - If no workflow exists, or apply reports unsupported/no-op/failed diagnostics that require manual action, keep edits scoped to owning source files and never patch generated files directly.
141
- - For compound requests, split the request into workflows and apply each \`planPath\` in order, such as \`create-module\` followed by \`add-field\`.
142
- - **CLI-only fallback (MCP not connected):** \`akan mcp\` starts a stdio MCP server, so the \`list_workflows\`/\`plan_workflow\`/\`apply_workflow\` tools exist only when your agent is wired to it as an MCP client. When they are unavailable, the CLI is a first-class equivalent: \`akan workflow list\` / \`explain <name>\` / \`plan <name> ... --format json --out <planPath>\` / \`apply <planPath> --format json\`, \`akan doctor --strict --format json\` for validation, and \`akan repair generated|imports|module-shape --app <app> --format json\` for repairs. Scaffolding primitives (\`create-module\`/\`create-scalar\`/\`create-service\` take the target app/lib as a POSITIONAL arg; \`add-field\`/\`add-enum-field\` use \`--app\`/\`--module\` flags) call the same code the workflows do.
150
+ - **Prefer an Akan workflow to a direct source edit.** A direct edit is denied when an allowlisted workflow or repair tool can make the change.
151
+ - Inspect with \`akan mcp --mode plan\` (\`list_workflows\`, \`explain_workflow\`, \`plan_workflow\`); apply with \`akan mcp --mode apply\`, which allowlists \`apply_workflow\`, \`run_validation\`, and the repair tools.
152
+ - If \`plan_workflow\` returns \`planPath\` or \`next.tool=apply_workflow\`, call \`apply_workflow({ planPath })\` before editing source. Then run \`run_validation\` with \`validationTarget\` when present, otherwise \`applyReportPath\`.
153
+ - Split a compound request into workflows and apply each \`planPath\` in order, such as \`create-module\` followed by \`add-field\`.
154
+ - When no workflow exists, or apply reports unsupported/no-op/failed diagnostics needing manual action, keep edits scoped to owning source files and never patch generated files directly.
155
+ - **MCP not connected?** The CLI is a first-class equivalent \u2014 \`akan workflow list\` / \`explain\` / \`plan\` / \`apply\`, \`akan doctor\`, \`akan repair\`. See the onboarding guide's CLI-only fallback for the exact flags.
143
156
 
144
157
  ## Validation
145
158
 
@@ -148,7 +161,19 @@ ${context.validationCommands.map((command) => `- \`${command}\``).join(`
148
161
 
149
162
  ## Framework Guide
150
163
 
151
- ${frameworkGuide.trim()}`;
164
+ ${frameworkGuide.trim()}
165
+
166
+ ## Before You Finish
167
+
168
+ 1. \`bun run akan lint <appName>\` \u2014 Tailwind class order, \`Err\`, \`console\`, \`#private\` scope, unused imports.
169
+ 2. \`bun run akan typecheck <appName>\` \u2014 server/client boundary violations.
170
+ 3. \`bun run akan sync <appName>\` if you added, renamed, or deleted any file.
171
+ 4. Did you write or change a \`.tsx\` file? \`bun run akan quality ssr\` \u2014 did the server share hold, and did you add
172
+ a \`"use client"\` you cannot justify?
173
+ 5. Re-read the file you wrote against its section above.
174
+ 6. Did you add a comment? Delete it unless it documents an external constraint or a security decision.
175
+ 7. Did behavior or an invariant change? Update the module's \`*.abstract.md\`.`;
176
+ return version ? stampBlockVersion(block, version) : block;
152
177
  };
153
178
  var renderAgentsMd = async (workspace, existing) => {
154
179
  const block = await renderManagedBlock(workspace);
@@ -171,6 +196,8 @@ var renderClaudeMd = async (workspace) => {
171
196
  return `# ${context.repoName} \u2014 Claude Code Guide
172
197
 
173
198
  @AGENTS.md
199
+
200
+ ${CLAUDE_COMMENT_RULE}
174
201
  `;
175
202
  };
176
203
  var renderCursorRule = () => `---
@@ -218,7 +245,7 @@ class AgentRunner extends runner("agent") {
218
245
  written.push(`${exec.type}s/${exec.name}/AGENTS.md`);
219
246
  } catch (error) {
220
247
  const message = error instanceof Error ? error.message : String(error);
221
- Logger.rawLog(`Skipped ${exec.type}s/${exec.name}/AGENTS.md (${message})`, undefined, "warn");
248
+ Logger.rawLog(`Skipped ${exec.type}s/${exec.name}/AGENTS.md (${message})`, undefined, "error");
222
249
  }
223
250
  };
224
251
  for (const name of libNames)
@@ -0,0 +1,76 @@
1
+ // @bun
2
+ // pkgs/@akanjs/devkit/libSource.ts
3
+ class LibSource {
4
+ static manifestKey = "akan";
5
+ static unhashedDirs = ["env"];
6
+ #lib;
7
+ constructor(lib) {
8
+ this.#lib = lib;
9
+ }
10
+ get #prefix() {
11
+ return `libs/${this.#lib.name}/`;
12
+ }
13
+ #isHashed(file) {
14
+ const relative = file.slice(this.#prefix.length);
15
+ return !LibSource.unhashedDirs.includes(relative.split("/")[0] ?? "");
16
+ }
17
+ async#hashableContent(file) {
18
+ const content = await this.#lib.workspace.readFile(file);
19
+ if (file !== `${this.#prefix}package.json`)
20
+ return content;
21
+ const manifest = JSON.parse(content);
22
+ delete manifest[LibSource.manifestKey];
23
+ return JSON.stringify(manifest);
24
+ }
25
+ async computeHash() {
26
+ const files = (await this.#lib.workspace.listGitFiles([`libs/${this.#lib.name}`], { untracked: true })).filter((file) => this.#isHashed(file));
27
+ const hasher = new Bun.CryptoHasher("sha256");
28
+ for (const file of files) {
29
+ hasher.update(file);
30
+ hasher.update("\x00");
31
+ hasher.update(await this.#hashableContent(file));
32
+ hasher.update("\x00");
33
+ }
34
+ return hasher.digest("hex").slice(0, 32);
35
+ }
36
+ async read() {
37
+ const manifest = await this.#lib.getPackageJson();
38
+ const akan = manifest[LibSource.manifestKey];
39
+ return akan?.source ?? null;
40
+ }
41
+ async write({ origin, sha }) {
42
+ const [manifest, hash] = await Promise.all([this.#lib.getPackageJson(), this.computeHash()]);
43
+ const akan = manifest[LibSource.manifestKey] ?? {};
44
+ const stamp = { origin, sha, hash, syncedAt: new Date().toISOString() };
45
+ await this.#lib.setPackageJson({ ...manifest, [LibSource.manifestKey]: { ...akan, source: stamp } });
46
+ return stamp;
47
+ }
48
+ async syncStamp({ origin, sha }) {
49
+ const [current, hash] = await Promise.all([this.read(), this.computeHash()]);
50
+ if (current?.origin === origin && current.sha === sha && current.hash === hash)
51
+ return { stamp: current, changed: false };
52
+ return { stamp: await this.write({ origin, sha }), changed: true };
53
+ }
54
+ async status() {
55
+ const [stamp, hash] = await Promise.all([this.read(), this.computeHash()]);
56
+ const drift = !stamp ? "unstamped" : stamp.hash === hash ? "clean" : "drifted";
57
+ return { lib: this.#lib.name, drift, stamp, hash };
58
+ }
59
+ }
60
+ function formatLibStatuses(statuses) {
61
+ const marks = { clean: "clean ", drifted: "DRIFTED ", unstamped: "unstamped" };
62
+ const sections = [
63
+ "Akan Library Source Status",
64
+ "",
65
+ ...statuses.map((status) => {
66
+ const origin = status.stamp ? `${status.stamp.origin}@${status.stamp.sha}` : "no akan.source in package.json";
67
+ return ` ${marks[status.drift]} libs/${status.lib} ${origin}`;
68
+ }),
69
+ "",
70
+ `drifted: ${statuses.filter((status) => status.drift === "drifted").length} / ${statuses.length}`
71
+ ];
72
+ return sections.join(`
73
+ `);
74
+ }
75
+
76
+ export { LibSource, formatLibStatuses };
@@ -37,10 +37,10 @@ var resolveMobileTargets = async (app, selection) => {
37
37
  if (!selection && targets.length === 1)
38
38
  return targets;
39
39
  if (!selection) {
40
- const choices2 = await getMobileTargetChoices(app);
41
- if (choices2.length === 1)
42
- return resolveMobileTargets(app, choices2[0]);
43
- throw new Error(`Multiple mobile targets found for ${app.name}. Pass --target <${choices2.join("|")}|all>.`);
40
+ const choices = await getMobileTargetChoices(app);
41
+ if (choices.length === 1)
42
+ return resolveMobileTargets(app, choices[0]);
43
+ throw new Error(`Multiple mobile targets found for ${app.name}. Pass --target <${choices.join("|")}|all>.`);
44
44
  }
45
45
  if (selection === "all") {
46
46
  if (Object.keys(config.mobile.targets).length > 1)