elcrm 1.1.19 → 1.1.21

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 (2) hide show
  1. package/dist/index.js +669 -70
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -2,8 +2,8 @@
2
2
  // @bun
3
3
 
4
4
  // src/index.ts
5
- import { readFileSync as readFileSync23 } from "fs";
6
- import { dirname as dirname4, join as join18 } from "path";
5
+ import { readFileSync as readFileSync28 } from "fs";
6
+ import { dirname as dirname4, join as join25 } from "path";
7
7
  import { fileURLToPath as fileURLToPath2 } from "url";
8
8
 
9
9
  // src/parseArgs.ts
@@ -35,7 +35,9 @@ function parseArgs(argv = process.argv.slice(2)) {
35
35
  "name",
36
36
  "dir",
37
37
  "folder",
38
- "out"
38
+ "out",
39
+ "api",
40
+ "web"
39
41
  ]);
40
42
  if (valueOpts.has(body)) {
41
43
  options[body] = next;
@@ -3669,10 +3671,11 @@ async function cmdMigrate(parsed) {
3669
3671
  }
3670
3672
 
3671
3673
  // src/commands/doctor.ts
3672
- import { resolve as resolve5 } from "path";
3674
+ import { resolve as resolve10 } from "path";
3673
3675
 
3674
- // src/commands/docs.ts
3675
- import { join as join13, resolve as resolve3 } from "path";
3676
+ // src/lib/kit/doctor-kit.ts
3677
+ import { existsSync as existsSync15 } from "fs";
3678
+ import { join as join17, resolve as resolve7 } from "path";
3676
3679
 
3677
3680
  // src/lib/projectKind.ts
3678
3681
  function detectProjectKind(cwd) {
@@ -3702,6 +3705,478 @@ function isElcrmLibPackage(cwd) {
3702
3705
  return typeof name === "string" && name.startsWith("@elcrm/");
3703
3706
  }
3704
3707
 
3708
+ // src/lib/kit/paths.ts
3709
+ import { existsSync as existsSync13, readFileSync as readFileSync20 } from "fs";
3710
+ import { join as join13, resolve as resolve4 } from "path";
3711
+
3712
+ // src/lib/kit/config.ts
3713
+ import { existsSync as existsSync12, mkdirSync as mkdirSync2, readFileSync as readFileSync19, writeFileSync as writeFileSync6 } from "fs";
3714
+ import { homedir } from "os";
3715
+ import { join as join12, resolve as resolve3 } from "path";
3716
+ var FILE = "kit-paths.json";
3717
+ function globalConfigPath() {
3718
+ return join12(homedir(), ".elcrm", FILE);
3719
+ }
3720
+ function localConfigPath(cwd) {
3721
+ return join12(resolve3(cwd), ".elcrm", FILE);
3722
+ }
3723
+ function readJson(path) {
3724
+ if (!existsSync12(path))
3725
+ return {};
3726
+ try {
3727
+ return JSON.parse(readFileSync19(path, "utf8"));
3728
+ } catch {
3729
+ return {};
3730
+ }
3731
+ }
3732
+ function loadKitPathsConfig(cwd) {
3733
+ const global = readJson(globalConfigPath());
3734
+ const local = readJson(localConfigPath(cwd));
3735
+ return {
3736
+ apiDir: local.apiDir || global.apiDir,
3737
+ webDir: local.webDir || global.webDir
3738
+ };
3739
+ }
3740
+ function readName(dir) {
3741
+ try {
3742
+ const j = JSON.parse(readFileSync19(join12(dir, "package.json"), "utf8"));
3743
+ return j.name ?? null;
3744
+ } catch {
3745
+ return null;
3746
+ }
3747
+ }
3748
+ function validateApiDir(dir) {
3749
+ const p = resolve3(dir);
3750
+ if (!existsSync12(join12(p, "src/kit/catalog.ts")))
3751
+ return null;
3752
+ return p;
3753
+ }
3754
+ function validateWebDir(dir) {
3755
+ const p = resolve3(dir);
3756
+ if (readName(p) !== "elui-web")
3757
+ return null;
3758
+ return p;
3759
+ }
3760
+ function saveKitPathsConfig(cwd, patch, global = false) {
3761
+ const path = global ? globalConfigPath() : localConfigPath(cwd);
3762
+ const prev = readJson(path);
3763
+ const next = { ...prev, ...patch };
3764
+ if (next.apiDir) {
3765
+ const ok = validateApiDir(next.apiDir);
3766
+ if (!ok)
3767
+ throw new Error("apiDir: \u043D\u0443\u0436\u0435\u043D \u043A\u0430\u0442\u0430\u043B\u043E\u0433 elUI.api (src/kit/catalog.ts)");
3768
+ next.apiDir = ok;
3769
+ }
3770
+ if (next.webDir) {
3771
+ const ok = validateWebDir(next.webDir);
3772
+ if (!ok)
3773
+ throw new Error("webDir: \u043D\u0443\u0436\u0435\u043D \u043A\u0430\u0442\u0430\u043B\u043E\u0433 elUI.web (package name elui-web)");
3774
+ next.webDir = ok;
3775
+ }
3776
+ mkdirSync2(join12(path, ".."), { recursive: true });
3777
+ writeFileSync6(path, JSON.stringify(next, null, 2) + `
3778
+ `, "utf8");
3779
+ return next;
3780
+ }
3781
+ function persistKitPathsFromOpts(opts) {
3782
+ if (opts.dry)
3783
+ return;
3784
+ const patch = {};
3785
+ if (opts.apiDir?.trim())
3786
+ patch.apiDir = opts.apiDir.trim();
3787
+ if (opts.webDir?.trim())
3788
+ patch.webDir = opts.webDir.trim();
3789
+ if (!patch.apiDir && !patch.webDir)
3790
+ return;
3791
+ saveKitPathsConfig(opts.cwd, patch, opts.global ?? true);
3792
+ }
3793
+
3794
+ // src/lib/kit/paths.ts
3795
+ function readName2(dir) {
3796
+ try {
3797
+ const j = JSON.parse(readFileSync20(join13(dir, "package.json"), "utf8"));
3798
+ return j.name ?? null;
3799
+ } catch {
3800
+ return null;
3801
+ }
3802
+ }
3803
+ function findEluiApiDir(from, override) {
3804
+ if (override?.trim()) {
3805
+ const p = resolve4(override.trim());
3806
+ if (existsSync13(join13(p, "src/kit/catalog.ts")))
3807
+ return p;
3808
+ return null;
3809
+ }
3810
+ const env = process.env.ELUI_API_DIR?.trim();
3811
+ if (env && existsSync13(join13(env, "src/kit/catalog.ts")))
3812
+ return resolve4(env);
3813
+ const saved = loadKitPathsConfig(from).apiDir;
3814
+ if (saved && existsSync13(join13(saved, "src/kit/catalog.ts")))
3815
+ return resolve4(saved);
3816
+ let dir = resolve4(from);
3817
+ for (let i = 0;i < 8; i++) {
3818
+ for (const name of ["elUI.api", "ui-kit.api"]) {
3819
+ const candidate = join13(dir, name);
3820
+ if (existsSync13(join13(candidate, "src/kit/catalog.ts")))
3821
+ return candidate;
3822
+ }
3823
+ const parent = join13(dir, "..");
3824
+ if (parent === dir)
3825
+ break;
3826
+ dir = parent;
3827
+ }
3828
+ return null;
3829
+ }
3830
+ function findEluiWebDir(from, override) {
3831
+ if (override?.trim()) {
3832
+ const p = resolve4(override.trim());
3833
+ if (readName2(p) === "elui-web")
3834
+ return p;
3835
+ return null;
3836
+ }
3837
+ const env = process.env.ELUI_WEB_DIR?.trim();
3838
+ if (env && readName2(env) === "elui-web")
3839
+ return resolve4(env);
3840
+ const saved = loadKitPathsConfig(from).webDir;
3841
+ if (saved && readName2(saved) === "elui-web")
3842
+ return resolve4(saved);
3843
+ let dir = resolve4(from);
3844
+ for (let i = 0;i < 8; i++) {
3845
+ for (const name of ["elUI.web", "Uikit.web"]) {
3846
+ const candidate = join13(dir, name);
3847
+ if (readName2(candidate) === "elui-web")
3848
+ return candidate;
3849
+ }
3850
+ dir = join13(dir, "..");
3851
+ }
3852
+ return null;
3853
+ }
3854
+ function kitPkgSlug(npmName) {
3855
+ if (npmName === "@elcrm/components")
3856
+ return "components";
3857
+ if (npmName === "@elcrm/form")
3858
+ return "form";
3859
+ return null;
3860
+ }
3861
+ function manifestFileName(npmName) {
3862
+ return npmName.replace("@", "").replace("/", "-") + ".json";
3863
+ }
3864
+
3865
+ // src/lib/kit/sync.ts
3866
+ import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync23, writeFileSync as writeFileSync8 } from "fs";
3867
+ import { join as join16, resolve as resolve6 } from "path";
3868
+ import { spawnSync as spawnSync2 } from "child_process";
3869
+
3870
+ // src/lib/kit/emit.ts
3871
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync22, writeFileSync as writeFileSync7 } from "fs";
3872
+ import { join as join15, resolve as resolve5 } from "path";
3873
+
3874
+ // src/lib/kit/parse-dts.ts
3875
+ import { readFileSync as readFileSync21, readdirSync as readdirSync8, statSync as statSync8 } from "fs";
3876
+ import { basename, join as join14 } from "path";
3877
+ var SKIP_SLUGS = new Set([
3878
+ "index",
3879
+ "types",
3880
+ "cx",
3881
+ "overlayZ",
3882
+ "lazyRoute",
3883
+ "stackGap",
3884
+ "rovingKeys",
3885
+ "CheckIcon",
3886
+ "useControllableValue",
3887
+ "badgeText"
3888
+ ]);
3889
+ function isMainComponentDoc(doc) {
3890
+ if (!doc)
3891
+ return false;
3892
+ if (doc.includes("@example"))
3893
+ return true;
3894
+ const lines = doc.split(`
3895
+ `).filter((l) => l.trim());
3896
+ if (lines.length >= 2)
3897
+ return true;
3898
+ if (/^Props\s/i.test(doc.trim()))
3899
+ return false;
3900
+ return doc.length > 72;
3901
+ }
3902
+ function componentDoc(content, slug) {
3903
+ const fnAnchor = "export declare function " + slug;
3904
+ const propsAnchor = "export type " + slug + "Props";
3905
+ const fnDoc = leadingDoc(content, fnAnchor);
3906
+ const propsDoc = leadingDoc(content, propsAnchor);
3907
+ if (isMainComponentDoc(fnDoc))
3908
+ return fnDoc;
3909
+ if (isMainComponentDoc(propsDoc))
3910
+ return propsDoc;
3911
+ return fnDoc || propsDoc;
3912
+ }
3913
+ function leadingDoc(content, anchor) {
3914
+ const idx = content.indexOf(anchor);
3915
+ if (idx < 0)
3916
+ return "";
3917
+ const head = content.slice(0, idx).trimEnd();
3918
+ const start = head.lastIndexOf("/**");
3919
+ if (start < 0)
3920
+ return "";
3921
+ const end = head.indexOf("*/", start);
3922
+ if (end < 0)
3923
+ return "";
3924
+ return head.slice(start + 3, end).split(`
3925
+ `).map((l) => l.replace(/^\s*\*\s?/, "").trimEnd()).join(`
3926
+ `).trim();
3927
+ }
3928
+ function examplesFromDoc(doc) {
3929
+ const out = [];
3930
+ const fence = String.fromCharCode(96, 96, 96);
3931
+ const re = new RegExp("@example\\s*\\n?\\s*" + fence + "[\\w]*\\s*\\n([\\s\\S]*?)" + fence, "g");
3932
+ let m;
3933
+ let i = 0;
3934
+ while (m = re.exec(doc)) {
3935
+ const code = m[1].trim();
3936
+ if (!code)
3937
+ continue;
3938
+ out.push({
3939
+ title: i === 0 ? "\u041F\u0440\u0438\u043C\u0435\u0440" : "\u041F\u0440\u0438\u043C\u0435\u0440 " + (i + 1),
3940
+ code
3941
+ });
3942
+ i++;
3943
+ }
3944
+ return out;
3945
+ }
3946
+ function descriptionFromDoc(doc) {
3947
+ const lines = doc.split(`
3948
+ `).map((l) => l.trim()).filter(Boolean);
3949
+ const parts = [];
3950
+ for (const line of lines) {
3951
+ if (line.startsWith("@"))
3952
+ break;
3953
+ parts.push(line);
3954
+ }
3955
+ return parts.join(" ").replace(/\s+/g, " ").trim();
3956
+ }
3957
+ function propsFromPropsBlock(block) {
3958
+ const out = [];
3959
+ const re = /\/\*\*([\s\S]*?)\*\/\s*(?:readonly\s+)?(\w+)\??\s*:\s*([^;]+);/g;
3960
+ let m;
3961
+ while (m = re.exec(block)) {
3962
+ const name = m[2];
3963
+ if (name === "className" || name.startsWith("__"))
3964
+ continue;
3965
+ const doc = m[1].split(`
3966
+ `).map((l) => l.replace(/^\s*\*\s?/, "").trim()).filter(Boolean).join(" ");
3967
+ out.push({
3968
+ name,
3969
+ type: m[3].replace(/\s+/g, " ").trim(),
3970
+ description: doc
3971
+ });
3972
+ }
3973
+ return out;
3974
+ }
3975
+ function parseComponentDts(content, slug) {
3976
+ if (!new RegExp("export declare function " + slug + "\\b").test(content)) {
3977
+ return null;
3978
+ }
3979
+ const doc = componentDoc(content, slug);
3980
+ const propsMatch = content.match(new RegExp("export type " + slug + "Props\\s*=\\s*([\\s\\S]*?);\\s*(?:export|$)"));
3981
+ const propsBlock = propsMatch?.[1] ?? "";
3982
+ return {
3983
+ slug,
3984
+ title: slug,
3985
+ description: descriptionFromDoc(doc) || slug,
3986
+ importFrom: "@elcrm/components",
3987
+ examples: examplesFromDoc(doc),
3988
+ props: propsFromPropsBlock(propsBlock)
3989
+ };
3990
+ }
3991
+ function scanDistComponents(distDir, importFrom) {
3992
+ const out = [];
3993
+ for (const name of readdirSync8(distDir)) {
3994
+ if (!name.endsWith(".d.ts"))
3995
+ continue;
3996
+ const slug = basename(name, ".d.ts");
3997
+ if (SKIP_SLUGS.has(slug) || slug.includes("."))
3998
+ continue;
3999
+ const path = join14(distDir, name);
4000
+ if (!statSync8(path).isFile())
4001
+ continue;
4002
+ const content = readFileSync21(path, "utf8");
4003
+ const parsed = parseComponentDts(content, slug);
4004
+ if (!parsed)
4005
+ continue;
4006
+ parsed.importFrom = importFrom;
4007
+ out.push(parsed);
4008
+ }
4009
+ out.sort((a, b) => a.slug.localeCompare(b.slug));
4010
+ return out;
4011
+ }
4012
+
4013
+ // src/lib/kit/emit.ts
4014
+ function readPkg(cwd) {
4015
+ const j = JSON.parse(readFileSync22(join15(cwd, "package.json"), "utf8"));
4016
+ if (!j.name?.startsWith("@elcrm/")) {
4017
+ throw new Error("elcrm kit emit \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 npm-\u043F\u0430\u043A\u0435\u0442\u0435 @elcrm/*");
4018
+ }
4019
+ return { name: j.name, version: j.version ?? "0.0.0" };
4020
+ }
4021
+ function emitKitManifest(cwd) {
4022
+ const pkg = readPkg(cwd);
4023
+ const pkgSlug = kitPkgSlug(pkg.name);
4024
+ if (!pkgSlug) {
4025
+ throw new Error(`elcrm kit emit \u2014 \u043F\u0430\u043A\u0435\u0442 ${pkg.name} \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F (\u043D\u0443\u0436\u0435\u043D components|form)`);
4026
+ }
4027
+ const distDir = join15(cwd, "dist");
4028
+ const components = scanDistComponents(distDir, pkg.name).map((c2) => ({
4029
+ ...c2,
4030
+ importFrom: pkg.name
4031
+ }));
4032
+ return {
4033
+ format: "elcrm-kit-manifest-v1",
4034
+ package: pkg.name,
4035
+ version: pkg.version,
4036
+ pkg: pkgSlug,
4037
+ generatedAt: new Date().toISOString(),
4038
+ components
4039
+ };
4040
+ }
4041
+ function writeKitManifest(cwd, manifest, dry = false) {
4042
+ const written = [];
4043
+ const targets = [
4044
+ join15(cwd, ".elcrm", "kit-manifest.json"),
4045
+ join15(cwd, "dist", "kit-manifest.json")
4046
+ ];
4047
+ const body = `${JSON.stringify(manifest, null, 2)}
4048
+ `;
4049
+ for (const path of targets) {
4050
+ if (dry) {
4051
+ written.push(path);
4052
+ continue;
4053
+ }
4054
+ mkdirSync3(join15(path, ".."), { recursive: true });
4055
+ writeFileSync7(path, body, "utf8");
4056
+ written.push(path);
4057
+ }
4058
+ return written;
4059
+ }
4060
+ function readKitManifest(path) {
4061
+ return JSON.parse(readFileSync22(resolve5(path), "utf8"));
4062
+ }
4063
+
4064
+ // src/lib/kit/sync.ts
4065
+ function patchPkgVersions(apiDir, npmName, version, dry) {
4066
+ const path = join16(apiDir, "src/kit/pkg-versions.ts");
4067
+ let src = readFileSync23(path, "utf8");
4068
+ const re = new RegExp(`("${npmName.replace("/", "\\/")}"\\s*:\\s*")[^"]+(")`);
4069
+ if (!re.test(src)) {
4070
+ console.warn(c.yellow(` ! FALLBACK_DEPS: \u043D\u0435\u0442 ${npmName} \u0432 pkg-versions.ts`));
4071
+ return;
4072
+ }
4073
+ const next = src.replace(re, `$1${version}$2`);
4074
+ if (next === src)
4075
+ return;
4076
+ if (!dry)
4077
+ writeFileSync8(path, next, "utf8");
4078
+ console.log(` ${dry ? "~" : c.green("+")} pkg-versions \u2192 ${npmName} ${version}`);
4079
+ }
4080
+ function patchWebDep(webDir, npmName, version, dry) {
4081
+ const path = join16(webDir, "package.json");
4082
+ const j = JSON.parse(readFileSync23(path, "utf8"));
4083
+ if (!j.dependencies?.[npmName])
4084
+ return;
4085
+ j.dependencies[npmName] = version;
4086
+ if (!dry)
4087
+ writeFileSync8(path, `${JSON.stringify(j, null, 2)}
4088
+ `, "utf8");
4089
+ console.log(` ${dry ? "~" : c.green("+")} elUI.web \u2192 ${npmName}@${version}`);
4090
+ }
4091
+ function runReseed(apiDir, webDir, dry) {
4092
+ if (dry) {
4093
+ console.log(c.gray(" [dry] bun run kit:reseed"));
4094
+ return;
4095
+ }
4096
+ const env = { ...process.env, KIT_RESEED: "1" };
4097
+ if (webDir)
4098
+ env.ELUI_WEB_DIR = webDir;
4099
+ const r = spawnSync2("bun", ["run", "kit:reseed"], {
4100
+ cwd: apiDir,
4101
+ env,
4102
+ stdio: "inherit"
4103
+ });
4104
+ if (r.status !== 0)
4105
+ throw new Error("kit:reseed \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439");
4106
+ }
4107
+ async function syncKitManifest(opts) {
4108
+ const cwd = resolve6(opts.cwd);
4109
+ const dry = opts.dry ?? false;
4110
+ const reseed = opts.reseed !== false;
4111
+ persistKitPathsFromOpts({
4112
+ cwd,
4113
+ apiDir: opts.apiDir,
4114
+ webDir: opts.webDir,
4115
+ dry
4116
+ });
4117
+ let manifest;
4118
+ if (opts.manifestPath) {
4119
+ manifest = readKitManifest(opts.manifestPath);
4120
+ } else if (existsSync14(join16(cwd, ".elcrm", "kit-manifest.json"))) {
4121
+ manifest = readKitManifest(join16(cwd, ".elcrm", "kit-manifest.json"));
4122
+ } else {
4123
+ manifest = emitKitManifest(cwd);
4124
+ }
4125
+ const apiDir = findEluiApiDir(cwd, opts.apiDir);
4126
+ if (!apiDir) {
4127
+ throw new Error("\u041D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D elUI.api (elcrm kit config --api=\u2026 --global \u0438\u043B\u0438 ELUI_API_DIR). Manifest: elcrm kit emit");
4128
+ }
4129
+ const outDir = join16(apiDir, "src/kit/manifests");
4130
+ const outFile = join16(outDir, manifestFileName(manifest.package));
4131
+ if (!dry) {
4132
+ mkdirSync4(outDir, { recursive: true });
4133
+ writeFileSync8(outFile, `${JSON.stringify(manifest, null, 2)}
4134
+ `, "utf8");
4135
+ }
4136
+ console.log(`${dry ? "[dry] " : ""}sync \u2192 ${outFile} (${manifest.components.length} \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432, ${manifest.version})`);
4137
+ patchPkgVersions(apiDir, manifest.package, manifest.version, dry);
4138
+ const webDir = opts.patchWeb === false ? null : findEluiWebDir(cwd, opts.webDir);
4139
+ if (webDir)
4140
+ patchWebDep(webDir, manifest.package, manifest.version, dry);
4141
+ if (reseed)
4142
+ runReseed(apiDir, webDir, dry);
4143
+ }
4144
+ async function emitAndSync(opts) {
4145
+ const manifest = emitKitManifest(opts.cwd);
4146
+ writeKitManifest(opts.cwd, manifest, opts.dry);
4147
+ console.log(`elcrm kit emit: ${manifest.components.length} \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432 \u2192 .elcrm/ + dist/`);
4148
+ await syncKitManifest({ ...opts, cwd: opts.cwd, manifestPath: undefined });
4149
+ }
4150
+
4151
+ // src/lib/kit/doctor-kit.ts
4152
+ async function doctorKitSync(opts) {
4153
+ const cwd = resolve7(opts.cwd);
4154
+ const dry = opts.dry ?? false;
4155
+ if (!isElcrmLibPackage(cwd))
4156
+ return false;
4157
+ const pkg = readPkgAt(cwd);
4158
+ if (!pkg?.name || !kitPkgSlug(pkg.name))
4159
+ return false;
4160
+ if (!existsSync15(join17(cwd, "dist"))) {
4161
+ console.log(c.dim(`
4162
+ kit: \u043F\u0440\u043E\u043F\u0443\u0441\u043A \u2014 \u043D\u0435\u0442 dist/ (\u0441\u043D\u0430\u0447\u0430\u043B\u0430 npm run build)`));
4163
+ return false;
4164
+ }
4165
+ console.log(`
4166
+ ${dry ? "[dry] " : ""}kit: sync \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u0438 elUI (${pkg.name})\u2026`);
4167
+ try {
4168
+ await emitAndSync({ cwd, dry, reseed: false });
4169
+ return true;
4170
+ } catch (err) {
4171
+ console.warn(c.yellow("kit: " + (err instanceof Error ? err.message : String(err))));
4172
+ console.warn(c.dim(" \u0417\u0430\u0434\u0430\u0439\u0442\u0435 elcrm kit config --api=\u2026 --web=\u2026 --global"));
4173
+ return false;
4174
+ }
4175
+ }
4176
+
4177
+ // src/commands/docs.ts
4178
+ import { join as join19, resolve as resolve8 } from "path";
4179
+
3705
4180
  // src/lib/elcrmDocsCatalog.ts
