@akanjs/cli 2.3.9-rc.9 → 2.3.9

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/index.js CHANGED
@@ -12502,6 +12502,7 @@ void run().catch((error) => {
12502
12502
  }
12503
12503
  // pkgs/@akanjs/devkit/applicationReleasePackager.ts
12504
12504
  import { cp, mkdir as mkdir9, rm as rm4 } from "fs/promises";
12505
+ import path38 from "path";
12505
12506
 
12506
12507
  // pkgs/@akanjs/devkit/uploadRelease.ts
12507
12508
  import { HttpClient as HttpClient2, Logger as Logger9 } from "akanjs/common";
@@ -12617,13 +12618,9 @@ class ApplicationReleasePackager {
12617
12618
  await cp(`${this.#app.dist.cwdPath}/backend`, `${buildPath}/backend`, { recursive: true });
12618
12619
  await cp(this.#app.dist.cwdPath, buildRoot, { recursive: true });
12619
12620
  await rm4(`${buildRoot}/frontend/.next`, { recursive: true, force: true });
12620
- await this.#app.workspace.spawn("tar", [
12621
- "-zcf",
12622
- `${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}-release.tar.gz`,
12623
- "-C",
12624
- buildRoot,
12625
- "./"
12626
- ]);
12621
+ const releaseRoot = this.#app.workspace.workspaceRoot;
12622
+ const releaseArchivePath = path38.relative(releaseRoot, `${releaseRoot}/releases/builds/${this.#app.name}-release.tar.gz`).split(path38.sep).join("/");
12623
+ await this.#app.workspace.spawn("tar", ["-zcf", releaseArchivePath, "-C", buildRoot, "./"], { cwd: releaseRoot });
12627
12624
  await this.#writeCsrZipIfPresent();
12628
12625
  return { platformVersion };
12629
12626
  }
