digdesign 0.6.2 → 0.8.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.
- package/dist/index.js +465 -227
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Command } from "commander";
|
|
|
5
5
|
|
|
6
6
|
// src/commands/add.ts
|
|
7
7
|
import { access as access4, mkdir as mkdir3, writeFile as writeFile5 } from "fs/promises";
|
|
8
|
-
import
|
|
8
|
+
import path8 from "path";
|
|
9
9
|
|
|
10
10
|
// src/lib/config.ts
|
|
11
11
|
import { access, readFile, writeFile } from "fs/promises";
|
|
@@ -55,6 +55,10 @@ async function writeConfig(config, cwd = process.cwd()) {
|
|
|
55
55
|
);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// src/lib/framework.ts
|
|
59
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
60
|
+
import path2 from "path";
|
|
61
|
+
|
|
58
62
|
// src/lib/errors.ts
|
|
59
63
|
var DigError = class extends Error {
|
|
60
64
|
constructor(message, options) {
|
|
@@ -63,6 +67,48 @@ var DigError = class extends Error {
|
|
|
63
67
|
}
|
|
64
68
|
};
|
|
65
69
|
|
|
70
|
+
// src/lib/framework.ts
|
|
71
|
+
var CLI_FRAMEWORKS = ["react", "vue"];
|
|
72
|
+
function parseFramework(value) {
|
|
73
|
+
const normalized = value.trim().toLowerCase();
|
|
74
|
+
if (CLI_FRAMEWORKS.includes(normalized)) return normalized;
|
|
75
|
+
if (normalized === "svelte" || normalized === "angular") {
|
|
76
|
+
throw new DigError(
|
|
77
|
+
`CLI \u0647\u0646\u0648\u0632 ${normalized === "svelte" ? "Svelte" : "Angular"} \u0631\u0627 \u0646\u0635\u0628 \u0646\u0645\u06CC\u200C\u06A9\u0646\u062F.
|
|
78
|
+
\u06A9\u062F \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0631\u0627 \u0627\u0632 \u062A\u0628 \xAB\u062F\u0633\u062A\u06CC\xBB \u0635\u0641\u062D\u0647\u0654 \u0645\u0633\u062A\u0646\u062F\u0627\u062A \u0628\u06AF\u06CC\u0631\u06CC\u062F\u060C \u06CC\u0627 /r/${normalized}/<name>.json \u0631\u0627 \u062F\u0633\u062A\u06CC \u0628\u0646\u0648\u06CC\u0633\u06CC\u062F.`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
throw new DigError(`\u0641\u0631\u06CC\u0645\u200C\u0648\u0631\u06A9 \xAB${value}\xBB \u0634\u0646\u0627\u062E\u062A\u0647 \u0646\u0634\u062F. \u06CC\u06A9\u06CC \u0627\u0632 ${CLI_FRAMEWORKS.join("\u060C ")} \u0631\u0627 \u0628\u062F\u0647\u06CC\u062F.`);
|
|
82
|
+
}
|
|
83
|
+
async function readPackageJson(cwd) {
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(await readFile2(path2.join(cwd, "package.json"), "utf8"));
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function detectFramework(cwd) {
|
|
91
|
+
const pkg = await readPackageJson(cwd);
|
|
92
|
+
if (!pkg) return void 0;
|
|
93
|
+
const deps = { ...pkg.devDependencies, ...pkg.dependencies };
|
|
94
|
+
if ("react" in deps || "next" in deps) return "react";
|
|
95
|
+
if ("vue" in deps || "nuxt" in deps) return "vue";
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
async function resolveFramework(cwd, config, flag) {
|
|
99
|
+
if (flag) return parseFramework(flag);
|
|
100
|
+
if (config?.framework) return config.framework;
|
|
101
|
+
const detected = await detectFramework(cwd);
|
|
102
|
+
if (detected) return detected;
|
|
103
|
+
try {
|
|
104
|
+
const shadcn = JSON.parse(await readFile2(path2.join(cwd, "components.json"), "utf8"));
|
|
105
|
+
const hint = `${shadcn.framework ?? ""} ${shadcn.$schema ?? ""} ${shadcn.tailwind?.css ?? ""}`.toLowerCase();
|
|
106
|
+
if (/vue|nuxt/.test(hint)) return "vue";
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
return "react";
|
|
110
|
+
}
|
|
111
|
+
|
|
66
112
|
// src/lib/prompt.ts
|
|
67
113
|
import * as readline from "readline/promises";
|
|
68
114
|
import { stdin, stdout } from "process";
|
|
@@ -127,19 +173,19 @@ function hashContent(content) {
|
|
|
127
173
|
}
|
|
128
174
|
|
|
129
175
|
// src/lib/lock.ts
|
|
130
|
-
import { mkdir, readFile as
|
|
131
|
-
import
|
|
176
|
+
import { mkdir, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
177
|
+
import path3 from "path";
|
|
132
178
|
var LOCK_DIR = ".dig";
|
|
133
179
|
var LOCK_FILE_NAME = "registry-lock.json";
|
|
134
180
|
function emptyLock() {
|
|
135
181
|
return { components: {} };
|
|
136
182
|
}
|
|
137
183
|
function lockFilePath(cwd) {
|
|
138
|
-
return
|
|
184
|
+
return path3.join(cwd, LOCK_DIR, LOCK_FILE_NAME);
|
|
139
185
|
}
|
|
140
186
|
async function readLock(cwd = process.cwd()) {
|
|
141
187
|
try {
|
|
142
|
-
const raw = await
|
|
188
|
+
const raw = await readFile3(lockFilePath(cwd), "utf8");
|
|
143
189
|
const parsed = JSON.parse(raw);
|
|
144
190
|
return { components: parsed?.components ?? {} };
|
|
145
191
|
} catch (error) {
|
|
@@ -148,7 +194,7 @@ async function readLock(cwd = process.cwd()) {
|
|
|
148
194
|
}
|
|
149
195
|
}
|
|
150
196
|
async function writeLock(lock, cwd = process.cwd()) {
|
|
151
|
-
await mkdir(
|
|
197
|
+
await mkdir(path3.join(cwd, LOCK_DIR), { recursive: true });
|
|
152
198
|
await writeFile2(lockFilePath(cwd), `${JSON.stringify(lock, null, 2)}
|
|
153
199
|
`, "utf8");
|
|
154
200
|
}
|
|
@@ -168,7 +214,7 @@ function mergeComponentEntry(existing, patch) {
|
|
|
168
214
|
// src/lib/package-manager.ts
|
|
169
215
|
import { access as access2 } from "fs/promises";
|
|
170
216
|
import { spawn } from "child_process";
|
|
171
|
-
import
|
|
217
|
+
import path4 from "path";
|
|
172
218
|
var LOCKFILES = [
|
|
173
219
|
["pnpm", "pnpm-lock.yaml"],
|
|
174
220
|
["yarn", "yarn.lock"],
|
|
@@ -178,7 +224,7 @@ var LOCKFILES = [
|
|
|
178
224
|
async function detectPackageManager(cwd = process.cwd()) {
|
|
179
225
|
for (const [pm, lockfile] of LOCKFILES) {
|
|
180
226
|
try {
|
|
181
|
-
await access2(
|
|
227
|
+
await access2(path4.join(cwd, lockfile));
|
|
182
228
|
return pm;
|
|
183
229
|
} catch {
|
|
184
230
|
continue;
|
|
@@ -248,9 +294,12 @@ function installDependencies(pm, deps, options = {}) {
|
|
|
248
294
|
}
|
|
249
295
|
|
|
250
296
|
// src/lib/registry.ts
|
|
251
|
-
function
|
|
297
|
+
function registryBase(registryUrl, framework) {
|
|
298
|
+
return !framework || framework === "react" ? `${registryUrl}/r` : `${registryUrl}/r/${framework}`;
|
|
299
|
+
}
|
|
300
|
+
function resolveUrl(nameOrUrl, registryUrl, framework) {
|
|
252
301
|
if (/^https?:\/\//.test(nameOrUrl)) return nameOrUrl;
|
|
253
|
-
return `${registryUrl}
|
|
302
|
+
return `${registryBase(registryUrl, framework)}/${nameOrUrl}.json`;
|
|
254
303
|
}
|
|
255
304
|
async function fetchFromRegistry(url, subject) {
|
|
256
305
|
let response;
|
|
@@ -288,28 +337,28 @@ async function fetchFromRegistry(url, subject) {
|
|
|
288
337
|
function normalizeType(type) {
|
|
289
338
|
return type.startsWith("registry:") ? `dig:${type.slice("registry:".length)}` : type;
|
|
290
339
|
}
|
|
291
|
-
async function fetchRegistryIndex(registryUrl) {
|
|
292
|
-
const url = `${registryUrl}/
|
|
340
|
+
async function fetchRegistryIndex(registryUrl, framework) {
|
|
341
|
+
const url = `${registryBase(registryUrl, framework)}/registry.json`;
|
|
293
342
|
const response = await fetchFromRegistry(url, "\u0641\u0647\u0631\u0633\u062A\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC");
|
|
294
343
|
const index = await response.json();
|
|
295
344
|
for (const item of index.items) item.type = normalizeType(item.type);
|
|
296
345
|
return index;
|
|
297
346
|
}
|
|
298
|
-
async function fetchRegistryItem(nameOrUrl, registryUrl) {
|
|
299
|
-
const url = resolveUrl(nameOrUrl, registryUrl);
|
|
347
|
+
async function fetchRegistryItem(nameOrUrl, registryUrl, framework) {
|
|
348
|
+
const url = resolveUrl(nameOrUrl, registryUrl, framework);
|
|
300
349
|
const response = await fetchFromRegistry(url, `\xAB${nameOrUrl}\xBB`);
|
|
301
350
|
const item = await response.json();
|
|
302
351
|
item.type = normalizeType(item.type);
|
|
303
352
|
for (const file of item.files ?? []) file.type = normalizeType(file.type);
|
|
304
353
|
return item;
|
|
305
354
|
}
|
|
306
|
-
async function resolveRegistryTree(names, registryUrl) {
|
|
355
|
+
async function resolveRegistryTree(names, registryUrl, framework) {
|
|
307
356
|
const resolved = /* @__PURE__ */ new Map();
|
|
308
357
|
const queued = new Set(names);
|
|
309
358
|
const queue = [...names];
|
|
310
359
|
while (queue.length) {
|
|
311
360
|
const next = queue.shift();
|
|
312
|
-
const item = await fetchRegistryItem(next, registryUrl);
|
|
361
|
+
const item = await fetchRegistryItem(next, registryUrl, framework);
|
|
313
362
|
if (resolved.has(item.name)) continue;
|
|
314
363
|
resolved.set(item.name, item);
|
|
315
364
|
for (const dep of item.registryDependencies ?? []) {
|
|
@@ -323,14 +372,14 @@ async function resolveRegistryTree(names, registryUrl) {
|
|
|
323
372
|
}
|
|
324
373
|
|
|
325
374
|
// src/lib/snapshot.ts
|
|
326
|
-
import { access as access3, mkdir as mkdir2, readFile as
|
|
327
|
-
import
|
|
328
|
-
var SNAPSHOT_ROOT =
|
|
375
|
+
import { access as access3, mkdir as mkdir2, readFile as readFile4, rm, writeFile as writeFile3 } from "fs/promises";
|
|
376
|
+
import path5 from "path";
|
|
377
|
+
var SNAPSHOT_ROOT = path5.join(".dig", "snapshots");
|
|
329
378
|
function snapshotDirFor(cwd, name, version) {
|
|
330
|
-
return
|
|
379
|
+
return path5.join(cwd, SNAPSHOT_ROOT, name, version);
|
|
331
380
|
}
|
|
332
381
|
function snapshotRelativePath(name, version) {
|
|
333
|
-
return `${SNAPSHOT_ROOT.split(
|
|
382
|
+
return `${SNAPSHOT_ROOT.split(path5.sep).join("/")}/${name}/${version}`;
|
|
334
383
|
}
|
|
335
384
|
async function fileExists(target) {
|
|
336
385
|
try {
|
|
@@ -347,19 +396,19 @@ async function writeComponentSnapshot(cwd, name, version, files) {
|
|
|
347
396
|
const dir = snapshotDirFor(cwd, name, version);
|
|
348
397
|
const pending = [];
|
|
349
398
|
for (const [relFilePath, content] of entries) {
|
|
350
|
-
const target =
|
|
399
|
+
const target = path5.join(dir, relFilePath);
|
|
351
400
|
const existedBefore = await fileExists(target);
|
|
352
401
|
pending.push({
|
|
353
402
|
target,
|
|
354
403
|
content,
|
|
355
404
|
existedBefore,
|
|
356
|
-
originalContent: existedBefore ? await
|
|
405
|
+
originalContent: existedBefore ? await readFile4(target, "utf8") : void 0
|
|
357
406
|
});
|
|
358
407
|
}
|
|
359
408
|
const written = [];
|
|
360
409
|
try {
|
|
361
410
|
for (const item of pending) {
|
|
362
|
-
await mkdir2(
|
|
411
|
+
await mkdir2(path5.dirname(item.target), { recursive: true });
|
|
363
412
|
await writeFile3(item.target, item.content, "utf8");
|
|
364
413
|
written.push(item);
|
|
365
414
|
}
|
|
@@ -377,7 +426,7 @@ async function writeComponentSnapshot(cwd, name, version, files) {
|
|
|
377
426
|
}
|
|
378
427
|
async function readSnapshotFile(cwd, name, version, relFilePath) {
|
|
379
428
|
try {
|
|
380
|
-
return await
|
|
429
|
+
return await readFile4(path5.join(snapshotDirFor(cwd, name, version), relFilePath), "utf8");
|
|
381
430
|
} catch (error) {
|
|
382
431
|
if (error.code === "ENOENT") return void 0;
|
|
383
432
|
throw error;
|
|
@@ -385,9 +434,9 @@ async function readSnapshotFile(cwd, name, version, relFilePath) {
|
|
|
385
434
|
}
|
|
386
435
|
|
|
387
436
|
// src/lib/target.ts
|
|
388
|
-
import
|
|
437
|
+
import path6 from "path";
|
|
389
438
|
function toPosixPath(relativePath) {
|
|
390
|
-
return relativePath.split(
|
|
439
|
+
return relativePath.split(path6.sep).join("/");
|
|
391
440
|
}
|
|
392
441
|
function targetPathOptions(config) {
|
|
393
442
|
return { tailwindCss: config.tailwind.css, themeCss: config.tailwind.theme };
|
|
@@ -414,8 +463,8 @@ function resolveTargetPath(filePath, aliases, options = {}) {
|
|
|
414
463
|
}
|
|
415
464
|
|
|
416
465
|
// src/commands/design-md.ts
|
|
417
|
-
import { readFile as
|
|
418
|
-
import
|
|
466
|
+
import { readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
467
|
+
import path7 from "path";
|
|
419
468
|
var DESIGN_MD_FILE = "DESIGN.md";
|
|
420
469
|
var GENERATED_MARK = "Generated by Dig";
|
|
421
470
|
async function fetchDesignMd(registryUrl, options) {
|
|
@@ -438,13 +487,13 @@ async function fetchDesignMd(registryUrl, options) {
|
|
|
438
487
|
}
|
|
439
488
|
async function readIfExists(target) {
|
|
440
489
|
try {
|
|
441
|
-
return await
|
|
490
|
+
return await readFile5(target, "utf8");
|
|
442
491
|
} catch {
|
|
443
492
|
return null;
|
|
444
493
|
}
|
|
445
494
|
}
|
|
446
495
|
async function writeDesignMd(cwd, registryUrl, options) {
|
|
447
|
-
const target =
|
|
496
|
+
const target = path7.join(cwd, DESIGN_MD_FILE);
|
|
448
497
|
const existing = await readIfExists(target);
|
|
449
498
|
if (existing !== null && !options.force) {
|
|
450
499
|
const generated = existing.includes(GENERATED_MARK);
|
|
@@ -472,7 +521,7 @@ function readDesignMdSettings(text) {
|
|
|
472
521
|
}
|
|
473
522
|
}
|
|
474
523
|
async function refreshDesignMd(cwd, registryUrl, overrides = {}, log = console.log) {
|
|
475
|
-
const target =
|
|
524
|
+
const target = path7.join(cwd, DESIGN_MD_FILE);
|
|
476
525
|
const existing = await readIfExists(target);
|
|
477
526
|
if (existing === null || !existing.includes(GENERATED_MARK)) return "skipped";
|
|
478
527
|
try {
|
|
@@ -489,7 +538,7 @@ async function refreshDesignMd(cwd, registryUrl, overrides = {}, log = console.l
|
|
|
489
538
|
async function designMdCommand(options) {
|
|
490
539
|
const cwd = process.cwd();
|
|
491
540
|
const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
|
|
492
|
-
const previous = readDesignMdSettings(await readIfExists(
|
|
541
|
+
const previous = readDesignMdSettings(await readIfExists(path7.join(cwd, DESIGN_MD_FILE)) ?? "");
|
|
493
542
|
const explicit = Object.fromEntries(Object.entries(options).filter(([, value]) => value !== void 0 && value !== false));
|
|
494
543
|
options = { ...previous, ...explicit };
|
|
495
544
|
const result = await writeDesignMd(cwd, config.registryUrl, { ...options, replaceGenerated: true });
|
|
@@ -507,13 +556,13 @@ async function pathExists(target) {
|
|
|
507
556
|
return false;
|
|
508
557
|
}
|
|
509
558
|
}
|
|
510
|
-
async function pickComponents(registryUrl) {
|
|
559
|
+
async function pickComponents(registryUrl, framework) {
|
|
511
560
|
if (!process.stdin.isTTY) {
|
|
512
561
|
throw new DigError(
|
|
513
562
|
"\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0631\u0627 \u0628\u062F\u0647\u06CC\u062F: dig add button\n \u0628\u0631\u0627\u06CC \u062F\u06CC\u062F\u0646\u0650 \u0641\u0647\u0631\u0633\u062A: dig list"
|
|
514
563
|
);
|
|
515
564
|
}
|
|
516
|
-
const index = await fetchRegistryIndex(registryUrl);
|
|
565
|
+
const index = await fetchRegistryIndex(registryUrl, framework);
|
|
517
566
|
const choices = index.items.filter((item) => item.type === "dig:ui").map((item) => ({
|
|
518
567
|
value: item.name,
|
|
519
568
|
label: item.title ? `${item.name} \u2014 ${item.title}` : item.name
|
|
@@ -533,18 +582,25 @@ ${choices.length} \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u062F\u0631
|
|
|
533
582
|
}
|
|
534
583
|
async function addCommand(components, options) {
|
|
535
584
|
const cwd = process.cwd();
|
|
536
|
-
const
|
|
537
|
-
const
|
|
585
|
+
const hasConfig = await configExists(cwd);
|
|
586
|
+
const config = hasConfig ? await readConfig(cwd) : defaultConfig();
|
|
587
|
+
const framework = await resolveFramework(cwd, config, options.framework);
|
|
588
|
+
if (hasConfig && framework !== "react" && config.framework !== framework && !options.dryRun) {
|
|
589
|
+
config.framework = framework;
|
|
590
|
+
await writeConfig(config, cwd);
|
|
591
|
+
}
|
|
592
|
+
config.framework = framework === "react" ? config.framework : framework;
|
|
593
|
+
const names = components.length ? components : await pickComponents(config.registryUrl, framework);
|
|
538
594
|
if (names.length === 0) return;
|
|
539
|
-
console.log(`\u062F\u0631\u06CC\u0627\u0641\u062A \u0627\u0632 ${config.registryUrl} \u2026`);
|
|
540
|
-
const items = await resolveRegistryTree(names, config.registryUrl);
|
|
595
|
+
console.log(`\u062F\u0631\u06CC\u0627\u0641\u062A \u0627\u0632 ${config.registryUrl}${framework === "react" ? "" : ` (${framework})`} \u2026`);
|
|
596
|
+
const items = await resolveRegistryTree(names, config.registryUrl, framework);
|
|
541
597
|
let overwrite = Boolean(options.overwrite);
|
|
542
598
|
if (!overwrite && !options.skipExisting) {
|
|
543
599
|
const existing = [];
|
|
544
600
|
for (const item of items.values()) {
|
|
545
601
|
for (const file of item.files) {
|
|
546
|
-
const target =
|
|
547
|
-
if (await pathExists(target)) existing.push(
|
|
602
|
+
const target = path8.join(cwd, resolveTargetPath(file.path, config.aliases, targetPathOptions(config)));
|
|
603
|
+
if (await pathExists(target)) existing.push(path8.relative(cwd, target));
|
|
548
604
|
}
|
|
549
605
|
}
|
|
550
606
|
if (existing.length) {
|
|
@@ -565,11 +621,11 @@ ${existing.length} \u0641\u0627\u06CC\u0644 \u0627\u0632 \u0642\u0628\u0644 \u06
|
|
|
565
621
|
for (const item of items.values()) {
|
|
566
622
|
for (const dep of item.dependencies ?? []) npmDeps.add(dep);
|
|
567
623
|
for (const file of item.files) {
|
|
568
|
-
const target =
|
|
624
|
+
const target = path8.join(cwd, resolveTargetPath(file.path, config.aliases, targetPathOptions(config)));
|
|
569
625
|
const exists3 = await pathExists(target);
|
|
570
626
|
const mark = exists3 ? overwrite ? "~" : "-" : "+";
|
|
571
627
|
const note = exists3 ? overwrite ? " (\u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC)" : " (\u0631\u062F \u0645\u06CC\u200C\u0634\u0648\u062F\u060C \u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F)" : "";
|
|
572
|
-
console.log(`${mark} ${
|
|
628
|
+
console.log(`${mark} ${path8.relative(cwd, target)}${note}`);
|
|
573
629
|
}
|
|
574
630
|
}
|
|
575
631
|
console.log(`
|
|
@@ -592,17 +648,17 @@ ${items.size} \u0622\u06CC\u062A\u0645 \u0627\u0632 \u0631\u062C\u06CC\u0633\u06
|
|
|
592
648
|
const writtenFiles = {};
|
|
593
649
|
const writtenContents = {};
|
|
594
650
|
for (const file of item.files) {
|
|
595
|
-
const target =
|
|
651
|
+
const target = path8.join(cwd, resolveTargetPath(file.path, config.aliases, targetPathOptions(config)));
|
|
596
652
|
if (!overwrite && await pathExists(target)) {
|
|
597
|
-
if (!options.skipExisting) console.log(`- \u0631\u062F \u0634\u062F (\u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F): ${
|
|
653
|
+
if (!options.skipExisting) console.log(`- \u0631\u062F \u0634\u062F (\u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F): ${path8.relative(cwd, target)}`);
|
|
598
654
|
skipped++;
|
|
599
655
|
continue;
|
|
600
656
|
}
|
|
601
|
-
await mkdir3(
|
|
657
|
+
await mkdir3(path8.dirname(target), { recursive: true });
|
|
602
658
|
await writeFile5(target, file.content, "utf8");
|
|
603
|
-
console.log(`+ ${
|
|
659
|
+
console.log(`+ ${path8.relative(cwd, target)}`);
|
|
604
660
|
written++;
|
|
605
|
-
const relPath = toPosixPath(
|
|
661
|
+
const relPath = toPosixPath(path8.relative(cwd, target));
|
|
606
662
|
writtenFiles[relPath] = file.hash ?? hashContent(file.content);
|
|
607
663
|
writtenContents[relPath] = file.content;
|
|
608
664
|
}
|
|
@@ -625,7 +681,7 @@ ${items.size} \u0622\u06CC\u062A\u0645 \u0627\u0632 \u0631\u062C\u06CC\u0633\u06
|
|
|
625
681
|
if (lockChanged) {
|
|
626
682
|
await writeLock(lock, cwd);
|
|
627
683
|
console.log(`
|
|
628
|
-
\u062B\u0628\u062A \u0634\u062F \u062F\u0631 ${
|
|
684
|
+
\u062B\u0628\u062A \u0634\u062F \u062F\u0631 ${path8.join(".dig", "registry-lock.json")}`);
|
|
629
685
|
}
|
|
630
686
|
console.log(`
|
|
631
687
|
${written} \u0641\u0627\u06CC\u0644 \u0646\u0648\u0634\u062A\u0647 \u0634\u062F\u060C ${skipped} \u0641\u0627\u06CC\u0644 \u0631\u062F \u0634\u062F.`);
|
|
@@ -700,7 +756,7 @@ var TYPE_LABEL = {
|
|
|
700
756
|
};
|
|
701
757
|
async function loadItems(cwd) {
|
|
702
758
|
const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
|
|
703
|
-
const index = await fetchRegistryIndex(config.registryUrl);
|
|
759
|
+
const index = await fetchRegistryIndex(config.registryUrl, config.framework);
|
|
704
760
|
return index.items;
|
|
705
761
|
}
|
|
706
762
|
function matchesType(item, type) {
|
|
@@ -752,8 +808,8 @@ async function searchCommand(query, options) {
|
|
|
752
808
|
}
|
|
753
809
|
|
|
754
810
|
// src/lib/diff.ts
|
|
755
|
-
import { readFile as
|
|
756
|
-
import
|
|
811
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
812
|
+
import path9 from "path";
|
|
757
813
|
var STATUS_PRIORITY = [
|
|
758
814
|
"conflict",
|
|
759
815
|
"untracked",
|
|
@@ -783,7 +839,7 @@ function overallStatus(files) {
|
|
|
783
839
|
}
|
|
784
840
|
async function readLocalHash(target) {
|
|
785
841
|
try {
|
|
786
|
-
const content = await
|
|
842
|
+
const content = await readFile6(target, "utf8");
|
|
787
843
|
return hashContent(content);
|
|
788
844
|
} catch (error) {
|
|
789
845
|
if (error.code === "ENOENT") return void 0;
|
|
@@ -791,8 +847,8 @@ async function readLocalHash(target) {
|
|
|
791
847
|
}
|
|
792
848
|
}
|
|
793
849
|
async function computeFileDrift(cwd, aliases, lockEntry, file, paths) {
|
|
794
|
-
const target =
|
|
795
|
-
const relPath = toPosixPath(
|
|
850
|
+
const target = path9.join(cwd, resolveTargetPath(file.path, aliases, paths));
|
|
851
|
+
const relPath = toPosixPath(path9.relative(cwd, target));
|
|
796
852
|
const registryHash = file.hash ?? hashContent(file.content);
|
|
797
853
|
const installedHash = lockEntry?.files[relPath];
|
|
798
854
|
const localHash = await readLocalHash(target);
|
|
@@ -821,8 +877,8 @@ async function computeComponentDrift(params) {
|
|
|
821
877
|
}
|
|
822
878
|
|
|
823
879
|
// src/lib/file-diff-details.ts
|
|
824
|
-
import { readFile as
|
|
825
|
-
import
|
|
880
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
881
|
+
import path10 from "path";
|
|
826
882
|
|
|
827
883
|
// src/lib/text-diff.ts
|
|
828
884
|
var DEFAULT_CONTEXT = 3;
|
|
@@ -931,7 +987,7 @@ function looksBinary(buffer) {
|
|
|
931
987
|
async function readTextFileSafely(target) {
|
|
932
988
|
let buffer;
|
|
933
989
|
try {
|
|
934
|
-
buffer = await
|
|
990
|
+
buffer = await readFile7(target);
|
|
935
991
|
} catch (error) {
|
|
936
992
|
if (error.code === "ENOENT") return void 0;
|
|
937
993
|
throw error;
|
|
@@ -971,7 +1027,7 @@ async function computeFileDiffDetails(params) {
|
|
|
971
1027
|
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."
|
|
972
1028
|
};
|
|
973
1029
|
}
|
|
974
|
-
const target =
|
|
1030
|
+
const target = path10.join(cwd, resolveTargetPath(registryFile.path, aliases, paths));
|
|
975
1031
|
const localContent = await readTextFileSafely(target);
|
|
976
1032
|
if (localContent === "binary") {
|
|
977
1033
|
return {
|
|
@@ -1057,7 +1113,7 @@ async function diffCommand(components, options) {
|
|
|
1057
1113
|
for (const name of components) {
|
|
1058
1114
|
let registryItem;
|
|
1059
1115
|
try {
|
|
1060
|
-
registryItem = await fetchRegistryItem(name, config.registryUrl);
|
|
1116
|
+
registryItem = await fetchRegistryItem(name, config.registryUrl, config.framework);
|
|
1061
1117
|
} catch (error) {
|
|
1062
1118
|
console.error(`\u2717 ${name}: ${error.message}`);
|
|
1063
1119
|
continue;
|
|
@@ -1528,8 +1584,8 @@ function dependencyRiskLevel(highImpactComponents) {
|
|
|
1528
1584
|
}
|
|
1529
1585
|
|
|
1530
1586
|
// src/lib/repo-scanner.ts
|
|
1531
|
-
import { readFile as
|
|
1532
|
-
import
|
|
1587
|
+
import { readFile as readFile8, readdir } from "fs/promises";
|
|
1588
|
+
import path11 from "path";
|
|
1533
1589
|
import ts2 from "typescript";
|
|
1534
1590
|
|
|
1535
1591
|
// src/lib/ast-engine.ts
|
|
@@ -1604,11 +1660,11 @@ async function collectSourceFiles(root) {
|
|
|
1604
1660
|
for (const entry of entries) {
|
|
1605
1661
|
if (entry.isDirectory()) {
|
|
1606
1662
|
if (IGNORED_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
1607
|
-
await walk(
|
|
1663
|
+
await walk(path11.join(dir, entry.name));
|
|
1608
1664
|
continue;
|
|
1609
1665
|
}
|
|
1610
|
-
if (entry.isFile() && SCAN_EXTENSIONS.has(
|
|
1611
|
-
results.push(
|
|
1666
|
+
if (entry.isFile() && SCAN_EXTENSIONS.has(path11.extname(entry.name))) {
|
|
1667
|
+
results.push(path11.join(dir, entry.name));
|
|
1612
1668
|
}
|
|
1613
1669
|
}
|
|
1614
1670
|
}
|
|
@@ -1635,7 +1691,7 @@ async function scanRepositoryForComponent(params) {
|
|
|
1635
1691
|
for (const filePath of sourceFilePaths) {
|
|
1636
1692
|
let content;
|
|
1637
1693
|
try {
|
|
1638
|
-
content = await
|
|
1694
|
+
content = await readFile8(filePath, "utf8");
|
|
1639
1695
|
} catch {
|
|
1640
1696
|
continue;
|
|
1641
1697
|
}
|
|
@@ -1649,7 +1705,7 @@ async function scanRepositoryForComponent(params) {
|
|
|
1649
1705
|
const snippets = findJsxUsagesInSource(sourceFile, tagName);
|
|
1650
1706
|
if (snippets.length === 0) continue;
|
|
1651
1707
|
files.push({
|
|
1652
|
-
path: toPosixPath(
|
|
1708
|
+
path: toPosixPath(path11.relative(projectRoot, filePath)),
|
|
1653
1709
|
usages: snippets.length,
|
|
1654
1710
|
snippets
|
|
1655
1711
|
});
|
|
@@ -2054,9 +2110,178 @@ async function impactCommand(name, options) {
|
|
|
2054
2110
|
}
|
|
2055
2111
|
}
|
|
2056
2112
|
|
|
2113
|
+
// src/commands/review.ts
|
|
2114
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
2115
|
+
import path12 from "path";
|
|
2116
|
+
|
|
2117
|
+
// src/lib/version.ts
|
|
2118
|
+
var CLI_VERSION = "0.8.0";
|
|
2119
|
+
|
|
2120
|
+
// src/commands/mcp.ts
|
|
2121
|
+
import { createInterface as createInterface2 } from "readline";
|
|
2122
|
+
var DEFAULT_MCP_URL = "https://docs.digdesign.ir/api/mcp";
|
|
2123
|
+
function parseSse(body) {
|
|
2124
|
+
const messages = [];
|
|
2125
|
+
for (const event of body.split(/\r?\n\r?\n/)) {
|
|
2126
|
+
const data = event.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).replace(/^ /, "")).join("\n");
|
|
2127
|
+
if (!data) continue;
|
|
2128
|
+
try {
|
|
2129
|
+
messages.push(JSON.parse(data));
|
|
2130
|
+
} catch {
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
return messages;
|
|
2134
|
+
}
|
|
2135
|
+
function requestIds(message) {
|
|
2136
|
+
const list = Array.isArray(message) ? message : [message];
|
|
2137
|
+
return list.filter((item) => typeof item === "object" && item !== null).filter((item) => item.method !== void 0 && item.id !== void 0 && item.id !== null).map((item) => item.id);
|
|
2138
|
+
}
|
|
2139
|
+
function createBridge(url, fetchImpl = fetch) {
|
|
2140
|
+
let sessionId;
|
|
2141
|
+
let protocolVersion;
|
|
2142
|
+
return {
|
|
2143
|
+
async handle(line) {
|
|
2144
|
+
let message;
|
|
2145
|
+
try {
|
|
2146
|
+
message = JSON.parse(line);
|
|
2147
|
+
} catch {
|
|
2148
|
+
return [{ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } }];
|
|
2149
|
+
}
|
|
2150
|
+
const ids = requestIds(message);
|
|
2151
|
+
const fail = (reason) => ids.map((id) => ({ jsonrpc: "2.0", id, error: { code: -32603, message: `Dig MCP (${url}): ${reason}` } }));
|
|
2152
|
+
let response;
|
|
2153
|
+
try {
|
|
2154
|
+
response = await fetchImpl(url, {
|
|
2155
|
+
method: "POST",
|
|
2156
|
+
headers: {
|
|
2157
|
+
"content-type": "application/json",
|
|
2158
|
+
accept: "application/json, text/event-stream",
|
|
2159
|
+
"user-agent": `digdesign-cli/${CLI_VERSION}`,
|
|
2160
|
+
...sessionId && { "mcp-session-id": sessionId },
|
|
2161
|
+
...protocolVersion && { "mcp-protocol-version": protocolVersion }
|
|
2162
|
+
},
|
|
2163
|
+
body: line
|
|
2164
|
+
});
|
|
2165
|
+
} catch (error) {
|
|
2166
|
+
return fail(`network error: ${error.message}`);
|
|
2167
|
+
}
|
|
2168
|
+
sessionId = response.headers.get("mcp-session-id") ?? sessionId;
|
|
2169
|
+
if (response.status === 202) return [];
|
|
2170
|
+
const body = await response.text();
|
|
2171
|
+
if (!response.ok) return fail(`HTTP ${response.status} ${body.slice(0, 200)}`);
|
|
2172
|
+
let replies;
|
|
2173
|
+
if ((response.headers.get("content-type") ?? "").includes("text/event-stream")) replies = parseSse(body);
|
|
2174
|
+
else {
|
|
2175
|
+
try {
|
|
2176
|
+
replies = body.trim() ? [JSON.parse(body)] : [];
|
|
2177
|
+
} catch {
|
|
2178
|
+
return fail(`invalid JSON response: ${body.slice(0, 200)}`);
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
for (const reply of replies.flat()) {
|
|
2182
|
+
const result = reply?.result;
|
|
2183
|
+
if (result?.protocolVersion) protocolVersion = result.protocolVersion;
|
|
2184
|
+
}
|
|
2185
|
+
return replies;
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
async function mcpCommand(options) {
|
|
2190
|
+
const url = options.url ?? process.env.DIG_MCP_URL ?? DEFAULT_MCP_URL;
|
|
2191
|
+
const bridge = createBridge(url);
|
|
2192
|
+
const pending = /* @__PURE__ */ new Set();
|
|
2193
|
+
process.stderr.write(`dig mcp: stdio bridge to ${url}
|
|
2194
|
+
`);
|
|
2195
|
+
const input = createInterface2({ input: process.stdin, crlfDelay: Infinity });
|
|
2196
|
+
for await (const line of input) {
|
|
2197
|
+
if (!line.trim()) continue;
|
|
2198
|
+
const task = bridge.handle(line).then((replies) => {
|
|
2199
|
+
for (const reply of replies) process.stdout.write(`${JSON.stringify(reply)}
|
|
2200
|
+
`);
|
|
2201
|
+
});
|
|
2202
|
+
pending.add(task);
|
|
2203
|
+
void task.finally(() => pending.delete(task));
|
|
2204
|
+
}
|
|
2205
|
+
await Promise.all(pending);
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
// src/commands/review.ts
|
|
2209
|
+
var MAX_FILE_CHARS = 2e5;
|
|
2210
|
+
var MAX_FILES_PER_CALL = 25;
|
|
2211
|
+
async function readReviewFiles(paths, cwd) {
|
|
2212
|
+
const files = [];
|
|
2213
|
+
for (const input of paths) {
|
|
2214
|
+
const absolute = path12.resolve(cwd, input);
|
|
2215
|
+
let code;
|
|
2216
|
+
try {
|
|
2217
|
+
code = await readFile9(absolute, "utf8");
|
|
2218
|
+
} catch {
|
|
2219
|
+
throw new DigError(`\u0641\u0627\u06CC\u0644 \u062E\u0648\u0627\u0646\u062F\u0647 \u0646\u0634\u062F: ${input}`);
|
|
2220
|
+
}
|
|
2221
|
+
if (code.length > MAX_FILE_CHARS) throw new DigError(`${input} \u0628\u0632\u0631\u06AF\u200C\u062A\u0631 \u0627\u0632 ${MAX_FILE_CHARS} \u0646\u0648\u06CC\u0633\u0647 \u0627\u0633\u062A\u061B \u0628\u0647 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u06A9\u0648\u0686\u06A9\u200C\u062A\u0631 \u0628\u0634\u06A9\u0646.`);
|
|
2222
|
+
files.push({ path: path12.relative(cwd, absolute) || input, code });
|
|
2223
|
+
}
|
|
2224
|
+
return files;
|
|
2225
|
+
}
|
|
2226
|
+
async function callReview(url, files, fetchImpl = fetch) {
|
|
2227
|
+
let response;
|
|
2228
|
+
try {
|
|
2229
|
+
response = await fetchImpl(url, {
|
|
2230
|
+
method: "POST",
|
|
2231
|
+
headers: {
|
|
2232
|
+
"content-type": "application/json",
|
|
2233
|
+
accept: "application/json, text/event-stream",
|
|
2234
|
+
"user-agent": `digdesign-cli/${CLI_VERSION}`
|
|
2235
|
+
},
|
|
2236
|
+
body: JSON.stringify({
|
|
2237
|
+
jsonrpc: "2.0",
|
|
2238
|
+
id: 1,
|
|
2239
|
+
method: "tools/call",
|
|
2240
|
+
params: { name: "review_code", arguments: { files } }
|
|
2241
|
+
})
|
|
2242
|
+
});
|
|
2243
|
+
} catch (error) {
|
|
2244
|
+
throw new DigError(`\u0627\u062A\u0635\u0627\u0644 \u0628\u0647 ${url} \u0628\u0631\u0642\u0631\u0627\u0631 \u0646\u0634\u062F: ${error.message}`);
|
|
2245
|
+
}
|
|
2246
|
+
const body = await response.text();
|
|
2247
|
+
if (!response.ok) throw new DigError(`\u0628\u0627\u0632\u0628\u06CC\u0646\u06CC \u0634\u06A9\u0633\u062A \u062E\u0648\u0631\u062F (HTTP ${response.status}).`);
|
|
2248
|
+
const message = (response.headers.get("content-type") ?? "").includes("text/event-stream") ? parseSse(body)[0] : JSON.parse(body);
|
|
2249
|
+
const text = message?.result?.content?.[0]?.text;
|
|
2250
|
+
if (!text) throw new DigError("\u067E\u0627\u0633\u062E\u0650 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0632 \u0633\u0631\u0648\u0631 MCP.");
|
|
2251
|
+
return JSON.parse(text);
|
|
2252
|
+
}
|
|
2253
|
+
async function reviewCommand(paths, options) {
|
|
2254
|
+
if (paths.length === 0) throw new DigError("\u062D\u062F\u0627\u0642\u0644 \u06CC\u06A9 \u0645\u0633\u06CC\u0631 \u0641\u0627\u06CC\u0644 \u0628\u062F\u0647: dig review src/app/page.tsx");
|
|
2255
|
+
const url = options.url ?? process.env.DIG_MCP_URL ?? DEFAULT_MCP_URL;
|
|
2256
|
+
const files = await readReviewFiles(paths, process.cwd());
|
|
2257
|
+
const merged = [];
|
|
2258
|
+
const reviewed = [];
|
|
2259
|
+
for (let i = 0; i < files.length; i += MAX_FILES_PER_CALL) {
|
|
2260
|
+
const result = await callReview(url, files.slice(i, i + MAX_FILES_PER_CALL));
|
|
2261
|
+
if (result.status === "invalid_input") throw new DigError(result.message ?? "\u0648\u0631\u0648\u062F\u06CC\u0650 \u0628\u0627\u0632\u0628\u06CC\u0646\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A.");
|
|
2262
|
+
merged.push(...result.findings ?? []);
|
|
2263
|
+
reviewed.push(...result.reviewed ?? []);
|
|
2264
|
+
}
|
|
2265
|
+
const errors = merged.filter((finding) => finding.severity !== "warning").length;
|
|
2266
|
+
const status = errors > 0 ? "issues" : merged.length > 0 ? "warnings" : "ok";
|
|
2267
|
+
if (options.json) {
|
|
2268
|
+
console.log(JSON.stringify({ status, findings: merged, reviewed }, null, 2));
|
|
2269
|
+
} else {
|
|
2270
|
+
for (const finding of merged) {
|
|
2271
|
+
const where = `${finding.file ?? ""}:${finding.line}`;
|
|
2272
|
+
console.log(`${where} [${finding.severity ?? "error"}] ${finding.rule}
|
|
2273
|
+
${finding.found}
|
|
2274
|
+
\u2190 ${finding.fix}`);
|
|
2275
|
+
}
|
|
2276
|
+
const lines = reviewed.reduce((sum, file) => sum + file.lines, 0);
|
|
2277
|
+
console.log(merged.length === 0 ? `\u062A\u062E\u0644\u0641\u06CC \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F (${files.length} \u0641\u0627\u06CC\u0644\u060C ${lines} \u062E\u0637).` : `${merged.length} \u06CC\u0627\u0641\u062A\u0647 \u062F\u0631 ${files.length} \u0641\u0627\u06CC\u0644 (${lines} \u062E\u0637).`);
|
|
2278
|
+
}
|
|
2279
|
+
if (errors > 0) process.exitCode = 1;
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2057
2282
|
// src/commands/skill.ts
|
|
2058
2283
|
import { mkdir as mkdir4, writeFile as writeFile6 } from "fs/promises";
|
|
2059
|
-
import
|
|
2284
|
+
import path13 from "path";
|
|
2060
2285
|
var SKILL_PATH = ".claude/skills/dig-design-system/SKILL.md";
|
|
2061
2286
|
async function skillCommand() {
|
|
2062
2287
|
const cwd = process.cwd();
|
|
@@ -2069,21 +2294,21 @@ async function skillCommand() {
|
|
|
2069
2294
|
throw new DigError(`\u0627\u062A\u0635\u0627\u0644 \u0628\u0647 ${url.origin} \u0628\u0631\u0627\u06CC \u06AF\u0631\u0641\u062A\u0646\u0650 Skill \u0628\u0631\u0642\u0631\u0627\u0631 \u0646\u0634\u062F.`);
|
|
2070
2295
|
}
|
|
2071
2296
|
if (!response.ok) throw new DigError(`Skill \u06AF\u0631\u0641\u062A\u0647 \u0646\u0634\u062F: ${response.status}`);
|
|
2072
|
-
const target =
|
|
2073
|
-
await mkdir4(
|
|
2297
|
+
const target = path13.join(cwd, SKILL_PATH);
|
|
2298
|
+
await mkdir4(path13.dirname(target), { recursive: true });
|
|
2074
2299
|
await writeFile6(target, await response.text(), "utf8");
|
|
2075
2300
|
console.log(`\u2713 ${SKILL_PATH} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F (Skill\u0650 \u0631\u0633\u0645\u06CC\u0650 \u062F\u06CC\u06AF \u0628\u0631\u0627\u06CC Claude Code).`);
|
|
2076
2301
|
}
|
|
2077
2302
|
|
|
2078
2303
|
// src/commands/tokens.ts
|
|
2079
|
-
import { mkdir as mkdir5, readFile as
|
|
2080
|
-
import
|
|
2304
|
+
import { mkdir as mkdir5, readFile as readFile10, writeFile as writeFile7 } from "fs/promises";
|
|
2305
|
+
import path14 from "path";
|
|
2081
2306
|
async function tokensCommand(options) {
|
|
2082
2307
|
const cwd = process.cwd();
|
|
2083
2308
|
const registryUrl = await configExists(cwd) ? (await readConfig(cwd)).registryUrl : defaultConfig().registryUrl;
|
|
2084
|
-
const designMd = await
|
|
2309
|
+
const designMd = await readFile10(path14.join(cwd, DESIGN_MD_FILE), "utf8").catch(() => "");
|
|
2085
2310
|
const brand = options.brand ?? readDesignMdSettings(designMd).brand;
|
|
2086
|
-
const outDir =
|
|
2311
|
+
const outDir = path14.join(cwd, options.out ?? "tokens");
|
|
2087
2312
|
await mkdir5(outDir, { recursive: true });
|
|
2088
2313
|
for (const mode of ["light", "dark"]) {
|
|
2089
2314
|
const url = new URL("/api/tokens", registryUrl);
|
|
@@ -2099,20 +2324,20 @@ async function tokensCommand(options) {
|
|
|
2099
2324
|
const body = await response.json().catch(() => null);
|
|
2100
2325
|
throw new DigError(`\u062A\u0648\u06A9\u0646\u200C\u0647\u0627 \u06AF\u0631\u0641\u062A\u0647 \u0646\u0634\u062F: ${body?.error ?? response.status}`);
|
|
2101
2326
|
}
|
|
2102
|
-
const file =
|
|
2327
|
+
const file = path14.join(outDir, `dig-${mode}.tokens.json`);
|
|
2103
2328
|
await writeFile7(file, await response.text(), "utf8");
|
|
2104
|
-
console.log(`\u2713 ${
|
|
2329
|
+
console.log(`\u2713 ${path14.relative(cwd, file)} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F${brand ? ` (\u0628\u0631\u0646\u062F ${brand})` : ""}.`);
|
|
2105
2330
|
}
|
|
2106
2331
|
console.log("\n\u062F\u0631 \u0641\u06CC\u06AF\u0645\u0627 \u0647\u0631 \u0641\u0627\u06CC\u0644 \u0631\u0627 \u06CC\u06A9 mode \u0627\u0632 Variables \u0648\u0627\u0631\u062F \u06A9\u0646\u06CC\u062F (\u0631\u0648\u0634\u0646 \u0648 \u062A\u06CC\u0631\u0647).");
|
|
2107
2332
|
}
|
|
2108
2333
|
|
|
2109
2334
|
// src/commands/init.ts
|
|
2110
|
-
import { readFile as
|
|
2111
|
-
import
|
|
2335
|
+
import { readFile as readFile13, writeFile as writeFile11 } from "fs/promises";
|
|
2336
|
+
import path19 from "path";
|
|
2112
2337
|
|
|
2113
2338
|
// src/lib/agent-instructions.ts
|
|
2114
|
-
import { mkdir as mkdir6, readFile as
|
|
2115
|
-
import
|
|
2339
|
+
import { mkdir as mkdir6, readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
|
|
2340
|
+
import path15 from "path";
|
|
2116
2341
|
var AGENT_CLIENTS = [
|
|
2117
2342
|
{ value: "claude-code", label: "Claude Code (CLAUDE.md)" },
|
|
2118
2343
|
{ value: "codex", label: "Codex (AGENTS.md)" },
|
|
@@ -2164,25 +2389,94 @@ async function writeAgentInstructions(cwd, registryUrl, options) {
|
|
|
2164
2389
|
const files = await fetchSetupFiles(registryUrl, options);
|
|
2165
2390
|
const instructions = files.find((item) => /^[A-Z]+\.md$/.test(item.path) && item.path !== "DESIGN.md");
|
|
2166
2391
|
if (!instructions) throw new DigError("\u0641\u0627\u06CC\u0644\u0650 \u0627\u06CC\u062C\u0646\u062A \u062F\u0631 \u067E\u0627\u0633\u062E\u0650 \u0633\u0631\u0648\u0631 \u0646\u0628\u0648\u062F.");
|
|
2167
|
-
const target =
|
|
2168
|
-
const existing = await
|
|
2392
|
+
const target = path15.join(cwd, instructions.path);
|
|
2393
|
+
const existing = await readFile11(target, "utf8").catch(() => null);
|
|
2169
2394
|
await writeFile8(target, mergeInstructions(existing, instructions.content), "utf8");
|
|
2170
2395
|
console.log(
|
|
2171
2396
|
existing === null ? `\u2713 ${instructions.path} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F (\u0627\u06CC\u062C\u0646\u062A \u0641\u0648\u0646\u062A \u0648 \u0631\u0646\u06AF \u0631\u0627 \u0627\u0632 \u0627\u06CC\u0646 \u0641\u0627\u06CC\u0644 \u0645\u06CC\u200C\u062E\u0648\u0627\u0646\u062F \u0648 \u062F\u0648\u0628\u0627\u0631\u0647 \u0646\u0645\u06CC\u200C\u067E\u0631\u0633\u062F).` : `\u2713 \u0628\u062E\u0634\u0650 \u062F\u06CC\u06AF \u062F\u0631 ${instructions.path} \u0628\u0647\u200C\u0631\u0648\u0632 \u0634\u062F\u061B \u0628\u0642\u06CC\u0647\u0654 \u0641\u0627\u06CC\u0644 \u062F\u0633\u062A \u0646\u062E\u0648\u0631\u062F.`
|
|
2172
2397
|
);
|
|
2173
2398
|
const skill = files.find((item) => item.path.endsWith("/SKILL.md"));
|
|
2174
2399
|
if (skill) {
|
|
2175
|
-
const skillTarget =
|
|
2176
|
-
await mkdir6(
|
|
2400
|
+
const skillTarget = path15.join(cwd, skill.path);
|
|
2401
|
+
await mkdir6(path15.dirname(skillTarget), { recursive: true });
|
|
2177
2402
|
await writeFile8(skillTarget, skill.content, "utf8");
|
|
2178
2403
|
console.log(`\u2713 ${skill.path} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F (Skill\u0650 \u0631\u0633\u0645\u06CC\u0650 \u062F\u06CC\u06AF \u0628\u0631\u0627\u06CC \u0633\u0627\u062E\u062A\u0646\u0650 \u0635\u0641\u062D\u0647).`);
|
|
2179
2404
|
}
|
|
2180
2405
|
return instructions.path;
|
|
2181
2406
|
}
|
|
2182
2407
|
|
|
2183
|
-
// src/
|
|
2184
|
-
import {
|
|
2185
|
-
import
|
|
2408
|
+
// src/lib/fonts.ts
|
|
2409
|
+
import { mkdir as mkdir7, writeFile as writeFile9 } from "fs/promises";
|
|
2410
|
+
import path16 from "path";
|
|
2411
|
+
var WEIGHTS = [
|
|
2412
|
+
["Thin", 100],
|
|
2413
|
+
["UltraLight", 200],
|
|
2414
|
+
["Light", 300],
|
|
2415
|
+
["Regular", 400],
|
|
2416
|
+
["Medium", 500],
|
|
2417
|
+
["DemiBold", 600],
|
|
2418
|
+
["Bold", 700],
|
|
2419
|
+
["ExtraBold", 800],
|
|
2420
|
+
["Black", 900]
|
|
2421
|
+
];
|
|
2422
|
+
var YEKAN_WEIGHTS = [
|
|
2423
|
+
["Thin", 100],
|
|
2424
|
+
["Light", 300],
|
|
2425
|
+
["Regular", 400],
|
|
2426
|
+
["Medium", 500],
|
|
2427
|
+
["Bold", 700],
|
|
2428
|
+
["ExtraBold", 800],
|
|
2429
|
+
["Black", 900]
|
|
2430
|
+
];
|
|
2431
|
+
var VAZIR_URL = "https://cdn.jsdelivr.net/npm/@fontsource-variable/vazirmatn/files/vazirmatn-arabic-wght-normal.woff2";
|
|
2432
|
+
function fontPlan(font) {
|
|
2433
|
+
if (font === "yekan") {
|
|
2434
|
+
return { family: "IRANYekanFN", files: YEKAN_WEIGHTS.map(([n, weight]) => ({ file: `IRANYekan${n}.woff2`, weight })) };
|
|
2435
|
+
}
|
|
2436
|
+
if (font === "vazirmatn") return { family: "Vazirmatn", remote: VAZIR_URL };
|
|
2437
|
+
return { family: "IRANSansX", files: WEIGHTS.map(([n, weight]) => ({ file: `IRANSansXFaNum-${n}.woff2`, weight })) };
|
|
2438
|
+
}
|
|
2439
|
+
function fontFaceCss(font) {
|
|
2440
|
+
const plan = fontPlan(font);
|
|
2441
|
+
const header = "/* \u0641\u0648\u0646\u062A\u0650 \u0641\u0627\u0631\u0633\u06CC\u0650 \u067E\u0631\u0648\u0698\u0647\u061B \u0628\u0627 \xABdig init --font \u2026\xBB \u0633\u0627\u062E\u062A\u0647 \u0634\u062F. \u0644\u0646\u06AF\u0631 \u062F\u0631 brand.css \u06CC\u0627 dig-theme.css \u0627\u0633\u062A. */\n";
|
|
2442
|
+
if ("remote" in plan) {
|
|
2443
|
+
return `${header}@font-face {
|
|
2444
|
+
font-family: "${plan.family}";
|
|
2445
|
+
src: url("${plan.remote}") format("woff2");
|
|
2446
|
+
font-weight: 100 900;
|
|
2447
|
+
font-display: swap;
|
|
2448
|
+
}
|
|
2449
|
+
`;
|
|
2450
|
+
}
|
|
2451
|
+
return header + plan.files.map(
|
|
2452
|
+
({ file, weight }) => `@font-face {
|
|
2453
|
+
font-family: "${plan.family}";
|
|
2454
|
+
src: url("/fonts/${file}") format("woff2");
|
|
2455
|
+
font-weight: ${weight};
|
|
2456
|
+
font-display: swap;
|
|
2457
|
+
}
|
|
2458
|
+
`
|
|
2459
|
+
).join("");
|
|
2460
|
+
}
|
|
2461
|
+
async function installFonts(cwd, registryUrl, font, cssRel) {
|
|
2462
|
+
const plan = fontPlan(font);
|
|
2463
|
+
if ("files" in plan) {
|
|
2464
|
+
const dir = path16.join(cwd, "public", "fonts");
|
|
2465
|
+
await mkdir7(dir, { recursive: true });
|
|
2466
|
+
for (const { file } of plan.files) {
|
|
2467
|
+
const res = await fetch(new URL(`/fonts/${file}`, registryUrl));
|
|
2468
|
+
if (!res.ok) return false;
|
|
2469
|
+
await writeFile9(path16.join(dir, file), Buffer.from(await res.arrayBuffer()));
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
const target = path16.join(cwd, cssRel);
|
|
2473
|
+
await mkdir7(path16.dirname(target), { recursive: true });
|
|
2474
|
+
await writeFile9(target, fontFaceCss(font), "utf8");
|
|
2475
|
+
return true;
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
// src/lib/theme-files.ts
|
|
2479
|
+
import path17 from "path";
|
|
2186
2480
|
|
|
2187
2481
|
// src/lib/brand-ramp.ts
|
|
2188
2482
|
var RAMP_STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
|
|
@@ -2286,9 +2580,8 @@ function buildBrandRamp(hex) {
|
|
|
2286
2580
|
}
|
|
2287
2581
|
|
|
2288
2582
|
// src/lib/theme-files.ts
|
|
2289
|
-
import path14 from "path";
|
|
2290
2583
|
function importPath(fromFile, toFile) {
|
|
2291
|
-
const rel = toPosixPath(
|
|
2584
|
+
const rel = toPosixPath(path17.relative(path17.dirname(fromFile), toFile));
|
|
2292
2585
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
2293
2586
|
}
|
|
2294
2587
|
function globalsCss(paths, extraStatements = []) {
|
|
@@ -2450,10 +2743,12 @@ function splitTheme(projectCss, digThemeCss) {
|
|
|
2450
2743
|
}
|
|
2451
2744
|
|
|
2452
2745
|
// src/commands/theme.ts
|
|
2746
|
+
import { access as access5, mkdir as mkdir8, readFile as readFile12, writeFile as writeFile10 } from "fs/promises";
|
|
2747
|
+
import path18 from "path";
|
|
2453
2748
|
var THEME_ITEM = "theme";
|
|
2454
2749
|
async function readIfExists2(target) {
|
|
2455
2750
|
try {
|
|
2456
|
-
return await
|
|
2751
|
+
return await readFile12(target, "utf8");
|
|
2457
2752
|
} catch {
|
|
2458
2753
|
return null;
|
|
2459
2754
|
}
|
|
@@ -2467,7 +2762,7 @@ async function exists(target) {
|
|
|
2467
2762
|
}
|
|
2468
2763
|
}
|
|
2469
2764
|
function themePaths(config) {
|
|
2470
|
-
const dir =
|
|
2765
|
+
const dir = path18.posix.dirname(toPosixPath(config.tailwind.css));
|
|
2471
2766
|
return {
|
|
2472
2767
|
css: config.tailwind.css,
|
|
2473
2768
|
theme: config.tailwind.theme ?? `${dir}/dig-theme.css`,
|
|
@@ -2501,9 +2796,9 @@ async function recordTheme(cwd, config, themeRel, item) {
|
|
|
2501
2796
|
await writeLock(lock, cwd);
|
|
2502
2797
|
}
|
|
2503
2798
|
async function writeText(cwd, rel, content) {
|
|
2504
|
-
const target =
|
|
2505
|
-
await
|
|
2506
|
-
await
|
|
2799
|
+
const target = path18.join(cwd, rel);
|
|
2800
|
+
await mkdir8(path18.dirname(target), { recursive: true });
|
|
2801
|
+
await writeFile10(target, content, "utf8");
|
|
2507
2802
|
}
|
|
2508
2803
|
async function installTheme(cwd, config, options) {
|
|
2509
2804
|
const paths = themePaths(config);
|
|
@@ -2511,7 +2806,7 @@ async function installTheme(cwd, config, options) {
|
|
|
2511
2806
|
const ramp = options.brand ? buildBrandRamp(options.brand) : void 0;
|
|
2512
2807
|
await writeText(cwd, paths.theme, item.content);
|
|
2513
2808
|
console.log(`\u2713 \u062A\u0645\u0650 \u062F\u06CC\u06AF \u062F\u0631 ${paths.theme} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F (\u0645\u0627\u0644\u0650 \u062F\u06CC\u06AF\u061B \u0628\u0627 \xABdig update theme\xBB \u0628\u0647\u200C\u0631\u0648\u0632 \u0645\u06CC\u200C\u0634\u0648\u062F).`);
|
|
2514
|
-
const existingBrand = await readIfExists2(
|
|
2809
|
+
const existingBrand = await readIfExists2(path18.join(cwd, paths.brand));
|
|
2515
2810
|
if (existingBrand === null) {
|
|
2516
2811
|
await writeText(cwd, paths.brand, brandCss({ ramp, hex: options.brand }));
|
|
2517
2812
|
console.log(`\u2713 ${paths.brand} \u0633\u0627\u062E\u062A\u0647 \u0634\u062F (\u0645\u0627\u0644\u0650 \u067E\u0631\u0648\u0698\u0647\u061B \u062F\u06CC\u06AF \u0647\u0631\u06AF\u0632 \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC\u200C\u0627\u0634 \u0646\u0645\u06CC\u200C\u06A9\u0646\u062F).`);
|
|
@@ -2522,10 +2817,10 @@ async function installTheme(cwd, config, options) {
|
|
|
2522
2817
|
console.log(`- ${paths.brand} \u0627\u0632 \u0642\u0628\u0644 \u0647\u0633\u062A\u061B \u062F\u0633\u062A \u0646\u062E\u0648\u0631\u062F.`);
|
|
2523
2818
|
}
|
|
2524
2819
|
if (ramp) reportContrast(ramp.primaryContrast);
|
|
2525
|
-
const cssTarget =
|
|
2820
|
+
const cssTarget = path18.join(cwd, paths.css);
|
|
2526
2821
|
const existingCss = await readIfExists2(cssTarget);
|
|
2527
2822
|
if (!isBoilerplateCss(existingCss) && existingCss !== null) {
|
|
2528
|
-
await
|
|
2823
|
+
await writeFile10(`${cssTarget}.backup`, existingCss, "utf8");
|
|
2529
2824
|
console.log(` \u0646\u0633\u062E\u0647\u0654 \u0642\u0628\u0644\u06CC\u0650 ${paths.css} \u062F\u0631 ${paths.css}.backup \u0646\u06AF\u0647 \u062F\u0627\u0634\u062A\u0647 \u0634\u062F.`);
|
|
2530
2825
|
console.log(` \u0627\u06AF\u0631 \u0622\u0646 \u0641\u0627\u06CC\u0644 \u062A\u0645\u0650 \u0642\u062F\u06CC\u0645\u06CC\u0650 \u062F\u06CC\u06AF \u0628\u0648\u062F\u060C \u0628\u0647\u200C\u062C\u0627\u06CC init \u0627\u0632 \xABdig theme split\xBB \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u06A9\u0646\u06CC\u062F.`);
|
|
2531
2826
|
}
|
|
@@ -2558,11 +2853,11 @@ async function themeSplitCommand(options) {
|
|
|
2558
2853
|
return;
|
|
2559
2854
|
}
|
|
2560
2855
|
const paths = themePaths(config);
|
|
2561
|
-
const current = await readIfExists2(
|
|
2856
|
+
const current = await readIfExists2(path18.join(cwd, paths.css));
|
|
2562
2857
|
if (current === null || isBoilerplateCss(current)) {
|
|
2563
2858
|
throw new DigError(`${paths.css} \u062A\u0645\u06CC \u0646\u062F\u0627\u0631\u062F\u061B \u0628\u0631\u0627\u06CC \u0646\u0635\u0628\u0650 \u062A\u0627\u0632\u0647 \xABdig init\xBB \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F.`);
|
|
2564
2859
|
}
|
|
2565
|
-
if (await exists(
|
|
2860
|
+
if (await exists(path18.join(cwd, paths.brand))) {
|
|
2566
2861
|
throw new DigError(`${paths.brand} \u0627\u0632 \u0642\u0628\u0644 \u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F\u061B \u0628\u0631\u0627\u06CC \u062C\u0644\u0648\u06AF\u06CC\u0631\u06CC \u0627\u0632 \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC\u060C \u0627\u0648\u0644 \u062C\u0627\u0628\u0647\u200C\u062C\u0627\u06CC\u0634 \u06A9\u0646\u06CC\u062F.`);
|
|
2567
2862
|
}
|
|
2568
2863
|
console.log(`\u062F\u0631\u06CC\u0627\u0641\u062A\u0650 \u062A\u0645\u0650 \u062F\u06CC\u06AF \u0627\u0632 ${config.registryUrl} \u2026`);
|
|
@@ -2581,7 +2876,7 @@ ${split.changed} \u062A\u0648\u06A9\u0646\u0650 \u0639\u0648\u0636\u200C\u0634\u
|
|
|
2581
2876
|
console.log(split.body || "(\u062E\u0627\u0644\u06CC: \u067E\u0631\u0648\u0698\u0647 \u0686\u06CC\u0632\u06CC \u062F\u0631 \u062A\u0645 \u0639\u0648\u0636 \u0646\u06A9\u0631\u062F\u0647)");
|
|
2582
2877
|
return;
|
|
2583
2878
|
}
|
|
2584
|
-
await
|
|
2879
|
+
await writeFile10(path18.join(cwd, `${paths.css}.backup`), current, "utf8");
|
|
2585
2880
|
await writeText(cwd, paths.theme, item.content);
|
|
2586
2881
|
await writeText(cwd, paths.brand, brandCss({ body: split.body }));
|
|
2587
2882
|
await writeText(cwd, paths.css, globalsCss(paths, split.extraStatements));
|
|
@@ -2605,7 +2900,7 @@ async function themeBrandCommand(hex) {
|
|
|
2605
2900
|
}
|
|
2606
2901
|
const paths = themePaths(config);
|
|
2607
2902
|
const ramp = buildBrandRamp(hex);
|
|
2608
|
-
const existing = await readIfExists2(
|
|
2903
|
+
const existing = await readIfExists2(path18.join(cwd, paths.brand)) ?? brandCss({});
|
|
2609
2904
|
const next = replaceBrandRamp(existing, ramp, hex);
|
|
2610
2905
|
await writeText(cwd, paths.brand, next);
|
|
2611
2906
|
console.log(`\u2713 \u0631\u0645\u067E\u0650 \u0628\u0631\u0646\u062F \u062F\u0631 ${paths.brand} \u0627\u0632 ${hex} \u0633\u0627\u062E\u062A\u0647 \u0634\u062F (Shade \u06F6\u06F0\u06F0 = \u0631\u0646\u06AF\u0650 \u062F\u0627\u062F\u0647\u200C\u0634\u062F\u0647).`);
|
|
@@ -2623,14 +2918,14 @@ async function themeBrandCommand(hex) {
|
|
|
2623
2918
|
// src/commands/init.ts
|
|
2624
2919
|
async function readIfExists3(target) {
|
|
2625
2920
|
try {
|
|
2626
|
-
return await
|
|
2921
|
+
return await readFile13(target, "utf8");
|
|
2627
2922
|
} catch {
|
|
2628
2923
|
return null;
|
|
2629
2924
|
}
|
|
2630
2925
|
}
|
|
2631
2926
|
async function writeTheme(config, cwd, options) {
|
|
2632
2927
|
const paths = themePaths(config);
|
|
2633
|
-
const existing = await readIfExists3(
|
|
2928
|
+
const existing = await readIfExists3(path19.join(cwd, paths.css));
|
|
2634
2929
|
if (!isBoilerplateCss(existing) && !options.yes) {
|
|
2635
2930
|
console.log(`
|
|
2636
2931
|
${paths.css} \u0627\u0632 \u0642\u0628\u0644 \u0645\u062D\u062A\u0648\u0627 \u062F\u0627\u0631\u062F.`);
|
|
@@ -2651,11 +2946,37 @@ ${paths.css} \u0627\u0632 \u0642\u0628\u0644 \u0645\u062D\u062A\u0648\u0627 \u06
|
|
|
2651
2946
|
\u0646\u0635\u0628 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627\u06CC \u062A\u0645 \u0628\u0627 ${pm} \u2026`);
|
|
2652
2947
|
await installDependencies(pm, item.dependencies, { cwd });
|
|
2653
2948
|
}
|
|
2949
|
+
await writeFonts(cwd, config, paths, options.font);
|
|
2654
2950
|
return "written";
|
|
2655
2951
|
}
|
|
2952
|
+
async function writeFonts(cwd, config, paths, font) {
|
|
2953
|
+
const fontsCss = `${path19.posix.dirname(paths.css.split(path19.sep).join("/"))}/dig-fonts.css`;
|
|
2954
|
+
const ok = await installFonts(cwd, config.registryUrl, font, fontsCss).catch(() => false);
|
|
2955
|
+
if (!ok) {
|
|
2956
|
+
console.log("- \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0641\u0648\u0646\u062A \u062F\u0627\u0646\u0644\u0648\u062F \u0646\u0634\u062F\u061B \u0628\u0639\u062F\u0627\u064B \xABdig init\xBB \u0631\u0627 \u062F\u0648\u0628\u0627\u0631\u0647 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F (\u062A\u0627 \u0622\u0646 \u0645\u0648\u0642\u0639 \u0641\u0648\u0646\u062A\u0650 \u0633\u06CC\u0633\u062A\u0645 \u0645\u06CC\u200C\u0646\u0634\u06CC\u0646\u062F).");
|
|
2957
|
+
return;
|
|
2958
|
+
}
|
|
2959
|
+
const fontStatement = `@import "${importPath(paths.css, fontsCss)}";`;
|
|
2960
|
+
await writeFile11(path19.join(cwd, paths.css), globalsCss(paths, [fontStatement]), "utf8");
|
|
2961
|
+
console.log(`\u2713 \u0641\u0648\u0646\u062A\u0650 ${fontPlan(font).family} \u062F\u0631 ${fontsCss} \u0648 public/fonts \u0646\u0635\u0628 \u0634\u062F.`);
|
|
2962
|
+
if (font && font !== "iransans") {
|
|
2963
|
+
const brandPath = path19.join(cwd, paths.brand);
|
|
2964
|
+
const brand = await readIfExists3(brandPath) ?? "";
|
|
2965
|
+
await writeFile11(
|
|
2966
|
+
brandPath,
|
|
2967
|
+
`${brand.trimEnd()}
|
|
2968
|
+
|
|
2969
|
+
:root {
|
|
2970
|
+
--font-anchor: "${fontPlan(font).family}";
|
|
2971
|
+
}
|
|
2972
|
+
`,
|
|
2973
|
+
"utf8"
|
|
2974
|
+
);
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2656
2977
|
async function applyRtl(cwd) {
|
|
2657
2978
|
for (const candidate of ["src/app/layout.tsx", "app/layout.tsx"]) {
|
|
2658
|
-
const target =
|
|
2979
|
+
const target = path19.join(cwd, candidate);
|
|
2659
2980
|
const source = await readIfExists3(target);
|
|
2660
2981
|
if (source === null) continue;
|
|
2661
2982
|
if (/<html[^>]*\sdir=/.test(source)) {
|
|
@@ -2668,7 +2989,7 @@ async function applyRtl(cwd) {
|
|
|
2668
2989
|
match[0],
|
|
2669
2990
|
`<html${match[1]} dir="rtl">`.replace(/\s+dir="rtl">$/, ' dir="rtl">')
|
|
2670
2991
|
);
|
|
2671
|
-
await
|
|
2992
|
+
await writeFile11(target, updated, "utf8");
|
|
2672
2993
|
console.log(`\u2713 dir="rtl" \u0628\u0647 ${candidate} \u0627\u0636\u0627\u0641\u0647 \u0634\u062F.`);
|
|
2673
2994
|
return;
|
|
2674
2995
|
}
|
|
@@ -2687,7 +3008,14 @@ async function askProjectDecisions(options) {
|
|
|
2687
3008
|
}
|
|
2688
3009
|
async function initCommand(options) {
|
|
2689
3010
|
const cwd = process.cwd();
|
|
3011
|
+
const framework = await resolveFramework(cwd, void 0, options.framework);
|
|
2690
3012
|
const base = defaultConfig();
|
|
3013
|
+
if (framework === "vue") {
|
|
3014
|
+
const css = await readIfExists3(path19.join(cwd, "src/assets/main.css")) !== null ? "src/assets/main.css" : "src/style.css";
|
|
3015
|
+
const dir = path19.posix.dirname(css);
|
|
3016
|
+
base.framework = "vue";
|
|
3017
|
+
base.tailwind = { ...base.tailwind, css, theme: `${dir}/dig-theme.css`, brand: `${dir}/brand.css` };
|
|
3018
|
+
}
|
|
2691
3019
|
if (await configExists(cwd)) {
|
|
2692
3020
|
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);
|
|
2693
3021
|
if (!overwrite) {
|
|
@@ -2700,16 +3028,18 @@ async function initCommand(options) {
|
|
|
2700
3028
|
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),
|
|
2701
3029
|
tailwind: await (async () => {
|
|
2702
3030
|
const css = await ask("\u0645\u0633\u06CC\u0631 \u0641\u0627\u06CC\u0644 CSS \u0627\u0635\u0644\u06CC \u062A\u06CC\u0644\u0648\u06CC\u0646\u062F\u061F", base.tailwind.css);
|
|
2703
|
-
const dir =
|
|
3031
|
+
const dir = path19.posix.dirname(css.split(path19.sep).join("/"));
|
|
2704
3032
|
return { ...base.tailwind, css, theme: `${dir}/dig-theme.css`, brand: `${dir}/brand.css` };
|
|
2705
3033
|
})()
|
|
2706
3034
|
};
|
|
2707
3035
|
const decisions = await askProjectDecisions(options);
|
|
2708
3036
|
options = { ...options, brand: decisions.brand };
|
|
2709
3037
|
await writeConfig(config, cwd);
|
|
2710
|
-
console.log(`\u2713 ${
|
|
2711
|
-
const theme2 = await writeTheme(config, cwd, options);
|
|
2712
|
-
if (config.rtl) {
|
|
3038
|
+
console.log(`\u2713 ${path19.join(cwd, "dig.json")} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F.`);
|
|
3039
|
+
const theme2 = await writeTheme(config, cwd, { ...options, font: decisions.font });
|
|
3040
|
+
if (config.rtl && framework === "vue") {
|
|
3041
|
+
console.log('- dir="rtl" \u0631\u0627 \u062E\u0648\u062F\u062A\u0627\u0646 \u0631\u0648\u06CC \u062A\u06AF <html> \u062F\u0631 index.html \u0628\u06AF\u0630\u0627\u0631\u06CC\u062F (Vue \u0686\u06CC\u062F\u0645\u0627\u0646\u0650 \u0631\u06CC\u0634\u0647\u200C\u0627\u06CC \u0646\u062F\u0627\u0631\u062F \u06A9\u0647 \u062E\u0648\u062F\u06A9\u0627\u0631 \u062F\u0633\u062A \u0628\u062E\u0648\u0631\u062F).');
|
|
3042
|
+
} else if (config.rtl) {
|
|
2713
3043
|
await applyRtl(cwd);
|
|
2714
3044
|
}
|
|
2715
3045
|
await writeDesignMd(cwd, config.registryUrl, { brand: decisions.brand, font: decisions.font }).catch((error) => {
|
|
@@ -2731,102 +3061,9 @@ async function initCommand(options) {
|
|
|
2731
3061
|
}
|
|
2732
3062
|
}
|
|
2733
3063
|
|
|
2734
|
-
// src/commands/mcp.ts
|
|
2735
|
-
import { createInterface as createInterface2 } from "readline";
|
|
2736
|
-
|
|
2737
|
-
// src/lib/version.ts
|
|
2738
|
-
var CLI_VERSION = "0.6.2";
|
|
2739
|
-
|
|
2740
|
-
// src/commands/mcp.ts
|
|
2741
|
-
var DEFAULT_MCP_URL = "https://docs.digdesign.ir/api/mcp";
|
|
2742
|
-
function parseSse(body) {
|
|
2743
|
-
const messages = [];
|
|
2744
|
-
for (const event of body.split(/\r?\n\r?\n/)) {
|
|
2745
|
-
const data = event.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).replace(/^ /, "")).join("\n");
|
|
2746
|
-
if (!data) continue;
|
|
2747
|
-
try {
|
|
2748
|
-
messages.push(JSON.parse(data));
|
|
2749
|
-
} catch {
|
|
2750
|
-
}
|
|
2751
|
-
}
|
|
2752
|
-
return messages;
|
|
2753
|
-
}
|
|
2754
|
-
function requestIds(message) {
|
|
2755
|
-
const list = Array.isArray(message) ? message : [message];
|
|
2756
|
-
return list.filter((item) => typeof item === "object" && item !== null).filter((item) => item.method !== void 0 && item.id !== void 0 && item.id !== null).map((item) => item.id);
|
|
2757
|
-
}
|
|
2758
|
-
function createBridge(url, fetchImpl = fetch) {
|
|
2759
|
-
let sessionId;
|
|
2760
|
-
let protocolVersion;
|
|
2761
|
-
return {
|
|
2762
|
-
async handle(line) {
|
|
2763
|
-
let message;
|
|
2764
|
-
try {
|
|
2765
|
-
message = JSON.parse(line);
|
|
2766
|
-
} catch {
|
|
2767
|
-
return [{ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } }];
|
|
2768
|
-
}
|
|
2769
|
-
const ids = requestIds(message);
|
|
2770
|
-
const fail = (reason) => ids.map((id) => ({ jsonrpc: "2.0", id, error: { code: -32603, message: `Dig MCP (${url}): ${reason}` } }));
|
|
2771
|
-
let response;
|
|
2772
|
-
try {
|
|
2773
|
-
response = await fetchImpl(url, {
|
|
2774
|
-
method: "POST",
|
|
2775
|
-
headers: {
|
|
2776
|
-
"content-type": "application/json",
|
|
2777
|
-
accept: "application/json, text/event-stream",
|
|
2778
|
-
"user-agent": `digdesign-cli/${CLI_VERSION}`,
|
|
2779
|
-
...sessionId && { "mcp-session-id": sessionId },
|
|
2780
|
-
...protocolVersion && { "mcp-protocol-version": protocolVersion }
|
|
2781
|
-
},
|
|
2782
|
-
body: line
|
|
2783
|
-
});
|
|
2784
|
-
} catch (error) {
|
|
2785
|
-
return fail(`network error: ${error.message}`);
|
|
2786
|
-
}
|
|
2787
|
-
sessionId = response.headers.get("mcp-session-id") ?? sessionId;
|
|
2788
|
-
if (response.status === 202) return [];
|
|
2789
|
-
const body = await response.text();
|
|
2790
|
-
if (!response.ok) return fail(`HTTP ${response.status} ${body.slice(0, 200)}`);
|
|
2791
|
-
let replies;
|
|
2792
|
-
if ((response.headers.get("content-type") ?? "").includes("text/event-stream")) replies = parseSse(body);
|
|
2793
|
-
else {
|
|
2794
|
-
try {
|
|
2795
|
-
replies = body.trim() ? [JSON.parse(body)] : [];
|
|
2796
|
-
} catch {
|
|
2797
|
-
return fail(`invalid JSON response: ${body.slice(0, 200)}`);
|
|
2798
|
-
}
|
|
2799
|
-
}
|
|
2800
|
-
for (const reply of replies.flat()) {
|
|
2801
|
-
const result = reply?.result;
|
|
2802
|
-
if (result?.protocolVersion) protocolVersion = result.protocolVersion;
|
|
2803
|
-
}
|
|
2804
|
-
return replies;
|
|
2805
|
-
}
|
|
2806
|
-
};
|
|
2807
|
-
}
|
|
2808
|
-
async function mcpCommand(options) {
|
|
2809
|
-
const url = options.url ?? process.env.DIG_MCP_URL ?? DEFAULT_MCP_URL;
|
|
2810
|
-
const bridge = createBridge(url);
|
|
2811
|
-
const pending = /* @__PURE__ */ new Set();
|
|
2812
|
-
process.stderr.write(`dig mcp: stdio bridge to ${url}
|
|
2813
|
-
`);
|
|
2814
|
-
const input = createInterface2({ input: process.stdin, crlfDelay: Infinity });
|
|
2815
|
-
for await (const line of input) {
|
|
2816
|
-
if (!line.trim()) continue;
|
|
2817
|
-
const task = bridge.handle(line).then((replies) => {
|
|
2818
|
-
for (const reply of replies) process.stdout.write(`${JSON.stringify(reply)}
|
|
2819
|
-
`);
|
|
2820
|
-
});
|
|
2821
|
-
pending.add(task);
|
|
2822
|
-
void task.finally(() => pending.delete(task));
|
|
2823
|
-
}
|
|
2824
|
-
await Promise.all(pending);
|
|
2825
|
-
}
|
|
2826
|
-
|
|
2827
3064
|
// src/lib/migration-apply.ts
|
|
2828
|
-
import { readFile as
|
|
2829
|
-
import
|
|
3065
|
+
import { readFile as readFile14, writeFile as writeFile12 } from "fs/promises";
|
|
3066
|
+
import path20 from "path";
|
|
2830
3067
|
|
|
2831
3068
|
// src/lib/migration-strategy.ts
|
|
2832
3069
|
var TextReplaceStrategy = class {
|
|
@@ -2903,7 +3140,7 @@ function looksBinary2(buffer) {
|
|
|
2903
3140
|
async function readTextFileSafely2(target) {
|
|
2904
3141
|
let buffer;
|
|
2905
3142
|
try {
|
|
2906
|
-
buffer = await
|
|
3143
|
+
buffer = await readFile14(target);
|
|
2907
3144
|
} catch (error) {
|
|
2908
3145
|
if (error.code === "ENOENT") return void 0;
|
|
2909
3146
|
throw error;
|
|
@@ -2948,8 +3185,8 @@ async function planOrApplyMigrations(params) {
|
|
|
2948
3185
|
};
|
|
2949
3186
|
}
|
|
2950
3187
|
const fileTargets = registryItem.files.map((file) => {
|
|
2951
|
-
const target =
|
|
2952
|
-
return { relPath: toPosixPath(
|
|
3188
|
+
const target = path20.join(cwd, resolveTargetPath(file.path, aliases, paths));
|
|
3189
|
+
return { relPath: toPosixPath(path20.relative(cwd, target)), target };
|
|
2953
3190
|
});
|
|
2954
3191
|
const currentContents = /* @__PURE__ */ new Map();
|
|
2955
3192
|
for (const ft of fileTargets) {
|
|
@@ -3015,12 +3252,12 @@ async function planOrApplyMigrations(params) {
|
|
|
3015
3252
|
const written = [];
|
|
3016
3253
|
try {
|
|
3017
3254
|
for (const item of pending) {
|
|
3018
|
-
await
|
|
3255
|
+
await writeFile12(item.target, item.newContent, "utf8");
|
|
3019
3256
|
written.push(item);
|
|
3020
3257
|
}
|
|
3021
3258
|
} catch (error) {
|
|
3022
3259
|
for (const item of written) {
|
|
3023
|
-
await
|
|
3260
|
+
await writeFile12(item.target, item.originalContent, "utf8");
|
|
3024
3261
|
}
|
|
3025
3262
|
return {
|
|
3026
3263
|
name,
|
|
@@ -3078,7 +3315,7 @@ async function runPlan(components, options) {
|
|
|
3078
3315
|
continue;
|
|
3079
3316
|
}
|
|
3080
3317
|
try {
|
|
3081
|
-
const item = await fetchRegistryItem(name, config.registryUrl);
|
|
3318
|
+
const item = await fetchRegistryItem(name, config.registryUrl, config.framework);
|
|
3082
3319
|
const toVersion = item.version ?? "0.0.0";
|
|
3083
3320
|
const migrations = getApplicableMigrations(
|
|
3084
3321
|
parseMigrations(name, item.migrations),
|
|
@@ -3149,7 +3386,7 @@ async function runApplyOrDryRun(components, options, dryRun) {
|
|
|
3149
3386
|
for (const name of components) {
|
|
3150
3387
|
let registryItem;
|
|
3151
3388
|
try {
|
|
3152
|
-
registryItem = await fetchRegistryItem(name, config.registryUrl);
|
|
3389
|
+
registryItem = await fetchRegistryItem(name, config.registryUrl, config.framework);
|
|
3153
3390
|
} catch (error) {
|
|
3154
3391
|
console.error(`\u2717 ${name}: ${error.message}`);
|
|
3155
3392
|
continue;
|
|
@@ -3344,7 +3581,7 @@ ${message}`);
|
|
|
3344
3581
|
}
|
|
3345
3582
|
let item;
|
|
3346
3583
|
try {
|
|
3347
|
-
item = await fetchRegistryItem(component, config.registryUrl);
|
|
3584
|
+
item = await fetchRegistryItem(component, config.registryUrl, config.framework);
|
|
3348
3585
|
} catch (error) {
|
|
3349
3586
|
console.error(`\u2717 ${component}: ${error.message}`);
|
|
3350
3587
|
return;
|
|
@@ -3432,7 +3669,7 @@ async function releaseCommand(components, options) {
|
|
|
3432
3669
|
const entries = [];
|
|
3433
3670
|
for (const name of components) {
|
|
3434
3671
|
try {
|
|
3435
|
-
const item = await fetchRegistryItem(name, config.registryUrl);
|
|
3672
|
+
const item = await fetchRegistryItem(name, config.registryUrl, config.framework);
|
|
3436
3673
|
const version = item.version ?? "0.0.0";
|
|
3437
3674
|
entries.push({
|
|
3438
3675
|
name,
|
|
@@ -3539,7 +3776,7 @@ async function statusCommand(components, options) {
|
|
|
3539
3776
|
const entries = [];
|
|
3540
3777
|
for (const name of components) {
|
|
3541
3778
|
try {
|
|
3542
|
-
const item = await fetchRegistryItem(name, config.registryUrl);
|
|
3779
|
+
const item = await fetchRegistryItem(name, config.registryUrl, config.framework);
|
|
3543
3780
|
const version = item.version ?? "0.0.0";
|
|
3544
3781
|
const lifecycle = parseLifecycle(name, version, item.deprecation);
|
|
3545
3782
|
const release = parseRelease(name, version, item.release);
|
|
@@ -3633,7 +3870,7 @@ ${entry.migrationHint}`);
|
|
|
3633
3870
|
|
|
3634
3871
|
// src/lib/missing-dependencies.ts
|
|
3635
3872
|
import { access as access6 } from "fs/promises";
|
|
3636
|
-
import
|
|
3873
|
+
import path21 from "path";
|
|
3637
3874
|
async function exists2(target) {
|
|
3638
3875
|
try {
|
|
3639
3876
|
await access6(target);
|
|
@@ -3649,12 +3886,12 @@ async function findMissingDependencies(params) {
|
|
|
3649
3886
|
...new Set(items.flatMap((item) => item.registryDependencies ?? []))
|
|
3650
3887
|
].filter((name) => !requested.has(name));
|
|
3651
3888
|
if (direct.length === 0) return [];
|
|
3652
|
-
const tree = await resolveRegistryTree(direct, config.registryUrl);
|
|
3889
|
+
const tree = await resolveRegistryTree(direct, config.registryUrl, config.framework);
|
|
3653
3890
|
const missing = [];
|
|
3654
3891
|
for (const item of tree.values()) {
|
|
3655
3892
|
if (requested.has(item.name) || lock.components[item.name]) continue;
|
|
3656
3893
|
for (const file of item.files) {
|
|
3657
|
-
const target =
|
|
3894
|
+
const target = path21.join(
|
|
3658
3895
|
cwd,
|
|
3659
3896
|
resolveTargetPath(file.path, config.aliases, targetPathOptions(config))
|
|
3660
3897
|
);
|
|
@@ -3668,8 +3905,8 @@ async function findMissingDependencies(params) {
|
|
|
3668
3905
|
}
|
|
3669
3906
|
|
|
3670
3907
|
// src/lib/update.ts
|
|
3671
|
-
import { mkdir as
|
|
3672
|
-
import
|
|
3908
|
+
import { mkdir as mkdir9, readFile as readFile15, rm as rm2, writeFile as writeFile13 } from "fs/promises";
|
|
3909
|
+
import path22 from "path";
|
|
3673
3910
|
function safeFileIndexes(drift) {
|
|
3674
3911
|
const indexes = [];
|
|
3675
3912
|
drift.files.forEach((file, index) => {
|
|
@@ -3689,7 +3926,7 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun, paths)
|
|
|
3689
3926
|
for (const index of safeIndexes) {
|
|
3690
3927
|
const file = drift.files[index];
|
|
3691
3928
|
const registryFile = registryItem.files[index];
|
|
3692
|
-
const target =
|
|
3929
|
+
const target = path22.join(cwd, resolveTargetPath(registryFile.path, aliases, paths));
|
|
3693
3930
|
const isNewFile = file.status === "new_upstream";
|
|
3694
3931
|
pending.push({
|
|
3695
3932
|
index,
|
|
@@ -3697,14 +3934,14 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun, paths)
|
|
|
3697
3934
|
newContent: registryFile.content,
|
|
3698
3935
|
newHash: registryFile.hash ?? hashContent(registryFile.content),
|
|
3699
3936
|
isNewFile,
|
|
3700
|
-
originalContent: isNewFile ? void 0 : await
|
|
3937
|
+
originalContent: isNewFile ? void 0 : await readFile15(target, "utf8")
|
|
3701
3938
|
});
|
|
3702
3939
|
}
|
|
3703
3940
|
const written = [];
|
|
3704
3941
|
try {
|
|
3705
3942
|
for (const item of pending) {
|
|
3706
|
-
if (item.isNewFile) await
|
|
3707
|
-
await
|
|
3943
|
+
if (item.isNewFile) await mkdir9(path22.dirname(item.target), { recursive: true });
|
|
3944
|
+
await writeFile13(item.target, item.newContent, "utf8");
|
|
3708
3945
|
written.push(item);
|
|
3709
3946
|
}
|
|
3710
3947
|
} catch (error) {
|
|
@@ -3712,7 +3949,7 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun, paths)
|
|
|
3712
3949
|
if (item.isNewFile) {
|
|
3713
3950
|
await rm2(item.target, { force: true });
|
|
3714
3951
|
} else {
|
|
3715
|
-
await
|
|
3952
|
+
await writeFile13(item.target, item.originalContent, "utf8");
|
|
3716
3953
|
}
|
|
3717
3954
|
}
|
|
3718
3955
|
return { actions, writtenHashes: {}, rollbackError: error.message };
|
|
@@ -3806,7 +4043,7 @@ async function updateCommand(components, options) {
|
|
|
3806
4043
|
for (const name of components) {
|
|
3807
4044
|
let registryItem;
|
|
3808
4045
|
try {
|
|
3809
|
-
registryItem = await fetchRegistryItem(name, config.registryUrl);
|
|
4046
|
+
registryItem = await fetchRegistryItem(name, config.registryUrl, config.framework);
|
|
3810
4047
|
} catch (error) {
|
|
3811
4048
|
console.error(`\u2717 ${name}: ${error.message}`);
|
|
3812
4049
|
continue;
|
|
@@ -4040,15 +4277,16 @@ var program = new Command();
|
|
|
4040
4277
|
program.name("dig").description("CLI \u0631\u0633\u0645\u06CC \u062F\u06CC\u0632\u0627\u06CC\u0646 \u0633\u06CC\u0633\u062A\u0645 \u062F\u06CC\u06AF").version(CLI_VERSION);
|
|
4041
4278
|
program.command("init").description(
|
|
4042
4279
|
'\u0631\u0627\u0647\u200C\u0627\u0646\u062F\u0627\u0632\u06CC \u062F\u06CC\u06AF \u062F\u0631 \u067E\u0631\u0648\u0698\u0647: dig.json\u060C \u062A\u0645 \u0648 \u062A\u0648\u06A9\u0646\u200C\u0647\u0627\u060C dir="rtl"\u060C DESIGN.md \u0648 \u0641\u0627\u06CC\u0644\u0650 \u062F\u0633\u062A\u0648\u0631\u0650 \u06A9\u0627\u0631\u06CC\u0650 \u0627\u06CC\u062C\u0646\u062A'
|
|
4043
|
-
).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).option("--brand <hex>", "\u0633\u0627\u062E\u062A\u0646\u0650 \u0631\u0645\u067E\u0650 \u0628\u0631\u0646\u062F \u0627\u0632 \u06CC\u06A9 \u0631\u0646\u06AF (\u0631\u0648\u06CC Shade \u06F6\u06F0\u06F0 \u0645\u06CC\u200C\u0646\u0634\u06CC\u0646\u062F)\u060C \u0645\u062B\u0644 #2C4C5B").option("--font <font>", "\u0641\u0648\u0646\u062A: iransans\u060C yekan \u06CC\u0627 vazirmatn").option("--agent <agent>", "\u0627\u06CC\u062C\u0646\u062A: claude-code\u060C codex\u060C cursor\u060C vscode \u06CC\u0627 gemini (\u0646\u0627\u0645\u0650 \u0641\u0627\u06CC\u0644\u0650 \u062F\u0633\u062A\u0648\u0631\u0650 \u06A9\u0627\u0631\u06CC \u0631\u0627 \u062A\u0639\u06CC\u06CC\u0646 \u0645\u06CC\u200C\u06A9\u0646\u062F)").action(initCommand);
|
|
4280
|
+
).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).option("--brand <hex>", "\u0633\u0627\u062E\u062A\u0646\u0650 \u0631\u0645\u067E\u0650 \u0628\u0631\u0646\u062F \u0627\u0632 \u06CC\u06A9 \u0631\u0646\u06AF (\u0631\u0648\u06CC Shade \u06F6\u06F0\u06F0 \u0645\u06CC\u200C\u0646\u0634\u06CC\u0646\u062F)\u060C \u0645\u062B\u0644 #2C4C5B").option("--font <font>", "\u0641\u0648\u0646\u062A: iransans\u060C yekan \u06CC\u0627 vazirmatn").option("--agent <agent>", "\u0627\u06CC\u062C\u0646\u062A: claude-code\u060C codex\u060C cursor\u060C vscode \u06CC\u0627 gemini (\u0646\u0627\u0645\u0650 \u0641\u0627\u06CC\u0644\u0650 \u062F\u0633\u062A\u0648\u0631\u0650 \u06A9\u0627\u0631\u06CC \u0631\u0627 \u062A\u0639\u06CC\u06CC\u0646 \u0645\u06CC\u200C\u06A9\u0646\u062F)").option("--framework <framework>", "react \u06CC\u0627 vue\u061B \u067E\u06CC\u0634\u200C\u0641\u0631\u0636 \u0627\u0632 package.json \u062D\u062F\u0633 \u0632\u062F\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F").action(initCommand);
|
|
4044
4281
|
program.command("tokens").description("\u062A\u0648\u06A9\u0646\u200C\u0647\u0627\u06CC \u062F\u06CC\u06AF \u062F\u0631 \u0642\u0627\u0644\u0628\u0650 \u0627\u0633\u062A\u0627\u0646\u062F\u0627\u0631\u062F\u0650 DTCG \u0628\u0631\u0627\u06CC \u0641\u06CC\u06AF\u0645\u0627 \u0648 \u0627\u0628\u0632\u0627\u0631\u0647\u0627\u06CC \u0637\u0631\u0627\u062D\u06CC\u060C \u06CC\u06A9 \u0641\u0627\u06CC\u0644 \u0628\u0631\u0627\u06CC \u0647\u0631 \u062D\u0627\u0644\u062A").option("--brand <hex>", "\u0631\u0646\u06AF \u0628\u0631\u0646\u062F\u061B \u067E\u06CC\u0634\u200C\u0641\u0631\u0636 \u0627\u0632 DESIGN.md\u0650 \u067E\u0631\u0648\u0698\u0647").option("--out <dir>", "\u067E\u0648\u0634\u0647\u0654 \u062E\u0631\u0648\u062C\u06CC (\u067E\u06CC\u0634\u200C\u0641\u0631\u0636 tokens)").action(tokensCommand);
|
|
4045
4282
|
program.command("skill").description("\u06AF\u0630\u0627\u0634\u062A\u0646 \u06CC\u0627 \u0628\u0647\u200C\u0631\u0648\u0632\u06A9\u0631\u062F\u0646\u0650 Skill\u0650 \u0631\u0633\u0645\u06CC\u0650 \u062F\u06CC\u06AF \u0628\u0631\u0627\u06CC Claude Code \u062F\u0631 .claude/skills").action(skillCommand);
|
|
4046
4283
|
program.command("design-md").description("\u0633\u0627\u062E\u062A\u0646\u0650 DESIGN.md \u062F\u0631 \u0631\u06CC\u0634\u0647\u0654 \u067E\u0631\u0648\u0698\u0647: \u062A\u0648\u06A9\u0646\u200C\u0647\u0627 \u0648 \u0642\u0627\u0639\u062F\u0647\u200C\u0647\u0627\u06CC \u062F\u06CC\u06AF \u0628\u0631\u0627\u06CC \u0627\u06CC\u062C\u0646\u062A\u200C\u0647\u0627\u06CC \u06A9\u062F\u0646\u0648\u06CC\u0633").option("--brand <hex>", "\u0631\u0646\u06AF \u0628\u0631\u0646\u062F \u062A\u0627 \u0631\u0646\u06AF\u200C\u0647\u0627\u06CC \u0648\u0627\u0642\u0639\u06CC\u0650 \u0628\u0631\u0646\u062F \u062F\u0631 \u0641\u0627\u06CC\u0644 \u0628\u06CC\u0627\u06CC\u062F\u060C \u0645\u062B\u0644 #2C4C5B").option("--secondary <hex>", "\u0631\u0646\u06AF \u062F\u0648\u0645\u0650 \u0628\u0631\u0646\u062F").option("--name <name>", "\u0646\u0627\u0645 \u0645\u062D\u0635\u0648\u0644").option("--font <font>", "\u0641\u0648\u0646\u062A: iransans\u060C yekan \u06CC\u0627 vazirmatn").option("--radius <radius>", "\u0634\u0639\u0627\u0639: none\u060C sm\u060C md\u060C lg\u060C xl \u06CC\u0627 full").option("--force", "\u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC\u0650 DESIGN.md\u0650 \u062F\u0633\u062A\u200C\u0646\u0648\u0634\u062A\u0647", false).action(designMdCommand);
|
|
4284
|
+
program.command("review").description("\u0628\u0627\u0632\u0628\u06CC\u0646\u06CC\u0650 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0631\u0627\u0628\u0637 \u0628\u0627 \u0642\u0648\u0627\u0639\u062F \u062F\u06CC\u06AF (\u0647\u0645\u0627\u0646 review_code \u062F\u0631 MCP)\u060C \u0628\u0627 \u0634\u0645\u0627\u0631\u0647\u0654 \u062E\u0637\u0650 \u0647\u0631 \u06CC\u0627\u0641\u062A\u0647").argument("<paths...>", "\u0645\u0633\u06CC\u0631 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC tsx/jsx").option("--url <url>", "\u0622\u062F\u0631\u0633 \u0633\u0631\u0648\u0631 MCP (\u067E\u06CC\u0634\u200C\u0641\u0631\u0636 https://docs.digdesign.ir/api/mcp)").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(reviewCommand);
|
|
4047
4285
|
var theme = program.command("theme").description("\u062A\u0645 \u0648 \u0628\u0631\u0646\u062F: \u062C\u062F\u0627 \u06A9\u0631\u062F\u0646\u0650 \u062A\u0645\u0650 \u062F\u06CC\u06AF \u0627\u0632 \u0628\u0631\u0646\u062F\u0650 \u067E\u0631\u0648\u0698\u0647");
|
|
4048
4286
|
theme.command("split").description("\u067E\u0631\u0648\u0698\u0647\u0654 \u0642\u062F\u06CC\u0645\u06CC \u0631\u0627 \u0628\u0647 \u0633\u0627\u062E\u062A\u0627\u0631\u0650 \u062F\u0648\u0641\u0627\u06CC\u0644\u06CC \u0645\u06CC\u200C\u0628\u0631\u062F (dig-theme.css \u0645\u0627\u0644\u0650 \u062F\u06CC\u06AF\u060C brand.css \u0645\u0627\u0644\u0650 \u067E\u0631\u0648\u0698\u0647)").option("--dry-run", "\u0641\u0642\u0637 \u0646\u0634\u0627\u0646 \u0628\u062F\u0647 \u0686\u0647 \u0686\u06CC\u0632\u06CC \u0628\u0647 brand.css \u0645\u06CC\u200C\u0631\u0648\u062F\u060C \u0686\u06CC\u0632\u06CC \u0646\u0646\u0648\u06CC\u0633", false).action(themeSplitCommand);
|
|
4049
4287
|
theme.command("brand").description("\u0631\u0645\u067E\u0650 \u0628\u0631\u0646\u062F \u0631\u0627 \u062F\u0631 brand.css \u0627\u0632 \u06CC\u06A9 \u0631\u0646\u06AF \u0645\u06CC\u200C\u0633\u0627\u0632\u062F\u061B \u0628\u0642\u06CC\u0647\u0654 \u0641\u0627\u06CC\u0644 \u062F\u0633\u062A \u0646\u0645\u06CC\u200C\u062E\u0648\u0631\u062F").argument("<hex>", "\u0631\u0646\u06AF\u0650 \u0627\u0635\u0644\u06CC\u0650 \u0628\u0631\u0646\u062F\u060C \u0645\u062B\u0644 #2C4C5B").action(themeBrandCommand);
|
|
4050
4288
|
program.command("mcp").description("\u0633\u0631\u0648\u0631 MCP \u062F\u06CC\u06AF \u0628\u0647 \u0634\u06A9\u0644 \u0633\u0631\u0648\u0631 \u0645\u062D\u0644\u06CC stdio\u060C \u0628\u0631\u0627\u06CC \u06A9\u0644\u0627\u06CC\u0646\u062A\u200C\u0647\u0627\u06CC\u06CC \u06A9\u0647 \u0622\u062F\u0631\u0633 HTTP \u0646\u0645\u06CC\u200C\u06AF\u06CC\u0631\u0646\u062F").option("--url <url>", "\u0622\u062F\u0631\u0633 \u0633\u0631\u0648\u0631 MCP (\u067E\u06CC\u0634\u200C\u0641\u0631\u0636 https://docs.digdesign.ir/api/mcp)").action(mcpCommand);
|
|
4051
|
-
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\u061B \u0628\u062F\u0648\u0646 \u0622\u0631\u06AF\u0648\u0645\u0627\u0646 \u0641\u0647\u0631\u0633\u062A \u0631\u0627 \u0646\u0634\u0627\u0646 \u0645\u06CC\u200C\u062F\u0647\u062F").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("-y, --yes", "\u0628\u062F\u0648\u0646 \u067E\u0631\u0633\u0634\u060C \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F \u0631\u0627 \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u06A9\u0646", false).option("--dry-run", "\u0641\u0642\u0637 \u0646\u0634\u0627\u0646 \u0628\u062F\u0647 \u0686\u0647 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC\u06CC \u0646\u0648\u0634\u062A\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F\u060C \u0686\u06CC\u0632\u06CC \u0646\u0646\u0648\u06CC\u0633", 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);
|
|
4289
|
+
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\u061B \u0628\u062F\u0648\u0646 \u0622\u0631\u06AF\u0648\u0645\u0627\u0646 \u0641\u0647\u0631\u0633\u062A \u0631\u0627 \u0646\u0634\u0627\u0646 \u0645\u06CC\u200C\u062F\u0647\u062F").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("-y, --yes", "\u0628\u062F\u0648\u0646 \u067E\u0631\u0633\u0634\u060C \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F \u0631\u0627 \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u06A9\u0646", false).option("--dry-run", "\u0641\u0642\u0637 \u0646\u0634\u0627\u0646 \u0628\u062F\u0647 \u0686\u0647 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC\u06CC \u0646\u0648\u0634\u062A\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F\u060C \u0686\u06CC\u0632\u06CC \u0646\u0646\u0648\u06CC\u0633", 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").option("--framework <framework>", "react \u06CC\u0627 vue\u061B \u067E\u06CC\u0634\u200C\u0641\u0631\u0636 \u0627\u0632 dig.json \u0648 \u0628\u0639\u062F package.json (\u0648\u0627\u0628\u0633\u062A\u06AF\u06CC vue \u06CC\u0639\u0646\u06CC Vue) \u062D\u062F\u0633 \u0632\u062F\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F").action(addCommand);
|
|
4052
4290
|
program.command("list").description("\u0641\u0647\u0631\u0633\u062A \u0622\u06CC\u062A\u0645\u200C\u0647\u0627\u06CC \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u062F\u06CC\u06AF").option("--type <type>", "\u0641\u06CC\u0644\u062A\u0631 \u0646\u0648\u0639: ui\u060C lib \u06CC\u0627 theme").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(listCommand);
|
|
4053
4291
|
program.command("search").description("\u062C\u0633\u062A\u062C\u0648\u06CC \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u062F\u0631 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC\u060C \u0628\u0627 \u0646\u0627\u0645 \u0641\u0627\u0631\u0633\u06CC \u06CC\u0627 \u0627\u0646\u06AF\u0644\u06CC\u0633\u06CC").argument("<query>", "\u0639\u0628\u0627\u0631\u062A \u062C\u0633\u062A\u062C\u0648").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(searchCommand);
|
|
4054
4292
|
program.command("diff").description(
|