3706
4181
  var ALL_ELCRM_DOCS = [
3707
4182
  "CLI.elCRM.md",
@@ -3734,33 +4209,33 @@ function docsForProject(kind, lib) {
3734
4209
 
3735
4210
  // src/lib/scaffoldFiles.ts
3736
4211
  import {
3737
- existsSync as existsSync12,
3738
- mkdirSync as mkdirSync2,
3739
- readdirSync as readdirSync8,
3740
- readFileSync as readFileSync19,
3741
- writeFileSync as writeFileSync6
4212
+ existsSync as existsSync16,
4213
+ mkdirSync as mkdirSync5,
4214
+ readdirSync as readdirSync9,
4215
+ readFileSync as readFileSync24,
4216
+ writeFileSync as writeFileSync9
3742
4217
  } from "fs";
3743
- import { dirname as dirname3, join as join12 } from "path";
4218
+ import { dirname as dirname3, join as join18 } from "path";
3744
4219
  function writeScaffoldFile(dest, body, opts) {
3745
4220
  if (opts.dry)
3746
4221
  return "dry";
3747
- if (!opts.force && existsSync12(dest))
4222
+ if (!opts.force && existsSync16(dest))
3748
4223
  return "skip";
3749
- mkdirSync2(dirname3(dest), { recursive: true });
3750
- writeFileSync6(dest, body, "utf8");
4224
+ mkdirSync5(dirname3(dest), { recursive: true });
4225
+ writeFileSync9(dest, body, "utf8");
3751
4226
  return "write";
3752
4227
  }
3753
4228
  function readBundled(relPath) {
3754
- const full = join12(packageRoot(), "templates", relPath);
3755
- if (!existsSync12(full)) {
4229
+ const full = join18(packageRoot(), "templates", relPath);
4230
+ if (!existsSync16(full)) {
3756
4231
  throw new Error(`\u0428\u0430\u0431\u043B\u043E\u043D \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${relPath}`);
3757
4232
  }
3758
- return readFileSync19(full, "utf8");
4233
+ return readFileSync24(full, "utf8");
3759
4234
  }
3760
4235
 
3761
4236
  // src/commands/docs.ts
3762
4237
  async function cmdDocs(parsed) {
3763
- const cwd = resolve3(parsed.options.dir || process.cwd());
4238
+ const cwd = resolve8(parsed.options.dir || process.cwd());
3764
4239
  const dry = hasFlag(parsed, "dry");
3765
4240
  const force = hasFlag(parsed, "force") || !hasFlag(parsed, "keep");
3766
4241
  const lib = hasFlag(parsed, "lib") || isElcrmLibPackage(cwd);
@@ -3768,8 +4243,8 @@ async function cmdDocs(parsed) {
3768
4243
  const names = docsForProject(kind, lib);
3769
4244
  console.log(`${dry ? "[dry] " : ""}elcrm docs \u2192 ${cwd}/docs (${kind}${lib ? ", lib" : ""})`);
3770
4245
  for (const name of names) {
3771
- const body = readBundled(join13("elcrm-docs", name));
3772
- const dest = join13(cwd, "docs", name);
4246
+ const body = readBundled(join19("elcrm-docs", name));
4247
+ const dest = join19(cwd, "docs", name);
3773
4248
  const action = writeScaffoldFile(dest, body, { dry, force });
3774
4249
  const mark = action === "write" ? c.green("+") : action === "skip" ? c.gray("=") : c.cyan("~");
3775
4250
  console.log(` ${mark} docs/${name}`);
@@ -3781,7 +4256,7 @@ async function cmdDocs(parsed) {
3781
4256
  }
3782
4257
 
3783
4258
  // src/commands/cursor.ts
3784
- import { join as join14, resolve as resolve4 } from "path";
4259
+ import { join as join20, resolve as resolve9 } from "path";
3785
4260
  var ALWAYS = ["elcrm.mdc", "elcrm-packages.mdc", "elcrm-jsdoc.mdc"];
3786
4261
  var UI = [
3787
4262
  "elcrm-ui.mdc",
@@ -3797,7 +4272,7 @@ function writeOne(dest, body, dry, force, label) {
3797
4272
  console.log(` ${mark} ${label}`);
3798
4273
  }
3799
4274
  async function cmdCursor(parsed) {
3800
- const cwd = resolve4(parsed.options.dir || process.cwd());
4275
+ const cwd = resolve9(parsed.options.dir || process.cwd());
3801
4276
  const dry = hasFlag(parsed, "dry");
3802
4277
  const force = hasFlag(parsed, "force") || !hasFlag(parsed, "keep");
3803
4278
  const lib = hasFlag(parsed, "lib") || isElcrmLibPackage(cwd);
@@ -3810,24 +4285,24 @@ async function cmdCursor(parsed) {
3810
4285
  ];
3811
4286
  console.log(`${dry ? "[dry] " : ""}elcrm cursor \u2192 ${cwd}/.cursor (${kind}${lib ? ", lib" : ""})`);
3812
4287
  for (const name of ruleNames) {
3813
- let body = readBundled(join14("elcrm-cursor", "rules", name));
4288
+ let body = readBundled(join20("elcrm-cursor", "rules", name));
3814
4289
  if (kind === "server" && (name === "elcrm-server.mdc" || name === "elcrm-rpc.mdc")) {
3815
4290
  body = body.replace("alwaysApply: false", "alwaysApply: true").replace(/^globs:.*\n/m, "");
3816
4291
  }
3817
- writeOne(join14(cwd, ".cursor", "rules", name), body, dry, force, `.cursor/rules/${name}`);
4292
+ writeOne(join20(cwd, ".cursor", "rules", name), body, dry, force, `.cursor/rules/${name}`);
3818
4293
  }
3819
4294
  const docs = docsForProject(kind, lib);
3820
4295
  for (const name of docs) {
3821
- const body = readBundled(join14("elcrm-docs", name));
3822
- writeOne(join14(cwd, ".cursor", "docs", name), body, dry, force, `.cursor/docs/${name}`);
4296
+ const body = readBundled(join20("elcrm-docs", name));
4297
+ writeOne(join20(cwd, ".cursor", "docs", name), body, dry, force, `.cursor/docs/${name}`);
3823
4298
  }
3824
- writeOne(join14(cwd, "AGENTS.md"), readBundled(join14("elcrm-cursor", "AGENTS.md")), dry, force, "AGENTS.md");
4299
+ writeOne(join20(cwd, "AGENTS.md"), readBundled(join20("elcrm-cursor", "AGENTS.md")), dry, force, "AGENTS.md");
3825
4300
  console.log(c.gray("\u0410\u0433\u0435\u043D\u0442: AGENTS.md + rules (\u0443\u0437\u043A\u0438\u0435 glob) + .cursor/docs. \u0421\u0434\u0430\u0447\u0430: elcrm test."));
3826
4301
  }
3827
4302
 
3828
4303
  // src/commands/doctor.ts
3829
4304
  async function cmdDoctor(parsed) {
3830
- const cwd = resolve5(parsed.options.dir || process.cwd());
4305
+ const cwd = resolve10(parsed.options.dir || process.cwd());
3831
4306
  const dry = hasFlag(parsed, "dry");
3832
4307
  const doDocs = hasFlag(parsed, "docs");
3833
4308
  const doTest = hasFlag(parsed, "test");
@@ -3871,6 +4346,7 @@ async function cmdDoctor(parsed) {
3871
4346
  }
3872
4347
  }
3873
4348
  const tidy = await tidyFrontProject({ cwd, pkg, dry, hacks });
4349
+ await doctorKitSync({ cwd, dry });
3874
4350
  if (doDocs) {
3875
4351
  const flags = dry ? ["dry"] : [];
3876
4352
  const child = {
@@ -3903,7 +4379,7 @@ doctor: \u0433\u043E\u0442\u043E\u0432\u043E (${n} \u043F\u0440\u0430\u0432\u043
3903
4379
  }
3904
4380
 
3905
4381
  // src/commands/build.ts
3906
- import { existsSync as existsSync13 } from "fs";
4382
+ import { existsSync as existsSync17 } from "fs";
3907
4383
 
3908
4384
  // src/lib/nextVersion.ts
3909
4385
  async function nextVersion() {
@@ -3926,9 +4402,9 @@ async function nextVersion() {
3926
4402
 
3927
4403
  // src/commands/build.ts
3928
4404
  function resolveEntrypoint() {
3929
- if (existsSync13("./src/index.js"))
4405
+ if (existsSync17("./src/index.js"))
3930
4406
  return "./src/index.js";
3931
- if (existsSync13("./src/index.ts"))
4407
+ if (existsSync17("./src/index.ts"))
3932
4408
  return "./src/index.ts";
3933
4409
  return "./src/index.js";
3934
4410
  }
@@ -4006,22 +4482,22 @@ async function cmdUuid() {
4006
4482
  }
4007
4483
 
4008
4484
  // src/commands/postbuild.ts
4009
- import { resolve as resolve6 } from "path";
4485
+ import { resolve as resolve11 } from "path";
4010
4486
 
4011
4487
  // src/vite/postbuild.ts
4012
4488
  import {
4013
- existsSync as existsSync14,
4014
- readdirSync as readdirSync10,
4015
- readFileSync as readFileSync21,
4489
+ existsSync as existsSync18,
4490
+ readdirSync as readdirSync11,
4491
+ readFileSync as readFileSync26,
4016
4492
  rmSync,
4017
- statSync as statSync8,
4018
- writeFileSync as writeFileSync8
4493
+ statSync as statSync9,
4494
+ writeFileSync as writeFileSync11
4019
4495
  } from "fs";
4020
- import { join as join16 } from "path";
4496
+ import { join as join22 } from "path";
4021
4497
 
4022
4498
  // src/vite/concat-css.ts
4023
- import { readdirSync as readdirSync9, readFileSync as readFileSync20, writeFileSync as writeFileSync7 } from "fs";
4024
- import { join as join15 } from "path";
4499
+ import { readdirSync as readdirSync10, readFileSync as readFileSync25, writeFileSync as writeFileSync10 } from "fs";
4500
+ import { join as join21 } from "path";
4025
4501
  var SKIP_CSS = new Set([
4026
4502
  "index.css",
4027
4503
  "tokens.css",
@@ -4036,11 +4512,11 @@ function concatDistCss(options) {
4036
4512
  outFile,
4037
4513
  ...options.skip ?? []
4038
4514
  ]);
4039
- const files = readdirSync9(options.distDir).filter((f) => f.endsWith(".css") && !skip.has(f)).sort();
4040
- const css = files.map((f) => readFileSync20(join15(options.distDir, f), "utf8")).join(`
4515
+ const files = readdirSync10(options.distDir).filter((f) => f.endsWith(".css") && !skip.has(f)).sort();
4516
+ const css = files.map((f) => readFileSync25(join21(options.distDir, f), "utf8")).join(`
4041
4517
  `);
4042
- const outPath = join15(options.distDir, outFile);
4043
- writeFileSync7(outPath, css);
4518
+ const outPath = join21(options.distDir, outFile);
4519
+ writeFileSync10(outPath, css);
4044
4520
  console.log(`[concat-css] ${files.length} \u0444\u0430\u0439\u043B\u043E\u0432 \u2192 ${outFile}`);
4045
4521
  return outPath;
4046
4522
  }
@@ -4057,19 +4533,19 @@ function postbuildLib(options) {
4057
4533
  distDir: dist,
4058
4534
  outFile: options.cssOutFile ?? "index.css"
4059
4535
  });
4060
- for (const name of readdirSync10(dist)) {
4061
- const dir = join16(dist, name);
4062
- if (!statSync8(dir).isDirectory())
4536
+ for (const name of readdirSync11(dist)) {
4537
+ const dir = join22(dist, name);
4538
+ if (!statSync9(dir).isDirectory())
4063
4539
  continue;
4064
4540
  if (keep.has(name))
4065
4541
  continue;
4066
- const impl = join16(dir, `${name}.d.ts`);
4067
- const nestedIndex = join16(dir, "index.d.ts");
4068
- const flat = join16(dist, `${name}.d.ts`);
4069
- const src = existsSync14(impl) ? impl : existsSync14(nestedIndex) ? nestedIndex : null;
4542
+ const impl = join22(dir, `${name}.d.ts`);
4543
+ const nestedIndex = join22(dir, "index.d.ts");
4544
+ const flat = join22(dist, `${name}.d.ts`);
4545
+ const src = existsSync18(impl) ? impl : existsSync18(nestedIndex) ? nestedIndex : null;
4070
4546
  if (src) {
4071
- const text = readFileSync21(src, "utf8").replace(/from ['"]\.\.\/([^'"]+)['"]/g, `from './$1'`);
4072
- writeFileSync8(flat, text);
4547
+ const text = readFileSync26(src, "utf8").replace(/from ['"]\.\.\/([^'"]+)['"]/g, `from './$1'`);
4548
+ writeFileSync11(flat, text);
4073
4549
  console.log(`[postbuild] types \u2192 ${name}.d.ts`);
4074
4550
  }
4075
4551
  rmSync(dir, { recursive: true, force: true });
@@ -4080,7 +4556,7 @@ function postbuildLib(options) {
4080
4556
  // src/commands/postbuild.ts
4081
4557
  async function cmdPostbuild(parsed) {
4082
4558
  const dir = parsed.options.dir || "./dist";
4083
- const distDir = resolve6(process.cwd(), dir);
4559
+ const distDir = resolve11(process.cwd(), dir);
4084
4560
  postbuildLib({ distDir });
4085
4561
  console.log(`[postbuild] \u0433\u043E\u0442\u043E\u0432\u043E: ${distDir}`);
4086
4562
  }
@@ -4148,8 +4624,8 @@ css-sanitize: \u043F\u043E\u0447\u0438\u043D\u0435\u043D\u043E ${cleaned.length}
4148
4624
  }
4149
4625
 
4150
4626
  // src/commands/audit.ts
4151
- import { existsSync as existsSync15, readFileSync as readFileSync22 } from "fs";
4152
- import { join as join17, relative as relative4 } from "path";
4627
+ import { existsSync as existsSync19, readFileSync as readFileSync27 } from "fs";
4628
+ import { join as join23, relative as relative4 } from "path";
4153
4629
  function scan(cwd) {
4154
4630
  const out = [];
4155
4631
  const pkg = readPackageJson(cwd);
@@ -4180,7 +4656,7 @@ function scan(cwd) {
4180
4656
  }
4181
4657
  const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r));
4182
4658
  for (const file of files) {
4183
- const text = readFileSync22(file, "utf8");
4659
+ const text = readFileSync27(file, "utf8");
4184
4660
  const rel2 = relative4(cwd, file);
4185
4661
  const lines = text.split(/\r?\n/);
4186
4662
  lines.forEach((line, i) => {
@@ -4282,7 +4758,7 @@ function scan(cwd) {
4282
4758
  return false;
4283
4759
  if (/theme(-light|-dark)?\.css$/.test(f))
4284
4760
  return false;
4285
- const t = readFileSync22(f, "utf8");
4761
+ const t = readFileSync27(f, "utf8");
4286
4762
  return t.includes("--field-border:") && t.includes("--button-secondary-bg:") && t.includes("transparent");
4287
4763
  });
4288
4764
  if (toolbarMarkers.length > 1) {
@@ -4293,8 +4769,8 @@ function scan(cwd) {
4293
4769
  fix: "\u043E\u0431\u0449\u0438\u0439 \u043A\u043B\u0430\u0441\u0441 + theme tokens"
4294
4770
  });
4295
4771
  }
4296
- const styleDir = ["src/style", "src/styles", "style"].map((d) => join17(cwd, d)).find(existsSync15);
4297
- if (styleDir && !existsSync15(join17(styleDir, "elcrm.css"))) {
4772
+ const styleDir = ["src/style", "src/styles", "style"].map((d) => join23(cwd, d)).find(existsSync19);
4773
+ if (styleDir && !existsSync19(join23(styleDir, "elcrm.css"))) {
4298
4774
  out.push({
4299
4775
  level: "info",
4300
4776
  code: "elcrm-css",
@@ -4302,8 +4778,8 @@ function scan(cwd) {
4302
4778
  fix: "elcrm css"
4303
4779
  });
4304
4780
  }
4305
- if (styleDir && existsSync15(join17(styleDir, "elcrm.css"))) {
4306
- const elcrmCss = readFileSync22(join17(styleDir, "elcrm.css"), "utf8");
4781
+ if (styleDir && existsSync19(join23(styleDir, "elcrm.css"))) {
4782
+ const elcrmCss = readFileSync27(join23(styleDir, "elcrm.css"), "utf8");
4307
4783
  for (const m of elcrmCss.matchAll(/@import\s+["'](@elcrm\/[^"']+)["']/g)) {
4308
4784
  const spec = m[1];
4309
4785
  if (!resolveElcrmCssImport(spec, cwd)) {
@@ -4311,7 +4787,7 @@ function scan(cwd) {
4311
4787
  level: "error",
4312
4788
  code: "elcrm-import-missing",
4313
4789
  message: `\u0431\u0438\u0442\u044B\u0439 @import "${spec}" \u2014 \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442 \u0432 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u043E\u043C \u043F\u0430\u043A\u0435\u0442\u0435`,
4314
- file: relative4(cwd, join17(styleDir, "elcrm.css")),
4790
+ file: relative4(cwd, join23(styleDir, "elcrm.css")),
4315
4791
  fix: "elcrm css"
4316
4792
  });
4317
4793
  }
@@ -4323,8 +4799,8 @@ function scan(cwd) {
4323
4799
  "theme-light.css",
4324
4800
  "theme-dark.css",
4325
4801
  "theme.css"
4326
- ].map((f) => join17(styleDir, f));
4327
- const themeText = themeFiles.filter(existsSync15).map((f) => readFileSync22(f, "utf8")).join(`
4802
+ ].map((f) => join23(styleDir, f));
4803
+ const themeText = themeFiles.filter(existsSync19).map((f) => readFileSync27(f, "utf8")).join(`
4328
4804
  `);
4329
4805
  if (themeText && !/--popup-shadow\s*:/.test(themeText) && /--date-popup-shadow\s*:|--select-background\s*:/.test(themeText)) {
4330
4806
  out.push({
@@ -4346,7 +4822,7 @@ function scan(cwd) {
4346
4822
  for (const file of files) {
4347
4823
  if (!file.endsWith(".css"))
4348
4824
  continue;
4349
- const text = readFileSync22(file, "utf8");
4825
+ const text = readFileSync27(file, "utf8");
4350
4826
  const relPath = relative4(cwd, file);
4351
4827
  const lines = text.split(/\r?\n/);
4352
4828
  let prevNonEmpty = "";
@@ -4435,15 +4911,128 @@ ${c.cyan("\u2192")} elcrm migrate front${hasFlag(parsed, "dry") ? " --dry" : ""}
4435
4911
  console.log("\u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u0442\u0441\u044F: elcrm css && elcrm doctor");
4436
4912
  }
4437
4913
 
4914
+ // src/commands/kit.ts
4915
+ import { resolve as resolve12, join as join24 } from "path";
4916
+ function showKitHelp() {
4917
+ console.log(`elcrm kit \u2014 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u044F elUI (UIKit)
4918
+
4919
+ emit [--dir=\u2026] [--dry]
4920
+ dist/*.d.ts \u2192 .elcrm/kit-manifest.json + dist/kit-manifest.json
4921
+ \u0417\u0430\u043F\u0443\u0441\u043A\u0430\u0442\u044C \u0432 @elcrm/components | @elcrm/form \u043F\u043E\u0441\u043B\u0435 build
4922
+
4923
+ sync [--api=\u2026] [--web=\u2026] [--no-reseed] [--dry]
4924
+ manifest \u2192 elUI.api/src/kit/manifests/*.json
4925
+ \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0435\u0442 pkg-versions + elUI.web deps + kit:reseed
4926
+
4927
+ pull [--api=\u2026] [--no-reseed] [--dry]
4928
+ emit + sync (\u0443\u0434\u043E\u0431\u043D\u043E \u0432 postbuild)
4929
+
4930
+ config [--api=\u2026] [--web=\u2026] [--global] [--local] [--show]
4931
+ \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u043F\u0443\u0442\u0438 elUI (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E ~/.elcrm/kit-paths.json)
4932
+
4933
+ \u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 (\u043E\u043F\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u043E): ELUI_API_DIR, ELUI_WEB_DIR
4934
+
4935
+ \u041E\u0434\u0438\u043D \u0440\u0430\u0437:
4936
+ elcrm kit config --api=/path/to/elUI.api --web=/path/to/elUI.web --global
4937
+
4938
+ \u041F\u0440\u0438\u043C\u0435\u0440 (components):
4939
+ npm run build && elcrm kit pull
4940
+
4941
+ \u041F\u0440\u0438\u043C\u0435\u0440 (elUI.api \u043F\u043E\u0441\u043B\u0435 pull):
4942
+ bun run kit:reseed
4943
+ `);
4944
+ }
4945
+ async function cmdKit(parsed) {
4946
+ const sub = parsed.positionals[0] || "help";
4947
+ const cwd = resolve12(parsed.options.dir || process.cwd());
4948
+ const dry = hasFlag(parsed, "dry");
4949
+ const noReseed = hasFlag(parsed, "no-reseed");
4950
+ if (sub === "help" || sub === "--help") {
4951
+ showKitHelp();
4952
+ return;
4953
+ }
4954
+ if (sub === "emit") {
4955
+ const pkg = readPkgAt(cwd);
4956
+ if (!pkg?.name || !kitPkgSlug(pkg.name)) {
4957
+ console.error(c.red("kit emit \u2014 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0439\u0442\u0435 \u0432 @elcrm/components \u0438\u043B\u0438 @elcrm/form"));
4958
+ process.exit(1);
4959
+ }
4960
+ const manifest = emitKitManifest(cwd);
4961
+ const paths = writeKitManifest(cwd, manifest, dry);
4962
+ console.log(`${dry ? "[dry] " : ""}emit: ${manifest.components.length} \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432`);
4963
+ for (const p of paths)
4964
+ console.log(` \u2192 ${p}`);
4965
+ return;
4966
+ }
4967
+ if (sub === "sync") {
4968
+ await syncKitManifest({
4969
+ cwd,
4970
+ apiDir: parsed.options.api,
4971
+ webDir: parsed.options.web,
4972
+ dry,
4973
+ reseed: !noReseed
4974
+ });
4975
+ return;
4976
+ }
4977
+ if (sub === "config") {
4978
+ const global = hasFlag(parsed, "global") || !hasFlag(parsed, "local");
4979
+ const show = hasFlag(parsed, "show") || !parsed.options.api && !parsed.options.web;
4980
+ if (show && !parsed.options.api && !parsed.options.web) {
4981
+ const saved = loadKitPathsConfig(cwd);
4982
+ const api = findEluiApiDir(cwd, parsed.options.api) ?? "(\u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D)";
4983
+ const web = findEluiWebDir(cwd, parsed.options.web) ?? "(\u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D)";
4984
+ console.log("elcrm kit config:");
4985
+ console.log(" \u0444\u0430\u0439\u043B:", global ? "~/.elcrm/kit-paths.json" : ".elcrm/kit-paths.json");
4986
+ console.log(" apiDir (\u0444\u0430\u0439\u043B):", saved.apiDir ?? "\u2014");
4987
+ console.log(" webDir (\u0444\u0430\u0439\u043B):", saved.webDir ?? "\u2014");
4988
+ console.log(" apiDir (resolve):", api);
4989
+ console.log(" webDir (resolve):", web);
4990
+ if (!saved.apiDir) {
4991
+ console.log(c.dim(`
4992
+ \u0417\u0430\u0434\u0430\u0442\u044C: elcrm kit config --api=\u2026 --web=\u2026 --global`));
4993
+ }
4994
+ return;
4995
+ }
4996
+ const next = saveKitPathsConfig(cwd, {
4997
+ ...parsed.options.api ? { apiDir: parsed.options.api } : {},
4998
+ ...parsed.options.web ? { webDir: parsed.options.web } : {}
4999
+ }, global);
5000
+ const where = global ? "~/.elcrm/kit-paths.json" : join24(cwd, ".elcrm/kit-paths.json");
5001
+ console.log(c.green("kit config \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D \u2192 " + where));
5002
+ if (next.apiDir)
5003
+ console.log(" apiDir:", next.apiDir);
5004
+ if (next.webDir)
5005
+ console.log(" webDir:", next.webDir);
5006
+ return;
5007
+ }
5008
+ if (sub === "pull") {
5009
+ if (!isElcrmLibPackage(cwd)) {
5010
+ console.error(c.red("kit pull \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 npm-\u043F\u0430\u043A\u0435\u0442\u0435 @elcrm/components | @elcrm/form"));
5011
+ process.exit(1);
5012
+ }
5013
+ await emitAndSync({
5014
+ cwd,
5015
+ apiDir: parsed.options.api,
5016
+ webDir: parsed.options.web,
5017
+ dry,
5018
+ reseed: !noReseed
5019
+ });
5020
+ return;
5021
+ }
5022
+ console.error(`\u041D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u043F\u043E\u0434\u043A\u043E\u043C\u0430\u043D\u0434\u0430 kit: ${sub}`);
5023
+ showKitHelp();
5024
+ process.exit(1);
5025
+ }
5026
+
4438
5027
  // src/index.ts
4439
5028
  function cliVersion() {
4440
5029
  const here = dirname4(fileURLToPath2(import.meta.url));
4441
5030
  for (const p of [
4442
- join18(here, "..", "package.json"),
4443
- join18(here, "package.json")
5031
+ join25(here, "..", "package.json"),
5032
+ join25(here, "package.json")
4444
5033
  ]) {
4445
5034
  try {
4446
- const v = JSON.parse(readFileSync23(p, "utf8")).version;
5035
+ const v = JSON.parse(readFileSync28(p, "utf8")).version;
4447
5036
  if (typeof v === "string" && v)
4448
5037
  return v;
4449
5038
  } catch {}
@@ -4479,6 +5068,12 @@ function showHelp() {
4479
5068
  .cursor/rules + .cursor/docs + AGENTS.md
4480
5069
  --lib: \u0447\u0435\u043A\u043B\u0438\u0441\u0442 \u0430\u0432\u0442\u043E\u0440\u0430 @elcrm/*
4481
5070
 
5071
+ kit <emit|sync|pull|config> [--api=\u2026] [--web=\u2026] [--no-reseed] [--dry]
5072
+ config \u2014 \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u043F\u0443\u0442\u0438 \u0432 ~/.elcrm/kit-paths.json (--global)
5073
+ emit \u2014 manifest \u0438\u0437 dist/*.d.ts (@elcrm/components|form)
5074
+ sync \u2014 manifest \u2192 elUI.api/src/kit/manifests + reseed
5075
+ pull \u2014 emit + sync (postbuild \u043F\u0430\u043A\u0435\u0442\u0430)
5076
+
4482
5077
  migrate <name> [--dry] | migrate --list
4483
5078
  front|ui = \u043F\u0430\u043A\u0435\u0442 \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0439 (aliases, form-native, native-html, tokens, \u2026)
4484
5079
  \u043F\u043E \u043E\u0434\u043D\u043E\u0439: form-aliases, form-native, native-html, form-tokens, components,
@@ -4490,6 +5085,7 @@ function showHelp() {
4490
5085
  \u041F\u043E\u0441\u043B\u0435 --fix \u0434\u043B\u044F imports: elcrm css
4491
5086
 
4492
5087
  doctor [--dry] [--docs] [--test] [--hacks] [--dir=\u2026]
5088
+ migrate + css; \u0432 @elcrm/components|form + dist/ \u2192 kit pull
4493
5089
  \u041D\u0430\u0432\u0435\u0434\u0435\u043D\u0438\u0435 \u043F\u043E\u0440\u044F\u0434\u043A\u0430 (\u043F\u0440\u043E\u0441\u0442\u043E \u0432\u044B\u0437\u0432\u0430\u0442\u044C). \u041F\u0440\u0435\u0432\u044C\u044E: --dry
4494
5090
  package.json \u2192 migrate front \u2192 css. --docs / --test \u043F\u043E \u0436\u0435\u043B\u0430\u043D\u0438\u044E
4495
5091
 
@@ -4563,6 +5159,9 @@ async function main() {
4563
5159
  case "cursor":
4564
5160
  await cmdCursor(parsed);
4565
5161
  break;
5162
+ case "kit":
5163
+ await cmdKit(parsed);
5164
+ break;
4566
5165
  case "uuid":
4567
5166
  await cmdUuid();
4568
5167
  break;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "elcrm",
3
- "version": "1.1.19",
4
- "description": "CLI @elcrm/*: doctor --fix (порядок проекта), update, css, docs, cursor, migrate",
3
+ "version": "1.1.21",
4
+ "description": "CLI @elcrm/*: doctor --fix (порядок проекта), update, css, docs, cursor, kit, migrate",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "MaSkal <dev@elcrm.online>",
@@ -52,7 +52,7 @@
52
52
  "build": "bun build ./src/index.ts --outdir ./dist --target bun --format esm && bun build ./src/vite/plugin-css-scoped.ts --outfile ./dist/vite/plugin-css-scoped.js --target node --format esm --packages external && bun build ./src/vite/discover-lib.ts --outfile ./dist/vite/discover-lib.js --target node --format esm --packages external && bun build ./src/vite/concat-css.ts --outfile ./dist/vite/concat-css.js --target node --format esm --packages external && bun build ./src/vite/check-tokens.ts --outfile ./dist/vite/check-tokens.js --target node --format esm --packages external && bun build ./src/vite/postbuild.ts --outfile ./dist/vite/postbuild.js --target node --format esm --packages external && bun build ./src/vite/minify-css-vars.ts --outfile ./dist/vite/minify-css-vars.js --target node --format esm --packages external && node ./scripts/chmod-bin.mjs && node ./scripts/copy-vite-dts.mjs",
53
53
  "dev": "bun run ./src/index.ts",
54
54
  "elcrm": "bun run ./src/index.ts",
55
- "test": "bun test src/lib/migrate src/lib/tidy.test.ts src/vite",
55
+ "test": "bun test src/lib/migrate src/lib/tidy.test.ts src/lib/kit src/vite",
56
56
  "link:local": "bun run build && bun link",
57
57
  "prepublishOnly": "bun run test && bun run build",
58
58
  "publish:npm": "npm publish"