digdesign 0.1.0

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 +2895 -0
  2. package/package.json +34 -0
package/dist/index.js ADDED
@@ -0,0 +1,2895 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/commands/add.ts
7
+ import { access as access4, mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
8
+ import path6 from "path";
9
+
10
+ // src/lib/config.ts
11
+ import { access, readFile, writeFile } from "fs/promises";
12
+ import path from "path";
13
+ var CONFIG_FILE = "dig.json";
14
+ var PRODUCTION_REGISTRY_URL = "https://docs.digdesign.ir";
15
+ function defaultConfig() {
16
+ return {
17
+ $schema: `${PRODUCTION_REGISTRY_URL}/schema/dig-config.json`,
18
+ style: "new-york",
19
+ rtl: true,
20
+ tsx: true,
21
+ tailwind: {
22
+ css: "src/app/globals.css",
23
+ baseColor: "neutral",
24
+ cssVariables: true
25
+ },
26
+ aliases: {
27
+ components: "src/components",
28
+ ui: "src/components/ui",
29
+ lib: "src/lib",
30
+ hooks: "src/hooks"
31
+ },
32
+ registryUrl: process.env.DIG_REGISTRY_URL ?? PRODUCTION_REGISTRY_URL
33
+ };
34
+ }
35
+ async function configExists(cwd = process.cwd()) {
36
+ try {
37
+ await access(path.join(cwd, CONFIG_FILE));
38
+ return true;
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+ async function readConfig(cwd = process.cwd()) {
44
+ const raw = await readFile(path.join(cwd, CONFIG_FILE), "utf8");
45
+ return JSON.parse(raw);
46
+ }
47
+ async function writeConfig(config, cwd = process.cwd()) {
48
+ await writeFile(
49
+ path.join(cwd, CONFIG_FILE),
50
+ `${JSON.stringify(config, null, 2)}
51
+ `,
52
+ "utf8"
53
+ );
54
+ }
55
+
56
+ // src/lib/hash.ts
57
+ import { createHash } from "crypto";
58
+ function hashContent(content) {
59
+ const normalized = content.replace(/\r\n/g, "\n");
60
+ return `sha256:${createHash("sha256").update(normalized, "utf8").digest("hex")}`;
61
+ }
62
+
63
+ // src/lib/lock.ts
64
+ import { mkdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
65
+ import path2 from "path";
66
+ var LOCK_DIR = ".dig";
67
+ var LOCK_FILE_NAME = "registry-lock.json";
68
+ function emptyLock() {
69
+ return { components: {} };
70
+ }
71
+ function lockFilePath(cwd) {
72
+ return path2.join(cwd, LOCK_DIR, LOCK_FILE_NAME);
73
+ }
74
+ async function readLock(cwd = process.cwd()) {
75
+ try {
76
+ const raw = await readFile2(lockFilePath(cwd), "utf8");
77
+ const parsed = JSON.parse(raw);
78
+ return { components: parsed?.components ?? {} };
79
+ } catch (error) {
80
+ if (error.code === "ENOENT") return emptyLock();
81
+ throw error;
82
+ }
83
+ }
84
+ async function writeLock(lock, cwd = process.cwd()) {
85
+ await mkdir(path2.join(cwd, LOCK_DIR), { recursive: true });
86
+ await writeFile2(lockFilePath(cwd), `${JSON.stringify(lock, null, 2)}
87
+ `, "utf8");
88
+ }
89
+ function mergeComponentEntry(existing, patch) {
90
+ return {
91
+ version: patch.version ?? existing?.version ?? "0.0.0",
92
+ registryUrl: patch.registryUrl,
93
+ installedAt: patch.installedAt,
94
+ files: {
95
+ ...existing?.files,
96
+ ...patch.writtenFiles
97
+ },
98
+ snapshot: patch.snapshot ?? existing?.snapshot
99
+ };
100
+ }
101
+
102
+ // src/lib/package-manager.ts
103
+ import { access as access2 } from "fs/promises";
104
+ import { spawn } from "child_process";
105
+ import path3 from "path";
106
+ var LOCKFILES = [
107
+ ["pnpm", "pnpm-lock.yaml"],
108
+ ["yarn", "yarn.lock"],
109
+ ["bun", "bun.lockb"],
110
+ ["npm", "package-lock.json"]
111
+ ];
112
+ async function detectPackageManager(cwd = process.cwd()) {
113
+ for (const [pm, lockfile] of LOCKFILES) {
114
+ try {
115
+ await access2(path3.join(cwd, lockfile));
116
+ return pm;
117
+ } catch {
118
+ continue;
119
+ }
120
+ }
121
+ return "npm";
122
+ }
123
+ function installArgs(pm, deps, dev) {
124
+ const verb = pm === "npm" ? "install" : "add";
125
+ const devFlag = dev ? pm === "npm" ? "--save-dev" : "-D" : null;
126
+ return devFlag ? [verb, devFlag, ...deps] : [verb, ...deps];
127
+ }
128
+ function installDependencies(pm, deps, options = {}) {
129
+ if (!deps.length) return Promise.resolve();
130
+ const args = installArgs(pm, deps, Boolean(options.dev));
131
+ return new Promise((resolve, reject) => {
132
+ const child = spawn(pm, args, {
133
+ cwd: options.cwd ?? process.cwd(),
134
+ stdio: "inherit",
135
+ shell: process.platform === "win32"
136
+ });
137
+ child.on("exit", (code) => {
138
+ if (code === 0) resolve();
139
+ else reject(new Error(`${pm} ${args.join(" ")} \u0628\u0627 \u06A9\u062F ${code} \u0634\u06A9\u0633\u062A \u062E\u0648\u0631\u062F.`));
140
+ });
141
+ child.on("error", reject);
142
+ });
143
+ }
144
+
145
+ // src/lib/registry.ts
146
+ function resolveUrl(nameOrUrl, registryUrl) {
147
+ if (/^https?:\/\//.test(nameOrUrl)) return nameOrUrl;
148
+ return `${registryUrl}/r/${nameOrUrl}.json`;
149
+ }
150
+ async function fetchRegistryIndex(registryUrl) {
151
+ const url = `${registryUrl}/r/registry.json`;
152
+ const response = await fetch(url);
153
+ if (!response.ok) {
154
+ throw new Error(`\u062F\u0631\u06CC\u0627\u0641\u062A \u0641\u0647\u0631\u0633\u062A\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F (${response.status}): ${url}`);
155
+ }
156
+ return await response.json();
157
+ }
158
+ async function fetchRegistryItem(nameOrUrl, registryUrl) {
159
+ const url = resolveUrl(nameOrUrl, registryUrl);
160
+ const response = await fetch(url);
161
+ if (!response.ok) {
162
+ throw new Error(`\u062F\u0631\u06CC\u0627\u0641\u062A \xAB${nameOrUrl}\xBB \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F (${response.status}): ${url}`);
163
+ }
164
+ return await response.json();
165
+ }
166
+ async function resolveRegistryTree(names, registryUrl) {
167
+ const resolved = /* @__PURE__ */ new Map();
168
+ const queued = new Set(names);
169
+ const queue = [...names];
170
+ while (queue.length) {
171
+ const next = queue.shift();
172
+ const item = await fetchRegistryItem(next, registryUrl);
173
+ if (resolved.has(item.name)) continue;
174
+ resolved.set(item.name, item);
175
+ for (const dep of item.registryDependencies ?? []) {
176
+ if (!queued.has(dep)) {
177
+ queued.add(dep);
178
+ queue.push(dep);
179
+ }
180
+ }
181
+ }
182
+ return resolved;
183
+ }
184
+
185
+ // src/lib/snapshot.ts
186
+ import { access as access3, mkdir as mkdir2, readFile as readFile3, rm, writeFile as writeFile3 } from "fs/promises";
187
+ import path4 from "path";
188
+ var SNAPSHOT_ROOT = path4.join(".dig", "snapshots");
189
+ function snapshotDirFor(cwd, name, version) {
190
+ return path4.join(cwd, SNAPSHOT_ROOT, name, version);
191
+ }
192
+ function snapshotRelativePath(name, version) {
193
+ return `${SNAPSHOT_ROOT.split(path4.sep).join("/")}/${name}/${version}`;
194
+ }
195
+ async function fileExists(target) {
196
+ try {
197
+ await access3(target);
198
+ return true;
199
+ } catch {
200
+ return false;
201
+ }
202
+ }
203
+ async function writeComponentSnapshot(cwd, name, version, files) {
204
+ const relativePath = snapshotRelativePath(name, version);
205
+ const entries = Object.entries(files);
206
+ if (entries.length === 0) return { path: relativePath };
207
+ const dir = snapshotDirFor(cwd, name, version);
208
+ const pending = [];
209
+ for (const [relFilePath, content] of entries) {
210
+ const target = path4.join(dir, relFilePath);
211
+ const existedBefore = await fileExists(target);
212
+ pending.push({
213
+ target,
214
+ content,
215
+ existedBefore,
216
+ originalContent: existedBefore ? await readFile3(target, "utf8") : void 0
217
+ });
218
+ }
219
+ const written = [];
220
+ try {
221
+ for (const item of pending) {
222
+ await mkdir2(path4.dirname(item.target), { recursive: true });
223
+ await writeFile3(item.target, item.content, "utf8");
224
+ written.push(item);
225
+ }
226
+ } catch (error) {
227
+ for (const item of written) {
228
+ if (item.existedBefore) {
229
+ await writeFile3(item.target, item.originalContent, "utf8");
230
+ } else {
231
+ await rm(item.target, { force: true });
232
+ }
233
+ }
234
+ return { error: error.message };
235
+ }
236
+ return { path: relativePath };
237
+ }
238
+ async function readSnapshotFile(cwd, name, version, relFilePath) {
239
+ try {
240
+ return await readFile3(path4.join(snapshotDirFor(cwd, name, version), relFilePath), "utf8");
241
+ } catch (error) {
242
+ if (error.code === "ENOENT") return void 0;
243
+ throw error;
244
+ }
245
+ }
246
+
247
+ // src/lib/target.ts
248
+ import path5 from "path";
249
+ function toPosixPath(relativePath) {
250
+ return relativePath.split(path5.sep).join("/");
251
+ }
252
+ function resolveTargetPath(filePath, aliases) {
253
+ const match = filePath.match(/(?:^|\/)(components|lib|hooks)\/(.*)$/);
254
+ const cleaned = match ? `${match[1]}/${match[2]}` : filePath;
255
+ if (cleaned.startsWith("components/ui/")) {
256
+ return `${aliases.ui}/${cleaned.slice("components/ui/".length)}`;
257
+ }
258
+ if (cleaned.startsWith("components/")) {
259
+ return `${aliases.components}/${cleaned.slice("components/".length)}`;
260
+ }
261
+ if (cleaned.startsWith("lib/")) {
262
+ return `${aliases.lib}/${cleaned.slice("lib/".length)}`;
263
+ }
264
+ if (cleaned.startsWith("hooks/")) {
265
+ return `${aliases.hooks}/${cleaned.slice("hooks/".length)}`;
266
+ }
267
+ return `${aliases.components}/${cleaned}`;
268
+ }
269
+
270
+ // src/commands/add.ts
271
+ async function pathExists(target) {
272
+ try {
273
+ await access4(target);
274
+ return true;
275
+ } catch {
276
+ return false;
277
+ }
278
+ }
279
+ async function addCommand(components, options) {
280
+ const cwd = process.cwd();
281
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
282
+ console.log(`\u062F\u0631\u06CC\u0627\u0641\u062A \u0627\u0632 ${config.registryUrl} \u2026`);
283
+ const items = await resolveRegistryTree(components, config.registryUrl);
284
+ const dependencies = /* @__PURE__ */ new Set();
285
+ const devDependencies = /* @__PURE__ */ new Set();
286
+ let written = 0;
287
+ let skipped = 0;
288
+ const lock = await readLock(cwd);
289
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
290
+ let lockChanged = false;
291
+ for (const item of items.values()) {
292
+ for (const dep of item.dependencies ?? []) dependencies.add(dep);
293
+ for (const dep of item.devDependencies ?? []) devDependencies.add(dep);
294
+ const writtenFiles = {};
295
+ const writtenContents = {};
296
+ for (const file of item.files) {
297
+ const target = path6.join(cwd, resolveTargetPath(file.path, config.aliases));
298
+ if (!options.overwrite && await pathExists(target)) {
299
+ console.log(`- \u0631\u062F \u0634\u062F (\u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F): ${path6.relative(cwd, target)}`);
300
+ skipped++;
301
+ continue;
302
+ }
303
+ await mkdir3(path6.dirname(target), { recursive: true });
304
+ await writeFile4(target, file.content, "utf8");
305
+ console.log(`+ ${path6.relative(cwd, target)}`);
306
+ written++;
307
+ const relPath = toPosixPath(path6.relative(cwd, target));
308
+ writtenFiles[relPath] = file.hash ?? hashContent(file.content);
309
+ writtenContents[relPath] = file.content;
310
+ }
311
+ if (Object.keys(writtenFiles).length > 0) {
312
+ const version = item.version ?? "0.0.0";
313
+ const snapshotResult = await writeComponentSnapshot(cwd, item.name, version, writtenContents);
314
+ if ("error" in snapshotResult) {
315
+ console.log(` \u26A0 \u06AF\u0631\u0641\u062A\u0646 \u0639\u06A9\u0633\u200C\u0641\u0648\u0631\u06CC \u0628\u0631\u0627\u06CC \xAB${item.name}\xBB \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F: ${snapshotResult.error}`);
316
+ }
317
+ lock.components[item.name] = mergeComponentEntry(lock.components[item.name], {
318
+ version: item.version,
319
+ registryUrl: config.registryUrl,
320
+ installedAt,
321
+ writtenFiles,
322
+ snapshot: "path" in snapshotResult ? { version, path: snapshotResult.path, installedAt } : void 0
323
+ });
324
+ lockChanged = true;
325
+ }
326
+ }
327
+ if (lockChanged) {
328
+ await writeLock(lock, cwd);
329
+ console.log(`
330
+ \u062B\u0628\u062A \u0634\u062F \u062F\u0631 ${path6.join(".dig", "registry-lock.json")}`);
331
+ }
332
+ console.log(`
333
+ ${written} \u0641\u0627\u06CC\u0644 \u0646\u0648\u0634\u062A\u0647 \u0634\u062F\u060C ${skipped} \u0641\u0627\u06CC\u0644 \u0631\u062F \u0634\u062F.`);
334
+ if (!dependencies.size && !devDependencies.size) return;
335
+ if (options.install === false) {
336
+ console.log("\n\u0628\u0631\u0627\u06CC \u0646\u0635\u0628 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627 \u062F\u0633\u062A\u06CC \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F:");
337
+ if (dependencies.size) console.log(` ${[...dependencies].join(" ")}`);
338
+ if (devDependencies.size) console.log(` (dev) ${[...devDependencies].join(" ")}`);
339
+ return;
340
+ }
341
+ const pm = await detectPackageManager(cwd);
342
+ if (dependencies.size) {
343
+ console.log(`
344
+ \u0646\u0635\u0628 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627 \u0628\u0627 ${pm} \u2026`);
345
+ await installDependencies(pm, [...dependencies], { cwd });
346
+ }
347
+ if (devDependencies.size) {
348
+ await installDependencies(pm, [...devDependencies], { dev: true, cwd });
349
+ }
350
+ }
351
+
352
+ // src/lib/diff.ts
353
+ import { readFile as readFile4 } from "fs/promises";
354
+ import path7 from "path";
355
+ var STATUS_PRIORITY = [
356
+ "conflict",
357
+ "untracked",
358
+ "missing_locally",
359
+ "update_available",
360
+ "new_upstream",
361
+ "local_customized",
362
+ "up_to_date"
363
+ ];
364
+ function classifyFile(installedHash, localHash, registryHash) {
365
+ if (installedHash === void 0) {
366
+ return localHash === void 0 ? "new_upstream" : "untracked";
367
+ }
368
+ if (localHash === void 0) return "missing_locally";
369
+ const registryChanged = registryHash !== installedHash;
370
+ const localChanged = localHash !== installedHash;
371
+ if (registryChanged && localChanged) return "conflict";
372
+ if (registryChanged) return "update_available";
373
+ if (localChanged) return "local_customized";
374
+ return "up_to_date";
375
+ }
376
+ function overallStatus(files) {
377
+ for (const status of STATUS_PRIORITY) {
378
+ if (files.some((file) => file.status === status)) return status;
379
+ }
380
+ return "up_to_date";
381
+ }
382
+ async function readLocalHash(target) {
383
+ try {
384
+ const content = await readFile4(target, "utf8");
385
+ return hashContent(content);
386
+ } catch (error) {
387
+ if (error.code === "ENOENT") return void 0;
388
+ throw error;
389
+ }
390
+ }
391
+ async function computeFileDrift(cwd, aliases, lockEntry, file) {
392
+ const target = path7.join(cwd, resolveTargetPath(file.path, aliases));
393
+ const relPath = toPosixPath(path7.relative(cwd, target));
394
+ const registryHash = file.hash ?? hashContent(file.content);
395
+ const installedHash = lockEntry?.files[relPath];
396
+ const localHash = await readLocalHash(target);
397
+ return {
398
+ path: relPath,
399
+ status: classifyFile(installedHash, localHash, registryHash),
400
+ installedHash,
401
+ localHash,
402
+ registryHash
403
+ };
404
+ }
405
+ async function computeComponentDrift(params) {
406
+ const { cwd, aliases, registryUrl, lockEntry, registryItem } = params;
407
+ const files = await Promise.all(
408
+ registryItem.files.map((file) => computeFileDrift(cwd, aliases, lockEntry, file))
409
+ );
410
+ return {
411
+ name: registryItem.name,
412
+ tracked: lockEntry !== void 0,
413
+ installedVersion: lockEntry?.version,
414
+ latestVersion: registryItem.version ?? "0.0.0",
415
+ registryUrl,
416
+ status: overallStatus(files),
417
+ files
418
+ };
419
+ }
420
+
421
+ // src/lib/file-diff-details.ts
422
+ import { readFile as readFile5 } from "fs/promises";
423
+ import path8 from "path";
424
+
425
+ // src/lib/text-diff.ts
426
+ var DEFAULT_CONTEXT = 3;
427
+ function normalizeLines(text) {
428
+ const normalized = text.replace(/\r\n/g, "\n");
429
+ const lines = normalized.split("\n");
430
+ if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
431
+ return lines;
432
+ }
433
+ function diffLines(oldText, newText) {
434
+ const oldLines = normalizeLines(oldText);
435
+ const newLines = normalizeLines(newText);
436
+ const n = oldLines.length;
437
+ const m = newLines.length;
438
+ const lcs = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
439
+ for (let i2 = n - 1; i2 >= 0; i2--) {
440
+ for (let j2 = m - 1; j2 >= 0; j2--) {
441
+ lcs[i2][j2] = oldLines[i2] === newLines[j2] ? lcs[i2 + 1][j2 + 1] + 1 : Math.max(lcs[i2 + 1][j2], lcs[i2][j2 + 1]);
442
+ }
443
+ }
444
+ const result = [];
445
+ let i = 0;
446
+ let j = 0;
447
+ while (i < n && j < m) {
448
+ if (oldLines[i] === newLines[j]) {
449
+ result.push({ type: "context", text: oldLines[i] });
450
+ i++;
451
+ j++;
452
+ } else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
453
+ result.push({ type: "remove", text: oldLines[i] });
454
+ i++;
455
+ } else {
456
+ result.push({ type: "add", text: newLines[j] });
457
+ j++;
458
+ }
459
+ }
460
+ while (i < n) {
461
+ result.push({ type: "remove", text: oldLines[i] });
462
+ i++;
463
+ }
464
+ while (j < m) {
465
+ result.push({ type: "add", text: newLines[j] });
466
+ j++;
467
+ }
468
+ return result;
469
+ }
470
+ function toHunks(lines, context = DEFAULT_CONTEXT) {
471
+ const windows = [];
472
+ lines.forEach((line, index) => {
473
+ if (line.type === "context") return;
474
+ const start = Math.max(0, index - context);
475
+ const end = Math.min(lines.length, index + context + 1);
476
+ const last = windows[windows.length - 1];
477
+ if (last && start <= last[1]) {
478
+ last[1] = Math.max(last[1], end);
479
+ } else {
480
+ windows.push([start, end]);
481
+ }
482
+ });
483
+ if (windows.length === 0) return [];
484
+ const oldLineNoAt = new Array(lines.length + 1);
485
+ const newLineNoAt = new Array(lines.length + 1);
486
+ oldLineNoAt[0] = 1;
487
+ newLineNoAt[0] = 1;
488
+ for (let index = 0; index < lines.length; index++) {
489
+ oldLineNoAt[index + 1] = oldLineNoAt[index] + (lines[index].type === "add" ? 0 : 1);
490
+ newLineNoAt[index + 1] = newLineNoAt[index] + (lines[index].type === "remove" ? 0 : 1);
491
+ }
492
+ return windows.map(([start, end]) => {
493
+ const hunkLines = lines.slice(start, end);
494
+ return {
495
+ oldStart: oldLineNoAt[start],
496
+ oldLines: hunkLines.filter((line) => line.type !== "add").length,
497
+ newStart: newLineNoAt[start],
498
+ newLines: hunkLines.filter((line) => line.type !== "remove").length,
499
+ lines: hunkLines
500
+ };
501
+ });
502
+ }
503
+ function formatUnifiedDiff(oldLabel, newLabel, oldText, newText, context = DEFAULT_CONTEXT) {
504
+ const hunks = toHunks(diffLines(oldText, newText), context);
505
+ if (hunks.length === 0) return "";
506
+ const header = [`--- ${oldLabel}`, `+++ ${newLabel}`];
507
+ const body = hunks.flatMap((hunk) => [
508
+ `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`,
509
+ ...hunk.lines.map((line) => {
510
+ const prefix = line.type === "add" ? "+" : line.type === "remove" ? "-" : " ";
511
+ return `${prefix}${line.text}`;
512
+ })
513
+ ]);
514
+ return [...header, ...body].join("\n");
515
+ }
516
+
517
+ // src/lib/file-diff-details.ts
518
+ var BINARY_PROBE_BYTES = 8e3;
519
+ function looksBinary(buffer) {
520
+ const probe = buffer.subarray(0, Math.min(buffer.length, BINARY_PROBE_BYTES));
521
+ if (probe.includes(0)) return true;
522
+ try {
523
+ new TextDecoder("utf-8", { fatal: true }).decode(probe);
524
+ return false;
525
+ } catch {
526
+ return true;
527
+ }
528
+ }
529
+ async function readTextFileSafely(target) {
530
+ let buffer;
531
+ try {
532
+ buffer = await readFile5(target);
533
+ } catch (error) {
534
+ if (error.code === "ENOENT") return void 0;
535
+ throw error;
536
+ }
537
+ if (looksBinary(buffer)) return "binary";
538
+ return buffer.toString("utf8");
539
+ }
540
+ var NO_BASELINE_CONFLICT_NOTE = "\u062A\u0648\u062C\u0647: \u0639\u06A9\u0633\u200C\u0641\u0648\u0631\u06CC\u0650 \u0646\u0635\u0628\u200C\u0634\u062F\u0647 \u0628\u0631\u0627\u06CC \u0627\u06CC\u0646 \u0641\u0627\u06CC\u0644 \u062F\u0631 \u062F\u0633\u062A\u0631\u0633 \u0646\u06CC\u0633\u062A (\u067E\u0631\u0648\u0698\u0647\u200C\u0627\u06CC \u0642\u062F\u06CC\u0645\u06CC\u200C\u062A\u0631 \u0627\u0632 \u0641\u0627\u0632 \u06F5\u060C \u06CC\u0627 \u0641\u0627\u06CC\u0644 \u062F\u0631 \u0622\u0646 \u0646\u0633\u062E\u0647 \u0630\u062E\u06CC\u0631\u0647 \u0646\u0634\u062F\u0647) \u2014 \u0627\u06CC\u0646 \u062F\u06CC\u0641 \u062A\u0641\u0627\u0648\u062A \u0645\u0633\u062A\u0642\u06CC\u0645 \u0645\u062D\u0644\u06CC/\u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0627\u0633\u062A\u060C \u0646\u0647 \u06CC\u06A9 \u0645\u0642\u0627\u06CC\u0633\u0647\u0654 \u0633\u0647\u200C\u0637\u0631\u0641\u0647\u0654 \u0648\u0627\u0642\u0639\u06CC.";
541
+ function countChanges(lines) {
542
+ let added = 0;
543
+ let removed = 0;
544
+ for (const line of lines) {
545
+ if (line.type === "add") added++;
546
+ else if (line.type === "remove") removed++;
547
+ }
548
+ return { added, removed };
549
+ }
550
+ function describeThreeWayConflict(baseContent, localContent, registryContent) {
551
+ const local = countChanges(diffLines(baseContent, localContent));
552
+ const registry = countChanges(diffLines(baseContent, registryContent));
553
+ return `\u0646\u0633\u0628\u062A \u0628\u0647 \u0646\u0633\u062E\u0647\u0654 \u0646\u0635\u0628\u200C\u0634\u062F\u0647 (\u067E\u0627\u06CC\u0647): \u0634\u0645\u0627 ${local.added + local.removed} \u062E\u0637 \u062A\u063A\u06CC\u06CC\u0631 \u062F\u0627\u062F\u0647\u200C\u0627\u06CC\u062F (+${local.added}/-${local.removed})\u060C \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC ${registry.added + registry.removed} \u062E\u0637 \u062A\u063A\u06CC\u06CC\u0631 \u062F\u0627\u062F\u0647 (+${registry.added}/-${registry.removed}). \u062F\u06CC\u06AF \u062E\u0648\u062F\u06A9\u0627\u0631 \u0627\u062F\u063A\u0627\u0645 \u0646\u0645\u06CC\u200C\u06A9\u0646\u062F \u2014 \u062F\u0648 \u062F\u06CC\u0641\u0650 \u0628\u0627\u0644\u0627 \u0631\u0627 \u062F\u0633\u062A\u06CC \u0628\u0631\u0631\u0633\u06CC \u06A9\u0646\u06CC\u062F.`;
554
+ }
555
+ async function computeFileDiffDetails(params) {
556
+ const { cwd, aliases, name, snapshotVersion, file, registryFile } = params;
557
+ if (file.status === "up_to_date") return void 0;
558
+ if (file.status === "new_upstream") {
559
+ return {
560
+ path: file.path,
561
+ sections: [],
562
+ note: "\u0641\u0627\u06CC\u0644 \u062C\u062F\u06CC\u062F \u062F\u0631 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC\u061B \u0647\u0646\u0648\u0632 \u0646\u0635\u0628 \u0646\u0634\u062F\u0647 \u2014 \u0686\u06CC\u0632\u06CC \u0628\u0631\u0627\u06CC \u0645\u0642\u0627\u06CC\u0633\u0647 \u0646\u06CC\u0633\u062A."
563
+ };
564
+ }
565
+ if (file.status === "missing_locally") {
566
+ return {
567
+ path: file.path,
568
+ sections: [],
569
+ note: file.registryHash === file.installedHash ? "\u0641\u0627\u06CC\u0644 \u0628\u0647\u200C\u0635\u0648\u0631\u062A \u0645\u062D\u0644\u06CC \u062D\u0630\u0641 \u0634\u062F\u0647\u061B \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0627\u0632 \u0632\u0645\u0627\u0646 \u0646\u0635\u0628 \u062A\u063A\u06CC\u06CC\u0631\u06CC \u0646\u06A9\u0631\u062F\u0647." : "\u0641\u0627\u06CC\u0644 \u0628\u0647\u200C\u0635\u0648\u0631\u062A \u0645\u062D\u0644\u06CC \u062D\u0630\u0641 \u0634\u062F\u0647 \u0648 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0647\u0645 \u062A\u063A\u06CC\u06CC\u0631 \u06A9\u0631\u062F\u0647 \u2014 \u0645\u0642\u0627\u06CC\u0633\u0647\u200C\u0627\u06CC \u0628\u0627 \u0641\u0627\u06CC\u0644 \u062D\u0630\u0641\u200C\u0634\u062F\u0647 \u0645\u0645\u06A9\u0646 \u0646\u06CC\u0633\u062A."
570
+ };
571
+ }
572
+ const target = path8.join(cwd, resolveTargetPath(registryFile.path, aliases));
573
+ const localContent = await readTextFileSafely(target);
574
+ if (localContent === "binary") {
575
+ return {
576
+ path: file.path,
577
+ skippedReason: "binary",
578
+ sections: [],
579
+ note: "\u0641\u0627\u06CC\u0644 \u0645\u062D\u0644\u06CC \u0628\u0627\u06CC\u0646\u0631\u06CC \u06CC\u0627 \u063A\u06CC\u0631 UTF-8 \u062A\u0634\u062E\u06CC\u0635 \u062F\u0627\u062F\u0647 \u0634\u062F\u061B \u062F\u06CC\u0641 \u0631\u062F \u0634\u062F."
580
+ };
581
+ }
582
+ if (localContent === void 0) {
583
+ return {
584
+ path: file.path,
585
+ sections: [],
586
+ note: "\u0641\u0627\u06CC\u0644 \u0645\u062D\u0644\u06CC \u0647\u0646\u06AF\u0627\u0645 \u0645\u062D\u0627\u0633\u0628\u0647\u0654 \u062F\u06CC\u0641 \u062F\u06CC\u06AF\u0631 \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F."
587
+ };
588
+ }
589
+ const registryContent = registryFile.content;
590
+ const sections = [];
591
+ let note;
592
+ if (file.status === "update_available") {
593
+ const diff = formatUnifiedDiff("\u0646\u0635\u0628\u200C\u0634\u062F\u0647", "\u0631\u062C\u06CC\u0633\u062A\u0631\u06CC", localContent, registryContent);
594
+ if (diff) sections.push({ label: "\u0646\u0635\u0628\u200C\u0634\u062F\u0647 \u2192 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC", diff });
595
+ } else if (file.status === "local_customized") {
596
+ const diff = formatUnifiedDiff("\u0646\u0635\u0628\u200C\u0634\u062F\u0647", "\u0645\u062D\u0644\u06CC", registryContent, localContent);
597
+ if (diff) sections.push({ label: "\u0646\u0635\u0628\u200C\u0634\u062F\u0647 \u2192 \u0645\u062D\u0644\u06CC", diff });
598
+ } else if (file.status === "conflict") {
599
+ const baseContent = snapshotVersion ? await readSnapshotFile(cwd, name, snapshotVersion, file.path) : void 0;
600
+ if (baseContent !== void 0) {
601
+ const baseToLocal = formatUnifiedDiff("\u067E\u0627\u06CC\u0647 (\u0646\u0635\u0628\u200C\u0634\u062F\u0647)", "\u0645\u062D\u0644\u06CC", baseContent, localContent);
602
+ const baseToRegistry = formatUnifiedDiff(
603
+ "\u067E\u0627\u06CC\u0647 (\u0646\u0635\u0628\u200C\u0634\u062F\u0647)",
604
+ "\u0631\u062C\u06CC\u0633\u062A\u0631\u06CC",
605
+ baseContent,
606
+ registryContent
607
+ );
608
+ if (baseToLocal) sections.push({ label: "\u067E\u0627\u06CC\u0647 \u2192 \u0645\u062D\u0644\u06CC (\u062A\u063A\u06CC\u06CC\u0631\u0627\u062A \u0634\u0645\u0627)", diff: baseToLocal });
609
+ if (baseToRegistry) {
610
+ sections.push({ label: "\u067E\u0627\u06CC\u0647 \u2192 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC (\u062A\u063A\u06CC\u06CC\u0631\u0627\u062A \u0628\u0627\u0644\u0627\u062F\u0633\u062A)", diff: baseToRegistry });
611
+ }
612
+ note = describeThreeWayConflict(baseContent, localContent, registryContent);
613
+ } else {
614
+ const diff = formatUnifiedDiff("\u0645\u062D\u0644\u06CC", "\u0631\u062C\u06CC\u0633\u062A\u0631\u06CC", localContent, registryContent);
615
+ if (diff) sections.push({ label: "\u0645\u062D\u0644\u06CC \u2192 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC", diff });
616
+ note = NO_BASELINE_CONFLICT_NOTE;
617
+ }
618
+ } else if (file.status === "untracked") {
619
+ const diff = formatUnifiedDiff("\u0645\u062D\u0644\u06CC", "\u0631\u062C\u06CC\u0633\u062A\u0631\u06CC", localContent, registryContent);
620
+ if (diff) {
621
+ sections.push({ label: "\u0645\u062D\u0644\u06CC \u2192 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC (\u0628\u062F\u0648\u0646 \u0631\u06A9\u0648\u0631\u062F \u0646\u0635\u0628)", diff });
622
+ } else {
623
+ note = "\u0641\u0627\u06CC\u0644 \u0645\u062D\u0644\u06CC \u0628\u0627 \u0646\u0633\u062E\u0647\u0654 \u0641\u0639\u0644\u06CC \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u06CC\u06A9\u0633\u0627\u0646 \u0627\u0633\u062A (\u0631\u06A9\u0648\u0631\u062F\u06CC \u062F\u0631 lock \u0646\u062F\u0627\u0631\u062F).";
624
+ }
625
+ }
626
+ return { path: file.path, sections, note };
627
+ }
628
+
629
+ // src/lib/status-labels.ts
630
+ var STATUS_LABEL = {
631
+ up_to_date: "\u0628\u0647\u200C\u0631\u0648\u0632",
632
+ update_available: "\u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC \u0627\u06CC\u0645\u0646 \u0645\u0648\u062C\u0648\u062F \u0627\u0633\u062A",
633
+ local_customized: "\u062A\u063A\u06CC\u06CC\u0631 \u0645\u062D\u0644\u06CC \u0634\u0646\u0627\u0633\u0627\u06CC\u06CC \u0634\u062F",
634
+ conflict: "\u062A\u0639\u0627\u0631\u0636 \u2014 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0648 \u0641\u0627\u06CC\u0644 \u0645\u062D\u0644\u06CC \u0647\u0631 \u062F\u0648 \u062A\u063A\u06CC\u06CC\u0631 \u06A9\u0631\u062F\u0647\u200C\u0627\u0646\u062F",
635
+ missing_locally: "\u0641\u0627\u06CC\u0644 \u0628\u0647\u200C\u0635\u0648\u0631\u062A \u0645\u062D\u0644\u06CC \u062D\u0630\u0641 \u0634\u062F\u0647",
636
+ untracked: "\u0641\u0627\u06CC\u0644\u06CC \u0646\u0627\u0645\u0631\u062A\u0628\u0637 \u0628\u0627 dig \u062F\u0631 \u0627\u06CC\u0646 \u0645\u0633\u06CC\u0631 \u0647\u0633\u062A (\u0628\u062F\u0648\u0646 \u0631\u06A9\u0648\u0631\u062F \u0646\u0635\u0628)",
637
+ new_upstream: "\u0641\u0627\u06CC\u0644 \u062C\u062F\u06CC\u062F \u062F\u0631 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u2014 \u0647\u0646\u0648\u0632 \u0646\u0635\u0628 \u0646\u0634\u062F\u0647"
638
+ };
639
+ var STATUS_ICON = {
640
+ up_to_date: "\u2713",
641
+ update_available: "\u2191",
642
+ local_customized: "\u270E",
643
+ conflict: "\u26A0",
644
+ missing_locally: "\u2717",
645
+ untracked: "?",
646
+ new_upstream: "+"
647
+ };
648
+
649
+ // src/commands/diff.ts
650
+ async function diffCommand(components, options) {
651
+ const cwd = process.cwd();
652
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
653
+ const lock = await readLock(cwd);
654
+ const results = [];
655
+ for (const name of components) {
656
+ let registryItem;
657
+ try {
658
+ registryItem = await fetchRegistryItem(name, config.registryUrl);
659
+ } catch (error) {
660
+ console.error(`\u2717 ${name}: ${error.message}`);
661
+ continue;
662
+ }
663
+ const lockEntry = lock.components[name];
664
+ const drift = await computeComponentDrift({
665
+ cwd,
666
+ aliases: config.aliases,
667
+ registryUrl: config.registryUrl,
668
+ lockEntry,
669
+ registryItem
670
+ });
671
+ let details;
672
+ if (options.details) {
673
+ const computed = await Promise.all(
674
+ drift.files.map(
675
+ (file, index) => file.status === "up_to_date" ? Promise.resolve(void 0) : computeFileDiffDetails({
676
+ cwd,
677
+ aliases: config.aliases,
678
+ name,
679
+ snapshotVersion: lockEntry?.snapshot?.version,
680
+ file,
681
+ registryFile: registryItem.files[index]
682
+ })
683
+ )
684
+ );
685
+ details = computed.filter((d) => d !== void 0);
686
+ }
687
+ results.push({ drift, details });
688
+ }
689
+ if (options.json) {
690
+ console.log(
691
+ JSON.stringify(
692
+ results.map(({ drift, details }) => details ? { ...drift, details } : drift),
693
+ null,
694
+ 2
695
+ )
696
+ );
697
+ return;
698
+ }
699
+ for (const { drift, details } of results) {
700
+ printComponentDrift(drift, details);
701
+ }
702
+ }
703
+ function printComponentDrift(drift, details) {
704
+ console.log(`
705
+ ${drift.name}`);
706
+ if (!drift.tracked) {
707
+ console.log(" \u0631\u06A9\u0648\u0631\u062F\u06CC \u062F\u0631 .dig/registry-lock.json \u0646\u06CC\u0633\u062A (\u0628\u0627 dig \u0646\u0635\u0628 \u0646\u0634\u062F\u0647).");
708
+ }
709
+ console.log(
710
+ ` \u0646\u0635\u0628\u200C\u0634\u062F\u0647: ${drift.installedVersion ?? "\u2014"} \u2192 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC: ${drift.latestVersion}`
711
+ );
712
+ console.log(` \u0648\u0636\u0639\u06CC\u062A \u06A9\u0644\u06CC: ${STATUS_ICON[drift.status]} ${STATUS_LABEL[drift.status]}`);
713
+ for (const file of drift.files) {
714
+ console.log(` ${STATUS_ICON[file.status]} ${file.path} \u2014 ${STATUS_LABEL[file.status]}`);
715
+ const detail = details?.find((d) => d.path === file.path);
716
+ if (!detail) continue;
717
+ if (detail.note) console.log(` ${detail.note}`);
718
+ for (const section of detail.sections) {
719
+ console.log(`
720
+ --- ${section.label} ---`);
721
+ for (const line of section.diff.split("\n")) {
722
+ console.log(` ${line}`);
723
+ }
724
+ }
725
+ }
726
+ }
727
+
728
+ // src/lib/doctor.ts
729
+ var DEPENDENCY_IMPACT_WARNING_THRESHOLD = 5;
730
+ var ADOPTION_RATE_WARNING_THRESHOLD = 50;
731
+ function migrationFinding(component) {
732
+ return {
733
+ category: "migration",
734
+ severity: "high",
735
+ component: component.name,
736
+ reason: [
737
+ `${component.usageCount} \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u0634\u0646\u0627\u0633\u0627\u06CC\u06CC \u0634\u062F`,
738
+ "\u0645\u0647\u0627\u062C\u0631\u062A \u0645\u0648\u062C\u0648\u062F \u0627\u0633\u062A",
739
+ `\u0631\u0648\u06CC ${component.dependencyImpact} \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u0650 \u062F\u06CC\u06AF\u0631 \u0627\u062B\u0631 \u0645\u06CC\u200C\u06AF\u0630\u0627\u0631\u062F`
740
+ ].join("\n"),
741
+ recommendation: `\u062F\u0633\u062A\u0648\u0631 \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F:
742
+ dig migrate ${component.name} --plan`
743
+ };
744
+ }
745
+ function lifecycleFinding(component) {
746
+ return {
747
+ category: "lifecycle",
748
+ severity: "medium",
749
+ component: component.name,
750
+ reason: "\u0648\u0636\u0639\u06CC\u062A\u0650 \u0686\u0631\u062E\u0647\u0654 \u062D\u06CC\u0627\u062A\u0650 \u0627\u06CC\u0646 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0645\u0646\u0633\u0648\u062E/\u062D\u0630\u0641\u200C\u0634\u062F\u0647 \u0627\u0633\u062A.",
751
+ recommendation: component.replacement ? `\u0628\u0647\u200C\u062C\u0627\u06CC\u0634 \u0627\u0632 ${component.replacement} \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u06A9\u0646\u06CC\u062F.` : "\u0628\u0631\u0627\u06CC \u062C\u0627\u06CC\u06AF\u0632\u06CC\u0646\u0650 \u067E\u06CC\u0634\u0646\u0647\u0627\u062F\u06CC \u0628\u0647 \u0645\u0633\u062A\u0646\u062F\u0627\u062A\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0645\u0631\u0627\u062C\u0639\u0647 \u06A9\u0646\u06CC\u062F."
752
+ };
753
+ }
754
+ function dependencyFinding(component) {
755
+ return {
756
+ category: "dependency",
757
+ severity: "warning",
758
+ component: component.name,
759
+ reason: `\u0627\u06CC\u0646 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0631\u0648\u06CC ${component.dependencyImpact} \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u0650 \u062F\u06CC\u06AF\u0631 \u0627\u062B\u0631 \u0645\u06CC\u200C\u06AF\u0630\u0627\u0631\u062F \u2014 \u062A\u063A\u06CC\u06CC\u0631\u062F\u0627\u062F\u0646\u0634 \u0631\u06CC\u0633\u06A9\u0650 \u06AF\u0633\u062A\u0631\u062F\u0647 \u062F\u0627\u0631\u062F.`,
760
+ recommendation: `dig governance ${component.name} \u0631\u0627 \u067E\u06CC\u0634 \u0627\u0632 \u0647\u0631 \u062A\u063A\u06CC\u06CC\u0631\u0650 \u0634\u06A9\u0646\u0646\u062F\u0647 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F.`
761
+ };
762
+ }
763
+ function usageFinding(component) {
764
+ return {
765
+ category: "usage",
766
+ severity: "warning",
767
+ component: component.name,
768
+ reason: "\u0627\u06CC\u0646 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0646\u0635\u0628 \u0634\u062F\u0647 \u0648\u0644\u06CC \u0647\u06CC\u0686\u200C\u062C\u0627 \u062F\u0631 \u0631\u06CC\u067E\u0627\u0632\u06CC\u062A\u0648\u0631\u06CC \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u0646\u0645\u06CC\u200C\u0634\u0648\u062F.",
769
+ recommendation: "\u0628\u0631\u0631\u0633\u06CC \u06A9\u0646\u06CC\u062F \u06A9\u0647 \u0622\u06CC\u0627 \u0647\u0646\u0648\u0632 \u0644\u0627\u0632\u0645 \u0627\u0633\u062A\u061B \u062F\u0631 \u063A\u06CC\u0631\u0650 \u0627\u06CC\u0646 \u0635\u0648\u0631\u062A \u062D\u0630\u0641\u0634 \u06A9\u0646\u06CC\u062F."
770
+ };
771
+ }
772
+ function adoptionFinding(adoptionRate) {
773
+ return {
774
+ category: "adoption",
775
+ severity: "warning",
776
+ reason: `\u0646\u0631\u062E\u0650 \u067E\u0630\u06CC\u0631\u0634 ${adoptionRate}\u066A \u0627\u0633\u062A \u2014 \u06A9\u0645\u062A\u0631 \u0627\u0632 \u0622\u0633\u062A\u0627\u0646\u0647\u0654 ${ADOPTION_RATE_WARNING_THRESHOLD}\u066A.`,
777
+ recommendation: "\u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627\u06CC \u0628\u062F\u0648\u0646\u0650 \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u0631\u0627 \u0628\u0627 dig health --json \u0628\u0631\u0631\u0633\u06CC \u06A9\u0646\u06CC\u062F."
778
+ };
779
+ }
780
+ var ruleBasedAnalyzer = {
781
+ id: "rule-based",
782
+ async analyze(report) {
783
+ const findings = [];
784
+ if (report.components.length > 0 && report.health.adoptionRate < ADOPTION_RATE_WARNING_THRESHOLD) {
785
+ findings.push(adoptionFinding(report.health.adoptionRate));
786
+ }
787
+ for (const component of report.components) {
788
+ if (component.migrationPending) findings.push(migrationFinding(component));
789
+ if (component.deprecated) findings.push(lifecycleFinding(component));
790
+ if (component.dependencyImpact > DEPENDENCY_IMPACT_WARNING_THRESHOLD) {
791
+ findings.push(dependencyFinding(component));
792
+ }
793
+ if (component.usageCount === 0) findings.push(usageFinding(component));
794
+ }
795
+ return {
796
+ summary: findings.length === 0 ? "\u0633\u0627\u0644\u0645" : "\u0646\u06CC\u0627\u0632\u0645\u0646\u062F\u0650 \u062A\u0648\u062C\u0647",
797
+ findings
798
+ };
799
+ }
800
+ };
801
+ function resolveAnalyzer() {
802
+ return ruleBasedAnalyzer;
803
+ }
804
+
805
+ // src/lib/ai-prompts.ts
806
+ var AI_RESPONSE_SCHEMA_HINT = '{"findings": string[], "reasoning": string[], "recommendations": string[], "confidence": "high" | "medium" | "low"}';
807
+ function buildIntelligencePrompt(context) {
808
+ const lines = [
809
+ "You are analyzing aggregated design-system health metrics. No source code or file contents are included below \u2014 only metadata and counts.",
810
+ "",
811
+ `Overall adoption rate: ${context.health.adoptionRate}%`,
812
+ `Overall dependency risk: ${context.health.risk}`,
813
+ "",
814
+ "Components:"
815
+ ];
816
+ for (const component of context.components) {
817
+ const parts = [
818
+ `usageCount=${component.usageCount}`,
819
+ `deprecated=${component.deprecated}`,
820
+ `migrationPending=${component.migrationPending}`,
821
+ `dependencyImpact=${component.dependencyImpact}`,
822
+ `changeType=${component.changeType}`
823
+ ];
824
+ if (component.replacement) parts.push(`replacement=${component.replacement}`);
825
+ lines.push(`- ${component.name}: ${parts.join(", ")}`);
826
+ }
827
+ lines.push(
828
+ "",
829
+ "Identify findings, explain your reasoning, and suggest recommendations based ONLY on the metrics above.",
830
+ `Respond with ONLY strict JSON matching exactly this shape (no prose, no markdown fences): ${AI_RESPONSE_SCHEMA_HINT}`
831
+ );
832
+ return lines.join("\n");
833
+ }
834
+
835
+ // src/lib/llm-provider.ts
836
+ var ANTHROPIC_API_URL = "https://api.anthropic.com/v1/messages";
837
+ var DEFAULT_MODEL = "claude-sonnet-5";
838
+ var REQUEST_TIMEOUT_MS = 15e3;
839
+ function readAIConfig() {
840
+ const provider = process.env.DIG_AI_PROVIDER;
841
+ const apiKey = process.env.ANTHROPIC_API_KEY;
842
+ if (!provider || !apiKey) return void 0;
843
+ return { provider, apiKey, model: process.env.DIG_AI_MODEL || DEFAULT_MODEL };
844
+ }
845
+ function validateAIResponse(data) {
846
+ const errors = [];
847
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
848
+ const isStringArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
849
+ if (!isRecord(data)) {
850
+ return { valid: false, errors: ["\u067E\u0627\u0633\u062E\u0650 AI \u06CC\u06A9 \u0622\u0628\u062C\u06A9\u062A \u0646\u06CC\u0633\u062A."] };
851
+ }
852
+ if (!isStringArray(data.findings)) errors.push("findings \u0628\u0627\u06CC\u062F \u0622\u0631\u0627\u06CC\u0647\u200C\u0627\u06CC \u0627\u0632 \u0631\u0634\u062A\u0647 \u0628\u0627\u0634\u062F.");
853
+ if (!isStringArray(data.reasoning)) errors.push("reasoning \u0628\u0627\u06CC\u062F \u0622\u0631\u0627\u06CC\u0647\u200C\u0627\u06CC \u0627\u0632 \u0631\u0634\u062A\u0647 \u0628\u0627\u0634\u062F.");
854
+ if (!isStringArray(data.recommendations)) errors.push("recommendations \u0628\u0627\u06CC\u062F \u0622\u0631\u0627\u06CC\u0647\u200C\u0627\u06CC \u0627\u0632 \u0631\u0634\u062A\u0647 \u0628\u0627\u0634\u062F.");
855
+ if (!["high", "medium", "low"].includes(data.confidence)) {
856
+ errors.push('confidence \u0628\u0627\u06CC\u062F \u06CC\u06A9\u06CC \u0627\u0632 "high"/"medium"/"low" \u0628\u0627\u0634\u062F.');
857
+ }
858
+ return errors.length === 0 ? { valid: true, data } : { valid: false, errors };
859
+ }
860
+ function toReasoningEntry(raw) {
861
+ return {
862
+ reasoning: [
863
+ ...raw.findings.map((finding) => `\u06CC\u0627\u0641\u062A\u0647: ${finding}`),
864
+ ...raw.reasoning,
865
+ ...raw.recommendations.map((recommendation) => `\u062A\u0648\u0635\u06CC\u0647: ${recommendation}`)
866
+ ],
867
+ confidence: raw.confidence
868
+ };
869
+ }
870
+ async function callClaude(prompt, config) {
871
+ const controller = new AbortController();
872
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
873
+ try {
874
+ const response = await fetch(ANTHROPIC_API_URL, {
875
+ method: "POST",
876
+ headers: {
877
+ "content-type": "application/json",
878
+ "x-api-key": config.apiKey,
879
+ "anthropic-version": "2023-06-01"
880
+ },
881
+ body: JSON.stringify({
882
+ model: config.model,
883
+ max_tokens: 1024,
884
+ messages: [{ role: "user", content: prompt }]
885
+ }),
886
+ signal: controller.signal
887
+ });
888
+ if (!response.ok) {
889
+ throw new Error(`Claude API responded with status ${response.status}`);
890
+ }
891
+ const payload = await response.json();
892
+ const text = payload.content?.find((block) => block.type === "text")?.text;
893
+ if (!text) throw new Error("\u067E\u0627\u0633\u062E\u0650 Claude \u0628\u062E\u0634\u0650 \u0645\u062A\u0646\u06CC \u0646\u062F\u0627\u0634\u062A.");
894
+ return JSON.parse(text);
895
+ } finally {
896
+ clearTimeout(timeout);
897
+ }
898
+ }
899
+ var llmProvider = {
900
+ id: "llm-claude",
901
+ async analyze(context) {
902
+ const config = readAIConfig();
903
+ if (!config) return ruleBasedProvider.analyze(context);
904
+ try {
905
+ const prompt = buildIntelligencePrompt(context);
906
+ const rawJson = await callClaude(prompt, config);
907
+ const validation = validateAIResponse(rawJson);
908
+ if (!validation.valid) return ruleBasedProvider.analyze(context);
909
+ return { status: "ok", entries: [toReasoningEntry(validation.data)] };
910
+ } catch {
911
+ return ruleBasedProvider.analyze(context);
912
+ }
913
+ }
914
+ };
915
+
916
+ // src/lib/ai-provider.ts
917
+ var SEVERITY_TO_CONFIDENCE = {
918
+ high: "high",
919
+ medium: "medium",
920
+ warning: "low"
921
+ };
922
+ var ruleBasedProvider = {
923
+ id: "rule-based",
924
+ async analyze(context) {
925
+ const analysis = await ruleBasedAnalyzer.analyze(context);
926
+ const entries = analysis.findings.map((finding) => ({
927
+ category: finding.category,
928
+ component: finding.component,
929
+ reasoning: finding.reason.split("\n"),
930
+ confidence: SEVERITY_TO_CONFIDENCE[finding.severity]
931
+ }));
932
+ return { status: "ok", entries };
933
+ }
934
+ };
935
+ function resolveAIProvider() {
936
+ const config = readAIConfig();
937
+ return config?.provider === "claude" ? llmProvider : ruleBasedProvider;
938
+ }
939
+
940
+ // src/lib/dependency-graph.ts
941
+ function buildDependencyGraph(items) {
942
+ const dependencies = /* @__PURE__ */ new Map();
943
+ const dependents = /* @__PURE__ */ new Map();
944
+ for (const item of items) {
945
+ dependencies.set(item.name, item.registryDependencies ?? []);
946
+ }
947
+ for (const item of items) {
948
+ for (const dep of item.registryDependencies ?? []) {
949
+ const list = dependents.get(dep);
950
+ if (list) {
951
+ list.push(item.name);
952
+ } else {
953
+ dependents.set(dep, [item.name]);
954
+ }
955
+ }
956
+ }
957
+ return { dependencies, dependents };
958
+ }
959
+ function traverse(edges, start) {
960
+ const visited = /* @__PURE__ */ new Set();
961
+ const queue = [...edges.get(start) ?? []];
962
+ while (queue.length) {
963
+ const current = queue.shift();
964
+ if (visited.has(current)) continue;
965
+ visited.add(current);
966
+ queue.push(...edges.get(current) ?? []);
967
+ }
968
+ return [...visited];
969
+ }
970
+ function getAffectedComponents(graph, name) {
971
+ return traverse(graph.dependents, name);
972
+ }
973
+
974
+ // src/lib/lifecycle.ts
975
+ function parseLifecycle(component, version, raw) {
976
+ if (!raw) return { status: "active", component, version };
977
+ return { ...raw, component, version };
978
+ }
979
+
980
+ // src/lib/migration.ts
981
+ var DEFAULT_STRATEGY_FOR_TYPE = {
982
+ replace: "text",
983
+ manual: "manual",
984
+ codemod: "ast"
985
+ };
986
+ function resolveMigrationStrategyId(migration) {
987
+ return migration.strategy ?? DEFAULT_STRATEGY_FOR_TYPE[migration.type];
988
+ }
989
+ function parseMigrations(component, raw) {
990
+ return (raw ?? []).map((migration) => ({ ...migration, component }));
991
+ }
992
+ function compareVersions(a, b) {
993
+ const partsA = a.split(".").map((part) => Number(part) || 0);
994
+ const partsB = b.split(".").map((part) => Number(part) || 0);
995
+ const length = Math.max(partsA.length, partsB.length);
996
+ for (let i = 0; i < length; i++) {
997
+ const diff = (partsA[i] ?? 0) - (partsB[i] ?? 0);
998
+ if (diff !== 0) return diff;
999
+ }
1000
+ return 0;
1001
+ }
1002
+ function getApplicableMigrations(migrations, installedVersion, latestVersion) {
1003
+ return migrations.filter(
1004
+ (migration) => compareVersions(installedVersion, migration.toVersion) < 0 && compareVersions(migration.toVersion, latestVersion) <= 0
1005
+ ).sort((a, b) => compareVersions(a.toVersion, b.toVersion));
1006
+ }
1007
+
1008
+ // src/lib/release.ts
1009
+ function parseRelease(component, version, raw) {
1010
+ if (!raw) return void 0;
1011
+ return { ...raw, component, version };
1012
+ }
1013
+ var CHANGE_TYPE_ORDER = ["breaking", "added", "changed", "fixed"];
1014
+ function groupChangesByType(changes) {
1015
+ if (!changes || changes.length === 0) return [];
1016
+ return CHANGE_TYPE_ORDER.map((type) => ({
1017
+ type,
1018
+ entries: changes.filter((change) => change.type === type)
1019
+ })).filter((group) => group.entries.length > 0);
1020
+ }
1021
+
1022
+ // src/lib/governance.ts
1023
+ function deriveChangeType(release) {
1024
+ if (!release) return "unknown";
1025
+ const hasBreakingChange = (release.changes ?? []).some((change) => change.type === "breaking");
1026
+ return hasBreakingChange ? "breaking" : release.type;
1027
+ }
1028
+ function deriveDeprecatedApis(lifecycle) {
1029
+ if (lifecycle.status === "active") return [];
1030
+ if (lifecycle.message) return [lifecycle.message];
1031
+ const verb = lifecycle.status === "removed" ? "\u062D\u0630\u0641 \u0634\u062F\u0647" : "\u0645\u0646\u0633\u0648\u062E \u0634\u062F\u0647";
1032
+ return [`${lifecycle.component} ${verb} \u0627\u0633\u062A`];
1033
+ }
1034
+ function deriveActions(input) {
1035
+ const actions = [];
1036
+ if (input.requiredMigration) actions.push("\u0627\u062C\u0631\u0627\u06CC \u0645\u0647\u0627\u062C\u0631\u062A");
1037
+ if (input.affectedComponents.length > 0) actions.push("\u0628\u0631\u0631\u0633\u06CC\u0650 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627\u06CC \u062A\u062D\u062A\u200C\u062A\u0623\u062B\u06CC\u0631");
1038
+ if (input.deprecatedApis.length > 0) actions.push("\u0628\u0647\u200C\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC\u0650 \u0627\u0633\u062A\u0641\u0627\u062F\u0647\u200C\u0647\u0627\u06CC \u0645\u0646\u0633\u0648\u062E");
1039
+ if (input.changeType === "breaking" && actions.length === 0) {
1040
+ actions.push("\u0628\u0631\u0631\u0633\u06CC\u0650 \u062F\u0642\u06CC\u0642\u0650 \u0627\u06CC\u0646 \u062A\u063A\u06CC\u06CC\u0631\u0650 \u0634\u06A9\u0646\u0646\u062F\u0647 \u067E\u06CC\u0634 \u0627\u0632 \u0627\u0631\u062A\u0642\u0627");
1041
+ }
1042
+ return actions;
1043
+ }
1044
+ function buildGovernanceReport(params) {
1045
+ const {
1046
+ name,
1047
+ version,
1048
+ registryIndexItems,
1049
+ rawMigrations,
1050
+ rawRelease,
1051
+ rawLifecycle,
1052
+ installedVersion,
1053
+ repositoryImpact
1054
+ } = params;
1055
+ const graph = buildDependencyGraph(registryIndexItems);
1056
+ const affectedComponents = getAffectedComponents(graph, name);
1057
+ const lifecycle = parseLifecycle(name, version, rawLifecycle);
1058
+ const release = parseRelease(name, version, rawRelease);
1059
+ const migrations = parseMigrations(name, rawMigrations);
1060
+ const requiredMigration = installedVersion !== void 0 ? getApplicableMigrations(migrations, installedVersion, version).length > 0 : migrations.length > 0;
1061
+ const deprecatedApis = deriveDeprecatedApis(lifecycle);
1062
+ const changeType = deriveChangeType(release);
1063
+ const changedTokens = release?.tokens ?? [];
1064
+ const actions = deriveActions({ changeType, affectedComponents, requiredMigration, deprecatedApis });
1065
+ return {
1066
+ component: name,
1067
+ version,
1068
+ changeType,
1069
+ impact: { affectedComponents, requiredMigration, deprecatedApis, changedTokens },
1070
+ repositoryImpact,
1071
+ actions
1072
+ };
1073
+ }
1074
+
1075
+ // src/lib/health.ts
1076
+ var MAX_HIGH_IMPACT_COMPONENTS = 5;
1077
+ var HIGH_IMPACT_THRESHOLD = 5;
1078
+ function buildHealthReport(params) {
1079
+ const { registryIndexItems, installedComponents } = params;
1080
+ const usedNames = installedComponents.filter((c) => c.usageCount > 0).map((c) => c.name);
1081
+ const unusedNames = installedComponents.filter((c) => c.usageCount === 0).map((c) => c.name);
1082
+ const installedCount = installedComponents.length;
1083
+ const rate = installedCount === 0 ? 0 : Math.round(usedNames.length / installedCount * 100);
1084
+ const pending = [];
1085
+ for (const component of installedComponents) {
1086
+ const migrations = parseMigrations(component.name, component.rawMigrations);
1087
+ const applicable = getApplicableMigrations(
1088
+ migrations,
1089
+ component.lockVersion,
1090
+ component.latestVersion
1091
+ );
1092
+ if (applicable.length > 0) {
1093
+ pending.push({ component: component.name, count: applicable.length });
1094
+ }
1095
+ }
1096
+ const deprecated = [];
1097
+ const removed = [];
1098
+ for (const component of installedComponents) {
1099
+ const lifecycle = parseLifecycle(component.name, component.latestVersion, component.rawLifecycle);
1100
+ if (lifecycle.status === "deprecated") {
1101
+ deprecated.push({ component: component.name, replacement: lifecycle.replacement });
1102
+ } else if (lifecycle.status === "removed") {
1103
+ removed.push({ component: component.name, replacement: lifecycle.replacement });
1104
+ }
1105
+ }
1106
+ const graph = buildDependencyGraph(registryIndexItems);
1107
+ const highImpactComponents = registryIndexItems.map((item) => ({ name: item.name, affectedCount: getAffectedComponents(graph, item.name).length })).filter((entry) => entry.affectedCount > 0).sort((a, b) => b.affectedCount - a.affectedCount).slice(0, MAX_HIGH_IMPACT_COMPONENTS);
1108
+ return {
1109
+ adoption: {
1110
+ rate,
1111
+ installed: installedCount,
1112
+ used: usedNames.length,
1113
+ unused: unusedNames.length
1114
+ },
1115
+ migrations: { pending },
1116
+ lifecycle: { deprecated, removed },
1117
+ dependencyRisk: { highImpactComponents },
1118
+ repositoryUsage: { unusedComponents: unusedNames }
1119
+ };
1120
+ }
1121
+ function dependencyRiskLevel(highImpactComponents) {
1122
+ if (highImpactComponents.length === 0) return "low";
1123
+ return highImpactComponents[0].affectedCount >= HIGH_IMPACT_THRESHOLD ? "high" : "medium";
1124
+ }
1125
+
1126
+ // src/lib/repo-scanner.ts
1127
+ import { readFile as readFile6, readdir } from "fs/promises";
1128
+ import path9 from "path";
1129
+ import ts2 from "typescript";
1130
+
1131
+ // src/lib/ast-engine.ts
1132
+ import ts from "typescript";
1133
+ function toPascalCase(slug) {
1134
+ return slug.split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
1135
+ }
1136
+ function findRenamePropEdits(sourceFile, migration) {
1137
+ if (migration.type !== "codemod") return [];
1138
+ const spec = migration.transform;
1139
+ if (!spec || spec.operation !== "rename-prop" || !spec.from || !spec.to) return [];
1140
+ const from = spec.from;
1141
+ const to = spec.to;
1142
+ const targetTag = toPascalCase(migration.component);
1143
+ const edits = [];
1144
+ function visit(node) {
1145
+ if ((ts.isJsxSelfClosingElement(node) || ts.isJsxOpeningElement(node)) && ts.isIdentifier(node.tagName) && node.tagName.text === targetTag) {
1146
+ for (const attr of node.attributes.properties) {
1147
+ if (ts.isJsxAttribute(attr) && ts.isIdentifier(attr.name) && attr.name.text === from && attr.initializer === void 0) {
1148
+ edits.push({
1149
+ start: attr.getStart(sourceFile),
1150
+ end: attr.getEnd(),
1151
+ text: `${to}="${from}"`
1152
+ });
1153
+ }
1154
+ }
1155
+ }
1156
+ ts.forEachChild(node, visit);
1157
+ }
1158
+ visit(sourceFile);
1159
+ return edits;
1160
+ }
1161
+ var tsAstEngine = {
1162
+ parser(content) {
1163
+ return {
1164
+ sourceFile: ts.createSourceFile(
1165
+ "component.tsx",
1166
+ content,
1167
+ ts.ScriptTarget.Latest,
1168
+ true,
1169
+ ts.ScriptKind.TSX
1170
+ ),
1171
+ edits: []
1172
+ };
1173
+ },
1174
+ transform(ast, migration) {
1175
+ return { sourceFile: ast.sourceFile, edits: findRenamePropEdits(ast.sourceFile, migration) };
1176
+ },
1177
+ print(ast) {
1178
+ if (ast.edits.length === 0) return ast.sourceFile.text;
1179
+ let result = ast.sourceFile.text;
1180
+ const sortedByStartDescending = [...ast.edits].sort((a, b) => b.start - a.start);
1181
+ for (const edit of sortedByStartDescending) {
1182
+ result = result.slice(0, edit.start) + edit.text + result.slice(edit.end);
1183
+ }
1184
+ return result;
1185
+ }
1186
+ };
1187
+
1188
+ // src/lib/repo-scanner.ts
1189
+ var SCAN_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".jsx"]);
1190
+ var IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".dig", "dist", "build", "out", "coverage"]);
1191
+ async function collectSourceFiles(root) {
1192
+ const results = [];
1193
+ async function walk(dir) {
1194
+ let entries;
1195
+ try {
1196
+ entries = await readdir(dir, { withFileTypes: true });
1197
+ } catch {
1198
+ return;
1199
+ }
1200
+ for (const entry of entries) {
1201
+ if (entry.isDirectory()) {
1202
+ if (IGNORED_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
1203
+ await walk(path9.join(dir, entry.name));
1204
+ continue;
1205
+ }
1206
+ if (entry.isFile() && SCAN_EXTENSIONS.has(path9.extname(entry.name))) {
1207
+ results.push(path9.join(dir, entry.name));
1208
+ }
1209
+ }
1210
+ }
1211
+ await walk(root);
1212
+ return results;
1213
+ }
1214
+ function findJsxUsagesInSource(sourceFile, tagName) {
1215
+ const snippets = [];
1216
+ function visit(node) {
1217
+ if ((ts2.isJsxSelfClosingElement(node) || ts2.isJsxOpeningElement(node)) && ts2.isIdentifier(node.tagName) && node.tagName.text === tagName) {
1218
+ snippets.push(node.getText(sourceFile));
1219
+ }
1220
+ ts2.forEachChild(node, visit);
1221
+ }
1222
+ visit(sourceFile);
1223
+ return snippets;
1224
+ }
1225
+ async function scanRepositoryForComponent(params) {
1226
+ const { projectRoot, component } = params;
1227
+ const tagName = toPascalCase(component);
1228
+ const sourceFilePaths = await collectSourceFiles(projectRoot);
1229
+ const files = [];
1230
+ let usageCount = 0;
1231
+ for (const filePath of sourceFilePaths) {
1232
+ let content;
1233
+ try {
1234
+ content = await readFile6(filePath, "utf8");
1235
+ } catch {
1236
+ continue;
1237
+ }
1238
+ const sourceFile = ts2.createSourceFile(
1239
+ filePath,
1240
+ content,
1241
+ ts2.ScriptTarget.Latest,
1242
+ true,
1243
+ ts2.ScriptKind.TSX
1244
+ );
1245
+ const snippets = findJsxUsagesInSource(sourceFile, tagName);
1246
+ if (snippets.length === 0) continue;
1247
+ files.push({
1248
+ path: toPosixPath(path9.relative(projectRoot, filePath)),
1249
+ usages: snippets.length,
1250
+ snippets
1251
+ });
1252
+ usageCount += snippets.length;
1253
+ }
1254
+ files.sort((a, b) => a.path.localeCompare(b.path));
1255
+ return { component, files, usageCount };
1256
+ }
1257
+
1258
+ // src/lib/intelligence-export.ts
1259
+ var INTELLIGENCE_EXPORT_SCHEMA_VERSION = "v2";
1260
+ function buildIntelligenceExport(params) {
1261
+ const { registryIndexItems, installedComponents } = params;
1262
+ const healthReport = buildHealthReport({
1263
+ registryIndexItems,
1264
+ installedComponents: installedComponents.map(
1265
+ ({ rawRelease: _rawRelease, ...rest }) => rest
1266
+ )
1267
+ });
1268
+ const components = installedComponents.map((component) => {
1269
+ const governance = buildGovernanceReport({
1270
+ name: component.name,
1271
+ version: component.latestVersion,
1272
+ registryIndexItems,
1273
+ rawMigrations: component.rawMigrations,
1274
+ rawRelease: component.rawRelease,
1275
+ rawLifecycle: component.rawLifecycle,
1276
+ installedVersion: component.lockVersion
1277
+ });
1278
+ const lifecycle = parseLifecycle(component.name, component.latestVersion, component.rawLifecycle);
1279
+ return {
1280
+ name: component.name,
1281
+ usageCount: component.usageCount,
1282
+ deprecated: governance.impact.deprecatedApis.length > 0,
1283
+ migrationPending: governance.impact.requiredMigration,
1284
+ dependencyImpact: governance.impact.affectedComponents.length,
1285
+ changeType: governance.changeType,
1286
+ replacement: lifecycle.replacement
1287
+ };
1288
+ });
1289
+ return {
1290
+ system: { version: INTELLIGENCE_EXPORT_SCHEMA_VERSION },
1291
+ health: {
1292
+ adoptionRate: healthReport.adoption.rate,
1293
+ risk: dependencyRiskLevel(healthReport.dependencyRisk.highImpactComponents)
1294
+ },
1295
+ components
1296
+ };
1297
+ }
1298
+ function validateIntelligenceExport(data) {
1299
+ const errors = [];
1300
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1301
+ if (!isRecord(data)) {
1302
+ return { valid: false, errors: ["\u0631\u06CC\u0634\u0647\u0654 \u062E\u0631\u0648\u062C\u06CC \u0628\u0627\u06CC\u062F \u06CC\u06A9 \u0622\u0628\u062C\u06A9\u062A \u0628\u0627\u0634\u062F."] };
1303
+ }
1304
+ if (!isRecord(data.system) || typeof data.system.version !== "string") {
1305
+ errors.push("system.version \u0628\u0627\u06CC\u062F \u06CC\u06A9 \u0631\u0634\u062A\u0647 \u0628\u0627\u0634\u062F.");
1306
+ }
1307
+ if (!isRecord(data.health)) {
1308
+ errors.push("health \u0628\u0627\u06CC\u062F \u06CC\u06A9 \u0622\u0628\u062C\u06A9\u062A \u0628\u0627\u0634\u062F.");
1309
+ } else {
1310
+ if (typeof data.health.adoptionRate !== "number") {
1311
+ errors.push("health.adoptionRate \u0628\u0627\u06CC\u062F \u0639\u062F\u062F \u0628\u0627\u0634\u062F.");
1312
+ }
1313
+ if (!["high", "medium", "low"].includes(data.health.risk)) {
1314
+ errors.push('health.risk \u0628\u0627\u06CC\u062F \u06CC\u06A9\u06CC \u0627\u0632 "high"/"medium"/"low" \u0628\u0627\u0634\u062F.');
1315
+ }
1316
+ }
1317
+ if (!Array.isArray(data.components)) {
1318
+ errors.push("components \u0628\u0627\u06CC\u062F \u06CC\u06A9 \u0622\u0631\u0627\u06CC\u0647 \u0628\u0627\u0634\u062F.");
1319
+ } else {
1320
+ data.components.forEach((entry, index) => {
1321
+ if (!isRecord(entry)) {
1322
+ errors.push(`components[${index}] \u0628\u0627\u06CC\u062F \u06CC\u06A9 \u0622\u0628\u062C\u06A9\u062A \u0628\u0627\u0634\u062F.`);
1323
+ return;
1324
+ }
1325
+ if (typeof entry.name !== "string") errors.push(`components[${index}].name \u0628\u0627\u06CC\u062F \u0631\u0634\u062A\u0647 \u0628\u0627\u0634\u062F.`);
1326
+ if (typeof entry.usageCount !== "number") {
1327
+ errors.push(`components[${index}].usageCount \u0628\u0627\u06CC\u062F \u0639\u062F\u062F \u0628\u0627\u0634\u062F.`);
1328
+ }
1329
+ if (typeof entry.deprecated !== "boolean") {
1330
+ errors.push(`components[${index}].deprecated \u0628\u0627\u06CC\u062F boolean \u0628\u0627\u0634\u062F.`);
1331
+ }
1332
+ if (typeof entry.migrationPending !== "boolean") {
1333
+ errors.push(`components[${index}].migrationPending \u0628\u0627\u06CC\u062F boolean \u0628\u0627\u0634\u062F.`);
1334
+ }
1335
+ if (typeof entry.dependencyImpact !== "number") {
1336
+ errors.push(`components[${index}].dependencyImpact \u0628\u0627\u06CC\u062F \u0639\u062F\u062F \u0628\u0627\u0634\u062F.`);
1337
+ }
1338
+ if (typeof entry.changeType !== "string") {
1339
+ errors.push(`components[${index}].changeType \u0628\u0627\u06CC\u062F \u0631\u0634\u062A\u0647 \u0628\u0627\u0634\u062F.`);
1340
+ }
1341
+ });
1342
+ }
1343
+ return errors.length === 0 ? { valid: true } : { valid: false, errors };
1344
+ }
1345
+ async function generateIntelligenceExport(params) {
1346
+ const { cwd, registryUrl } = params;
1347
+ const lock = await readLock(cwd);
1348
+ const index = await fetchRegistryIndex(registryUrl);
1349
+ const installedComponents = [];
1350
+ for (const [name, entry] of Object.entries(lock.components)) {
1351
+ let item;
1352
+ try {
1353
+ item = await fetchRegistryItem(name, registryUrl);
1354
+ } catch {
1355
+ continue;
1356
+ }
1357
+ if (item.type !== "registry:ui") continue;
1358
+ const scan = await scanRepositoryForComponent({ projectRoot: cwd, component: name });
1359
+ installedComponents.push({
1360
+ name,
1361
+ lockVersion: entry.version,
1362
+ latestVersion: item.version ?? entry.version,
1363
+ rawMigrations: item.migrations,
1364
+ rawRelease: item.release,
1365
+ rawLifecycle: item.deprecation,
1366
+ usageCount: scan.usageCount
1367
+ });
1368
+ }
1369
+ return buildIntelligenceExport({ registryIndexItems: index.items, installedComponents });
1370
+ }
1371
+
1372
+ // src/commands/doctor.ts
1373
+ var CATEGORY_LABEL_FA = {
1374
+ adoption: "\u067E\u0630\u06CC\u0631\u0634",
1375
+ migration: "\u0645\u0647\u0627\u062C\u0631\u062A",
1376
+ lifecycle: "\u0686\u0631\u062E\u0647\u0654 \u062D\u06CC\u0627\u062A",
1377
+ dependency: "\u0648\u0627\u0628\u0633\u062A\u06AF\u06CC",
1378
+ usage: "\u0627\u0633\u062A\u0641\u0627\u062F\u0647"
1379
+ };
1380
+ var SEVERITY_LABEL_FA = {
1381
+ high: "\u0628\u0627\u0644\u0627",
1382
+ medium: "\u0645\u062A\u0648\u0633\u0637",
1383
+ warning: "\u0647\u0634\u062F\u0627\u0631"
1384
+ };
1385
+ var CONFIDENCE_LABEL_FA = {
1386
+ high: "\u0628\u0627\u0644\u0627",
1387
+ medium: "\u0645\u062A\u0648\u0633\u0637",
1388
+ low: "\u067E\u0627\u06CC\u06CC\u0646"
1389
+ };
1390
+ async function doctorCommand(options) {
1391
+ const cwd = process.cwd();
1392
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
1393
+ let report;
1394
+ try {
1395
+ report = await generateIntelligenceExport({ cwd, registryUrl: config.registryUrl });
1396
+ } catch (error) {
1397
+ console.error(`\u2717 \u062F\u0631\u06CC\u0627\u0641\u062A\u0650 \u0641\u0647\u0631\u0633\u062A\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F: ${error.message}`);
1398
+ return;
1399
+ }
1400
+ const analysis = await resolveAnalyzer().analyze(report);
1401
+ const explanation = options.explain ? await resolveAIProvider().analyze(report) : void 0;
1402
+ if (options.json) {
1403
+ console.log(JSON.stringify(explanation ? { ...analysis, explain: explanation } : analysis, null, 2));
1404
+ return;
1405
+ }
1406
+ printDoctorReport(analysis);
1407
+ if (explanation) printExplainSection(explanation);
1408
+ }
1409
+ function printDoctorReport(analysis) {
1410
+ console.log("\n\u06AF\u0632\u0627\u0631\u0634\u0650 \u067E\u0632\u0634\u06A9\u0650 \u062F\u06CC\u06AF");
1411
+ console.log("\n\u062E\u0644\u0627\u0635\u0647\u0654 \u0633\u0644\u0627\u0645\u062A:");
1412
+ console.log(analysis.summary);
1413
+ if (analysis.findings.length === 0) {
1414
+ console.log("\n\u0647\u06CC\u0686 \u06CC\u0627\u0641\u062A\u0647\u200C\u0627\u06CC \u0646\u06CC\u0633\u062A.");
1415
+ return;
1416
+ }
1417
+ console.log("\n\u06CC\u0627\u0641\u062A\u0647\u200C\u0647\u0627:");
1418
+ analysis.findings.forEach((finding, index) => {
1419
+ console.log(`
1420
+ ${index + 1}. ${CATEGORY_LABEL_FA[finding.category]}${finding.component ? ` \u2014 ${finding.component}` : ""}`);
1421
+ console.log("\n\u0634\u062F\u062A:");
1422
+ console.log(SEVERITY_LABEL_FA[finding.severity]);
1423
+ console.log("\n\u062F\u0644\u06CC\u0644:");
1424
+ console.log(finding.reason);
1425
+ console.log("\n\u067E\u06CC\u0634\u0646\u0647\u0627\u062F:");
1426
+ console.log(finding.recommendation);
1427
+ });
1428
+ }
1429
+ function printExplainSection(explanation) {
1430
+ if (explanation.status === "not-configured") {
1431
+ console.log("\n\u062A\u062D\u0644\u06CC\u0644\u0650 AI:");
1432
+ console.log(explanation.message);
1433
+ return;
1434
+ }
1435
+ if (explanation.entries.length === 0) return;
1436
+ console.log("\n\u062A\u062D\u0644\u06CC\u0644\u0650 \u0627\u0633\u062A\u062F\u0644\u0627\u0644 (AI):");
1437
+ for (const entry of explanation.entries) {
1438
+ console.log(`
1439
+ \u06CC\u0627\u0641\u062A\u0647:`);
1440
+ console.log(
1441
+ entry.category ? `${CATEGORY_LABEL_FA[entry.category]}${entry.component ? ` \u2014 ${entry.component}` : ""}` : "\u062A\u062D\u0644\u06CC\u0644\u0650 \u06A9\u0644\u06CC"
1442
+ );
1443
+ console.log("\n\u0627\u0633\u062A\u062F\u0644\u0627\u0644:");
1444
+ for (const line of entry.reasoning) console.log(`- ${line}`);
1445
+ console.log("\n\u0627\u0637\u0645\u06CC\u0646\u0627\u0646:");
1446
+ console.log(CONFIDENCE_LABEL_FA[entry.confidence]);
1447
+ }
1448
+ }
1449
+
1450
+ // src/commands/export-health.ts
1451
+ async function exportHealthCommand(_options) {
1452
+ const cwd = process.cwd();
1453
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
1454
+ let report;
1455
+ try {
1456
+ report = await generateIntelligenceExport({ cwd, registryUrl: config.registryUrl });
1457
+ } catch (error) {
1458
+ console.error(`\u2717 \u062F\u0631\u06CC\u0627\u0641\u062A\u0650 \u0641\u0647\u0631\u0633\u062A\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F: ${error.message}`);
1459
+ return;
1460
+ }
1461
+ const validation = validateIntelligenceExport(report);
1462
+ if (!validation.valid) {
1463
+ console.error("\u2717 \u062E\u0631\u0648\u062C\u06CC\u0650 export-health \u0628\u0627 \u0642\u0631\u0627\u0631\u062F\u0627\u062F\u0650 \u062E\u0648\u062F\u0634 \u0645\u0637\u0627\u0628\u0642\u062A \u0646\u062F\u0627\u0634\u062A (\u0628\u0627\u06AF\u0650 \u062F\u0627\u062E\u0644\u06CC):");
1464
+ for (const error of validation.errors) console.error(` - ${error}`);
1465
+ return;
1466
+ }
1467
+ console.log(JSON.stringify(report, null, 2));
1468
+ }
1469
+
1470
+ // src/commands/governance.ts
1471
+ var CHANGE_TYPE_LABEL = {
1472
+ breaking: "\u0634\u06A9\u0646\u0646\u062F\u0647 (breaking)",
1473
+ major: "\u0628\u0632\u0631\u06AF (major)",
1474
+ minor: "\u0645\u062A\u0648\u0633\u0637 (minor)",
1475
+ patch: "\u0648\u0635\u0644\u0647 (patch)",
1476
+ unknown: "\u0646\u0627\u0645\u0634\u062E\u0635 (\u0628\u062F\u0648\u0646 \u06CC\u0627\u062F\u062F\u0627\u0634\u062A\u0650 \u0627\u0646\u062A\u0634\u0627\u0631)"
1477
+ };
1478
+ async function governanceCommand(name, options) {
1479
+ const cwd = process.cwd();
1480
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
1481
+ const lock = await readLock(cwd);
1482
+ let index;
1483
+ try {
1484
+ index = await fetchRegistryIndex(config.registryUrl);
1485
+ } catch (error) {
1486
+ console.error(`\u2717 \u062F\u0631\u06CC\u0627\u0641\u062A\u0650 \u0641\u0647\u0631\u0633\u062A\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F: ${error.message}`);
1487
+ return;
1488
+ }
1489
+ let item;
1490
+ try {
1491
+ item = await fetchRegistryItem(name, config.registryUrl);
1492
+ } catch (error) {
1493
+ console.error(`\u2717 ${name}: ${error.message}`);
1494
+ return;
1495
+ }
1496
+ const version = item.version ?? "0.0.0";
1497
+ const lockEntry = lock.components[name];
1498
+ const repositoryImpact = options.scan ? await (async () => {
1499
+ const scan = await scanRepositoryForComponent({ projectRoot: cwd, component: name });
1500
+ return { usageCount: scan.usageCount, files: scan.files.map((file) => file.path) };
1501
+ })() : void 0;
1502
+ const report = buildGovernanceReport({
1503
+ name,
1504
+ version,
1505
+ registryIndexItems: index.items,
1506
+ rawMigrations: item.migrations,
1507
+ rawRelease: item.release,
1508
+ rawLifecycle: item.deprecation,
1509
+ installedVersion: lockEntry?.version,
1510
+ repositoryImpact
1511
+ });
1512
+ if (options.json) {
1513
+ console.log(JSON.stringify(report, null, 2));
1514
+ return;
1515
+ }
1516
+ printGovernanceReport(report);
1517
+ }
1518
+ function printGovernanceReport(report) {
1519
+ console.log(`
1520
+ ${report.component} v${report.version}`);
1521
+ console.log("\n\u062A\u063A\u06CC\u06CC\u0631:");
1522
+ console.log(CHANGE_TYPE_LABEL[report.changeType]);
1523
+ if (report.impact.affectedComponents.length > 0) {
1524
+ console.log("\n\u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627\u06CC \u062A\u062D\u062A\u200C\u062A\u0623\u062B\u06CC\u0631:");
1525
+ for (const affected of report.impact.affectedComponents) console.log(`- ${affected}`);
1526
+ }
1527
+ if (report.impact.deprecatedApis.length > 0) {
1528
+ console.log("\n\u0686\u0631\u062E\u0647\u0654 \u062D\u06CC\u0627\u062A:");
1529
+ for (const message of report.impact.deprecatedApis) console.log(`- ${message}`);
1530
+ }
1531
+ if (report.impact.changedTokens.length > 0) {
1532
+ console.log("\n\u062A\u0648\u06A9\u0646\u200C\u0647\u0627\u06CC \u062A\u063A\u06CC\u06CC\u0631\u06CC\u0627\u0641\u062A\u0647:");
1533
+ for (const token of report.impact.changedTokens) console.log(`- ${token}`);
1534
+ }
1535
+ console.log("\n\u0645\u0647\u0627\u062C\u0631\u062A:");
1536
+ console.log(report.impact.requiredMigration ? "\u0645\u0648\u062C\u0648\u062F" : "\u0644\u0627\u0632\u0645 \u0646\u06CC\u0633\u062A");
1537
+ if (report.repositoryImpact) {
1538
+ console.log("\n\u0627\u0633\u062A\u0641\u0627\u062F\u0647\u0654 \u0645\u062D\u0644\u06CC:");
1539
+ console.log(String(report.repositoryImpact.usageCount));
1540
+ if (report.repositoryImpact.files.length > 0) {
1541
+ console.log("\n\u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u062D\u0644\u06CC\u0650 \u0627\u0633\u062A\u0641\u0627\u062F\u0647\u200C\u06A9\u0646\u0646\u062F\u0647:");
1542
+ for (const file of report.repositoryImpact.files) console.log(`- ${file}`);
1543
+ }
1544
+ }
1545
+ if (report.actions.length > 0) {
1546
+ console.log("\n\u0627\u0642\u062F\u0627\u0645\u0627\u062A\u0650 \u0644\u0627\u0632\u0645:");
1547
+ for (const action of report.actions) console.log(`- ${action}`);
1548
+ }
1549
+ }
1550
+
1551
+ // src/commands/health.ts
1552
+ var RISK_LEVEL_LABEL_FA = {
1553
+ high: "\u0628\u0627\u0644\u0627",
1554
+ medium: "\u0645\u062A\u0648\u0633\u0637",
1555
+ low: "\u067E\u0627\u06CC\u06CC\u0646"
1556
+ };
1557
+ async function healthCommand(options) {
1558
+ const cwd = process.cwd();
1559
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
1560
+ const lock = await readLock(cwd);
1561
+ let index;
1562
+ try {
1563
+ index = await fetchRegistryIndex(config.registryUrl);
1564
+ } catch (error) {
1565
+ console.error(`\u2717 \u062F\u0631\u06CC\u0627\u0641\u062A\u0650 \u0641\u0647\u0631\u0633\u062A\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F: ${error.message}`);
1566
+ return;
1567
+ }
1568
+ const installedComponents = [];
1569
+ for (const [name, entry] of Object.entries(lock.components)) {
1570
+ let item;
1571
+ try {
1572
+ item = await fetchRegistryItem(name, config.registryUrl);
1573
+ } catch {
1574
+ continue;
1575
+ }
1576
+ if (item.type !== "registry:ui") continue;
1577
+ const scan = await scanRepositoryForComponent({ projectRoot: cwd, component: name });
1578
+ installedComponents.push({
1579
+ name,
1580
+ lockVersion: entry.version,
1581
+ latestVersion: item.version ?? entry.version,
1582
+ rawMigrations: item.migrations,
1583
+ rawLifecycle: item.deprecation,
1584
+ usageCount: scan.usageCount
1585
+ });
1586
+ }
1587
+ const report = buildHealthReport({ registryIndexItems: index.items, installedComponents });
1588
+ if (options.json) {
1589
+ console.log(JSON.stringify(report, null, 2));
1590
+ return;
1591
+ }
1592
+ printHealthReport(report);
1593
+ }
1594
+ function printHealthReport(report) {
1595
+ console.log("\n\u06AF\u0632\u0627\u0631\u0634\u0650 \u0633\u0644\u0627\u0645\u062A\u0650 \u062F\u06CC\u0632\u0627\u06CC\u0646\u200C\u0633\u06CC\u0633\u062A\u0645");
1596
+ console.log("\n\u067E\u0630\u06CC\u0631\u0634:");
1597
+ console.log(`${report.adoption.rate}\u066A (${report.adoption.used} \u0627\u0632 ${report.adoption.installed} \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u0650 \u0646\u0635\u0628\u200C\u0634\u062F\u0647 \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F)`);
1598
+ const totalPendingMigrations = report.migrations.pending.reduce((sum, p) => sum + p.count, 0);
1599
+ console.log("\n\u0645\u0647\u0627\u062C\u0631\u062A:");
1600
+ console.log(
1601
+ totalPendingMigrations === 0 ? "\u0645\u0647\u0627\u062C\u0631\u062A\u06CC \u062F\u0631 \u0627\u0646\u062A\u0638\u0627\u0631 \u0646\u06CC\u0633\u062A" : `${totalPendingMigrations} \u0645\u0647\u0627\u062C\u0631\u062A\u0650 \u062F\u0631 \u0627\u0646\u062A\u0638\u0627\u0631`
1602
+ );
1603
+ for (const p of report.migrations.pending) console.log(`- ${p.component} (${p.count})`);
1604
+ console.log("\n\u0686\u0631\u062E\u0647\u0654 \u062D\u06CC\u0627\u062A:");
1605
+ console.log(`${report.lifecycle.deprecated.length} \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u0650 \u0645\u0646\u0633\u0648\u062E\u060C ${report.lifecycle.removed.length} \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u0650 \u062D\u0630\u0641\u200C\u0634\u062F\u0647`);
1606
+ for (const d of report.lifecycle.deprecated) {
1607
+ console.log(`- ${d.component}${d.replacement ? ` \u2192 \u062C\u0627\u06CC\u06AF\u0632\u06CC\u0646: ${d.replacement}` : ""}`);
1608
+ }
1609
+ for (const r of report.lifecycle.removed) {
1610
+ console.log(`- ${r.component}${r.replacement ? ` \u2192 \u062C\u0627\u06CC\u06AF\u0632\u06CC\u0646: ${r.replacement}` : ""}`);
1611
+ }
1612
+ console.log("\n\u0631\u06CC\u0633\u06A9\u0650 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC:");
1613
+ console.log(RISK_LEVEL_LABEL_FA[dependencyRiskLevel(report.dependencyRisk.highImpactComponents)]);
1614
+ for (const c of report.dependencyRisk.highImpactComponents) {
1615
+ console.log(`- ${c.name} \u0631\u0648\u06CC ${c.affectedCount} \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u0650 \u062F\u06CC\u06AF\u0631 \u0627\u062B\u0631 \u0645\u06CC\u200C\u06AF\u0630\u0627\u0631\u062F`);
1616
+ }
1617
+ console.log("\n\u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627\u06CC \u0628\u062F\u0648\u0646\u0650 \u0627\u0633\u062A\u0641\u0627\u062F\u0647:");
1618
+ console.log(String(report.repositoryUsage.unusedComponents.length));
1619
+ for (const name of report.repositoryUsage.unusedComponents) console.log(`- ${name}`);
1620
+ }
1621
+
1622
+ // src/commands/impact.ts
1623
+ async function impactCommand(name, options) {
1624
+ const cwd = process.cwd();
1625
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
1626
+ let index;
1627
+ try {
1628
+ index = await fetchRegistryIndex(config.registryUrl);
1629
+ } catch (error) {
1630
+ console.error(`\u2717 ${error.message}`);
1631
+ return;
1632
+ }
1633
+ const graph = buildDependencyGraph(index.items);
1634
+ const affected = getAffectedComponents(graph, name);
1635
+ if (options.json) {
1636
+ console.log(JSON.stringify({ name, affected }, null, 2));
1637
+ return;
1638
+ }
1639
+ if (affected.length === 0) {
1640
+ console.log(`\u0647\u06CC\u0686 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u06CC (\u0645\u0633\u062A\u0642\u06CC\u0645 \u06CC\u0627 \u063A\u06CC\u0631\u0645\u0633\u062A\u0642\u06CC\u0645) \u0628\u0647 \xAB${name}\xBB \u0648\u0627\u0628\u0633\u062A\u0647 \u0646\u06CC\u0633\u062A.`);
1641
+ return;
1642
+ }
1643
+ console.log("\u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627\u06CC \u062A\u062D\u062A\u200C\u062A\u0623\u062B\u06CC\u0631:");
1644
+ for (const item of affected) {
1645
+ console.log(`- ${item}`);
1646
+ }
1647
+ }
1648
+
1649
+ // src/commands/init.ts
1650
+ import path10 from "path";
1651
+
1652
+ // src/lib/prompt.ts
1653
+ import * as readline from "readline/promises";
1654
+ import { stdin, stdout } from "process";
1655
+ async function confirm(question, defaultValue) {
1656
+ const rl = readline.createInterface({ input: stdin, output: stdout });
1657
+ const suffix = defaultValue ? "(Y/n)" : "(y/N)";
1658
+ const raw = await rl.question(`${question} ${suffix} `);
1659
+ rl.close();
1660
+ const answer = raw.trim().toLowerCase();
1661
+ if (!answer) return defaultValue;
1662
+ return answer === "y" || answer === "yes";
1663
+ }
1664
+ async function ask(question, defaultValue) {
1665
+ const rl = readline.createInterface({ input: stdin, output: stdout });
1666
+ const raw = await rl.question(`${question} (${defaultValue}) `);
1667
+ rl.close();
1668
+ return raw.trim() || defaultValue;
1669
+ }
1670
+
1671
+ // src/commands/init.ts
1672
+ async function initCommand(options) {
1673
+ const cwd = process.cwd();
1674
+ const base = defaultConfig();
1675
+ if (await configExists(cwd)) {
1676
+ const overwrite = options.yes || await confirm("dig.json \u0627\u0632 \u0642\u0628\u0644 \u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F. \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0634\u0648\u062F\u061F", false);
1677
+ if (!overwrite) {
1678
+ console.log("\u0644\u063A\u0648 \u0634\u062F.");
1679
+ return;
1680
+ }
1681
+ }
1682
+ const config = options.yes ? base : {
1683
+ ...base,
1684
+ rtl: await confirm("\u067E\u0631\u0648\u0698\u0647 \u0631\u0627\u0633\u062A\u200C\u0628\u0647\u200C\u0686\u067E (RTL) \u0627\u0633\u062A\u061F", base.rtl),
1685
+ tailwind: {
1686
+ ...base.tailwind,
1687
+ css: await ask(
1688
+ "\u0645\u0633\u06CC\u0631 \u0641\u0627\u06CC\u0644 CSS \u0627\u0635\u0644\u06CC \u062A\u06CC\u0644\u0648\u06CC\u0646\u062F\u061F",
1689
+ base.tailwind.css
1690
+ )
1691
+ }
1692
+ };
1693
+ await writeConfig(config, cwd);
1694
+ console.log(`\u2713 ${path10.join(cwd, "dig.json")} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F.`);
1695
+ }
1696
+
1697
+ // src/lib/migration-apply.ts
1698
+ import { readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
1699
+ import path11 from "path";
1700
+
1701
+ // src/lib/migration-strategy.ts
1702
+ var TextReplaceStrategy = class {
1703
+ id = "text";
1704
+ mechanical = true;
1705
+ supports(migration) {
1706
+ return migration.type === "replace" && resolveMigrationStrategyId(migration) === "text";
1707
+ }
1708
+ apply(migration, content) {
1709
+ if (migration.type !== "replace") return content;
1710
+ return content.split(migration.from).join(migration.to);
1711
+ }
1712
+ plan(migration, content) {
1713
+ if (migration.type !== "replace" || !content.includes(migration.from)) {
1714
+ return { applicable: false };
1715
+ }
1716
+ return { applicable: true, newContent: this.apply(migration, content) };
1717
+ }
1718
+ };
1719
+ var ManualStrategy = class {
1720
+ id = "manual";
1721
+ mechanical = false;
1722
+ supports(migration) {
1723
+ return resolveMigrationStrategyId(migration) === "manual";
1724
+ }
1725
+ plan() {
1726
+ return { applicable: false };
1727
+ }
1728
+ apply(_migration, content) {
1729
+ return content;
1730
+ }
1731
+ };
1732
+ var AstTransformStrategy = class {
1733
+ constructor(engine = tsAstEngine) {
1734
+ this.engine = engine;
1735
+ }
1736
+ engine;
1737
+ id = "ast";
1738
+ mechanical = true;
1739
+ supports(migration) {
1740
+ return resolveMigrationStrategyId(migration) === "ast";
1741
+ }
1742
+ apply(migration, content) {
1743
+ const ast = this.engine.parser(content);
1744
+ const transformed = this.engine.transform(ast, migration);
1745
+ return this.engine.print(transformed);
1746
+ }
1747
+ plan(migration, content) {
1748
+ const newContent = this.apply(migration, content);
1749
+ return newContent === content ? { applicable: false } : { applicable: true, newContent };
1750
+ }
1751
+ };
1752
+ var MIGRATION_STRATEGIES = [
1753
+ new TextReplaceStrategy(),
1754
+ new ManualStrategy(),
1755
+ new AstTransformStrategy()
1756
+ ];
1757
+ function findMigrationStrategy(migration) {
1758
+ return MIGRATION_STRATEGIES.find((strategy) => strategy.supports(migration));
1759
+ }
1760
+
1761
+ // src/lib/migration-apply.ts
1762
+ var BINARY_PROBE_BYTES2 = 8e3;
1763
+ function looksBinary2(buffer) {
1764
+ const probe = buffer.subarray(0, Math.min(buffer.length, BINARY_PROBE_BYTES2));
1765
+ if (probe.includes(0)) return true;
1766
+ try {
1767
+ new TextDecoder("utf-8", { fatal: true }).decode(probe);
1768
+ return false;
1769
+ } catch {
1770
+ return true;
1771
+ }
1772
+ }
1773
+ async function readTextFileSafely2(target) {
1774
+ let buffer;
1775
+ try {
1776
+ buffer = await readFile7(target);
1777
+ } catch (error) {
1778
+ if (error.code === "ENOENT") return void 0;
1779
+ throw error;
1780
+ }
1781
+ if (looksBinary2(buffer)) return "binary";
1782
+ return buffer.toString("utf8");
1783
+ }
1784
+ function summarizeReplace(migration) {
1785
+ return `${migration.from} \u2192 ${migration.to}`;
1786
+ }
1787
+ function summarizeRenameProp(migration) {
1788
+ const spec = migration.transform;
1789
+ if (!spec || spec.operation !== "rename-prop" || !spec.from || !spec.to) return void 0;
1790
+ return `${spec.from} (prop \u0628\u0648\u0644\u06CC) \u2192 ${spec.to}="${spec.from}"`;
1791
+ }
1792
+ function summarizeMigration(migration) {
1793
+ if (migration.type === "replace") return summarizeReplace(migration);
1794
+ if (migration.type === "codemod") return summarizeRenameProp(migration);
1795
+ return void 0;
1796
+ }
1797
+ async function planOrApplyMigrations(params) {
1798
+ const { cwd, aliases, registryUrl, lock, registryItem, dryRun } = params;
1799
+ const name = registryItem.name;
1800
+ const lockEntry = lock.components[name];
1801
+ if (!lockEntry) {
1802
+ return { name, tracked: false, migrations: [], lockUpdated: false };
1803
+ }
1804
+ const toVersion = registryItem.version ?? "0.0.0";
1805
+ const applicable = getApplicableMigrations(
1806
+ parseMigrations(name, registryItem.migrations),
1807
+ lockEntry.version,
1808
+ toVersion
1809
+ );
1810
+ if (applicable.length === 0) {
1811
+ return {
1812
+ name,
1813
+ tracked: true,
1814
+ fromVersion: lockEntry.version,
1815
+ toVersion,
1816
+ migrations: [],
1817
+ lockUpdated: false
1818
+ };
1819
+ }
1820
+ const fileTargets = registryItem.files.map((file) => {
1821
+ const target = path11.join(cwd, resolveTargetPath(file.path, aliases));
1822
+ return { relPath: toPosixPath(path11.relative(cwd, target)), target };
1823
+ });
1824
+ const currentContents = /* @__PURE__ */ new Map();
1825
+ for (const ft of fileTargets) {
1826
+ currentContents.set(ft.relPath, await readTextFileSafely2(ft.target));
1827
+ }
1828
+ const workingContents = new Map(currentContents);
1829
+ const migrations = [];
1830
+ const changedPaths = /* @__PURE__ */ new Set();
1831
+ for (const migration of applicable) {
1832
+ const strategy = findMigrationStrategy(migration);
1833
+ if (!strategy || !strategy.mechanical) {
1834
+ migrations.push({ id: migration.id, type: migration.type, supported: false, files: [] });
1835
+ continue;
1836
+ }
1837
+ const files = [];
1838
+ for (const ft of fileTargets) {
1839
+ const current = workingContents.get(ft.relPath);
1840
+ if (current === void 0) {
1841
+ files.push({ path: ft.relPath, status: "missing-file" });
1842
+ continue;
1843
+ }
1844
+ if (current === "binary") {
1845
+ files.push({ path: ft.relPath, status: "binary" });
1846
+ continue;
1847
+ }
1848
+ const planned = strategy.plan(migration, current);
1849
+ if (!planned.applicable) {
1850
+ files.push({ path: ft.relPath, status: "not-found" });
1851
+ continue;
1852
+ }
1853
+ workingContents.set(ft.relPath, planned.newContent);
1854
+ changedPaths.add(ft.relPath);
1855
+ files.push({ path: ft.relPath, status: dryRun ? "would-apply" : "applied" });
1856
+ }
1857
+ migrations.push({
1858
+ id: migration.id,
1859
+ type: migration.type,
1860
+ supported: true,
1861
+ summary: summarizeMigration(migration),
1862
+ files
1863
+ });
1864
+ }
1865
+ if (dryRun || changedPaths.size === 0) {
1866
+ return {
1867
+ name,
1868
+ tracked: true,
1869
+ fromVersion: lockEntry.version,
1870
+ toVersion,
1871
+ migrations,
1872
+ lockUpdated: false
1873
+ };
1874
+ }
1875
+ const pending = [...changedPaths].map((relPath) => {
1876
+ const ft = fileTargets.find((f) => f.relPath === relPath);
1877
+ return {
1878
+ target: ft.target,
1879
+ relPath,
1880
+ // changedPaths فقط از رشته‌های متنیِ موجود می‌آید — هرگز undefined/"binary" نیست.
1881
+ originalContent: currentContents.get(relPath),
1882
+ newContent: workingContents.get(relPath)
1883
+ };
1884
+ });
1885
+ const written = [];
1886
+ try {
1887
+ for (const item of pending) {
1888
+ await writeFile5(item.target, item.newContent, "utf8");
1889
+ written.push(item);
1890
+ }
1891
+ } catch (error) {
1892
+ for (const item of written) {
1893
+ await writeFile5(item.target, item.originalContent, "utf8");
1894
+ }
1895
+ return {
1896
+ name,
1897
+ tracked: true,
1898
+ fromVersion: lockEntry.version,
1899
+ toVersion,
1900
+ migrations,
1901
+ lockUpdated: false,
1902
+ rollback: { error: error.message }
1903
+ };
1904
+ }
1905
+ const writtenContents = {};
1906
+ const writtenHashes = {};
1907
+ for (const item of pending) {
1908
+ writtenContents[item.relPath] = item.newContent;
1909
+ writtenHashes[item.relPath] = hashContent(item.newContent);
1910
+ }
1911
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
1912
+ const snapshotResult = await writeComponentSnapshot(cwd, name, toVersion, writtenContents);
1913
+ lock.components[name] = mergeComponentEntry(lockEntry, {
1914
+ version: toVersion,
1915
+ registryUrl,
1916
+ installedAt,
1917
+ writtenFiles: writtenHashes,
1918
+ snapshot: "path" in snapshotResult ? { version: toVersion, path: snapshotResult.path, installedAt } : void 0
1919
+ });
1920
+ return {
1921
+ name,
1922
+ tracked: true,
1923
+ fromVersion: lockEntry.version,
1924
+ toVersion,
1925
+ migrations,
1926
+ lockUpdated: true
1927
+ };
1928
+ }
1929
+
1930
+ // src/commands/migrate.ts
1931
+ async function migrateCommand(components, options) {
1932
+ if (options.apply) return runApplyOrDryRun(components, options, false);
1933
+ if (options.dryRun) return runApplyOrDryRun(components, options, true);
1934
+ if (options.plan) return runPlan(components, options);
1935
+ console.log(
1936
+ "\u06CC\u06A9\u06CC \u0627\u0632 \u0627\u06CC\u0646 \u062D\u0627\u0644\u062A\u200C\u0647\u0627 \u0631\u0627 \u0627\u0646\u062A\u062E\u0627\u0628 \u06A9\u0646\u06CC\u062F \u2014 \u0686\u06CC\u0632\u06CC \u0627\u0639\u0645\u0627\u0644 \u0646\u0634\u062F:\n dig migrate <component> --plan \u0641\u0642\u0637 \u0641\u0647\u0631\u0633\u062A\u0650 \u0645\u0647\u0627\u062C\u0631\u062A\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F\n dig migrate <component> --dry-run \u067E\u06CC\u0634\u200C\u0646\u0645\u0627\u06CC\u0634\u0650 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u062A\u062D\u062A\u200C\u062A\u0623\u062B\u06CC\u0631\u060C \u0628\u062F\u0648\u0646 \u0646\u0648\u0634\u062A\u0646\n dig migrate <component> --apply \u0627\u0639\u0645\u0627\u0644\u0650 \u0648\u0627\u0642\u0639\u06CC\u0650 \u0645\u0647\u0627\u062C\u0631\u062A\u200C\u0647\u0627\u06CC \u067E\u0634\u062A\u06CC\u0628\u0627\u0646\u06CC\u200C\u0634\u062F\u0647"
1937
+ );
1938
+ }
1939
+ async function runPlan(components, options) {
1940
+ const cwd = process.cwd();
1941
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
1942
+ const lock = await readLock(cwd);
1943
+ const plans = [];
1944
+ for (const name of components) {
1945
+ const lockEntry = lock.components[name];
1946
+ if (!lockEntry) {
1947
+ plans.push({ name, tracked: false, migrations: [] });
1948
+ continue;
1949
+ }
1950
+ try {
1951
+ const item = await fetchRegistryItem(name, config.registryUrl);
1952
+ const toVersion = item.version ?? "0.0.0";
1953
+ const migrations = getApplicableMigrations(
1954
+ parseMigrations(name, item.migrations),
1955
+ lockEntry.version,
1956
+ toVersion
1957
+ );
1958
+ plans.push({ name, tracked: true, fromVersion: lockEntry.version, toVersion, migrations });
1959
+ } catch (error) {
1960
+ plans.push({
1961
+ name,
1962
+ tracked: true,
1963
+ fromVersion: lockEntry.version,
1964
+ migrations: [],
1965
+ fetchError: error.message
1966
+ });
1967
+ }
1968
+ }
1969
+ if (options.json) {
1970
+ console.log(JSON.stringify({ plans }, null, 2));
1971
+ return;
1972
+ }
1973
+ for (const plan of plans) printPlan(plan);
1974
+ }
1975
+ function printPlan(plan) {
1976
+ console.log(`
1977
+ ${plan.name}`);
1978
+ if (!plan.tracked) {
1979
+ console.log(
1980
+ ` \u0631\u06A9\u0648\u0631\u062F\u06CC \u062F\u0631 .dig/registry-lock.json \u0646\u06CC\u0633\u062A \u2014 \u0627\u0628\u062A\u062F\u0627 dig add ${plan.name} \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F.`
1981
+ );
1982
+ return;
1983
+ }
1984
+ if (plan.fetchError) {
1985
+ console.log(` \u2717 \u062F\u0631\u06CC\u0627\u0641\u062A\u0650 \u0627\u0637\u0644\u0627\u0639\u0627\u062A\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F: ${plan.fetchError}`);
1986
+ return;
1987
+ }
1988
+ console.log("\n\u0627\u0632 \u0646\u0633\u062E\u0647\u0654:");
1989
+ console.log(plan.fromVersion);
1990
+ console.log("\n\u0628\u0647 \u0646\u0633\u062E\u0647\u0654:");
1991
+ console.log(plan.toVersion);
1992
+ console.log("");
1993
+ if (plan.migrations.length === 0) {
1994
+ console.log("\u0645\u0647\u0627\u062C\u0631\u062A\u06CC \u0645\u0648\u062C\u0648\u062F \u0646\u06CC\u0633\u062A.");
1995
+ } else {
1996
+ console.log("\u0645\u0647\u0627\u062C\u0631\u062A\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F:");
1997
+ for (const migration of plan.migrations) {
1998
+ console.log(`- ${migration.id}`);
1999
+ if (resolveMigrationStrategyId(migration) === "ast") {
2000
+ const isRenameProp = migration.type === "codemod" && migration.transform?.operation === "rename-prop";
2001
+ console.log("\n \u0645\u0647\u0627\u062C\u0631\u062A:");
2002
+ console.log(` ${migration.description}`);
2003
+ console.log("\n \u0631\u0627\u0647\u0628\u0631\u062F:");
2004
+ console.log(" AST");
2005
+ console.log("\n \u0648\u0636\u0639\u06CC\u062A:");
2006
+ console.log(
2007
+ isRenameProp ? " \u067E\u0634\u062A\u06CC\u0628\u0627\u0646\u06CC \u0645\u06CC\u200C\u0634\u0648\u062F \u2014 \u067E\u06CC\u0634\u200C\u0646\u0645\u0627\u06CC\u0634 \u0631\u0627 \u0628\u0627 dig migrate --dry-run \u0628\u0628\u06CC\u0646\u06CC\u062F." : " \u0646\u06CC\u0627\u0632\u0645\u0646\u062F\u0650 \u0645\u0628\u062F\u0644\u0650 AST \u2014 \u0647\u0646\u0648\u0632 \u067E\u0634\u062A\u06CC\u0628\u0627\u0646\u06CC \u0646\u0645\u06CC\u200C\u0634\u0648\u062F."
2008
+ );
2009
+ }
2010
+ }
2011
+ }
2012
+ console.log("\n\u0647\u06CC\u0686 \u0641\u0627\u06CC\u0644\u06CC \u062A\u063A\u06CC\u06CC\u0631 \u0646\u06A9\u0631\u062F.");
2013
+ }
2014
+ async function runApplyOrDryRun(components, options, dryRun) {
2015
+ const cwd = process.cwd();
2016
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
2017
+ const lock = await readLock(cwd);
2018
+ const results = [];
2019
+ for (const name of components) {
2020
+ let registryItem;
2021
+ try {
2022
+ registryItem = await fetchRegistryItem(name, config.registryUrl);
2023
+ } catch (error) {
2024
+ console.error(`\u2717 ${name}: ${error.message}`);
2025
+ continue;
2026
+ }
2027
+ const result = await planOrApplyMigrations({
2028
+ cwd,
2029
+ aliases: config.aliases,
2030
+ registryUrl: config.registryUrl,
2031
+ lock,
2032
+ registryItem,
2033
+ dryRun
2034
+ });
2035
+ results.push(result);
2036
+ }
2037
+ if (results.some((r) => r.lockUpdated)) {
2038
+ await writeLock(lock, cwd);
2039
+ }
2040
+ if (options.json) {
2041
+ console.log(JSON.stringify({ dryRun, results }, null, 2));
2042
+ return;
2043
+ }
2044
+ for (const result of results) printMigrationResult(result, dryRun);
2045
+ }
2046
+ function printMigrationResult(result, dryRun) {
2047
+ console.log(`
2048
+ ${result.name}`);
2049
+ if (!result.tracked) {
2050
+ console.log(
2051
+ ` \u0631\u06A9\u0648\u0631\u062F\u06CC \u062F\u0631 .dig/registry-lock.json \u0646\u06CC\u0633\u062A \u2014 \u0627\u0628\u062A\u062F\u0627 dig add ${result.name} \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F.`
2052
+ );
2053
+ return;
2054
+ }
2055
+ if (result.rollback) {
2056
+ console.log(
2057
+ " \u2717 \u0627\u0639\u0645\u0627\u0644\u0650 \u0645\u0647\u0627\u062C\u0631\u062A \u0628\u0627 \u062E\u0637\u0627 \u0645\u0648\u0627\u062C\u0647 \u0634\u062F\u061B \u0641\u0627\u06CC\u0644\u200C\u0647\u0627 \u0628\u0647 \u062D\u0627\u0644\u062A\u0650 \u0642\u0628\u0644 \u0628\u0627\u0632\u06AF\u0631\u062F\u0627\u0646\u062F\u0647 \u0634\u062F\u0646\u062F."
2058
+ );
2059
+ console.log(` \u062E\u0637\u0627: ${result.rollback.error}`);
2060
+ return;
2061
+ }
2062
+ if (result.migrations.length === 0) {
2063
+ console.log(` \u0627\u0632 ${result.fromVersion} \u062A\u0627 ${result.toVersion}: \u0645\u0647\u0627\u062C\u0631\u062A\u06CC \u0645\u0648\u062C\u0648\u062F \u0646\u06CC\u0633\u062A.`);
2064
+ return;
2065
+ }
2066
+ let anyFileTouched = false;
2067
+ for (const migration of result.migrations) {
2068
+ console.log("\n\u0645\u0647\u0627\u062C\u0631\u062A:");
2069
+ if (!migration.supported) {
2070
+ console.log(` \xAB${migration.id}\xBB (\u0646\u0648\u0639: ${migration.type}) \u2014 \u067E\u0634\u062A\u06CC\u0628\u0627\u0646\u06CC \u0646\u0645\u06CC\u200C\u0634\u0648\u062F\u060C \u0631\u062F \u0634\u062F.`);
2071
+ continue;
2072
+ }
2073
+ console.log(` ${migration.summary ?? `\xAB${migration.id}\xBB \u2014 \u0628\u062F\u0648\u0646\u0650 \u062E\u0644\u0627\u0635\u0647\u0654 \u0646\u0645\u0627\u06CC\u0634\u06CC`}`);
2074
+ const affected = migration.files.filter(
2075
+ (f) => f.status === "applied" || f.status === "would-apply"
2076
+ );
2077
+ if (affected.length === 0) {
2078
+ console.log(" \u0627\u0644\u06AF\u0648 \u062F\u0631 \u0647\u06CC\u0686 \u0641\u0627\u06CC\u0644\u06CC \u06CC\u0627\u0641\u062A \u0646\u0634\u062F.");
2079
+ continue;
2080
+ }
2081
+ anyFileTouched = true;
2082
+ console.log("\n \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u062A\u062D\u062A\u200C\u062A\u0623\u062B\u06CC\u0631:");
2083
+ for (const file of affected) console.log(` - ${file.path}`);
2084
+ }
2085
+ console.log("");
2086
+ if (dryRun) {
2087
+ console.log("\u0647\u06CC\u0686 \u0641\u0627\u06CC\u0644\u06CC \u062A\u063A\u06CC\u06CC\u0631 \u0646\u06A9\u0631\u062F.");
2088
+ } else if (anyFileTouched && result.lockUpdated) {
2089
+ console.log("\u0641\u0627\u06CC\u0644\u200C\u0647\u0627 \u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC \u0648 lock \u0628\u0647\u200C\u0631\u0648\u0632 \u0634\u062F.");
2090
+ } else {
2091
+ console.log("\u0647\u06CC\u0686 \u0641\u0627\u06CC\u0644\u06CC \u062A\u063A\u06CC\u06CC\u0631 \u0646\u06A9\u0631\u062F.");
2092
+ }
2093
+ }
2094
+
2095
+ // src/lib/migration-assistant.ts
2096
+ function extractMigrationPreview(migration) {
2097
+ if (migration.type === "replace") {
2098
+ return { from: migration.from, to: migration.to };
2099
+ }
2100
+ if (migration.type === "codemod" && migration.transform?.operation === "rename-prop" && migration.transform.from && migration.transform.to) {
2101
+ const tag = toPascalCase(migration.component);
2102
+ const { from, to } = migration.transform;
2103
+ return {
2104
+ from: `<${tag} ${from} />`,
2105
+ to: `<${tag} ${to}="${from}" />`
2106
+ };
2107
+ }
2108
+ return void 0;
2109
+ }
2110
+ function createRuleBasedMigrationAssistant(params) {
2111
+ const { component, applicableMigrations, affectedFiles } = params;
2112
+ return {
2113
+ id: "rule-based",
2114
+ async analyze(finding, intelligence) {
2115
+ const preview = applicableMigrations.map(extractMigrationPreview).find((p) => p !== void 0);
2116
+ if (preview) {
2117
+ return {
2118
+ component,
2119
+ issue: "\u0627\u0633\u062A\u0641\u0627\u062F\u0647\u0654 \u0645\u0646\u0633\u0648\u062E\u0650 \u0648\u06CC\u0698\u06AF\u06CC",
2120
+ finding: `\xAB${preview.from}\xBB \u0645\u0646\u0633\u0648\u062E \u0634\u062F\u0647 \u0627\u0633\u062A`,
2121
+ migration: preview,
2122
+ manualActionRequired: false,
2123
+ recommendation: `\u062F\u0633\u062A\u0648\u0631 \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F:
2124
+ dig migrate ${component} --plan`,
2125
+ affectedFiles
2126
+ };
2127
+ }
2128
+ const summary = intelligence.components.find((c) => c.name === component);
2129
+ if (summary?.replacement) {
2130
+ return {
2131
+ component,
2132
+ issue: "\u0627\u0633\u062A\u0641\u0627\u062F\u0647\u0654 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u0650 \u0645\u0646\u0633\u0648\u062E",
2133
+ finding: "\u0648\u0636\u0639\u06CC\u062A\u0650 \u0686\u0631\u062E\u0647\u0654 \u062D\u06CC\u0627\u062A\u0650 \u0627\u06CC\u0646 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0645\u0646\u0633\u0648\u062E/\u062D\u0630\u0641\u200C\u0634\u062F\u0647 \u0627\u0633\u062A.",
2134
+ replacement: summary.replacement,
2135
+ manualActionRequired: false,
2136
+ recommendation: `\u0628\u0647\u200C\u062C\u0627\u06CC \u0622\u0646 \u0627\u0632 ${summary.replacement} \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u06A9\u0646\u06CC\u062F.`,
2137
+ affectedFiles
2138
+ };
2139
+ }
2140
+ return {
2141
+ component,
2142
+ issue: "\u0646\u06CC\u0627\u0632\u0645\u0646\u062F\u0650 \u0628\u0631\u0631\u0633\u06CC\u0650 \u062F\u0633\u062A\u06CC",
2143
+ finding: finding.reason,
2144
+ manualActionRequired: true,
2145
+ recommendation: "\u0647\u06CC\u0686 \u0645\u0647\u0627\u062C\u0631\u062A\u0650 \u062E\u0648\u062F\u06A9\u0627\u0631 \u06CC\u0627 \u062C\u0627\u06CC\u06AF\u0632\u06CC\u0646\u0650 \u062B\u0628\u062A\u200C\u0634\u062F\u0647\u200C\u0627\u06CC \u0645\u0648\u062C\u0648\u062F \u0646\u06CC\u0633\u062A \u2014 \u0645\u0633\u062A\u0646\u062F\u0627\u062A\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u06CC\u0627 \u06CC\u0627\u062F\u062F\u0627\u0634\u062A\u0650 \u0627\u0646\u062A\u0634\u0627\u0631 \u0631\u0627 \u0628\u0631\u0631\u0633\u06CC \u06A9\u0646\u06CC\u062F.",
2146
+ affectedFiles
2147
+ };
2148
+ }
2149
+ };
2150
+ }
2151
+ function resolveMigrationAssistant(params) {
2152
+ return createRuleBasedMigrationAssistant(params);
2153
+ }
2154
+
2155
+ // src/commands/migrate-assist.ts
2156
+ var CONFIDENCE_LABEL_FA2 = {
2157
+ high: "\u0628\u0627\u0644\u0627",
2158
+ medium: "\u0645\u062A\u0648\u0633\u0637",
2159
+ low: "\u067E\u0627\u06CC\u06CC\u0646"
2160
+ };
2161
+ async function migrateAssistCommand(component, options) {
2162
+ const cwd = process.cwd();
2163
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
2164
+ const lock = await readLock(cwd);
2165
+ const lockEntry = lock.components[component];
2166
+ if (!lockEntry) {
2167
+ const message = `\u0631\u06A9\u0648\u0631\u062F\u06CC \u062F\u0631 .dig/registry-lock.json \u0646\u06CC\u0633\u062A \u2014 \u0627\u0628\u062A\u062F\u0627 dig add ${component} \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F.`;
2168
+ if (options.json) {
2169
+ console.log(JSON.stringify({ component, status: "not-installed", message }, null, 2));
2170
+ } else {
2171
+ console.log(`
2172
+ ${component}
2173
+
2174
+ ${message}`);
2175
+ }
2176
+ return;
2177
+ }
2178
+ let intelligence;
2179
+ try {
2180
+ intelligence = await generateIntelligenceExport({ cwd, registryUrl: config.registryUrl });
2181
+ } catch (error) {
2182
+ console.error(`\u2717 \u062F\u0631\u06CC\u0627\u0641\u062A\u0650 \u0641\u0647\u0631\u0633\u062A\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F: ${error.message}`);
2183
+ return;
2184
+ }
2185
+ const summary = intelligence.components.find((c) => c.name === component);
2186
+ if (!summary) {
2187
+ const message = `\xAB${component}\xBB \u062F\u0631 \u062F\u0627\u062F\u0647\u0654 \u0647\u0648\u0634\u0645\u0646\u062F\u06CC\u0650 \u0641\u0639\u0644\u06CC \u0646\u06CC\u0633\u062A (\u0634\u0627\u06CC\u062F \u0627\u0632 \u0646\u0648\u0639\u0650 registry:lib \u0628\u0627\u0634\u062F\u060C \u0646\u0647 UI).`;
2188
+ if (options.json) {
2189
+ console.log(JSON.stringify({ component, status: "no-data", message }, null, 2));
2190
+ } else {
2191
+ console.log(`
2192
+ ${component}
2193
+
2194
+ ${message}`);
2195
+ }
2196
+ return;
2197
+ }
2198
+ const analysis = await resolveAnalyzer().analyze(intelligence);
2199
+ const relevantFinding = analysis.findings.find(
2200
+ (f) => f.component === component && (f.category === "migration" || f.category === "lifecycle")
2201
+ );
2202
+ if (!relevantFinding) {
2203
+ const message = "\u0647\u06CC\u0686 \u06CC\u0627\u0641\u062A\u0647\u0654 \u0645\u0647\u0627\u062C\u0631\u062A \u06CC\u0627 \u0686\u0631\u062E\u0647\u0654\u200C\u062D\u06CC\u0627\u062A\u06CC \u0628\u0631\u0627\u06CC \u0627\u06CC\u0646 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0646\u06CC\u0633\u062A \u2014 \u0633\u0627\u0644\u0645 \u0627\u0633\u062A.";
2204
+ if (options.json) {
2205
+ console.log(JSON.stringify({ component, status: "healthy", message }, null, 2));
2206
+ } else {
2207
+ console.log(`
2208
+ ${component}
2209
+
2210
+ ${message}`);
2211
+ }
2212
+ return;
2213
+ }
2214
+ let item;
2215
+ try {
2216
+ item = await fetchRegistryItem(component, config.registryUrl);
2217
+ } catch (error) {
2218
+ console.error(`\u2717 ${component}: ${error.message}`);
2219
+ return;
2220
+ }
2221
+ const latestVersion = item.version ?? lockEntry.version;
2222
+ const applicableMigrations = getApplicableMigrations(
2223
+ parseMigrations(component, item.migrations),
2224
+ lockEntry.version,
2225
+ latestVersion
2226
+ );
2227
+ const scan = await scanRepositoryForComponent({ projectRoot: cwd, component });
2228
+ const assistant = resolveMigrationAssistant({
2229
+ component,
2230
+ applicableMigrations,
2231
+ affectedFiles: scan.files.map((file) => file.path)
2232
+ });
2233
+ const suggestion = await assistant.analyze(relevantFinding, intelligence);
2234
+ const explanation = options.explain ? await buildSelectionExplanation(suggestion, relevantFinding, intelligence) : void 0;
2235
+ if (options.json) {
2236
+ console.log(JSON.stringify(explanation ? { ...suggestion, explain: explanation } : suggestion, null, 2));
2237
+ return;
2238
+ }
2239
+ printMigrationSuggestion(suggestion);
2240
+ if (explanation) printSelectionExplanation(explanation);
2241
+ }
2242
+ async function buildSelectionExplanation(suggestion, finding, intelligence) {
2243
+ const selectedBecause = suggestion.migration ? "\u0686\u0648\u0646 \u06CC\u06A9 \u0645\u0647\u0627\u062C\u0631\u062A\u0650 \u062E\u0648\u062F\u06A9\u0627\u0631\u0650 \u0642\u0627\u0628\u0644\u200C\u0627\u0639\u0645\u0627\u0644 \u0628\u0631\u0627\u06CC \u0627\u06CC\u0646 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u062B\u0628\u062A \u0634\u062F\u0647 (\u0642\u0627\u0639\u062F\u0647\u0654 \u0627\u0648\u0644: \u0645\u0647\u0627\u062C\u0631\u062A \u0645\u0648\u062C\u0648\u062F \u0627\u0633\u062A)." : suggestion.replacement ? "\u0686\u0648\u0646 \u0645\u0647\u0627\u062C\u0631\u062A\u06CC \u0646\u06CC\u0633\u062A \u0648\u0644\u06CC \u0686\u0631\u062E\u0647\u0654\u200C\u062D\u06CC\u0627\u062A \u06CC\u06A9 \u062C\u0627\u06CC\u06AF\u0632\u06CC\u0646 \u0645\u0639\u0631\u0641\u06CC \u06A9\u0631\u062F\u0647 (\u0642\u0627\u0639\u062F\u0647\u0654 \u062F\u0648\u0645: \u062C\u0627\u06CC\u06AF\u0632\u06CC\u0646\u0650 \u0645\u0646\u0633\u0648\u062E\u06CC \u0645\u0648\u062C\u0648\u062F \u0627\u0633\u062A)." : "\u0686\u0648\u0646 \u0646\u0647 \u0645\u0647\u0627\u062C\u0631\u062A\u06CC \u0646\u0647 \u062C\u0627\u06CC\u06AF\u0632\u06CC\u0646\u06CC \u062B\u0628\u062A \u0646\u0634\u062F\u0647 (\u0642\u0627\u0639\u062F\u0647\u0654 \u0633\u0648\u0645: \u0646\u06CC\u0627\u0632\u0645\u0646\u062F\u0650 \u0628\u0631\u0631\u0633\u06CC\u0650 \u062F\u0633\u062A\u06CC).";
2244
+ const aiResponse = await resolveAIProvider().analyze(intelligence);
2245
+ const reasoning = aiResponse.status === "ok" ? aiResponse.entries.find((e) => e.component === finding.component && e.category === finding.category) ?? aiResponse.entries[0] : void 0;
2246
+ return { selectedBecause, reasoning };
2247
+ }
2248
+ function printSelectionExplanation(explanation) {
2249
+ console.log("\n\u0686\u0631\u0627 \u0627\u06CC\u0646 \u067E\u06CC\u0634\u0646\u0647\u0627\u062F \u0627\u0646\u062A\u062E\u0627\u0628 \u0634\u062F:");
2250
+ console.log(explanation.selectedBecause);
2251
+ if (explanation.reasoning) {
2252
+ console.log("\n\u0627\u0633\u062A\u062F\u0644\u0627\u0644:");
2253
+ for (const line of explanation.reasoning.reasoning) console.log(`- ${line}`);
2254
+ console.log("\n\u0627\u0637\u0645\u06CC\u0646\u0627\u0646:");
2255
+ console.log(CONFIDENCE_LABEL_FA2[explanation.reasoning.confidence]);
2256
+ }
2257
+ }
2258
+ function printMigrationSuggestion(suggestion) {
2259
+ console.log(`
2260
+ ${suggestion.component}`);
2261
+ console.log("\n\u0645\u0633\u0626\u0644\u0647:");
2262
+ console.log(suggestion.issue);
2263
+ console.log("\n\u06CC\u0627\u0641\u062A\u0647:");
2264
+ console.log(suggestion.finding);
2265
+ if (suggestion.migration) {
2266
+ console.log("\n\u0645\u0647\u0627\u062C\u0631\u062A\u0650 \u067E\u06CC\u0634\u0646\u0647\u0627\u062F\u06CC:");
2267
+ console.log("\n\u0627\u0632:");
2268
+ console.log(suggestion.migration.from);
2269
+ console.log("\n\u0628\u0647:");
2270
+ console.log(suggestion.migration.to);
2271
+ }
2272
+ if (suggestion.replacement) {
2273
+ console.log("\n\u062C\u0627\u06CC\u06AF\u0632\u06CC\u0646\u0650 \u067E\u06CC\u0634\u0646\u0647\u0627\u062F\u06CC:");
2274
+ console.log(suggestion.replacement);
2275
+ }
2276
+ console.log("\n\u067E\u06CC\u0634\u0646\u0647\u0627\u062F:");
2277
+ console.log(suggestion.recommendation);
2278
+ console.log("\n\u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u062A\u062D\u062A\u200C\u062A\u0623\u062B\u06CC\u0631:");
2279
+ if (suggestion.affectedFiles.length === 0) {
2280
+ console.log("(\u0647\u06CC\u0686\u200C\u06A9\u062F\u0627\u0645 \u062F\u0631 \u0631\u06CC\u067E\u0627\u0632\u06CC\u062A\u0648\u0631\u06CC\u0650 \u0645\u062D\u0644\u06CC \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F)");
2281
+ } else {
2282
+ for (const file of suggestion.affectedFiles) console.log(`- ${file}`);
2283
+ }
2284
+ }
2285
+
2286
+ // src/commands/release.ts
2287
+ var RELEASE_TYPE_LABEL = {
2288
+ major: "\u0628\u0632\u0631\u06AF (major)",
2289
+ minor: "\u0645\u062A\u0648\u0633\u0637 (minor)",
2290
+ patch: "\u0648\u0635\u0644\u0647 (patch)"
2291
+ };
2292
+ var CHANGE_TYPE_LABEL2 = {
2293
+ breaking: "\u0634\u06A9\u0646\u0646\u062F\u0647",
2294
+ added: "\u0627\u0641\u0632\u0648\u062F\u0647\u200C\u0634\u062F\u0647",
2295
+ changed: "\u062A\u063A\u06CC\u06CC\u0631\u06CC\u0627\u0641\u062A\u0647",
2296
+ fixed: "\u0631\u0641\u0639\u200C\u0634\u062F\u0647"
2297
+ };
2298
+ async function releaseCommand(components, options) {
2299
+ const cwd = process.cwd();
2300
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
2301
+ const entries = [];
2302
+ for (const name of components) {
2303
+ try {
2304
+ const item = await fetchRegistryItem(name, config.registryUrl);
2305
+ const version = item.version ?? "0.0.0";
2306
+ entries.push({
2307
+ name,
2308
+ version,
2309
+ release: parseRelease(name, version, item.release)
2310
+ });
2311
+ } catch (error) {
2312
+ entries.push({ name, fetchError: error.message });
2313
+ }
2314
+ }
2315
+ if (options.json) {
2316
+ console.log(
2317
+ JSON.stringify(
2318
+ {
2319
+ releases: entries.map((entry) => ({
2320
+ name: entry.name,
2321
+ version: entry.version,
2322
+ ...entry.fetchError ? { fetchError: entry.fetchError } : {},
2323
+ ...entry.release ? {
2324
+ type: entry.release.type,
2325
+ summary: entry.release.summary,
2326
+ changes: entry.release.changes ?? []
2327
+ } : { type: null, summary: null, changes: [] }
2328
+ }))
2329
+ },
2330
+ null,
2331
+ 2
2332
+ )
2333
+ );
2334
+ return;
2335
+ }
2336
+ for (const entry of entries) printReleaseEntry(entry);
2337
+ }
2338
+ function printReleaseEntry(entry) {
2339
+ if (entry.fetchError) {
2340
+ console.log(`
2341
+ \u2717 ${entry.name}: ${entry.fetchError}`);
2342
+ return;
2343
+ }
2344
+ console.log(`
2345
+ ${entry.name} v${entry.version}`);
2346
+ if (!entry.release) {
2347
+ console.log("\n\u0628\u062F\u0648\u0646 \u06CC\u0627\u062F\u062F\u0627\u0634\u062A\u0650 \u0627\u0646\u062A\u0634\u0627\u0631 \u0628\u0631\u0627\u06CC \u0627\u06CC\u0646 \u0646\u0633\u062E\u0647.");
2348
+ return;
2349
+ }
2350
+ console.log("\n\u0646\u0648\u0639 \u0627\u0646\u062A\u0634\u0627\u0631:");
2351
+ console.log(RELEASE_TYPE_LABEL[entry.release.type]);
2352
+ if (entry.release.summary) {
2353
+ console.log(`
2354
+ ${entry.release.summary}`);
2355
+ }
2356
+ const groups = groupChangesByType(entry.release.changes);
2357
+ if (groups.length === 0) return;
2358
+ console.log("\n\u062A\u063A\u06CC\u06CC\u0631\u0627\u062A:");
2359
+ for (const group of groups) {
2360
+ console.log(`
2361
+ ${CHANGE_TYPE_LABEL2[group.type]}:`);
2362
+ for (const change of group.entries) {
2363
+ console.log(`- ${change.description}`);
2364
+ }
2365
+ }
2366
+ }
2367
+
2368
+ // src/commands/scan.ts
2369
+ async function scanCommand(component, options) {
2370
+ const projectRoot = process.cwd();
2371
+ const result = await scanRepositoryForComponent({ projectRoot, component });
2372
+ if (options.json) {
2373
+ console.log(
2374
+ JSON.stringify(
2375
+ {
2376
+ component: result.component,
2377
+ usageCount: result.usageCount,
2378
+ files: result.files.map((file) => ({ path: file.path, usages: file.usages }))
2379
+ },
2380
+ null,
2381
+ 2
2382
+ )
2383
+ );
2384
+ return;
2385
+ }
2386
+ console.log(`
2387
+ ${result.component}`);
2388
+ if (result.usageCount === 0) {
2389
+ console.log("\n\u0647\u06CC\u0686 \u0627\u0633\u062A\u0641\u0627\u062F\u0647\u200C\u0627\u06CC \u0627\u0632 \u0627\u06CC\u0646 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u062F\u0631 \u0631\u06CC\u067E\u0627\u0632\u06CC\u062A\u0648\u0631\u06CC \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F.");
2390
+ return;
2391
+ }
2392
+ console.log("\n\u062A\u0639\u062F\u0627\u062F \u0627\u0633\u062A\u0641\u0627\u062F\u0647:");
2393
+ console.log(String(result.usageCount));
2394
+ console.log("\n\u0641\u0627\u06CC\u0644\u200C\u0647\u0627:");
2395
+ for (const file of result.files) console.log(`- ${file.path}`);
2396
+ }
2397
+
2398
+ // src/commands/status.ts
2399
+ var STATUS_LABEL2 = {
2400
+ active: "\u0641\u0639\u0627\u0644",
2401
+ deprecated: "\u0645\u0646\u0633\u0648\u062E\u200C\u0634\u062F\u0647",
2402
+ removed: "\u062D\u0630\u0641\u200C\u0634\u062F\u0647"
2403
+ };
2404
+ async function statusCommand(components, options) {
2405
+ const cwd = process.cwd();
2406
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
2407
+ const lock = await readLock(cwd);
2408
+ const entries = [];
2409
+ for (const name of components) {
2410
+ try {
2411
+ const item = await fetchRegistryItem(name, config.registryUrl);
2412
+ const version = item.version ?? "0.0.0";
2413
+ const lifecycle = parseLifecycle(name, version, item.deprecation);
2414
+ const release = parseRelease(name, version, item.release);
2415
+ let migrationHint;
2416
+ const allMigrations = parseMigrations(name, item.migrations);
2417
+ if (allMigrations.length > 0) {
2418
+ const lockEntry = lock.components[name];
2419
+ if (lockEntry) {
2420
+ const applicable = getApplicableMigrations(allMigrations, lockEntry.version, version);
2421
+ if (applicable.length > 0) {
2422
+ migrationHint = `${applicable.length} \u0645\u0647\u0627\u062C\u0631\u062A \u0627\u0632 \u0646\u0633\u062E\u0647\u0654 \u0646\u0635\u0628\u200C\u0634\u062F\u0647\u0654 \u0634\u0645\u0627 \u0645\u0648\u062C\u0648\u062F \u0627\u0633\u062A \u2014 dig migrate ${name} --plan`;
2423
+ }
2424
+ } else {
2425
+ migrationHint = `${allMigrations.length} \u0645\u0647\u0627\u062C\u0631\u062A \u0628\u0631\u0627\u06CC \u0627\u06CC\u0646 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u062B\u0628\u062A \u0634\u062F\u0647 \u2014 dig migrate ${name} --plan`;
2426
+ }
2427
+ }
2428
+ entries.push({
2429
+ name,
2430
+ version,
2431
+ lifecycle,
2432
+ releaseSummary: release?.summary,
2433
+ migrationHint
2434
+ });
2435
+ } catch (error) {
2436
+ entries.push({ name, fetchError: error.message });
2437
+ }
2438
+ }
2439
+ if (options.json) {
2440
+ console.log(
2441
+ JSON.stringify(
2442
+ {
2443
+ statuses: entries.map(
2444
+ (entry) => "fetchError" in entry ? { name: entry.name, fetchError: entry.fetchError } : {
2445
+ name: entry.name,
2446
+ version: entry.version,
2447
+ lifecycle: {
2448
+ status: entry.lifecycle.status,
2449
+ message: entry.lifecycle.message,
2450
+ replacement: entry.lifecycle.replacement,
2451
+ deprecatedSince: entry.lifecycle.deprecatedSince,
2452
+ removeIn: entry.lifecycle.removeIn
2453
+ }
2454
+ }
2455
+ )
2456
+ },
2457
+ null,
2458
+ 2
2459
+ )
2460
+ );
2461
+ return;
2462
+ }
2463
+ for (const entry of entries) printStatusEntry(entry);
2464
+ }
2465
+ function printStatusEntry(entry) {
2466
+ if ("fetchError" in entry) {
2467
+ console.log(`
2468
+ \u2717 ${entry.name}: ${entry.fetchError}`);
2469
+ return;
2470
+ }
2471
+ console.log(`
2472
+ ${entry.name}`);
2473
+ console.log("\n\u0646\u0633\u062E\u0647:");
2474
+ console.log(entry.version);
2475
+ console.log("\n\u0648\u0636\u0639\u06CC\u062A:");
2476
+ console.log(STATUS_LABEL2[entry.lifecycle.status]);
2477
+ if (entry.lifecycle.message) {
2478
+ console.log("\n\u067E\u06CC\u0627\u0645:");
2479
+ console.log(entry.lifecycle.message);
2480
+ }
2481
+ if (entry.lifecycle.deprecatedSince) {
2482
+ console.log("\n\u0645\u0646\u0633\u0648\u062E \u0627\u0632 \u0646\u0633\u062E\u0647\u0654:");
2483
+ console.log(entry.lifecycle.deprecatedSince);
2484
+ }
2485
+ if (entry.lifecycle.replacement) {
2486
+ console.log("\n\u062C\u0627\u06CC\u06AF\u0632\u06CC\u0646:");
2487
+ console.log(entry.lifecycle.replacement);
2488
+ }
2489
+ if (entry.lifecycle.removeIn) {
2490
+ console.log("\n\u0632\u0645\u0627\u0646\u0650 \u062D\u0630\u0641\u0650 \u0628\u0631\u0646\u0627\u0645\u0647\u200C\u0631\u06CC\u0632\u06CC\u200C\u0634\u062F\u0647:");
2491
+ console.log(entry.lifecycle.removeIn);
2492
+ }
2493
+ if (entry.releaseSummary) {
2494
+ console.log(`
2495
+ \u0622\u062E\u0631\u06CC\u0646 \u0627\u0646\u062A\u0634\u0627\u0631: ${entry.releaseSummary}`);
2496
+ }
2497
+ if (entry.migrationHint) {
2498
+ console.log(`
2499
+ ${entry.migrationHint}`);
2500
+ }
2501
+ }
2502
+
2503
+ // src/lib/update.ts
2504
+ import { mkdir as mkdir4, readFile as readFile8, rm as rm2, writeFile as writeFile6 } from "fs/promises";
2505
+ import path12 from "path";
2506
+ function safeFileIndexes(drift) {
2507
+ const indexes = [];
2508
+ drift.files.forEach((file, index) => {
2509
+ const isSafe = file.status === "update_available" || file.status === "new_upstream" && drift.tracked;
2510
+ if (isSafe) indexes.push(index);
2511
+ });
2512
+ return indexes;
2513
+ }
2514
+ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun) {
2515
+ const actions = new Array(drift.files.length).fill("skipped");
2516
+ const safeIndexes = safeFileIndexes(drift);
2517
+ if (dryRun) {
2518
+ for (const index of safeIndexes) actions[index] = "would-write";
2519
+ return { actions, writtenHashes: {} };
2520
+ }
2521
+ const pending = [];
2522
+ for (const index of safeIndexes) {
2523
+ const file = drift.files[index];
2524
+ const registryFile = registryItem.files[index];
2525
+ const target = path12.join(cwd, resolveTargetPath(registryFile.path, aliases));
2526
+ const isNewFile = file.status === "new_upstream";
2527
+ pending.push({
2528
+ index,
2529
+ target,
2530
+ newContent: registryFile.content,
2531
+ newHash: registryFile.hash ?? hashContent(registryFile.content),
2532
+ isNewFile,
2533
+ originalContent: isNewFile ? void 0 : await readFile8(target, "utf8")
2534
+ });
2535
+ }
2536
+ const written = [];
2537
+ try {
2538
+ for (const item of pending) {
2539
+ if (item.isNewFile) await mkdir4(path12.dirname(item.target), { recursive: true });
2540
+ await writeFile6(item.target, item.newContent, "utf8");
2541
+ written.push(item);
2542
+ }
2543
+ } catch (error) {
2544
+ for (const item of written) {
2545
+ if (item.isNewFile) {
2546
+ await rm2(item.target, { force: true });
2547
+ } else {
2548
+ await writeFile6(item.target, item.originalContent, "utf8");
2549
+ }
2550
+ }
2551
+ return { actions, writtenHashes: {}, rollbackError: error.message };
2552
+ }
2553
+ const writtenHashes = {};
2554
+ const writtenContents = {};
2555
+ for (const item of pending) {
2556
+ actions[item.index] = "written";
2557
+ const filePath = drift.files[item.index].path;
2558
+ writtenHashes[filePath] = item.newHash;
2559
+ writtenContents[filePath] = item.newContent;
2560
+ }
2561
+ const snapshotResult = await writeComponentSnapshot(
2562
+ cwd,
2563
+ registryItem.name,
2564
+ registryItem.version ?? "0.0.0",
2565
+ writtenContents
2566
+ );
2567
+ if ("error" in snapshotResult) {
2568
+ return { actions, writtenHashes, snapshotError: snapshotResult.error };
2569
+ }
2570
+ return { actions, writtenHashes, snapshotPath: snapshotResult.path };
2571
+ }
2572
+ async function updateComponent(params) {
2573
+ const { cwd, aliases, registryUrl, lock, registryItem, dryRun = false } = params;
2574
+ const name = registryItem.name;
2575
+ const lockEntry = lock.components[name];
2576
+ if (!lockEntry) {
2577
+ return { name, tracked: false, lockUpdated: false };
2578
+ }
2579
+ const drift = await computeComponentDrift({
2580
+ cwd,
2581
+ aliases,
2582
+ registryUrl,
2583
+ lockEntry,
2584
+ registryItem
2585
+ });
2586
+ const { actions, writtenHashes, rollbackError, snapshotPath, snapshotError } = await applySafeFiles(cwd, aliases, drift, registryItem, dryRun);
2587
+ if (rollbackError) {
2588
+ return {
2589
+ name,
2590
+ tracked: true,
2591
+ drift,
2592
+ actions,
2593
+ lockUpdated: false,
2594
+ rollback: { error: rollbackError }
2595
+ };
2596
+ }
2597
+ const hasWrites = Object.keys(writtenHashes).length > 0;
2598
+ if (hasWrites && !dryRun) {
2599
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
2600
+ lock.components[name] = mergeComponentEntry(lockEntry, {
2601
+ version: registryItem.version,
2602
+ registryUrl,
2603
+ installedAt,
2604
+ writtenFiles: writtenHashes,
2605
+ snapshot: snapshotPath ? { version: registryItem.version ?? "0.0.0", path: snapshotPath, installedAt } : void 0
2606
+ });
2607
+ }
2608
+ return {
2609
+ name,
2610
+ tracked: true,
2611
+ drift,
2612
+ actions,
2613
+ lockUpdated: hasWrites && !dryRun,
2614
+ snapshotError
2615
+ };
2616
+ }
2617
+
2618
+ // src/commands/update.ts
2619
+ function reportedStatus(result) {
2620
+ if (!result.tracked) return "not-tracked";
2621
+ if (result.rollback) return "rolled-back";
2622
+ const { drift, actions } = result;
2623
+ if (!drift || !actions) return "not-tracked";
2624
+ if (drift.status === "conflict") return "conflict";
2625
+ if (drift.status === "local_customized") return "local-customized";
2626
+ if (drift.status === "untracked") return "untracked";
2627
+ if (drift.status === "missing_locally") return "missing-locally";
2628
+ if (actions.includes("written")) return "updated";
2629
+ if (actions.includes("would-write")) return "would-update";
2630
+ return "up-to-date";
2631
+ }
2632
+ async function updateCommand(components, options) {
2633
+ const cwd = process.cwd();
2634
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
2635
+ const lock = await readLock(cwd);
2636
+ const dryRun = Boolean(options.dryRun);
2637
+ const entries = [];
2638
+ for (const name of components) {
2639
+ let registryItem;
2640
+ try {
2641
+ registryItem = await fetchRegistryItem(name, config.registryUrl);
2642
+ } catch (error) {
2643
+ console.error(`\u2717 ${name}: ${error.message}`);
2644
+ continue;
2645
+ }
2646
+ const snapshotVersion = lock.components[name]?.snapshot?.version;
2647
+ const result = await updateComponent({
2648
+ cwd,
2649
+ aliases: config.aliases,
2650
+ registryUrl: config.registryUrl,
2651
+ lock,
2652
+ registryItem,
2653
+ dryRun
2654
+ });
2655
+ entries.push({ result, registryItem, snapshotVersion });
2656
+ }
2657
+ if (entries.some((entry) => entry.result.lockUpdated)) {
2658
+ await writeLock(lock, cwd);
2659
+ }
2660
+ if (options.json) {
2661
+ console.log(
2662
+ JSON.stringify(
2663
+ {
2664
+ dryRun,
2665
+ components: entries.map(({ result }) => ({
2666
+ name: result.name,
2667
+ status: reportedStatus(result),
2668
+ files: (result.drift?.files ?? []).map((file, index) => ({
2669
+ path: file.path,
2670
+ status: file.status,
2671
+ action: result.actions?.[index] ?? "skipped"
2672
+ })),
2673
+ ...result.snapshotError ? { snapshotError: result.snapshotError } : {}
2674
+ }))
2675
+ },
2676
+ null,
2677
+ 2
2678
+ )
2679
+ );
2680
+ return;
2681
+ }
2682
+ if (entries.length > 1) {
2683
+ printUpdateSummary(entries);
2684
+ }
2685
+ for (const entry of entries) {
2686
+ if (dryRun) {
2687
+ printDryRunResult(entry.result);
2688
+ } else {
2689
+ await printUpdateResult(cwd, config, entry);
2690
+ }
2691
+ }
2692
+ }
2693
+ function printUpdateSummary(entries) {
2694
+ console.log("\u062E\u0644\u0627\u0635\u0647\u0654 \u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC");
2695
+ const buckets = [
2696
+ { icon: STATUS_ICON.update_available, title: "\u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC\u200C\u0647\u0627\u06CC \u0627\u06CC\u0645\u0646", withFiles: true, names: [] },
2697
+ { icon: STATUS_ICON.conflict, title: "\u062A\u0639\u0627\u0631\u0636\u200C\u0647\u0627", withFiles: true, names: [] },
2698
+ { icon: STATUS_ICON.local_customized, title: "\u062A\u063A\u06CC\u06CC\u0631\u0627\u062A \u0645\u062D\u0644\u06CC", withFiles: false, names: [] },
2699
+ { icon: STATUS_ICON.untracked, title: "\u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0646\u0627\u0645\u0631\u062A\u0628\u0637", withFiles: false, names: [] },
2700
+ { icon: STATUS_ICON.missing_locally, title: "\u062D\u0630\u0641\u200C\u0634\u062F\u0647\u200C\u0647\u0627\u06CC \u0645\u062D\u0644\u06CC", withFiles: false, names: [] },
2701
+ { icon: "\u2014", title: "\u0646\u0635\u0628\u200C\u0646\u0634\u062F\u0647 (\u0646\u06CC\u0627\u0632 \u0628\u0647 dig add)", withFiles: false, names: [] },
2702
+ { icon: STATUS_ICON.up_to_date, title: "\u0628\u0647\u200C\u0631\u0648\u0632", withFiles: false, names: [] }
2703
+ ];
2704
+ const bucketIndexFor = {
2705
+ updated: 0,
2706
+ "would-update": 0,
2707
+ conflict: 1,
2708
+ "local-customized": 2,
2709
+ untracked: 3,
2710
+ "missing-locally": 4,
2711
+ "not-tracked": 5,
2712
+ "rolled-back": 5,
2713
+ "up-to-date": 6
2714
+ };
2715
+ for (const entry of entries) {
2716
+ const status = reportedStatus(entry.result);
2717
+ buckets[bucketIndexFor[status]].names.push(entry);
2718
+ }
2719
+ for (const bucket of buckets) {
2720
+ if (bucket.names.length === 0) continue;
2721
+ console.log(`
2722
+ ${bucket.icon} ${bucket.title}:`);
2723
+ for (const entry of bucket.names) {
2724
+ console.log(` - ${entry.result.name}`);
2725
+ if (!bucket.withFiles) continue;
2726
+ const status = reportedStatus(entry.result);
2727
+ const files = entry.result.drift?.files ?? [];
2728
+ const actions = entry.result.actions ?? [];
2729
+ for (let index = 0; index < files.length; index++) {
2730
+ const isRelevant = status === "conflict" ? files[index].status === "conflict" : actions[index] === "written" || actions[index] === "would-write";
2731
+ if (isRelevant) console.log(` - ${files[index].path}`);
2732
+ }
2733
+ }
2734
+ }
2735
+ console.log("");
2736
+ }
2737
+ async function printUpdateResult(cwd, config, { result, registryItem, snapshotVersion }) {
2738
+ console.log(`
2739
+ ${result.name}`);
2740
+ if (!result.tracked) {
2741
+ console.log(
2742
+ ` \u0631\u06A9\u0648\u0631\u062F\u06CC \u062F\u0631 .dig/registry-lock.json \u0646\u06CC\u0633\u062A \u2014 \u0627\u0628\u062A\u062F\u0627 dig add ${result.name} \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F.`
2743
+ );
2744
+ return;
2745
+ }
2746
+ if (result.rollback) {
2747
+ console.log(
2748
+ " \u2717 \u0646\u0648\u0634\u062A\u0646\u0650 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627 \u0628\u0627 \u062E\u0637\u0627 \u0645\u0648\u0627\u062C\u0647 \u0634\u062F\u061B \u062A\u063A\u06CC\u06CC\u0631\u0627\u062A \u0627\u06CC\u0646 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0628\u0647 \u062D\u0627\u0644\u062A\u0650 \u0642\u0628\u0644 \u0628\u0627\u0632\u06AF\u0631\u062F\u0627\u0646\u062F\u0647 \u0634\u062F."
2749
+ );
2750
+ console.log(` \u062E\u0637\u0627: ${result.rollback.error}`);
2751
+ return;
2752
+ }
2753
+ const { drift, actions } = result;
2754
+ if (!drift || !actions) return;
2755
+ console.log(
2756
+ ` \u0646\u0633\u062E\u0647\u0654 \u0642\u0628\u0644\u06CC: ${drift.installedVersion ?? "\u2014"} \u2192 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC: ${drift.latestVersion}`
2757
+ );
2758
+ const wroteAnything = actions.includes("written");
2759
+ const nothingToDo = !wroteAnything && drift.files.every((file) => file.status === "up_to_date");
2760
+ if (nothingToDo) {
2761
+ console.log(" \u0628\u0647\u200C\u0631\u0648\u0632 \u0627\u0633\u062A\u061B \u06A9\u0627\u0631\u06CC \u0627\u0646\u062C\u0627\u0645 \u0646\u0634\u062F.");
2762
+ }
2763
+ for (let index = 0; index < drift.files.length; index++) {
2764
+ const file = drift.files[index];
2765
+ const action = actions[index];
2766
+ if (action === "written") {
2767
+ console.log(` \u2191 ${file.path} \u2014 \u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC \u0634\u062F`);
2768
+ continue;
2769
+ }
2770
+ if (file.status === "up_to_date") {
2771
+ console.log(` ${STATUS_ICON.up_to_date} ${file.path} \u2014 \u0628\u0647\u200C\u0631\u0648\u0632`);
2772
+ continue;
2773
+ }
2774
+ console.log(
2775
+ ` ${STATUS_ICON[file.status]} ${file.path} \u2014 ${STATUS_LABEL[file.status]} (\u062F\u0633\u062A\u200C\u0646\u062E\u0648\u0631\u062F\u0647 \u0645\u0627\u0646\u062F)`
2776
+ );
2777
+ if (file.status === "conflict" && registryItem) {
2778
+ const details = await computeFileDiffDetails({
2779
+ cwd,
2780
+ aliases: config.aliases,
2781
+ name: result.name,
2782
+ snapshotVersion,
2783
+ file,
2784
+ registryFile: registryItem.files[index]
2785
+ });
2786
+ if (details?.note) console.log(` ${details.note}`);
2787
+ for (const section of details?.sections ?? []) {
2788
+ console.log(`
2789
+ --- ${section.label} ---`);
2790
+ for (const line of section.diff.split("\n")) {
2791
+ console.log(` ${line}`);
2792
+ }
2793
+ }
2794
+ }
2795
+ }
2796
+ if (result.snapshotError) {
2797
+ console.log(` \u26A0 \u06AF\u0631\u0641\u062A\u0646 \u0639\u06A9\u0633\u200C\u0641\u0648\u0631\u06CC \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F (\u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC \u062E\u0648\u062F\u0634 \u0627\u0646\u062C\u0627\u0645 \u0634\u062F): ${result.snapshotError}`);
2798
+ }
2799
+ }
2800
+ function printDryRunResult(result) {
2801
+ console.log(`
2802
+ ${result.name}`);
2803
+ if (!result.tracked) {
2804
+ console.log(
2805
+ ` \u0631\u06A9\u0648\u0631\u062F\u06CC \u062F\u0631 .dig/registry-lock.json \u0646\u06CC\u0633\u062A \u2014 \u0627\u0628\u062A\u062F\u0627 dig add ${result.name} \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F.`
2806
+ );
2807
+ return;
2808
+ }
2809
+ const { drift, actions } = result;
2810
+ if (!drift || !actions) return;
2811
+ console.log(" \u0646\u0633\u062E\u0647:");
2812
+ console.log(` \u0646\u0635\u0628\u200C\u0634\u062F\u0647: ${drift.installedVersion ?? "\u2014"}`);
2813
+ console.log(` \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC: ${drift.latestVersion}`);
2814
+ console.log("");
2815
+ console.log(` ${STATUS_ICON[drift.status]} ${STATUS_LABEL[drift.status]}`);
2816
+ const wouldWriteAny = actions.includes("would-write");
2817
+ if (!wouldWriteAny) {
2818
+ console.log(" \u0647\u06CC\u0686 \u062A\u063A\u06CC\u06CC\u0631\u06CC \u0627\u0639\u0645\u0627\u0644 \u0646\u0645\u06CC\u200C\u0634\u062F.");
2819
+ return;
2820
+ }
2821
+ console.log(" \u0641\u0627\u06CC\u0644\u200C\u0647\u0627:");
2822
+ for (let index = 0; index < drift.files.length; index++) {
2823
+ const file = drift.files[index];
2824
+ const action = actions[index];
2825
+ if (action === "would-write") {
2826
+ console.log(` ${STATUS_ICON[file.status]} ${file.path} \u2014 \u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC \u062E\u0648\u0627\u0647\u062F \u0634\u062F`);
2827
+ continue;
2828
+ }
2829
+ if (file.status === "up_to_date") continue;
2830
+ console.log(
2831
+ ` ${STATUS_ICON[file.status]} ${file.path} \u2014 ${STATUS_LABEL[file.status]} (\u062F\u0633\u062A\u200C\u0646\u062E\u0648\u0631\u062F\u0647 \u0645\u06CC\u200C\u0645\u0627\u0646\u062F)`
2832
+ );
2833
+ }
2834
+ }
2835
+
2836
+ // src/index.ts
2837
+ var program = new Command();
2838
+ program.name("dig").description("CLI \u0631\u0633\u0645\u06CC \u062F\u06CC\u0632\u0627\u06CC\u0646 \u0633\u06CC\u0633\u062A\u0645 \u062F\u06CC\u06AF").version("0.1.0");
2839
+ program.command("init").description("\u0631\u0627\u0647\u200C\u0627\u0646\u062F\u0627\u0632\u06CC \u062F\u06CC\u06AF \u062F\u0631 \u067E\u0631\u0648\u0698\u0647 (\u0633\u0627\u062E\u062A dig.json)").option("--yes", "\u067E\u0627\u0633\u062E \u067E\u06CC\u0634\u200C\u0641\u0631\u0636 \u0628\u0647 \u0647\u0645\u0647\u0654 \u0633\u0648\u0627\u0644\u0627\u062A", false).action(initCommand);
2840
+ program.command("add").description("\u0627\u0641\u0632\u0648\u062F\u0646 \u06CC\u06A9 \u06CC\u0627 \u0686\u0646\u062F \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0627\u0632 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u062F\u06CC\u06AF").argument("<components...>", "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 (\u0645\u062B\u0644 button) \u06CC\u0627 \u0622\u062F\u0631\u0633 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC").option("--overwrite", "\u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F", false).option("--no-install", "\u0641\u0642\u0637 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627 \u0631\u0627 \u0628\u0646\u0648\u06CC\u0633\u060C \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627 \u0631\u0627 \u0646\u0635\u0628 \u0646\u06A9\u0646").action(addCommand);
2841
+ program.command("diff").description(
2842
+ "\u0645\u0642\u0627\u06CC\u0633\u0647\u0654 \u0641\u0642\u0637\u200C\u062E\u0648\u0627\u0646\u062F\u0646\u06CC\u0650 \u0646\u0633\u062E\u0647\u0654 \u0646\u0635\u0628\u200C\u0634\u062F\u0647 \u0628\u0627 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0648 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u062D\u0644\u06CC (\u0628\u062F\u0648\u0646 \u062A\u063A\u06CC\u06CC\u0631 \u0641\u0627\u06CC\u0644)"
2843
+ ).argument("<components...>", "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 (\u0645\u062B\u0644 button)").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).option(
2844
+ "--details",
2845
+ "\u0646\u0645\u0627\u06CC\u0634 \u062F\u06CC\u0641\u0650 \u06A9\u0627\u0645\u0644\u0650 \u0647\u0631 \u0641\u0627\u06CC\u0644\u0650 \u062A\u063A\u06CC\u06CC\u0631\u06CC\u0627\u0641\u062A\u0647 (\u0646\u0635\u0628\u200C\u0634\u062F\u0647\u2194\u0645\u062D\u0644\u06CC\u060C \u0646\u0635\u0628\u200C\u0634\u062F\u0647\u2194\u0631\u062C\u06CC\u0633\u062A\u0631\u06CC)",
2846
+ false
2847
+ ).action(diffCommand);
2848
+ program.command("update").description(
2849
+ "\u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC\u0650 \u0627\u06CC\u0645\u0646\u0650 \u06CC\u06A9 \u06CC\u0627 \u0686\u0646\u062F \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u0650 \u0646\u0635\u0628\u200C\u0634\u062F\u0647 \u2014 \u0641\u0642\u0637 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u062F\u0633\u062A\u200C\u0646\u062E\u0648\u0631\u062F\u0647 \u0631\u0627 \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0645\u06CC\u200C\u06A9\u0646\u062F"
2850
+ ).argument("<components...>", "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 (\u0645\u062B\u0644 button)").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).option(
2851
+ "--dry-run",
2852
+ "\u0641\u0642\u0637 \u0646\u0634\u0627\u0646 \u0628\u062F\u0647 \u0686\u0647 \u0686\u06CC\u0632\u06CC \u0628\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC \u0645\u06CC\u200C\u0634\u062F\u061B \u0647\u06CC\u0686 \u0641\u0627\u06CC\u0644 \u06CC\u0627 lock \u0627\u06CC \u0631\u0627 \u062A\u063A\u06CC\u06CC\u0631 \u0646\u062F\u0647",
2853
+ false
2854
+ ).action(updateCommand);
2855
+ program.command("impact").description(
2856
+ "\u0646\u0645\u0627\u06CC\u0634 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627\u06CC \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u06A9\u0647 (\u0645\u0633\u062A\u0642\u06CC\u0645 \u06CC\u0627 \u063A\u06CC\u0631\u0645\u0633\u062A\u0642\u06CC\u0645) \u0628\u0647 \u06CC\u06A9 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC \u0645\u062A\u06A9\u06CC\u200C\u0627\u0646\u062F"
2857
+ ).argument("<name>", "\u0646\u0627\u0645\u0650 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC \u06CC\u0627 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A (\u0645\u062B\u0644 utils)").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).action(impactCommand);
2858
+ program.command("migrate").description(
2859
+ "\u0628\u0631\u0631\u0633\u06CC/\u0627\u0639\u0645\u0627\u0644\u0650 \u0645\u0647\u0627\u062C\u0631\u062A\u200C\u0647\u0627\u06CC \u0628\u06CC\u0646\u0650 \u0646\u0633\u062E\u0647\u0654 \u0646\u0635\u0628\u200C\u0634\u062F\u0647 \u0648 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u2014 \u0641\u0642\u0637 \u062C\u0627\u06CC\u06AF\u0632\u06CC\u0646\u06CC\u0650 \u0645\u062A\u0646\u06CC\u0650 \u0633\u0627\u062F\u0647 (replace)"
2860
+ ).argument("<components...>", "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 (\u0645\u062B\u0644 button)").option("--plan", "\u0641\u0647\u0631\u0633\u062A\u0650 \u0633\u0628\u06A9\u0650 \u0645\u0647\u0627\u062C\u0631\u062A\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F\u060C \u0641\u0642\u0637 \u0628\u0631 \u0627\u0633\u0627\u0633\u0650 \u0646\u0633\u062E\u0647\u200C\u0647\u0627", false).option(
2861
+ "--dry-run",
2862
+ "\u067E\u06CC\u0634\u200C\u0646\u0645\u0627\u06CC\u0634\u0650 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0648\u0627\u0642\u0639\u0627\u064B \u062A\u062D\u062A\u200C\u062A\u0623\u062B\u06CC\u0631 (\u0628\u0627 \u062E\u0648\u0627\u0646\u062F\u0646\u0650 \u0645\u062D\u062A\u0648\u0627\u06CC \u0641\u0639\u0644\u06CC)\u061B \u0647\u06CC\u0686\u200C\u0686\u06CC\u0632 \u0646\u0648\u0634\u062A\u0647 \u0646\u0645\u06CC\u200C\u0634\u0648\u062F",
2863
+ false
2864
+ ).option(
2865
+ "--apply",
2866
+ "\u0627\u0639\u0645\u0627\u0644\u0650 \u0648\u0627\u0642\u0639\u06CC\u0650 \u0645\u0647\u0627\u062C\u0631\u062A\u200C\u0647\u0627\u06CC \u067E\u0634\u062A\u06CC\u0628\u0627\u0646\u06CC\u200C\u0634\u062F\u0647 (replace) \u0648 \u0628\u0647\u200C\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC\u0650 \u0639\u06A9\u0633\u200C\u0641\u0648\u0631\u06CC/lock",
2867
+ false
2868
+ ).option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).action(migrateCommand);
2869
+ program.command("migrate-assist").description(
2870
+ "\u067E\u06CC\u0634\u0646\u0647\u0627\u062F\u0650 \u0645\u0647\u0627\u062C\u0631\u062A \u0628\u0631\u0627\u06CC \u06CC\u06A9 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u0650 \u0645\u0634\u062E\u0635\u060C \u0627\u0632 \u0631\u0648\u06CC\u0650 \u06CC\u0627\u0641\u062A\u0647\u200C\u0647\u0627\u06CC dig doctor \u2014 \u0641\u0642\u0637 \u067E\u06CC\u0634\u0646\u0647\u0627\u062F \u0645\u06CC\u200C\u062F\u0647\u062F\u060C \u0647\u06CC\u0686 \u0641\u0627\u06CC\u0644\u06CC \u062A\u063A\u06CC\u06CC\u0631 \u0646\u0645\u06CC\u200C\u06A9\u0646\u062F"
2871
+ ).argument("<component>", "\u0646\u0627\u0645\u0650 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A (\u0645\u062B\u0644 button)").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).option("--explain", "\u0646\u0645\u0627\u06CC\u0634\u0650 \u0627\u06CC\u0646\u06A9\u0647 \u0686\u0631\u0627 \u0647\u0645\u06CC\u0646 \u067E\u06CC\u0634\u0646\u0647\u0627\u062F \u0627\u0646\u062A\u062E\u0627\u0628 \u0634\u062F (\u0627\u0641\u0632\u0648\u062F\u0646\u06CC \u2014 \u062E\u0631\u0648\u062C\u06CC\u0650 \u067E\u06CC\u0634\u200C\u0641\u0631\u0636 \u062A\u063A\u06CC\u06CC\u0631 \u0646\u0645\u06CC\u200C\u06A9\u0646\u062F)", false).action(migrateAssistCommand);
2872
+ program.command("release").description("\u0646\u0645\u0627\u06CC\u0634\u0650 \u06CC\u0627\u062F\u062F\u0627\u0634\u062A\u0650 \u0627\u0646\u062A\u0634\u0627\u0631\u0650 \u0646\u0633\u062E\u0647\u0654 \u0641\u0639\u0644\u06CC\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0628\u0631\u0627\u06CC \u06CC\u06A9 \u06CC\u0627 \u0686\u0646\u062F \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A").argument("<components...>", "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 (\u0645\u062B\u0644 button)").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).action(releaseCommand);
2873
+ program.command("status").description(
2874
+ "\u0646\u0645\u0627\u06CC\u0634\u0650 \u0648\u0636\u0639\u06CC\u062A\u0650 \u0686\u0631\u062E\u0647\u0654 \u062D\u06CC\u0627\u062A\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC (active/deprecated/removed) \u0628\u0631\u0627\u06CC \u06CC\u06A9 \u06CC\u0627 \u0686\u0646\u062F \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A"
2875
+ ).argument("<components...>", "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 (\u0645\u062B\u0644 button)").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).action(statusCommand);
2876
+ program.command("governance").description(
2877
+ "\u06AF\u0632\u0627\u0631\u0634\u0650 \u062D\u0627\u06A9\u0645\u06CC\u062A: \u062A\u0623\u062B\u06CC\u0631\u0650 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC + \u0645\u0647\u0627\u062C\u0631\u062A + \u0686\u0631\u062E\u0647\u0654 \u062D\u06CC\u0627\u062A + \u0627\u0646\u062A\u0634\u0627\u0631 \u0628\u0631\u0627\u06CC \u06CC\u06A9 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u060C \u062F\u0631 \u06CC\u06A9 \u0646\u06AF\u0627\u0647"
2878
+ ).argument("<component>", "\u0646\u0627\u0645\u0650 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A (\u0645\u062B\u0644 button)").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).option(
2879
+ "--scan",
2880
+ "\u0627\u0641\u0632\u0648\u062F\u0646\u0650 \u0627\u0633\u062A\u0641\u0627\u062F\u0647\u0654 \u0645\u062D\u0644\u06CC (repositoryImpact) \u0628\u0627 \u0627\u0633\u06A9\u0646\u0650 \u0641\u0642\u0637\u200C\u062E\u0648\u0627\u0646\u062F\u0646\u06CC\u0650 \u0631\u06CC\u067E\u0627\u0632\u06CC\u062A\u0648\u0631\u06CC\u0650 \u0641\u0639\u0644\u06CC",
2881
+ false
2882
+ ).action(governanceCommand);
2883
+ program.command("health").description(
2884
+ "\u06AF\u0632\u0627\u0631\u0634\u0650 \u06CC\u06A9\u067E\u0627\u0631\u0686\u0647\u0654 \u0633\u0644\u0627\u0645\u062A\u0650 \u062F\u06CC\u0632\u0627\u06CC\u0646\u200C\u0633\u06CC\u0633\u062A\u0645: \u067E\u0630\u06CC\u0631\u0634 + \u0645\u0647\u0627\u062C\u0631\u062A + \u0686\u0631\u062E\u0647\u0654 \u062D\u06CC\u0627\u062A + \u0631\u06CC\u0633\u06A9\u0650 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC + \u0627\u0633\u062A\u0641\u0627\u062F\u0647\u0654 \u0645\u062D\u0644\u06CC (\u0641\u0642\u0637\u200C\u062E\u0648\u0627\u0646\u062F\u0646\u06CC)"
2885
+ ).option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).action(healthCommand);
2886
+ program.command("export-health").description(
2887
+ "\u0642\u0631\u0627\u0631\u062F\u0627\u062F\u0650 \u0635\u0627\u062F\u0631\u0627\u062A\u0650 \u067E\u0627\u06CC\u062F\u0627\u0631\u0650 JSON \u0628\u0631\u0627\u06CC \u0645\u0635\u0631\u0641\u200C\u06A9\u0646\u0646\u062F\u0647\u200C\u0647\u0627\u06CC \u0622\u06CC\u0646\u062F\u0647 (per-component) \u2014 \u0647\u0645\u06CC\u0634\u0647 JSON\u060C \u0641\u0642\u0637\u200C\u062E\u0648\u0627\u0646\u062F\u0646\u06CC"
2888
+ ).option("--json", "\u0628\u062F\u0648\u0646\u0650 \u062A\u063A\u06CC\u06CC\u0631\u0650 \u062E\u0631\u0648\u062C\u06CC \u2014 \u0641\u0642\u0637 \u0628\u0631\u0627\u06CC \u0647\u0645\u0627\u0647\u0646\u06AF\u06CC \u0628\u0627 \u0628\u0642\u06CC\u0647\u0654 \u0641\u0631\u0645\u0627\u0646\u200C\u0647\u0627", false).action(exportHealthCommand);
2889
+ program.command("doctor").description(
2890
+ "\u062A\u062D\u0644\u06CC\u0644\u0650 \u0633\u0644\u0627\u0645\u062A\u0650 \u062F\u06CC\u0632\u0627\u06CC\u0646\u200C\u0633\u06CC\u0633\u062A\u0645 (\u0627\u0645\u0631\u0648\u0632: \u0642\u0627\u0646\u0648\u0646\u200C\u0645\u062D\u0648\u0631\u060C \u0645\u0639\u0645\u0627\u0631\u06CC \u0622\u0645\u0627\u062F\u0647\u0654 AI) \u0627\u0632 \u0631\u0648\u06CC IntelligenceExport \u2014 \u0641\u0642\u0637\u200C\u062E\u0648\u0627\u0646\u062F\u0646\u06CC"
2891
+ ).option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).option("--explain", "\u0646\u0645\u0627\u06CC\u0634\u0650 \u0631\u062F\u0650 \u0627\u0633\u062A\u062F\u0644\u0627\u0644 \u0648 \u0633\u0637\u062D\u0650 \u0627\u0637\u0645\u06CC\u0646\u0627\u0646\u0650 \u0647\u0631 \u06CC\u0627\u0641\u062A\u0647 (\u0627\u0641\u0632\u0648\u062F\u0646\u06CC \u2014 \u062E\u0631\u0648\u062C\u06CC\u0650 \u067E\u06CC\u0634\u200C\u0641\u0631\u0636 \u062A\u063A\u06CC\u06CC\u0631 \u0646\u0645\u06CC\u200C\u06A9\u0646\u062F)", false).action(doctorCommand);
2892
+ program.command("scan").description(
2893
+ "\u06CC\u0627\u0641\u062A\u0646\u0650 \u0627\u0633\u062A\u0641\u0627\u062F\u0647\u200C\u0647\u0627\u06CC JSX\u0650 \u06CC\u06A9 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u062F\u0631 \u0631\u06CC\u067E\u0627\u0632\u06CC\u062A\u0648\u0631\u06CC\u0650 \u0645\u062D\u0644\u06CC (\u0641\u0642\u0637\u200C\u062E\u0648\u0627\u0646\u062F\u0646\u06CC \u2014 \u0647\u06CC\u0686 \u0641\u0627\u06CC\u0644\u06CC \u062A\u063A\u06CC\u06CC\u0631 \u0646\u0645\u06CC\u200C\u06A9\u0646\u062F)"
2894
+ ).argument("<component>", "\u0646\u0627\u0645\u0650 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A (\u0645\u062B\u0644 button)").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).action(scanCommand);
2895
+ program.parseAsync();