@@ -12644,22 +12641,18 @@ class ApplicationReleasePackager {
12644
12641
  await cp(this.#app.dist.cwdPath, `${sourceRoot}/apps/${this.#app.name}`, { recursive: true });
12645
12642
  const libDeps = ["social", "shared", "platform", "util"];
12646
12643
  await Promise.all(libDeps.map((lib) => cp(`${this.#app.workspace.cwdPath}/libs/${lib}`, `${sourceRoot}/libs/${lib}`, { recursive: true })));
12647
- await Promise.all([".next", "ios", "android", "public/libs"].map(async (path38) => {
12648
- const targetPath = `${sourceRoot}/apps/${this.#app.name}/${path38}`;
12644
+ await Promise.all([".next", "ios", "android", "public/libs"].map(async (path39) => {
12645
+ const targetPath = `${sourceRoot}/apps/${this.#app.name}/${path39}`;
12649
12646
  if (await FileSys.dirExists(targetPath))
12650
12647
  await rm4(targetPath, { recursive: true, force: true });
12651
12648
  }));
12652
12649
  const syncPaths = [".husky", ".gitignore", "package.json"];
12653
- await Promise.all(syncPaths.map((path38) => cp(`${this.#app.workspace.cwdPath}/${path38}`, `${sourceRoot}/${path38}`, { recursive: true })));
12650
+ await Promise.all(syncPaths.map((path39) => cp(`${this.#app.workspace.cwdPath}/${path39}`, `${sourceRoot}/${path39}`, { recursive: true })));
12654
12651
  await this.#writeSourceTsconfig(sourceRoot, libDeps);
12655
12652
  await Bun.write(`${sourceRoot}/README.md`, readme);
12656
- await this.#app.workspace.spawn("tar", [
12657
- "-zcf",
12658
- `${this.#app.workspace.cwdPath}/releases/sources/${this.#app.name}-source.tar.gz`,
12659
- "-C",
12660
- sourceRoot,
12661
- "./"
12662
- ]);
12653
+ const sourceCwd = this.#app.workspace.cwdPath;
12654
+ const sourceArchivePath = path38.relative(sourceCwd, `${sourceCwd}/releases/sources/${this.#app.name}-source.tar.gz`).split(path38.sep).join("/");
12655
+ await this.#app.workspace.spawn("tar", ["-zcf", sourceArchivePath, "-C", sourceRoot, "./"], { cwd: sourceCwd });
12663
12656
  }
12664
12657
  async#resetSourceRoot(sourceRoot) {
12665
12658
  if (await FileSys.dirExists(sourceRoot)) {
@@ -12741,20 +12734,20 @@ class ApplicationReleasePackager {
12741
12734
  }
12742
12735
  }
12743
12736
  // pkgs/@akanjs/devkit/applicationTestPreload.ts
12744
- import path38 from "path";
12737
+ import path39 from "path";
12745
12738
  var SIGNAL_TEST_PRELOAD_PATH = "test/signalTest.preload.ts";
12746
12739
  async function resolveSignalTestPreloadPath(target) {
12747
12740
  const candidates = [];
12748
12741
  const addResolvedPackageCandidate = (basePath2) => {
12749
12742
  try {
12750
- candidates.push(path38.join(path38.dirname(Bun.resolveSync("akanjs/package.json", basePath2)), SIGNAL_TEST_PRELOAD_PATH));
12743
+ candidates.push(path39.join(path39.dirname(Bun.resolveSync("akanjs/package.json", basePath2)), SIGNAL_TEST_PRELOAD_PATH));
12751
12744
  } catch {}
12752
12745
  };
12753
12746
  addResolvedPackageCandidate(target.cwdPath);
12754
12747
  addResolvedPackageCandidate(process.cwd());
12755
- addResolvedPackageCandidate(path38.dirname(Bun.main));
12748
+ addResolvedPackageCandidate(path39.dirname(Bun.main));
12756
12749
  addResolvedPackageCandidate(import.meta.dir);
12757
- candidates.push(path38.join(target.cwdPath, "../../node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.join(target.cwdPath, "../../pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.join(process.cwd(), "node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.join(process.cwd(), "pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.join(path38.dirname(Bun.main), "../../akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.resolve(import.meta.dir, "../../akanjs", SIGNAL_TEST_PRELOAD_PATH));
12750
+ candidates.push(path39.join(target.cwdPath, "../../node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(target.cwdPath, "../../pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(process.cwd(), "node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(process.cwd(), "pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(path39.dirname(Bun.main), "../../akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.resolve(import.meta.dir, "../../akanjs", SIGNAL_TEST_PRELOAD_PATH));
12758
12751
  for (const candidate of [...new Set(candidates)]) {
12759
12752
  if (await Bun.file(candidate).exists())
12760
12753
  return candidate;
@@ -12764,7 +12757,7 @@ async function resolveSignalTestPreloadPath(target) {
12764
12757
  // pkgs/@akanjs/devkit/builder.ts
12765
12758
  import { existsSync as existsSync2 } from "fs";
12766
12759
  import { mkdir as mkdir10 } from "fs/promises";
12767
- import path39 from "path";
12760
+ import path40 from "path";
12768
12761
  var SKIP_ENTRY_DIR_SET = new Set(["node_modules", "dist", "build", ".git", ".next"]);
12769
12762
  var assetExtensions = [".css", ".md", ".js", ".png", ".ico", ".svg", ".json", ".template"];
12770
12763
  var assetLoader = Object.fromEntries(assetExtensions.map((ext) => [ext, "file"]));
@@ -12781,14 +12774,14 @@ class Builder {
12781
12774
  #globEntrypoints(cwd, pattern) {
12782
12775
  const glob = new Bun.Glob(pattern);
12783
12776
  return Array.from(glob.scanSync({ cwd, onlyFiles: true })).filter((relativePath) => {
12784
- const segments = relativePath.split(path39.sep);
12777
+ const segments = relativePath.split(path40.sep);
12785
12778
  return !segments.some((segment) => SKIP_ENTRY_DIR_SET.has(segment));
12786
- }).map((rel) => path39.join(cwd, rel));
12779
+ }).map((rel) => path40.join(cwd, rel));
12787
12780
  }
12788
12781
  #globFiles(cwd, pattern = "**/*.*") {
12789
12782
  const glob = new Bun.Glob(pattern);
12790
12783
  return Array.from(glob.scanSync({ cwd, onlyFiles: true })).filter((relativePath) => {
12791
- const segments = relativePath.split(path39.sep);
12784
+ const segments = relativePath.split(path40.sep);
12792
12785
  return !segments.some((segment) => SKIP_ENTRY_DIR_SET.has(segment));
12793
12786
  });
12794
12787
  }
@@ -12796,17 +12789,17 @@ class Builder {
12796
12789
  const out = [];
12797
12790
  for (const p of additionalEntryPoints) {
12798
12791
  if (p.includes("*")) {
12799
- const rel = p.startsWith(`${cwd}/`) || p.startsWith(`${cwd}${path39.sep}`) ? p.slice(cwd.length + 1) : p;
12792
+ const rel = p.startsWith(`${cwd}/`) || p.startsWith(`${cwd}${path40.sep}`) ? p.slice(cwd.length + 1) : p;
12800
12793
  out.push(...this.#globEntrypoints(cwd, rel));
12801
12794
  } else
12802
- out.push(path39.isAbsolute(p) ? p : path39.join(cwd, p));
12795
+ out.push(path40.isAbsolute(p) ? p : path40.join(cwd, p));
12803
12796
  }
12804
12797
  return out;
12805
12798
  }
12806
12799
  #getBuildOptions({ bundle = false, additionalEntryPoints = [] } = {}) {
12807
12800
  const cwd = this.#executor.cwdPath;
12808
12801
  const entrypoints = [
12809
- ...bundle ? [path39.join(cwd, "index.ts")] : this.#globEntrypoints(cwd, "**/*.{ts,tsx}"),
12802
+ ...bundle ? [path40.join(cwd, "index.ts")] : this.#globEntrypoints(cwd, "**/*.{ts,tsx}"),
12810
12803
  ...this.#resolveAdditionalEntrypoints(cwd, additionalEntryPoints)
12811
12804
  ];
12812
12805
  return {
@@ -12827,9 +12820,9 @@ class Builder {
12827
12820
  for (const relativePath of this.#globFiles(cwd)) {
12828
12821
  if (relativePath === "package.json")
12829
12822
  continue;
12830
- const sourcePath = path39.join(cwd, relativePath);
12831
- const targetPath = path39.join(this.#distExecutor.cwdPath, relativePath);
12832
- await mkdir10(path39.dirname(targetPath), { recursive: true });
12823
+ const sourcePath = path40.join(cwd, relativePath);
12824
+ const targetPath = path40.join(this.#distExecutor.cwdPath, relativePath);
12825
+ await mkdir10(path40.dirname(targetPath), { recursive: true });
12833
12826
  await Bun.write(targetPath, Bun.file(sourcePath));
12834
12827
  }
12835
12828
  }
@@ -12840,13 +12833,13 @@ class Builder {
12840
12833
  return withoutFormatDir;
12841
12834
  if (!hasDotSlash && withoutFormatDir === publishedPath)
12842
12835
  return publishedPath;
12843
- const parsed = path39.posix.parse(withoutFormatDir);
12836
+ const parsed = path40.posix.parse(withoutFormatDir);
12844
12837
  if (![".js", ".mjs", ".cjs"].includes(parsed.ext))
12845
12838
  return withoutFormatDir;
12846
- const withoutExt = path39.posix.join(parsed.dir, parsed.name);
12839
+ const withoutExt = path40.posix.join(parsed.dir, parsed.name);
12847
12840
  const sourcePath = withoutExt.startsWith("./") ? withoutExt.slice(2) : withoutExt;
12848
12841
  const sourceCandidates = [`${sourcePath}.ts`, `${sourcePath}.tsx`];
12849
- const matchedSource = sourceCandidates.find((candidate) => existsSync2(path39.join(this.#executor.cwdPath, candidate)));
12842
+ const matchedSource = sourceCandidates.find((candidate) => existsSync2(path40.join(this.#executor.cwdPath, candidate)));
12850
12843
  if (!matchedSource)
12851
12844
  return withoutFormatDir;
12852
12845
  return hasDotSlash ? `./${matchedSource}` : matchedSource;
@@ -12897,7 +12890,7 @@ class Builder {
12897
12890
  // pkgs/@akanjs/devkit/capacitorApp.ts
12898
12891
  import { cp as cp2, mkdir as mkdir11, rm as rm5 } from "fs/promises";
12899
12892
  import os from "os";
12900
- import path40 from "path";
12893
+ import path41 from "path";
12901
12894
  import { select as select2 } from "@inquirer/prompts";
12902
12895
  import { MobileProject } from "@trapezedev/project";
12903
12896
  import { capitalize as capitalize5 } from "akanjs/common";
@@ -13104,13 +13097,13 @@ var rootCapacitorConfigFilenames = [
13104
13097
  "capacitor.config.js",
13105
13098
  "capacitor.config.json"
13106
13099
  ];
13107
- var rootCapacitorConfigPaths = (appRoot) => rootCapacitorConfigFilenames.map((file) => path40.join(appRoot, file));
13100
+ var rootCapacitorConfigPaths = (appRoot) => rootCapacitorConfigFilenames.map((file) => path41.join(appRoot, file));
13108
13101
  async function clearRootCapacitorConfigs(appRoot) {
13109
13102
  await Promise.all(rootCapacitorConfigPaths(appRoot).map((file) => rm5(file, { force: true })));
13110
13103
  }
13111
13104
  async function writeRootCapacitorConfig(appRoot, content) {
13112
13105
  await clearRootCapacitorConfigs(appRoot);
13113
- await Bun.write(path40.join(appRoot, "capacitor.config.json"), content);
13106
+ await Bun.write(path41.join(appRoot, "capacitor.config.json"), content);
13114
13107
  }
13115
13108
  var getLocalIP = () => {
13116
13109
  const interfaces = os.networkInterfaces();
@@ -13219,13 +13212,13 @@ function buildIosNativeRunCommand({
13219
13212
  scheme = "App",
13220
13213
  configuration = "Debug"
13221
13214
  }) {
13222
- const derivedDataPath = path40.join(appRoot, "ios/DerivedData", device.id);
13215
+ const derivedDataPath = path41.join(appRoot, "ios/DerivedData", device.id);
13223
13216
  const productPlatform = device.kind === "device" ? "iphoneos" : "iphonesimulator";
13224
13217
  const destination = device.kind === "device" ? `id=${device.xcodebuildId ?? device.id}` : `platform=iOS Simulator,id=${device.id}`;
13225
13218
  return {
13226
13219
  configuration,
13227
13220
  derivedDataPath,
13228
- appPath: path40.join(derivedDataPath, "Build/Products", `${configuration}-${productPlatform}`, "App.app"),
13221
+ appPath: path41.join(derivedDataPath, "Build/Products", `${configuration}-${productPlatform}`, "App.app"),
13229
13222
  xcodebuildArgs: [
13230
13223
  "-project",
13231
13224
  "App.xcodeproj",
@@ -13435,7 +13428,7 @@ function materializeCapacitorConfig(target, { operation, localServerUrl, localIp
13435
13428
  ...capacitorConfig,
13436
13429
  appId,
13437
13430
  appName,
13438
- webDir: path40.posix.join(".akan", "mobile", name, "www"),
13431
+ webDir: path41.posix.join(".akan", "mobile", name, "www"),
13439
13432
  plugins: {
13440
13433
  CapacitorCookies: { enabled: true },
13441
13434
  ...pluginsConfig,
@@ -13492,10 +13485,10 @@ class CapacitorApp {
13492
13485
  constructor(app, target) {
13493
13486
  this.app = app;
13494
13487
  this.target = target;
13495
- this.targetRootPath = path40.posix.join(".akan", "mobile", this.target.name);
13496
- this.targetRoot = path40.join(this.app.cwdPath, this.targetRootPath);
13497
- this.targetWebRoot = path40.join(this.targetRoot, "www");
13498
- this.targetAssetRoot = path40.join(this.targetRoot, "assets");
13488
+ this.targetRootPath = path41.posix.join(".akan", "mobile", this.target.name);
13489
+ this.targetRoot = path41.join(this.app.cwdPath, this.targetRootPath);
13490
+ this.targetWebRoot = path41.join(this.targetRoot, "www");
13491
+ this.targetAssetRoot = path41.join(this.targetRoot, "assets");
13499
13492
  this.project = new MobileProject(this.app.cwdPath, {
13500
13493
  android: { path: this.androidRootPath },
13501
13494
  ios: { path: this.iosProjectPath }
@@ -13510,9 +13503,9 @@ class CapacitorApp {
13510
13503
  await mkdir11(this.targetRoot, { recursive: true });
13511
13504
  if (regenerate) {
13512
13505
  if (!platform || platform === "ios")
13513
- await rm5(path40.join(this.app.cwdPath, this.iosRootPath), { recursive: true, force: true });
13506
+ await rm5(path41.join(this.app.cwdPath, this.iosRootPath), { recursive: true, force: true });
13514
13507
  if (!platform || platform === "android")
13515
- await rm5(path40.join(this.app.cwdPath, this.androidRootPath), { recursive: true, force: true });
13508
+ await rm5(path41.join(this.app.cwdPath, this.androidRootPath), { recursive: true, force: true });
13516
13509
  }
13517
13510
  const project = this.project;
13518
13511
  await this.project.load();
@@ -13635,7 +13628,7 @@ ${textError instanceof Error ? textError.message : ""}`);
13635
13628
  const xcodebuildArgs = noAllowProvisioningUpdates ? command.xcodebuildArgs : [...command.xcodebuildArgs.slice(0, -1), "-allowProvisioningUpdates", ...command.xcodebuildArgs.slice(-1)];
13636
13629
  try {
13637
13630
  await this.#spawn("xcodebuild", xcodebuildArgs, {
13638
- cwd: path40.join(this.app.cwdPath, this.iosProjectPath),
13631
+ cwd: path41.join(this.app.cwdPath, this.iosProjectPath),
13639
13632
  env: mobileEnv
13640
13633
  });
13641
13634
  const devicectlId = runTarget.devicectlId ?? runTarget.id;
@@ -13660,7 +13653,7 @@ ${textError instanceof Error ? textError.message : ""}`);
13660
13653
  return isRecord2(this.target.ios) && typeof this.target.ios.scheme === "string" ? this.target.ios.scheme : "App";
13661
13654
  }
13662
13655
  async#getIosDevelopmentTeam() {
13663
- const pbxprojPath = path40.join(this.app.cwdPath, this.iosProjectPath, "App.xcodeproj/project.pbxproj");
13656
+ const pbxprojPath = path41.join(this.app.cwdPath, this.iosProjectPath, "App.xcodeproj/project.pbxproj");
13664
13657
  if (!await Bun.file(pbxprojPath).exists())
13665
13658
  return;
13666
13659
  return (await Bun.file(pbxprojPath).text()).match(/DEVELOPMENT_TEAM = ([^;]+);/)?.[1]?.trim();
@@ -13686,7 +13679,7 @@ ${error.message}`;
13686
13679
  await this.#spawnMobile("npx", ["cap", "sync", "android"], { operation, env });
13687
13680
  }
13688
13681
  async#updateAndroidBuildTypes() {
13689
- const appGradle = await FileEditor.create(path40.join(this.app.cwdPath, this.androidRootPath, "app/build.gradle"));
13682
+ const appGradle = await FileEditor.create(path41.join(this.app.cwdPath, this.androidRootPath, "app/build.gradle"));
13690
13683
  const buildTypesBlock = `
13691
13684
  debug {
13692
13685
  applicationIdSuffix ".debug"
@@ -13730,7 +13723,7 @@ ${error.message}`;
13730
13723
  const gradleCommand = isWindows ? "gradlew.bat" : "./gradlew";
13731
13724
  await this.app.spawn(gradleCommand, [assembleType === "apk" ? "assembleRelease" : "bundleRelease"], {
13732
13725
  stdio: "inherit",
13733
- cwd: path40.join(this.app.cwdPath, this.androidRootPath),
13726
+ cwd: path41.join(this.app.cwdPath, this.androidRootPath),
13734
13727
  env: await this.#commandEnv("release", env)
13735
13728
  });
13736
13729
  }
@@ -13738,10 +13731,10 @@ ${error.message}`;
13738
13731
  await this.#spawnMobile("npx", ["cap", "open", "android"], { operation: "local", env: "local" });
13739
13732
  }
13740
13733
  async#ensureAndroidAssetsDir() {
13741
- await mkdir11(path40.join(this.app.cwdPath, this.androidAssetsPath), { recursive: true });
13734
+ await mkdir11(path41.join(this.app.cwdPath, this.androidAssetsPath), { recursive: true });
13742
13735
  }
13743
13736
  async#ensureAndroidDebugKeystore() {
13744
- const keystorePath = path40.join(this.app.cwdPath, this.androidRootPath, "app/debug.keystore");
13737
+ const keystorePath = path41.join(this.app.cwdPath, this.androidRootPath, "app/debug.keystore");
13745
13738
  if (await Bun.file(keystorePath).exists())
13746
13739
  return;
13747
13740
  await this.#spawn("keytool", [
@@ -13780,7 +13773,7 @@ ${error.message}`;
13780
13773
  await this.#spawnMobile("npx", args, { operation, env }, { stdio: "inherit" });
13781
13774
  }
13782
13775
  async#assertAndroidReleaseSigningConfig() {
13783
- const gradlePropertiesPath = path40.join(this.app.cwdPath, this.androidRootPath, "gradle.properties");
13776
+ const gradlePropertiesPath = path41.join(this.app.cwdPath, this.androidRootPath, "gradle.properties");
13784
13777
  const gradleProperties = await Bun.file(gradlePropertiesPath).exists() ? await Bun.file(gradlePropertiesPath).text() : "";
13785
13778
  const missingKeys = getMissingAndroidReleaseSigningKeys({ gradleProperties });
13786
13779
  if (missingKeys.length > 0)
@@ -13807,12 +13800,12 @@ ${error.message}`;
13807
13800
  await this.#prepareAndroid({ operation: "release", env: "main" });
13808
13801
  }
13809
13802
  async prepareWww() {
13810
- const htmlSource = path40.join(this.app.dist.cwdPath, "csr", targetHtmlFilename(this.target));
13803
+ const htmlSource = path41.join(this.app.dist.cwdPath, "csr", targetHtmlFilename(this.target));
13811
13804
  if (!await Bun.file(htmlSource).exists())
13812
13805
  throw new Error(`CSR html for mobile target '${this.target.name}' not found: ${htmlSource}`);
13813
13806
  await rm5(this.targetWebRoot, { recursive: true, force: true });
13814
13807
  await mkdir11(this.targetWebRoot, { recursive: true });
13815
- await Bun.write(path40.join(this.targetWebRoot, "index.html"), this.#injectMobileTargetMeta(await Bun.file(htmlSource).text()));
13808
+ await Bun.write(path41.join(this.targetWebRoot, "index.html"), this.#injectMobileTargetMeta(await Bun.file(htmlSource).text()));
13816
13809
  }
13817
13810
  #injectMobileTargetMeta(html) {
13818
13811
  const basePath2 = this.target.basePath?.replace(/^\/+|\/+$/g, "") ?? "";
@@ -13832,7 +13825,7 @@ ${error.message}`;
13832
13825
  });
13833
13826
  const content = `${JSON.stringify(config, null, 2)}
13834
13827
  `;
13835
- await Bun.write(path40.join(this.targetRoot, "capacitor.config.json"), content);
13828
+ await Bun.write(path41.join(this.targetRoot, "capacitor.config.json"), content);
13836
13829
  return content;
13837
13830
  }
13838
13831
  async#prepareTargetAssets() {
@@ -13840,11 +13833,11 @@ ${error.message}`;
13840
13833
  return;
13841
13834
  await mkdir11(this.targetAssetRoot, { recursive: true });
13842
13835
  if (this.target.assets.icon)
13843
- await cp2(path40.join(this.app.cwdPath, this.target.assets.icon), path40.join(this.targetAssetRoot, "icon.png"), {
13836
+ await cp2(path41.join(this.app.cwdPath, this.target.assets.icon), path41.join(this.targetAssetRoot, "icon.png"), {
13844
13837
  force: true
13845
13838
  });
13846
13839
  if (this.target.assets.splash)
13847
- await cp2(path40.join(this.app.cwdPath, this.target.assets.splash), path40.join(this.targetAssetRoot, "splash.png"), {
13840
+ await cp2(path41.join(this.app.cwdPath, this.target.assets.splash), path41.join(this.targetAssetRoot, "splash.png"), {
13848
13841
  force: true
13849
13842
  });
13850
13843
  }
@@ -13852,11 +13845,11 @@ ${error.message}`;
13852
13845
  const files = this.target.files?.[platform];
13853
13846
  if (!files)
13854
13847
  return;
13855
- const platformRoot = path40.join(this.app.cwdPath, platform === "ios" ? this.iosRootPath : this.androidRootPath);
13848
+ const platformRoot = path41.join(this.app.cwdPath, platform === "ios" ? this.iosRootPath : this.androidRootPath);
13856
13849
  await Promise.all(Object.entries(files).map(async ([to, from]) => {
13857
- const targetPath = path40.join(platformRoot, to);
13858
- await mkdir11(path40.dirname(targetPath), { recursive: true });
13859
- await cp2(path40.join(this.app.cwdPath, from), targetPath, { force: true });
13850
+ const targetPath = path41.join(platformRoot, to);
13851
+ await mkdir11(path41.dirname(targetPath), { recursive: true });
13852
+ await cp2(path41.join(this.app.cwdPath, from), targetPath, { force: true });
13860
13853
  }));
13861
13854
  }
13862
13855
  async#generateAssets({ operation, env }) {
@@ -13866,7 +13859,7 @@ ${error.message}`;
13866
13859
  "@capacitor/assets",
13867
13860
  "generate",
13868
13861
  "--assetPath",
13869
- path40.posix.join(this.targetRootPath, "assets"),
13862
+ path41.posix.join(this.targetRootPath, "assets"),
13870
13863
  "--iosProject",
13871
13864
  this.iosProjectPath,
13872
13865
  "--androidProject",
@@ -14076,7 +14069,7 @@ var Pkg = createInternalArgToken("Pkg");
14076
14069
  var Module = createInternalArgToken("Module");
14077
14070
  var Workspace = createInternalArgToken("Workspace");
14078
14071
  // pkgs/@akanjs/devkit/commandDecorators/command.ts
14079
- import path41 from "path";
14072
+ import path42 from "path";
14080
14073
  import { confirm, input as input2, select as select3 } from "@inquirer/prompts";
14081
14074
  import { Logger as Logger10 } from "akanjs/common";
14082
14075
  import chalk6 from "chalk";
@@ -14580,7 +14573,7 @@ var runCommands = async (...commands) => {
14580
14573
  process.exit(1);
14581
14574
  });
14582
14575
  const __dirname2 = getDirname(import.meta.url);
14583
- const packageJsonCandidates = [`${path41.dirname(Bun.main)}/package.json`, `${__dirname2}/../package.json`];
14576
+ const packageJsonCandidates = [`${path42.dirname(Bun.main)}/package.json`, `${__dirname2}/../package.json`];
14584
14577
  let cliPackageJson = null;
14585
14578
  for (const packageJsonPath of packageJsonCandidates) {
14586
14579
  if (!await FileSys.fileExists(packageJsonPath))
@@ -14860,8 +14853,8 @@ var scanModuleSpecifiers = (source2, filePath, includeExports) => {
14860
14853
  return importSpecifiers;
14861
14854
  };
14862
14855
  var parseTsConfig = (tsConfigPath = "./tsconfig.json") => {
14863
- const configFile = ts10.readConfigFile(tsConfigPath, (path42) => {
14864
- return ts10.sys.readFile(path42);
14856
+ const configFile = ts10.readConfigFile(tsConfigPath, (path43) => {
14857
+ return ts10.sys.readFile(path43);
14865
14858
  });
14866
14859
  return ts10.parseJsonConfigFileContent(configFile.config, ts10.sys, realpathSync(tsConfigPath).replace(/[^/\\]+$/, ""));
14867
14860
  };
@@ -15077,6 +15070,571 @@ ${document}
15077
15070
  `;
15078
15071
  }
15079
15072
  }
15073
+ // pkgs/@akanjs/devkit/qualityScanner.ts
15074
+ import { createHash } from "crypto";
15075
+ import { readdir as readdir3, readFile as readFile2, stat as stat4 } from "fs/promises";
15076
+ import path43 from "path";
15077
+ import ignore2 from "ignore";
15078
+ import ts11 from "typescript";
15079
+ var MAX_FILE_LINES = 2000;
15080
+ var PLACEHOLDER_EXPORT_NAMES = new Set([
15081
+ "aa",
15082
+ "dumb",
15083
+ "dumb2",
15084
+ "someBaseLogic",
15085
+ "someCommonLogic",
15086
+ "someFrontendLogic",
15087
+ "someBackendLogic"
15088
+ ]);
15089
+ var SUGGESTED_RULES = [
15090
+ "Keep generated scanSync index files out of hand-written changes; generated indexes should only contain one-depth export statements.",
15091
+ "Generated Akan index files should not contain placeholder exports such as aa, dumb, dumb2, or some*Logic.",
15092
+ "Dictionary text should not contain scaffold wording, misspellings, or stale copied domain nouns.",
15093
+ "Warn earlier on very long Akan files: 500 lines for services, 800 lines for Template/Zone files, and 1000 lines for Util files.",
15094
+ "Global declarations, Window augmentation, and prototype mutation should stay in explicitly approved low-level integration files.",
15095
+ "Keep app root folders small and conventional: application code belongs under common, env, lib, page, private, public, script, srvkit, ui, or webkit.",
15096
+ "Keep apps/*/lib and libs/*/lib root files limited to generated support facets such as cnst.ts, db.ts, dict.ts, sig.ts, srv.ts, st.ts, useClient.ts, and useServer.ts.",
15097
+ "Use domain module folders consistently: lib/<model> for database modules, lib/_<service> for service modules, and lib/__scalar/<scalar> for scalar modules.",
15098
+ "Keep module UI filenames predictable: database modules use <Model>.Template/Unit/Util/View/Zone.tsx, service modules use <Service>.Util/Zone.tsx, and scalar modules use <Scalar>.Template/Unit.tsx.",
15099
+ "Move shared app utilities to apps/*/common instead of creating apps/*/base.",
15100
+ "Avoid large mixed-purpose class files; class export files should import helpers from neighboring utility files instead of declaring them inline."
15101
+ ];
15102
+ var APP_ROOT_FILES = new Set([
15103
+ "akan.app.json",
15104
+ "akan.config.ts",
15105
+ "capacitor.config.ts",
15106
+ "client.ts",
15107
+ "main.ts",
15108
+ "package.json",
15109
+ "server.ts",
15110
+ "tsconfig.json"
15111
+ ]);
15112
+ var LIB_ROOT_FILES = new Set([
15113
+ "cnst.ts",
15114
+ "db.ts",
15115
+ "dict.ts",
15116
+ "option.ts",
15117
+ "sig.ts",
15118
+ "srv.ts",
15119
+ "st.ts",
15120
+ "useClient.ts",
15121
+ "useServer.ts"
15122
+ ]);
15123
+ var CONVENTION_SUFFIXES = [
15124
+ ".constant.ts",
15125
+ ".dictionary.ts",
15126
+ ".document.ts",
15127
+ ".service.ts",
15128
+ ".signal.ts",
15129
+ ".store.ts"
15130
+ ];
15131
+
15132
+ class AkanQualityScanner {
15133
+ async scan(workspaceRoot) {
15134
+ const targetFiles = await this.#collectTargetFiles(workspaceRoot);
15135
+ const sourceFiles = await Promise.all(targetFiles.map((file) => this.#readSourceFile(workspaceRoot, file)));
15136
+ const warnings = [
15137
+ ...this.#scanGlobalQuality(sourceFiles),
15138
+ ...sourceFiles.flatMap((sourceFile2) => this.#scanSingleFileQuality(sourceFile2)),
15139
+ ...sourceFiles.flatMap((sourceFile2) => this.#scanConventionQuality(sourceFile2)),
15140
+ ...sourceFiles.flatMap((sourceFile2) => this.#scanLayoutQuality(sourceFile2))
15141
+ ];
15142
+ return {
15143
+ workspaceRoot,
15144
+ scannedFiles: sourceFiles.length,
15145
+ warnings: warnings.sort(compareWarnings),
15146
+ suggestedRules: SUGGESTED_RULES
15147
+ };
15148
+ }
15149
+ async#collectTargetFiles(workspaceRoot) {
15150
+ const ignoreFilter = ignore2().add(await this.#readGitIgnore(workspaceRoot));
15151
+ const files = [];
15152
+ for (const targetRoot of ["apps", "libs"]) {
15153
+ const absoluteTargetRoot = path43.join(workspaceRoot, targetRoot);
15154
+ if (!await isDirectory(absoluteTargetRoot))
15155
+ continue;
15156
+ await this.#walkTargetFiles(workspaceRoot, absoluteTargetRoot, ignoreFilter, files);
15157
+ }
15158
+ return files.sort();
15159
+ }
15160
+ async#readGitIgnore(workspaceRoot) {
15161
+ const gitIgnorePath = path43.join(workspaceRoot, ".gitignore");
15162
+ if (!await Bun.file(gitIgnorePath).exists())
15163
+ return [];
15164
+ return (await readFile2(gitIgnorePath, "utf8")).split(/\r?\n/);
15165
+ }
15166
+ async#walkTargetFiles(workspaceRoot, currentPath, ignoreFilter, files) {
15167
+ const entries = await readdir3(currentPath, { withFileTypes: true });
15168
+ for (const entry of entries) {
15169
+ const absolutePath = path43.join(currentPath, entry.name);
15170
+ const relativePath = toPosix(path43.relative(workspaceRoot, absolutePath));
15171
+ if (shouldSkipPath(relativePath, entry.isDirectory(), ignoreFilter))
15172
+ continue;
15173
+ if (entry.isDirectory()) {
15174
+ await this.#walkTargetFiles(workspaceRoot, absolutePath, ignoreFilter, files);
15175
+ continue;
15176
+ }
15177
+ if ((relativePath.endsWith(".ts") || relativePath.endsWith(".tsx")) && !relativePath.endsWith(".d.ts")) {
15178
+ files.push(relativePath);
15179
+ }
15180
+ }
15181
+ }
15182
+ async#readSourceFile(workspaceRoot, file) {
15183
+ const absolutePath = path43.join(workspaceRoot, file);
15184
+ const content = await readFile2(absolutePath, "utf8");
15185
+ return {
15186
+ file,
15187
+ absolutePath,
15188
+ content,
15189
+ sourceFile: ts11.createSourceFile(file, content, ts11.ScriptTarget.Latest, true, getScriptKind(file))
15190
+ };
15191
+ }
15192
+ #scanGlobalQuality(sourceFiles) {
15193
+ const exportedFunctionLikes = sourceFiles.flatMap((sourceFile2) => getExportedFunctionLikes(sourceFile2));
15194
+ const warnings = [];
15195
+ for (const [name, declarations] of groupBy(exportedFunctionLikes, (declaration) => declaration.name)) {
15196
+ if (declarations.length < 2)
15197
+ continue;
15198
+ warnings.push({
15199
+ rule: "akan.global.duplicate-exported-function-name",
15200
+ scope: "global",
15201
+ severity: "warning",
15202
+ message: `Exported function or class name "${name}" is declared in ${declarations.length} files.`,
15203
+ locations: declarations.map(({ file, line }) => ({ file, line }))
15204
+ });
15205
+ }
15206
+ const declarationsWithBody = exportedFunctionLikes.filter((declaration) => declaration.bodyFingerprint);
15207
+ for (const [fingerprint, declarations] of groupBy(declarationsWithBody, (declaration) => declaration.bodyFingerprint ?? "")) {
15208
+ const uniqueNames = new Set(declarations.map((declaration) => declaration.name));
15209
+ if (declarations.length < 2 || uniqueNames.size < 2 || fingerprint === "")
15210
+ continue;
15211
+ warnings.push({
15212
+ rule: "akan.global.duplicate-exported-function-body",
15213
+ scope: "global",
15214
+ severity: "warning",
15215
+ message: `Exported functions/classes share the same implementation body: ${declarations.map((declaration) => declaration.name).join(", ")}.`,
15216
+ locations: declarations.map(({ file, line }) => ({ file, line }))
15217
+ });
15218
+ }
15219
+ return warnings;
15220
+ }
15221
+ #scanSingleFileQuality(sourceFile2) {
15222
+ const warnings = [];
15223
+ const lineCount = sourceFile2.content.split(/\r?\n/).length;
15224
+ const recommendedLineLimit = getRecommendedLineLimit(sourceFile2.file);
15225
+ if (recommendedLineLimit && lineCount > recommendedLineLimit) {
15226
+ warnings.push({
15227
+ rule: "akan.file.recommended-max-lines",
15228
+ scope: "file",
15229
+ severity: "warning",
15230
+ file: sourceFile2.file,
15231
+ message: `File has ${lineCount} lines. Recommended limit for this file type is ${recommendedLineLimit} lines.`
15232
+ });
15233
+ }
15234
+ if (lineCount > MAX_FILE_LINES) {
15235
+ warnings.push({
15236
+ rule: "akan.file.max-lines",
15237
+ scope: "file",
15238
+ severity: "warning",
15239
+ file: sourceFile2.file,
15240
+ message: `File has ${lineCount} lines. Keep single files under ${MAX_FILE_LINES} lines.`
15241
+ });
15242
+ }
15243
+ warnings.push(...getPlaceholderExportWarnings(sourceFile2));
15244
+ warnings.push(...getDictionaryTextWarnings(sourceFile2));
15245
+ warnings.push(...getGlobalMutationWarnings(sourceFile2));
15246
+ const exportedClassNames = getExportedClassNames(sourceFile2.sourceFile);
15247
+ if (exportedClassNames.length === 0)
15248
+ return warnings;
15249
+ const allowedInterfaceNames = new Set(exportedClassNames.map((name) => `${name}Options`));
15250
+ for (const declaration of getTopLevelDeclarations(sourceFile2)) {
15251
+ if (declaration.kind === "class" && exportedClassNames.includes(declaration.name))
15252
+ continue;
15253
+ if (declaration.kind === "interface" && allowedInterfaceNames.has(declaration.name))
15254
+ continue;
15255
+ warnings.push({
15256
+ rule: "akan.file.class-export-global-declaration",
15257
+ scope: "file",
15258
+ severity: "warning",
15259
+ file: sourceFile2.file,
15260
+ line: declaration.line,
15261
+ message: `Class export files should not declare top-level ${declaration.kind} "${declaration.name}". Move helpers to another file and import them.`
15262
+ });
15263
+ }
15264
+ return warnings;
15265
+ }
15266
+ #scanConventionQuality(sourceFile2) {
15267
+ const suffix = CONVENTION_SUFFIXES.find((candidate) => sourceFile2.file.endsWith(candidate));
15268
+ if (!suffix)
15269
+ return [];
15270
+ const modelName = toPascalCase(path43.basename(sourceFile2.file, suffix));
15271
+ const warnings = [];
15272
+ for (const declaration of getTopLevelDeclarations(sourceFile2)) {
15273
+ if (isAllowedConventionDeclaration(suffix, modelName, declaration))
15274
+ continue;
15275
+ warnings.push({
15276
+ rule: `akan.convention${suffix.replace(".ts", "")}`,
15277
+ scope: "convention",
15278
+ severity: "warning",
15279
+ file: sourceFile2.file,
15280
+ line: declaration.line,
15281
+ message: `${path43.basename(sourceFile2.file)} should not declare top-level ${declaration.kind} "${declaration.name}". Allowed declarations: ${getConventionDescription(suffix, modelName)}.`
15282
+ });
15283
+ }
15284
+ return warnings;
15285
+ }
15286
+ #scanLayoutQuality(sourceFile2) {
15287
+ const segments = sourceFile2.file.split("/");
15288
+ const warnings = [];
15289
+ if (segments[0] === "apps" && segments.length === 3 && !APP_ROOT_FILES.has(segments[2])) {
15290
+ warnings.push({
15291
+ rule: "akan.layout.app-root-file",
15292
+ scope: "layout",
15293
+ severity: "warning",
15294
+ file: sourceFile2.file,
15295
+ message: `Unexpected app root file "${segments[2]}". Keep application code in conventional app folders.`
15296
+ });
15297
+ }
15298
+ const libRootFile = getLibRootFile(sourceFile2.file);
15299
+ if (libRootFile && !LIB_ROOT_FILES.has(libRootFile)) {
15300
+ warnings.push({
15301
+ rule: "akan.layout.lib-root-file",
15302
+ scope: "layout",
15303
+ severity: "warning",
15304
+ file: sourceFile2.file,
15305
+ message: `Unexpected lib root file "${libRootFile}". Keep direct lib root files limited to generated support facets.`
15306
+ });
15307
+ }
15308
+ const moduleUiWarning = getModuleUiWarning(sourceFile2.file);
15309
+ if (moduleUiWarning)
15310
+ warnings.push(moduleUiWarning);
15311
+ return warnings;
15312
+ }
15313
+ }
15314
+ function formatQualityScanResult(result) {
15315
+ const sections = [
15316
+ "Akan Code Quality Scan",
15317
+ `workspace: ${result.workspaceRoot}`,
15318
+ `scanned files: ${result.scannedFiles}`,
15319
+ `warnings: ${result.warnings.length}`,
15320
+ "",
15321
+ "Warnings:",
15322
+ "",
15323
+ ...formatQualityWarnings(result.warnings),
15324
+ "",
15325
+ "Suggested quality rules:",
15326
+ "",
15327
+ ...result.suggestedRules.map((rule) => ` - ${rule}`)
15328
+ ];
15329
+ return sections.join(`
15330
+ `);
15331
+ }
15332
+ function formatQualityWarnings(warnings) {
15333
+ if (warnings.length === 0)
15334
+ return ["No warnings found."];
15335
+ return warnings.flatMap((warning) => {
15336
+ const location = formatQualityLocation(warning.file, warning.line);
15337
+ const lines = [`${location} - warning ${warning.rule}: ${warning.message}`];
15338
+ if (warning.locations?.length) {
15339
+ lines.push(...warning.locations.map(({ file, line }) => ` note: related location ${formatQualityLocation(file, line)}`));
15340
+ }
15341
+ return lines;
15342
+ });
15343
+ }
15344
+ function formatQualityLocation(file, line) {
15345
+ return `${file ?? "<global>"}:${line ?? 1}:1`;
15346
+ }
15347
+ function getExportedFunctionLikes(sourceFile2) {
15348
+ const declarations = [];
15349
+ for (const statement of sourceFile2.sourceFile.statements) {
15350
+ if (ts11.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
15351
+ declarations.push({
15352
+ name: statement.name.text,
15353
+ kind: "function",
15354
+ file: sourceFile2.file,
15355
+ line: getLine(sourceFile2.sourceFile, statement),
15356
+ bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, statement.body)
15357
+ });
15358
+ }
15359
+ if (ts11.isClassDeclaration(statement) && statement.name && isExported(statement)) {
15360
+ declarations.push({
15361
+ name: statement.name.text,
15362
+ kind: "class",
15363
+ file: sourceFile2.file,
15364
+ line: getLine(sourceFile2.sourceFile, statement),
15365
+ bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, statement)
15366
+ });
15367
+ }
15368
+ if (ts11.isVariableStatement(statement) && isExported(statement)) {
15369
+ for (const declaration of statement.declarationList.declarations) {
15370
+ if (!ts11.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
15371
+ continue;
15372
+ declarations.push({
15373
+ name: declaration.name.text,
15374
+ kind: "function-variable",
15375
+ file: sourceFile2.file,
15376
+ line: getLine(sourceFile2.sourceFile, declaration),
15377
+ bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, declaration.initializer)
15378
+ });
15379
+ }
15380
+ }
15381
+ }
15382
+ return declarations;
15383
+ }
15384
+ function getExportedClassNames(sourceFile2) {
15385
+ return sourceFile2.statements.filter((statement) => ts11.isClassDeclaration(statement) && !!statement.name).filter((statement) => isExported(statement)).map((statement) => statement.name.text);
15386
+ }
15387
+ function getPlaceholderExportWarnings(sourceFile2) {
15388
+ if (!sourceFile2.file.endsWith("/index.ts") && !sourceFile2.file.endsWith("/index.tsx"))
15389
+ return [];
15390
+ return getTopLevelDeclarations(sourceFile2).filter((declaration) => declaration.exported && PLACEHOLDER_EXPORT_NAMES.has(declaration.name)).map((declaration) => ({
15391
+ rule: "akan.file.placeholder-export",
15392
+ scope: "file",
15393
+ severity: "warning",
15394
+ file: sourceFile2.file,
15395
+ line: declaration.line,
15396
+ message: `Generated or barrel index file should not export placeholder "${declaration.name}".`
15397
+ }));
15398
+ }
15399
+ function getDictionaryTextWarnings(sourceFile2) {
15400
+ if (!sourceFile2.file.endsWith(".dictionary.ts"))
15401
+ return [];
15402
+ const stalePatterns = [
15403
+ { pattern: /\b[A-Z][A-Za-z0-9]* description\b/, label: "scaffold description text" },
15404
+ { pattern: /settting/, label: "misspelling: settting" },
15405
+ { pattern: /\uBC30\uB108 \uC218/, label: "stale copied Korean domain noun: \uBC30\uB108 \uC218" }
15406
+ ];
15407
+ return stalePatterns.flatMap(({ pattern, label }) => findPatternLines(sourceFile2.content, pattern).map((line) => ({
15408
+ rule: "akan.file.dictionary-stale-text",
15409
+ scope: "file",
15410
+ severity: "warning",
15411
+ file: sourceFile2.file,
15412
+ line,
15413
+ message: `Dictionary text appears to contain ${label}.`
15414
+ })));
15415
+ }
15416
+ function getGlobalMutationWarnings(sourceFile2) {
15417
+ const warnings = [];
15418
+ for (const statement of sourceFile2.sourceFile.statements) {
15419
+ if (ts11.isModuleDeclaration(statement) && statement.name.getText(sourceFile2.sourceFile) === "global") {
15420
+ warnings.push({
15421
+ rule: "akan.file.global-declaration",
15422
+ scope: "file",
15423
+ severity: "warning",
15424
+ file: sourceFile2.file,
15425
+ line: getLine(sourceFile2.sourceFile, statement),
15426
+ message: "Global declarations require an explicit low-level integration allowlist."
15427
+ });
15428
+ }
15429
+ if (ts11.isInterfaceDeclaration(statement) && statement.name.text === "Window") {
15430
+ warnings.push({
15431
+ rule: "akan.file.window-augmentation",
15432
+ scope: "file",
15433
+ severity: "warning",
15434
+ file: sourceFile2.file,
15435
+ line: getLine(sourceFile2.sourceFile, statement),
15436
+ message: "Window augmentation should be isolated to approved browser integration files."
15437
+ });
15438
+ }
15439
+ if (ts11.isExpressionStatement(statement) && statement.expression.getText(sourceFile2.sourceFile).includes(".prototype.")) {
15440
+ warnings.push({
15441
+ rule: "akan.file.prototype-mutation",
15442
+ scope: "file",
15443
+ severity: "warning",
15444
+ file: sourceFile2.file,
15445
+ line: getLine(sourceFile2.sourceFile, statement),
15446
+ message: "Prototype mutation should be avoided or isolated to approved low-level integration files."
15447
+ });
15448
+ }
15449
+ }
15450
+ return warnings;
15451
+ }
15452
+ function getTopLevelDeclarations(sourceFile2) {
15453
+ return sourceFile2.sourceFile.statements.flatMap((statement) => getTopLevelDeclaration(sourceFile2.sourceFile, statement));
15454
+ }
15455
+ function getTopLevelDeclaration(sourceFile2, statement) {
15456
+ const line = getLine(sourceFile2, statement);
15457
+ if (ts11.isClassDeclaration(statement) && statement.name) {
15458
+ return [{ name: statement.name.text, kind: "class", line, exported: isExported(statement), node: statement }];
15459
+ }
15460
+ if (ts11.isFunctionDeclaration(statement) && statement.name) {
15461
+ return [{ name: statement.name.text, kind: "function", line, exported: isExported(statement), node: statement }];
15462
+ }
15463
+ if (ts11.isInterfaceDeclaration(statement)) {
15464
+ return [{ name: statement.name.text, kind: "interface", line, exported: isExported(statement), node: statement }];
15465
+ }
15466
+ if (ts11.isTypeAliasDeclaration(statement)) {
15467
+ return [{ name: statement.name.text, kind: "type", line, exported: isExported(statement), node: statement }];
15468
+ }
15469
+ if (ts11.isEnumDeclaration(statement)) {
15470
+ return [{ name: statement.name.text, kind: "enum", line, exported: isExported(statement), node: statement }];
15471
+ }
15472
+ if (ts11.isVariableStatement(statement)) {
15473
+ return statement.declarationList.declarations.filter((declaration) => ts11.isIdentifier(declaration.name)).map((declaration) => ({
15474
+ name: declaration.name.text,
15475
+ kind: "variable",
15476
+ line: getLine(sourceFile2, declaration),
15477
+ exported: isExported(statement),
15478
+ node: statement
15479
+ }));
15480
+ }
15481
+ if (ts11.isExportDeclaration(statement)) {
15482
+ return [{ name: "export declaration", kind: "export", line, exported: true, node: statement }];
15483
+ }
15484
+ return [];
15485
+ }
15486
+ function isAllowedConventionDeclaration(suffix, modelName, declaration) {
15487
+ if (suffix === ".dictionary.ts")
15488
+ return isExportedConst(declaration) && declaration.name === "dictionary";
15489
+ if (suffix === ".constant.ts")
15490
+ return isAllowedConstantDeclaration(modelName, declaration);
15491
+ if (suffix === ".document.ts")
15492
+ return declaration.kind === "class" && [`${modelName}Filter`, modelName, `${modelName}Model`].includes(declaration.name);
15493
+ if (suffix === ".service.ts")
15494
+ return declaration.kind === "class" && declaration.name === `${modelName}Service`;
15495
+ if (suffix === ".signal.ts")
15496
+ return declaration.kind === "class" && [`${modelName}Internal`, `${modelName}Slice`, `${modelName}Endpoint`].includes(declaration.name);
15497
+ if (suffix === ".store.ts")
15498
+ return declaration.kind === "class" && declaration.name === `${modelName}Store`;
15499
+ return false;
15500
+ }
15501
+ function isAllowedConstantDeclaration(modelName, declaration) {
15502
+ if (declaration.kind !== "class")
15503
+ return false;
15504
+ if ([`${modelName}Input`, `${modelName}Object`, modelName, `Light${modelName}`, `${modelName}Insight`].includes(declaration.name))
15505
+ return true;
15506
+ if (!ts11.isClassDeclaration(declaration.node))
15507
+ return false;
15508
+ const heritageClause = declaration.node.heritageClauses?.find((clause) => clause.token === ts11.SyntaxKind.ExtendsKeyword);
15509
+ const expression = heritageClause?.types[0]?.expression;
15510
+ return !!expression && expression.getText().startsWith("enumOf(");
15511
+ }
15512
+ function getConventionDescription(suffix, modelName) {
15513
+ if (suffix === ".dictionary.ts")
15514
+ return "export const dictionary";
15515
+ if (suffix === ".constant.ts")
15516
+ return `${modelName}Input, ${modelName}Object, ${modelName}, Light${modelName}, ${modelName}Insight, or enumOf classes`;
15517
+ if (suffix === ".document.ts")
15518
+ return `${modelName}Filter, ${modelName}, or ${modelName}Model`;
15519
+ if (suffix === ".service.ts")
15520
+ return `${modelName}Service`;
15521
+ if (suffix === ".signal.ts")
15522
+ return `${modelName}Internal, ${modelName}Slice, or ${modelName}Endpoint`;
15523
+ return `${modelName}Store`;
15524
+ }
15525
+ function getLibRootFile(file) {
15526
+ const segments = file.split("/");
15527
+ if (segments[0] === "libs" && segments.length === 4 && segments[2] === "lib")
15528
+ return segments[3];
15529
+ if (segments[0] === "apps" && segments.length === 5 && segments[2] === "lib")
15530
+ return segments[4];
15531
+ return null;
15532
+ }
15533
+ function getModuleUiWarning(file) {
15534
+ if (!file.endsWith(".tsx"))
15535
+ return null;
15536
+ const moduleInfo = getModuleInfo(file);
15537
+ if (!moduleInfo)
15538
+ return null;
15539
+ const { moduleName, fileName, kind } = moduleInfo;
15540
+ const pascalName = toPascalCase(moduleName.replace(/^_+/, ""));
15541
+ const allowedSuffixes = kind === "database" ? ["Template", "Unit", "Util", "View", "Zone"] : kind === "service" ? ["Util", "Zone"] : ["Template", "Unit"];
15542
+ const allowedFileNames = new Set(allowedSuffixes.map((suffix) => `${pascalName}.${suffix}.tsx`));
15543
+ if (allowedFileNames.has(fileName) || fileName.endsWith(".test.tsx") || fileName.endsWith(".spec.tsx"))
15544
+ return null;
15545
+ return {
15546
+ rule: "akan.layout.module-ui-file",
15547
+ scope: "layout",
15548
+ severity: "warning",
15549
+ file,
15550
+ message: `Unexpected ${kind} module UI filename "${fileName}". Expected one of: ${[...allowedFileNames].join(", ")}.`
15551
+ };
15552
+ }
15553
+ function getModuleInfo(file) {
15554
+ const segments = file.split("/");
15555
+ const libIndex = segments.indexOf("lib");
15556
+ if (libIndex < 0)
15557
+ return null;
15558
+ const moduleName = segments[libIndex + 1];
15559
+ if (moduleName === "__scalar") {
15560
+ if (segments.length !== libIndex + 4)
15561
+ return null;
15562
+ return { moduleName: segments[libIndex + 2], fileName: segments[libIndex + 3], kind: "scalar" };
15563
+ }
15564
+ if (segments.length !== libIndex + 3)
15565
+ return null;
15566
+ const fileName = segments[libIndex + 2];
15567
+ if (moduleName.startsWith("__")) {
15568
+ const scalarName = moduleName === "__scalar" ? segments[libIndex + 2] : moduleName;
15569
+ return { moduleName: scalarName, fileName, kind: "scalar" };
15570
+ }
15571
+ if (moduleName.startsWith("_"))
15572
+ return { moduleName, fileName, kind: "service" };
15573
+ return { moduleName, fileName, kind: "database" };
15574
+ }
15575
+ function isExportedConst(declaration) {
15576
+ return declaration.exported && ts11.isVariableStatement(declaration.node) && (declaration.node.declarationList.flags & ts11.NodeFlags.Const) !== 0;
15577
+ }
15578
+ function isExported(node) {
15579
+ return !!ts11.getCombinedModifierFlags(node) && !!(ts11.getCombinedModifierFlags(node) & ts11.ModifierFlags.Export);
15580
+ }
15581
+ function isFunctionLikeInitializer(node) {
15582
+ return !!node && (ts11.isArrowFunction(node) || ts11.isFunctionExpression(node));
15583
+ }
15584
+ function getBodyFingerprint(sourceFile2, node) {
15585
+ if (!node)
15586
+ return;
15587
+ const normalizedBody = node.getText(sourceFile2).replace(/\s+/g, " ").trim();
15588
+ if (normalizedBody.length < 80)
15589
+ return;
15590
+ return createHash("sha256").update(normalizedBody).digest("hex");
15591
+ }
15592
+ function shouldSkipPath(relativePath, isDirectory, ignoreFilter) {
15593
+ const ignorePath = isDirectory ? `${relativePath}/` : relativePath;
15594
+ return relativePath === ".git" || relativePath.includes("/.git/") || relativePath.includes("/node_modules/") || ignoreFilter.ignores(relativePath) || ignoreFilter.ignores(ignorePath);
15595
+ }
15596
+ async function isDirectory(absolutePath) {
15597
+ try {
15598
+ return (await stat4(absolutePath)).isDirectory();
15599
+ } catch {
15600
+ return false;
15601
+ }
15602
+ }
15603
+ function getScriptKind(file) {
15604
+ return file.endsWith(".tsx") ? ts11.ScriptKind.TSX : ts11.ScriptKind.TS;
15605
+ }
15606
+ function getLine(sourceFile2, node) {
15607
+ return sourceFile2.getLineAndCharacterOfPosition(node.getStart(sourceFile2)).line + 1;
15608
+ }
15609
+ function getRecommendedLineLimit(file) {
15610
+ if (file.endsWith(".service.ts"))
15611
+ return 500;
15612
+ if (file.endsWith(".Template.tsx") || file.endsWith(".Zone.tsx"))
15613
+ return 800;
15614
+ if (file.endsWith(".Util.tsx"))
15615
+ return 1000;
15616
+ return null;
15617
+ }
15618
+ function findPatternLines(content, pattern) {
15619
+ return content.split(/\r?\n/).flatMap((line, index) => pattern.test(line) ? [index + 1] : []);
15620
+ }
15621
+ function toPascalCase(value) {
15622
+ return value.replace(/(^|[-_./])([a-zA-Z0-9])/g, (_, __, char) => char.toUpperCase()).replace(/[-_./]/g, "");
15623
+ }
15624
+ function toPosix(value) {
15625
+ return value.split(path43.sep).join("/");
15626
+ }
15627
+ function groupBy(items, getKey) {
15628
+ const grouped = new Map;
15629
+ for (const item of items) {
15630
+ const key = getKey(item);
15631
+ grouped.set(key, [...grouped.get(key) ?? [], item]);
15632
+ }
15633
+ return grouped;
15634
+ }
15635
+ function compareWarnings(a, b) {
15636
+ return a.scope.localeCompare(b.scope) || (a.file ?? "").localeCompare(b.file ?? "") || (a.line ?? 0) - (b.line ?? 0) || a.rule.localeCompare(b.rule);
15637
+ }
15080
15638
  // pkgs/@akanjs/devkit/selectModel.ts
15081
15639
  import { select as select5 } from "@inquirer/prompts";
15082
15640
  // pkgs/@akanjs/devkit/streamAi.ts
@@ -16038,7 +16596,7 @@ class ApplicationCommand extends command("application", [ApplicationScript], ({
16038
16596
  import { Logger as Logger16 } from "akanjs/common";
16039
16597
 
16040
16598
  // pkgs/@akanjs/cli/package/package.runner.ts
16041
- import path42 from "path";
16599
+ import path44 from "path";
16042
16600
  import { Logger as Logger14 } from "akanjs/common";
16043
16601
  var {$: $2 } = globalThis.Bun;
16044
16602
 
@@ -16058,11 +16616,11 @@ class PackageRunner extends runner("package") {
16058
16616
  }
16059
16617
  async#getInstalledPackageJson() {
16060
16618
  const packageJsonCandidates = [
16061
- `${path42.dirname(Bun.main)}/package.json`,
16619
+ `${path44.dirname(Bun.main)}/package.json`,
16062
16620
  `${process.cwd()}/node_modules/akanjs/package.json`
16063
16621
  ];
16064
16622
  try {
16065
- packageJsonCandidates.unshift(Bun.resolveSync("akanjs/package.json", path42.dirname(Bun.main)));
16623
+ packageJsonCandidates.unshift(Bun.resolveSync("akanjs/package.json", path44.dirname(Bun.main)));
16066
16624
  } catch {}
16067
16625
  for (const packageJsonPath of packageJsonCandidates) {
16068
16626
  if (!await Bun.file(packageJsonPath).exists())
@@ -16071,7 +16629,7 @@ class PackageRunner extends runner("package") {
16071
16629
  if (packageJson.name === "akanjs" || packageJson.name === "@akanjs/cli")
16072
16630
  return packageJson;
16073
16631
  }
16074
- throw new Error(`[package] failed to locate akanjs package.json from ${path42.dirname(Bun.main)}`);
16632
+ throw new Error(`[package] failed to locate akanjs package.json from ${path44.dirname(Bun.main)}`);
16075
16633
  }
16076
16634
  async createPackage(workspace, pkgName) {
16077
16635
  await workspace.applyTemplate({ basePath: `pkgs/${pkgName}`, template: "pkgRoot", dict: { pkgName } });
@@ -16246,7 +16804,7 @@ class PackageScript extends script("package", [PackageRunner]) {
16246
16804
  }
16247
16805
 
16248
16806
  // pkgs/@akanjs/cli/cloud/cloud.runner.ts
16249
- import path43 from "path";
16807
+ import path45 from "path";
16250
16808
  import { confirm as confirm4, input as input5, select as select8 } from "@inquirer/prompts";
16251
16809
  import { Logger as Logger15, sleep } from "akanjs/common";
16252
16810
  import chalk7 from "chalk";
@@ -16517,7 +17075,7 @@ ${chalk7.green("\u27A4")} Authentication Required`));
16517
17075
  await Promise.all(akanPkgs.map(async (library) => {
16518
17076
  Logger15.info(`Publishing ${library}@${nextVersion} to ${registry ?? "npm"}...`);
16519
17077
  await workspace.spawn("npm", ["publish", "--tag", tag, ...this.#getRegistryArgs(registry), ...this.#getLocalRegistryAuthArgs(registry)], {
16520
- cwd: path43.join(workspace.workspaceRoot, "dist/pkgs", library),
17078
+ cwd: path45.join(workspace.workspaceRoot, "dist/pkgs", library),
16521
17079
  env: this.#getRegistryEnv(registry),
16522
17080
  stdio: "inherit"
16523
17081
  });
@@ -16571,11 +17129,12 @@ ${chalk7.green("\u27A4")} Authentication Required`));
16571
17129
  async downloadEnv(cloudApi2, workspace, workspaceId) {
16572
17130
  await workspace.mkdir("local");
16573
17131
  const localPath = await cloudApi2.downloadEnv(workspaceId);
16574
- await workspace.spawn("tar", ["-xf", localPath], { cwd: workspace.workspaceRoot });
17132
+ const relativePath = path45.relative(workspace.workspaceRoot, localPath).split(path45.sep).join("/");
17133
+ await workspace.spawn("tar", ["-xf", relativePath], { cwd: workspace.workspaceRoot });
16575
17134
  await workspace.remove(localPath);
16576
17135
  }
16577
17136
  async uploadEnv(cloudApi2, workspaceId, filePath) {
16578
- const file = new File([Bun.file(filePath)], path43.basename(filePath));
17137
+ const file = new File([Bun.file(filePath)], path45.basename(filePath));
16579
17138
  await cloudApi2.uploadEnv(workspaceId, file);
16580
17139
  }
16581
17140
  async downloadEnvByScp(workspace) {
@@ -16667,14 +17226,14 @@ class CloudScript extends script("cloud", [CloudRunner, ApplicationScript, Packa
16667
17226
  }
16668
17227
  async uploadEnv(workspace) {
16669
17228
  const workspaceId = workspace.getWorkspaceId({ allowEmpty: true });
16670
- const { path: path44 } = await this.cloudRunner.gatherEnvFiles(workspace);
17229
+ const { path: path46 } = await this.cloudRunner.gatherEnvFiles(workspace);
16671
17230
  if (workspaceId) {
16672
17231
  await this.login(workspace);
16673
17232
  const cloudApi2 = await CloudApi.fromHost(workspace);
16674
- await this.cloudRunner.uploadEnv(cloudApi2, workspaceId, path44);
17233
+ await this.cloudRunner.uploadEnv(cloudApi2, workspaceId, path46);
16675
17234
  return;
16676
17235
  }
16677
- await this.cloudRunner.uploadEnvByScp(workspace, path44);
17236
+ await this.cloudRunner.uploadEnvByScp(workspace, path46);
16678
17237
  }
16679
17238
  async deployAkan(workspace, { test = true, registryUrl } = {}) {
16680
17239
  const akanPkgs = await this.cloudRunner.getAkanPkgs(workspace);
@@ -16915,8 +17474,8 @@ class RepairRunner extends runner("repair") {
16915
17474
  }
16916
17475
 
16917
17476
  // pkgs/@akanjs/cli/workflow/workflow.runner.ts
16918
- import { mkdir as mkdir12, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
16919
- import path44 from "path";
17477
+ import { mkdir as mkdir12, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
17478
+ import path46 from "path";
16920
17479
  import { capitalize as capitalize8 } from "akanjs/common";
16921
17480
 
16922
17481
  // pkgs/@akanjs/cli/workflows/shared.ts
@@ -17459,7 +18018,7 @@ var workflowSpecs = [
17459
18018
  ];
17460
18019
 
17461
18020
  // pkgs/@akanjs/cli/workflow/workflow.runner.ts
17462
- var resolvePath = (filePath) => path44.isAbsolute(filePath) ? filePath : path44.join(process.cwd(), filePath);
18021
+ var resolvePath = (filePath) => path46.isAbsolute(filePath) ? filePath : path46.join(process.cwd(), filePath);
17463
18022
  var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
17464
18023
  var isWorkflowPlan2 = (value) => {
17465
18024
  if (!isRecord3(value))
@@ -17557,7 +18116,7 @@ var defaultValidationExecutor = (workspace) => async (command3) => {
17557
18116
  };
17558
18117
  }
17559
18118
  };
17560
- var readJsonFile = async (filePath) => JSON.parse(await readFile2(resolvePath(filePath), "utf8"));
18119
+ var readJsonFile = async (filePath) => JSON.parse(await readFile3(resolvePath(filePath), "utf8"));
17561
18120
  var planInputString2 = (plan2, key) => {
17562
18121
  const value = plan2.inputs[key];
17563
18122
  return typeof value === "string" ? value : "";
@@ -17653,7 +18212,7 @@ class WorkflowRunner extends runner("workflow") {
17653
18212
  const plan2 = createWorkflowPlan(spec, compactWorkflowInputs(inputs));
17654
18213
  if (out) {
17655
18214
  const outPath = resolvePath(out);
17656
- await mkdir12(path44.dirname(outPath), { recursive: true });
18215
+ await mkdir12(path46.dirname(outPath), { recursive: true });
17657
18216
  await writeFile2(outPath, jsonText(plan2));
17658
18217
  }
17659
18218
  return format === "json" ? jsonText(plan2) : renderWorkflowPlan(plan2);
@@ -17672,7 +18231,7 @@ class WorkflowRunner extends runner("workflow") {
17672
18231
  };
17673
18232
  let plan2;
17674
18233
  try {
17675
- const parsed = JSON.parse(await readFile2(resolvePath(planPath), "utf8"));
18234
+ const parsed = JSON.parse(await readFile3(resolvePath(planPath), "utf8"));
17676
18235
  if (!isWorkflowPlan2(parsed)) {
17677
18236
  const report = failedApplyReport("unknown", [
17678
18237
  {
@@ -19477,14 +20036,14 @@ class GuidelinePrompt extends Prompter {
19477
20036
  async#getScanFilePaths(matchPattern, { avoidDirs = ["node_modules", ".next"], filterText } = {}) {
19478
20037
  const glob = new Bun.Glob(matchPattern);
19479
20038
  const paths = [];
19480
- for await (const path45 of glob.scan({ cwd: this.workspace.workspaceRoot, absolute: true })) {
19481
- if (avoidDirs.some((dir) => path45.includes(dir)))
20039
+ for await (const path47 of glob.scan({ cwd: this.workspace.workspaceRoot, absolute: true })) {
20040
+ if (avoidDirs.some((dir) => path47.includes(dir)))
19482
20041
  continue;
19483
- const fileContent = await FileSys.readText(path45);
20042
+ const fileContent = await FileSys.readText(path47);
19484
20043
  const textFilter = filterText ? new RegExp(filterText) : null;
19485
20044
  if (filterText && !textFilter?.test(fileContent))
19486
20045
  continue;
19487
- paths.push(path45);
20046
+ paths.push(path47);
19488
20047
  }
19489
20048
  return paths;
19490
20049
  }
@@ -19804,7 +20363,7 @@ class LibraryCommand extends command("library", [LibraryScript], ({ public: targ
19804
20363
 
19805
20364
  // pkgs/@akanjs/cli/localRegistry/localRegistry.runner.ts
19806
20365
  import { mkdir as mkdir13, rm as rm6 } from "fs/promises";
19807
- import path45 from "path";
20366
+ import path47 from "path";
19808
20367
  import { Logger as Logger19 } from "akanjs/common";
19809
20368
  var defaultLocalRegistryUrl = "http://127.0.0.1:4873";
19810
20369
  var containerName = "akan-verdaccio";
@@ -19822,8 +20381,8 @@ class LocalRegistryRunner extends runner("localRegistry") {
19822
20381
  Logger19.info(`Local registry is already running at ${registry}`);
19823
20382
  return registry;
19824
20383
  } catch {}
19825
- const configPath2 = path45.join(workspace.workspaceRoot, "pkgs/@akanjs/cli/localRegistry/verdaccio.yaml");
19826
- const storagePath = path45.join(workspace.workspaceRoot, ".akan/verdaccio/storage");
20384
+ const configPath2 = path47.join(workspace.workspaceRoot, "pkgs/@akanjs/cli/localRegistry/verdaccio.yaml");
20385
+ const storagePath = path47.join(workspace.workspaceRoot, ".akan/verdaccio/storage");
19827
20386
  await mkdir13(storagePath, { recursive: true });
19828
20387
  await workspace.spawn("docker", [
19829
20388
  "run",
@@ -19846,13 +20405,13 @@ class LocalRegistryRunner extends runner("localRegistry") {
19846
20405
  try {
19847
20406
  await workspace.spawn("docker", ["rm", "-f", containerName], { stdio: "inherit" });
19848
20407
  } catch {}
19849
- await rm6(path45.join(workspace.workspaceRoot, ".akan/verdaccio"), { recursive: true, force: true });
20408
+ await rm6(path47.join(workspace.workspaceRoot, ".akan/verdaccio"), { recursive: true, force: true });
19850
20409
  Logger19.info("Local registry storage has been reset");
19851
20410
  }
19852
20411
  async smoke(workspace, { registryUrl } = {}) {
19853
20412
  const registry = this.getRegistryUrl(registryUrl);
19854
- const smokeRoot = path45.join(workspace.workspaceRoot, ".akan/e2e");
19855
- await rm6(path45.join(smokeRoot, smokeRepoName), { recursive: true, force: true });
20413
+ const smokeRoot = path47.join(workspace.workspaceRoot, ".akan/e2e");
20414
+ await rm6(path47.join(smokeRoot, smokeRepoName), { recursive: true, force: true });
19856
20415
  await workspace.spawn(process.execPath, [
19857
20416
  "dist/pkgs/create-akan-workspace/index.js",
19858
20417
  smokeRepoName,
@@ -19869,12 +20428,12 @@ class LocalRegistryRunner extends runner("localRegistry") {
19869
20428
  stdio: "inherit"
19870
20429
  });
19871
20430
  await workspace.spawn("akan", ["typecheck", smokeAppName], {
19872
- cwd: path45.join(smokeRoot, smokeRepoName),
20431
+ cwd: path47.join(smokeRoot, smokeRepoName),
19873
20432
  env: { ...process.env, AKAN_NPM_REGISTRY: registry, NPM_CONFIG_REGISTRY: registry },
19874
20433
  stdio: "inherit"
19875
20434
  });
19876
20435
  await workspace.spawn("akan", ["build", smokeAppName], {
19877
- cwd: path45.join(smokeRoot, smokeRepoName),
20436
+ cwd: path47.join(smokeRoot, smokeRepoName),
19878
20437
  env: { ...process.env, AKAN_NPM_REGISTRY: registry, NPM_CONFIG_REGISTRY: registry },
19879
20438
  stdio: "inherit"
19880
20439
  });
@@ -20053,8 +20612,46 @@ class PrimitiveCommand extends command("primitive", [PrimitiveScript], ({ public
20053
20612
  })) {
20054
20613
  }
20055
20614
 
20056
- // pkgs/@akanjs/cli/repair/repair.script.ts
20615
+ // pkgs/@akanjs/cli/quality/quality.script.ts
20057
20616
  import { Logger as Logger22 } from "akanjs/common";
20617
+
20618
+ // pkgs/@akanjs/cli/quality/quality.runner.ts
20619
+ class QualityRunner extends runner("quality") {
20620
+ async scan(workspace) {
20621
+ return await new AkanQualityScanner().scan(workspace.workspaceRoot);
20622
+ }
20623
+ }
20624
+
20625
+ // pkgs/@akanjs/cli/quality/quality.script.ts
20626
+ class QualityScript extends script("quality", [QualityRunner]) {
20627
+ async scan(workspace, format = "text") {
20628
+ const spinner2 = workspace.spinning("Scanning Akan code quality...");
20629
+ const result = await this.qualityRunner.scan(workspace);
20630
+ spinner2.succeed(`Quality scan completed (${result.scannedFiles} files, ${result.warnings.length} warnings)`);
20631
+ Logger22.rawLog(format === "json" ? JSON.stringify(result, null, 2) : formatQualityScanResult(result));
20632
+ }
20633
+ }
20634
+
20635
+ // pkgs/@akanjs/cli/quality/quality.command.ts
20636
+ class QualityCommand extends command("quality", [QualityScript], ({ public: target }) => ({
20637
+ quality: target({ desc: "Scan apps and libs for Akan code quality warnings" }).arg("action", String, {
20638
+ desc: "quality action",
20639
+ default: "scan",
20640
+ enum: ["scan"]
20641
+ }).option("format", String, {
20642
+ desc: "output format",
20643
+ default: "text",
20644
+ enum: ["text", "json"]
20645
+ }).with(Workspace).exec(async function(action, format, workspace) {
20646
+ if (action !== "scan")
20647
+ throw new Error(`Unknown quality action: ${action}. Use "scan".`);
20648
+ await this.qualityScript.scan(workspace, format);
20649
+ })
20650
+ })) {
20651
+ }
20652
+
20653
+ // pkgs/@akanjs/cli/repair/repair.script.ts
20654
+ import { Logger as Logger23 } from "akanjs/common";
20058
20655
  class RepairScript extends script("repair", [RepairRunner]) {
20059
20656
  async repair(kind, {
20060
20657
  workspace,
@@ -20063,7 +20660,7 @@ class RepairScript extends script("repair", [RepairRunner]) {
20063
20660
  target = null,
20064
20661
  format = "markdown"
20065
20662
  }) {
20066
- Logger22.rawLog(await this.repairRunner.repair(kind, {
20663
+ Logger23.rawLog(await this.repairRunner.repair(kind, {
20067
20664
  workspace,
20068
20665
  app,
20069
20666
  module,
@@ -20103,12 +20700,12 @@ class RepairCommand extends command("repair", [RepairScript], ({ public: target
20103
20700
  }
20104
20701
 
20105
20702
  // pkgs/@akanjs/cli/scalar/scalar.command.ts
20106
- import { Logger as Logger23, lowerlize as lowerlize3 } from "akanjs/common";
20703
+ import { Logger as Logger24, lowerlize as lowerlize3 } from "akanjs/common";
20107
20704
  class ScalarCommand extends command("scalar", [ScalarScript], ({ public: target }) => ({
20108
20705
  createScalar: target({ desc: "Create a new scalar type (simple data model without DB)" }).arg("scalarName", String, { desc: "name of scalar" }).with(Sys).option("ai", Boolean, { default: false, desc: "use ai to create scalar" }).option("format", String, { flag: "o", desc: "output format", default: "markdown", enum: ["markdown", "json"] }).exec(async function(scalarName, sys3, ai, format) {
20109
20706
  const name = lowerlize3(scalarName.replace(/ /g, ""));
20110
20707
  const report = ai ? await this.scalarScript.createScalarWithAi(sys3, name) : await this.scalarScript.createScalar(sys3, name);
20111
- Logger23.rawLog(renderPrimitiveReport(report, format));
20708
+ Logger24.rawLog(renderPrimitiveReport(report, format));
20112
20709
  }),
20113
20710
  removeScalar: target({ desc: "Remove a scalar type from an app or library" }).arg("scalarName", String, { desc: "name of scalar" }).with(Sys).exec(async function(scalarName, sys3) {
20114
20711
  await this.scalarScript.removeScalar(sys3, scalarName);
@@ -20117,7 +20714,7 @@ class ScalarCommand extends command("scalar", [ScalarScript], ({ public: target
20117
20714
  }
20118
20715
 
20119
20716
  // pkgs/@akanjs/cli/workflow/workflow.script.ts
20120
- import { Logger as Logger24 } from "akanjs/common";
20717
+ import { Logger as Logger25 } from "akanjs/common";
20121
20718
  class WorkflowScript extends script("workflow", [WorkflowRunner, ModuleScript, ScalarScript, PrimitiveScript]) {
20122
20719
  async workflow(action, workflow2, inputs, {
20123
20720
  format = "markdown",
@@ -20126,23 +20723,23 @@ class WorkflowScript extends script("workflow", [WorkflowRunner, ModuleScript, S
20126
20723
  workspace
20127
20724
  } = {}) {
20128
20725
  if (action === "list") {
20129
- Logger24.rawLog(this.workflowRunner.list({ format }));
20726
+ Logger25.rawLog(this.workflowRunner.list({ format }));
20130
20727
  return;
20131
20728
  }
20132
20729
  if (!workflow2)
20133
20730
  throw new Error(`Workflow name is required for "${action}".`);
20134
20731
  if (action === "explain") {
20135
- Logger24.rawLog(this.workflowRunner.explain(workflow2, { format }));
20732
+ Logger25.rawLog(this.workflowRunner.explain(workflow2, { format }));
20136
20733
  return;
20137
20734
  }
20138
20735
  if (action === "plan") {
20139
- Logger24.rawLog(await this.workflowRunner.plan(workflow2, inputs, { format, out }));
20736
+ Logger25.rawLog(await this.workflowRunner.plan(workflow2, inputs, { format, out }));
20140
20737
  return;
20141
20738
  }
20142
20739
  if (action === "apply") {
20143
20740
  if (!workspace)
20144
20741
  throw new Error("Workspace is required for workflow apply.");
20145
- Logger24.rawLog(await this.workflowRunner.apply(workflow2, {
20742
+ Logger25.rawLog(await this.workflowRunner.apply(workflow2, {
20146
20743
  dryRun,
20147
20744
  format,
20148
20745
  workspace,
@@ -20160,13 +20757,13 @@ class WorkflowScript extends script("workflow", [WorkflowRunner, ModuleScript, S
20160
20757
  if (action === "validate") {
20161
20758
  if (!workspace)
20162
20759
  throw new Error("Workspace is required for workflow validate.");
20163
- Logger24.rawLog(await this.workflowRunner.validate(workflow2, { format, workspace }));
20760
+ Logger25.rawLog(await this.workflowRunner.validate(workflow2, { format, workspace }));
20164
20761
  return;
20165
20762
  }
20166
20763
  if (action === "report") {
20167
20764
  if (!workspace)
20168
20765
  throw new Error("Workspace is required for workflow report.");
20169
- Logger24.rawLog(await this.workflowRunner.report(workflow2, { format, workspace }));
20766
+ Logger25.rawLog(await this.workflowRunner.report(workflow2, { format, workspace }));
20170
20767
  return;
20171
20768
  }
20172
20769
  throw new Error(`Unknown workflow action: ${action}. Use list, explain, plan, apply, validate, or report.`);
@@ -20190,11 +20787,11 @@ class WorkflowCommand extends command("workflow", [WorkflowScript], ({ public: t
20190
20787
  }
20191
20788
 
20192
20789
  // pkgs/@akanjs/cli/workspace/workspace.script.ts
20193
- import path47 from "path";
20194
- import { Logger as Logger25 } from "akanjs/common";
20790
+ import path49 from "path";
20791
+ import { Logger as Logger26 } from "akanjs/common";
20195
20792
 
20196
20793
  // pkgs/@akanjs/cli/workspace/workspace.runner.ts
20197
- import path46 from "path";
20794
+ import path48 from "path";
20198
20795
  var defaultWorkspacePeerDependencies = new Set([
20199
20796
  "@react-spring/web",
20200
20797
  "@use-gesture/react",
@@ -20245,7 +20842,7 @@ class WorkspaceRunner extends runner("workspace") {
20245
20842
  owner = ""
20246
20843
  }) {
20247
20844
  const cwdPath = process.cwd();
20248
- const workspaceRoot = path46.join(cwdPath, dirname2, repoName);
20845
+ const workspaceRoot = path48.join(cwdPath, dirname2, repoName);
20249
20846
  const normalizedRegistryUrl = registryUrl ? getNpmRegistryUrl(registryUrl) : undefined;
20250
20847
  const workspace = WorkspaceExecutor.fromRoot({ workspaceRoot, repoName });
20251
20848
  const templateSpinner = workspace.spinning(`Creating workspace template files in ${dirname2}/${repoName}...`);
@@ -20301,9 +20898,9 @@ class WorkspaceRunner extends runner("workspace") {
20301
20898
  }
20302
20899
  async#getCliPackageJson() {
20303
20900
  const packageJsonCandidates = [
20304
- path46.join(import.meta.dir, "../package.json"),
20305
- path46.join(import.meta.dir, "package.json"),
20306
- path46.join(path46.dirname(Bun.main), "package.json")
20901
+ path48.join(import.meta.dir, "../package.json"),
20902
+ path48.join(import.meta.dir, "package.json"),
20903
+ path48.join(path48.dirname(Bun.main), "package.json")
20307
20904
  ];
20308
20905
  try {
20309
20906
  packageJsonCandidates.unshift(Bun.resolveSync("@akanjs/cli/package.json", import.meta.dir));
@@ -20319,9 +20916,9 @@ class WorkspaceRunner extends runner("workspace") {
20319
20916
  }
20320
20917
  async#getAkanPackageJson() {
20321
20918
  const packageJsonCandidates = [
20322
- path46.join(import.meta.dir, "../../../akanjs/package.json"),
20323
- path46.join(process.cwd(), "pkgs/akanjs/package.json"),
20324
- path46.join(path46.dirname(Bun.main), "node_modules/akanjs/package.json")
20919
+ path48.join(import.meta.dir, "../../../akanjs/package.json"),
20920
+ path48.join(process.cwd(), "pkgs/akanjs/package.json"),
20921
+ path48.join(path48.dirname(Bun.main), "node_modules/akanjs/package.json")
20325
20922
  ];
20326
20923
  try {
20327
20924
  packageJsonCandidates.unshift(Bun.resolveSync("akanjs/package.json", import.meta.dir));
@@ -20335,13 +20932,13 @@ class WorkspaceRunner extends runner("workspace") {
20335
20932
  }
20336
20933
  let current = import.meta.dir;
20337
20934
  for (let depth = 0;depth < 6; depth++) {
20338
- const packageJsonPath = path46.join(current, "package.json");
20935
+ const packageJsonPath = path48.join(current, "package.json");
20339
20936
  if (await Bun.file(packageJsonPath).exists()) {
20340
20937
  const packageJson = await FileSys.readJson(packageJsonPath);
20341
20938
  if (packageJson.name === "akanjs")
20342
20939
  return packageJson;
20343
20940
  }
20344
- const parent = path46.dirname(current);
20941
+ const parent = path48.dirname(current);
20345
20942
  if (parent === current)
20346
20943
  break;
20347
20944
  current = parent;
@@ -20418,11 +21015,11 @@ class WorkspaceScript extends script("workspace", [
20418
21015
  } catch (_) {
20419
21016
  gitSpinner.fail("Git repository initialization failed. It's not fatal, you can commit manually");
20420
21017
  }
20421
- const workspacePath2 = path47.join(dirname2, repoName);
20422
- Logger25.rawLog(`
21018
+ const workspacePath2 = path49.join(dirname2, repoName);
21019
+ Logger26.rawLog(`
20423
21020
  \uD83C\uDF89 Welcome aboard! Workspace created in ${dirname2}/${repoName}`);
20424
- Logger25.rawLog(`\uD83D\uDE80 Run \`cd ${workspacePath2} && akan start ${appName}\` to start the development server.`);
20425
- Logger25.rawLog(`
21021
+ Logger26.rawLog(`\uD83D\uDE80 Run \`cd ${workspacePath2} && akan start ${appName}\` to start the development server.`);
21022
+ Logger26.rawLog(`
20426
21023
  \uD83D\uDC4B Happy coding!`);
20427
21024
  }
20428
21025
  async generateAgentRules(workspace, { overwrite = false, cursorRules = true } = {}) {
@@ -20538,4 +21135,4 @@ class WorkspaceCommand extends command("workspace", [WorkspaceScript], ({ public
20538
21135
  }
20539
21136
 
20540
21137
  // pkgs/@akanjs/cli/index.ts
20541
- runCommands(WorkspaceCommand, AgentCommand, ApplicationCommand, LibraryCommand, LocalRegistryCommand, PackageCommand, ModuleCommand, PageCommand, ContextCommand, CloudCommand, GuidelineCommand, ScalarCommand, PrimitiveCommand, RepairCommand, WorkflowCommand);
21138
+ runCommands(WorkspaceCommand, AgentCommand, ApplicationCommand, LibraryCommand, LocalRegistryCommand, PackageCommand, ModuleCommand, PageCommand, ContextCommand, CloudCommand, GuidelineCommand, ScalarCommand, PrimitiveCommand, QualityCommand, RepairCommand, WorkflowCommand);