digdesign 0.5.0 → 0.6.1
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 +357 -77
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/add.ts
|
|
7
|
-
import { access as access4, mkdir as mkdir3, writeFile as
|
|
8
|
-
import
|
|
7
|
+
import { access as access4, mkdir as mkdir3, writeFile as writeFile5 } from "fs/promises";
|
|
8
|
+
import path7 from "path";
|
|
9
9
|
|
|
10
10
|
// src/lib/config.ts
|
|
11
11
|
import { access, readFile, writeFile } from "fs/promises";
|
|
@@ -108,6 +108,16 @@ function parseSelection(raw, options) {
|
|
|
108
108
|
}
|
|
109
109
|
return [...picked];
|
|
110
110
|
}
|
|
111
|
+
async function select(question, options, defaultValue) {
|
|
112
|
+
const width = String(options.length).length;
|
|
113
|
+
for (const [index, option] of options.entries()) {
|
|
114
|
+
const number = String(index + 1).padStart(width);
|
|
115
|
+
const mark = option.value === defaultValue ? " (\u067E\u06CC\u0634\u200C\u0641\u0631\u0636)" : "";
|
|
116
|
+
console.log(` ${number}) ${option.label}${mark}`);
|
|
117
|
+
}
|
|
118
|
+
const raw = await askLine(`${question} `);
|
|
119
|
+
return parseSelection(raw, options)[0] ?? defaultValue;
|
|
120
|
+
}
|
|
111
121
|
|
|
112
122
|
// src/lib/hash.ts
|
|
113
123
|
import { createHash } from "crypto";
|
|
@@ -403,6 +413,91 @@ function resolveTargetPath(filePath, aliases, options = {}) {
|
|
|
403
413
|
return `${aliases.components}/${cleaned}`;
|
|
404
414
|
}
|
|
405
415
|
|
|
416
|
+
// src/commands/design-md.ts
|
|
417
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
418
|
+
import path6 from "path";
|
|
419
|
+
var DESIGN_MD_FILE = "DESIGN.md";
|
|
420
|
+
var GENERATED_MARK = "Generated by Dig";
|
|
421
|
+
async function fetchDesignMd(registryUrl, options) {
|
|
422
|
+
const url = new URL("/api/design-md", registryUrl);
|
|
423
|
+
for (const key2 of ["brand", "secondary", "name", "font", "radius"]) {
|
|
424
|
+
const value = options[key2];
|
|
425
|
+
if (value) url.searchParams.set(key2, value);
|
|
426
|
+
}
|
|
427
|
+
let response;
|
|
428
|
+
try {
|
|
429
|
+
response = await fetch(url);
|
|
430
|
+
} catch {
|
|
431
|
+
throw new DigError(`\u0627\u062A\u0635\u0627\u0644 \u0628\u0647 ${url.origin} \u0628\u0631\u0627\u06CC \u0633\u0627\u062E\u062A\u0646\u0650 DESIGN.md \u0628\u0631\u0642\u0631\u0627\u0631 \u0646\u0634\u062F.`);
|
|
432
|
+
}
|
|
433
|
+
if (!response.ok) {
|
|
434
|
+
const body = await response.json().catch(() => null);
|
|
435
|
+
throw new DigError(`DESIGN.md \u0633\u0627\u062E\u062A\u0647 \u0646\u0634\u062F: ${body?.error ?? response.status}`);
|
|
436
|
+
}
|
|
437
|
+
return response.text();
|
|
438
|
+
}
|
|
439
|
+
async function readIfExists(target) {
|
|
440
|
+
try {
|
|
441
|
+
return await readFile4(target, "utf8");
|
|
442
|
+
} catch {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
async function writeDesignMd(cwd, registryUrl, options) {
|
|
447
|
+
const target = path6.join(cwd, DESIGN_MD_FILE);
|
|
448
|
+
const existing = await readIfExists(target);
|
|
449
|
+
if (existing !== null && !options.force) {
|
|
450
|
+
const generated = existing.includes(GENERATED_MARK);
|
|
451
|
+
if (!generated || !options.replaceGenerated) {
|
|
452
|
+
console.log(
|
|
453
|
+
generated ? `- ${DESIGN_MD_FILE} \u0627\u0632 \u0642\u0628\u0644 \u0647\u0633\u062A\u061B \u062F\u0633\u062A \u0646\u062E\u0648\u0631\u062F. \u0628\u0647\u200C\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC: dig design-md` : `- ${DESIGN_MD_FILE} \u062F\u0633\u062A\u200C\u0646\u0648\u0634\u062A\u0647 \u0627\u0633\u062A\u061B \u062F\u0633\u062A \u0646\u062E\u0648\u0631\u062F. \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC: dig design-md --force`
|
|
454
|
+
);
|
|
455
|
+
return "skipped";
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
await writeFile4(target, await fetchDesignMd(registryUrl, options), "utf8");
|
|
459
|
+
console.log(`\u2713 ${DESIGN_MD_FILE} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F (\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).`);
|
|
460
|
+
return "written";
|
|
461
|
+
}
|
|
462
|
+
function readDesignMdSettings(text) {
|
|
463
|
+
const match = /<!-- dig:design-md (\{.*?\}) -->/.exec(text);
|
|
464
|
+
if (!match) return {};
|
|
465
|
+
try {
|
|
466
|
+
const parsed = JSON.parse(match[1]);
|
|
467
|
+
return Object.fromEntries(
|
|
468
|
+
["brand", "secondary", "name", "font", "radius"].filter((key2) => typeof parsed[key2] === "string").map((key2) => [key2, parsed[key2]])
|
|
469
|
+
);
|
|
470
|
+
} catch {
|
|
471
|
+
return {};
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
async function refreshDesignMd(cwd, registryUrl, overrides = {}, log = console.log) {
|
|
475
|
+
const target = path6.join(cwd, DESIGN_MD_FILE);
|
|
476
|
+
const existing = await readIfExists(target);
|
|
477
|
+
if (existing === null || !existing.includes(GENERATED_MARK)) return "skipped";
|
|
478
|
+
try {
|
|
479
|
+
const next = await fetchDesignMd(registryUrl, { ...readDesignMdSettings(existing), ...overrides });
|
|
480
|
+
if (next === existing) return "unchanged";
|
|
481
|
+
await writeFile4(target, next, "utf8");
|
|
482
|
+
log(`\u2713 ${DESIGN_MD_FILE} \u0647\u0645 \u0628\u0647\u200C\u0631\u0648\u0632 \u0634\u062F.`);
|
|
483
|
+
return "updated";
|
|
484
|
+
} catch {
|
|
485
|
+
log(`- ${DESIGN_MD_FILE} \u0628\u0647\u200C\u0631\u0648\u0632 \u0646\u0634\u062F (\u0627\u062A\u0635\u0627\u0644 \u0628\u0631\u0642\u0631\u0627\u0631 \u0646\u0634\u062F)\u061B \u0628\u0639\u062F\u0627\u064B: dig design-md`);
|
|
486
|
+
return "failed";
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
async function designMdCommand(options) {
|
|
490
|
+
const cwd = process.cwd();
|
|
491
|
+
const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
|
|
492
|
+
const previous = readDesignMdSettings(await readIfExists(path6.join(cwd, DESIGN_MD_FILE)) ?? "");
|
|
493
|
+
const explicit = Object.fromEntries(Object.entries(options).filter(([, value]) => value !== void 0 && value !== false));
|
|
494
|
+
options = { ...previous, ...explicit };
|
|
495
|
+
const result = await writeDesignMd(cwd, config.registryUrl, { ...options, replaceGenerated: true });
|
|
496
|
+
if (result === "written" && !options.brand) {
|
|
497
|
+
console.log(' \u0631\u0646\u06AF \u0628\u0631\u0646\u062F \u062F\u0627\u0631\u06CC\u062F\u061F dig design-md --brand "#2C4C5B" \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.');
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
406
501
|
// src/commands/add.ts
|
|
407
502
|
async function pathExists(target) {
|
|
408
503
|
try {
|
|
@@ -448,8 +543,8 @@ async function addCommand(components, options) {
|
|
|
448
543
|
const existing = [];
|
|
449
544
|
for (const item of items.values()) {
|
|
450
545
|
for (const file of item.files) {
|
|
451
|
-
const target =
|
|
452
|
-
if (await pathExists(target)) existing.push(
|
|
546
|
+
const target = path7.join(cwd, resolveTargetPath(file.path, config.aliases, targetPathOptions(config)));
|
|
547
|
+
if (await pathExists(target)) existing.push(path7.relative(cwd, target));
|
|
453
548
|
}
|
|
454
549
|
}
|
|
455
550
|
if (existing.length) {
|
|
@@ -470,11 +565,11 @@ ${existing.length} \u0641\u0627\u06CC\u0644 \u0627\u0632 \u0642\u0628\u0644 \u06
|
|
|
470
565
|
for (const item of items.values()) {
|
|
471
566
|
for (const dep of item.dependencies ?? []) npmDeps.add(dep);
|
|
472
567
|
for (const file of item.files) {
|
|
473
|
-
const target =
|
|
568
|
+
const target = path7.join(cwd, resolveTargetPath(file.path, config.aliases, targetPathOptions(config)));
|
|
474
569
|
const exists3 = await pathExists(target);
|
|
475
570
|
const mark = exists3 ? overwrite ? "~" : "-" : "+";
|
|
476
571
|
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)" : "";
|
|
477
|
-
console.log(`${mark} ${
|
|
572
|
+
console.log(`${mark} ${path7.relative(cwd, target)}${note}`);
|
|
478
573
|
}
|
|
479
574
|
}
|
|
480
575
|
console.log(`
|
|
@@ -497,17 +592,17 @@ ${items.size} \u0622\u06CC\u062A\u0645 \u0627\u0632 \u0631\u062C\u06CC\u0633\u06
|
|
|
497
592
|
const writtenFiles = {};
|
|
498
593
|
const writtenContents = {};
|
|
499
594
|
for (const file of item.files) {
|
|
500
|
-
const target =
|
|
595
|
+
const target = path7.join(cwd, resolveTargetPath(file.path, config.aliases, targetPathOptions(config)));
|
|
501
596
|
if (!overwrite && await pathExists(target)) {
|
|
502
|
-
if (!options.skipExisting) console.log(`- \u0631\u062F \u0634\u062F (\u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F): ${
|
|
597
|
+
if (!options.skipExisting) console.log(`- \u0631\u062F \u0634\u062F (\u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F): ${path7.relative(cwd, target)}`);
|
|
503
598
|
skipped++;
|
|
504
599
|
continue;
|
|
505
600
|
}
|
|
506
|
-
await mkdir3(
|
|
507
|
-
await
|
|
508
|
-
console.log(`+ ${
|
|
601
|
+
await mkdir3(path7.dirname(target), { recursive: true });
|
|
602
|
+
await writeFile5(target, file.content, "utf8");
|
|
603
|
+
console.log(`+ ${path7.relative(cwd, target)}`);
|
|
509
604
|
written++;
|
|
510
|
-
const relPath = toPosixPath(
|
|
605
|
+
const relPath = toPosixPath(path7.relative(cwd, target));
|
|
511
606
|
writtenFiles[relPath] = file.hash ?? hashContent(file.content);
|
|
512
607
|
writtenContents[relPath] = file.content;
|
|
513
608
|
}
|
|
@@ -530,10 +625,13 @@ ${items.size} \u0622\u06CC\u062A\u0645 \u0627\u0632 \u0631\u062C\u06CC\u0633\u06
|
|
|
530
625
|
if (lockChanged) {
|
|
531
626
|
await writeLock(lock, cwd);
|
|
532
627
|
console.log(`
|
|
533
|
-
\u062B\u0628\u062A \u0634\u062F \u062F\u0631 ${
|
|
628
|
+
\u062B\u0628\u062A \u0634\u062F \u062F\u0631 ${path7.join(".dig", "registry-lock.json")}`);
|
|
534
629
|
}
|
|
535
630
|
console.log(`
|
|
536
631
|
${written} \u0641\u0627\u06CC\u0644 \u0646\u0648\u0634\u062A\u0647 \u0634\u062F\u060C ${skipped} \u0641\u0627\u06CC\u0644 \u0631\u062F \u0634\u062F.`);
|
|
632
|
+
if (written > 0 && !options.dryRun && !options.skipExisting) {
|
|
633
|
+
await refreshDesignMd(cwd, config.registryUrl);
|
|
634
|
+
}
|
|
537
635
|
if (!dependencies.size && !devDependencies.size) return;
|
|
538
636
|
if (options.install === false) {
|
|
539
637
|
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:");
|
|
@@ -654,8 +752,8 @@ async function searchCommand(query, options) {
|
|
|
654
752
|
}
|
|
655
753
|
|
|
656
754
|
// src/lib/diff.ts
|
|
657
|
-
import { readFile as
|
|
658
|
-
import
|
|
755
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
756
|
+
import path8 from "path";
|
|
659
757
|
var STATUS_PRIORITY = [
|
|
660
758
|
"conflict",
|
|
661
759
|
"untracked",
|
|
@@ -685,7 +783,7 @@ function overallStatus(files) {
|
|
|
685
783
|
}
|
|
686
784
|
async function readLocalHash(target) {
|
|
687
785
|
try {
|
|
688
|
-
const content = await
|
|
786
|
+
const content = await readFile5(target, "utf8");
|
|
689
787
|
return hashContent(content);
|
|
690
788
|
} catch (error) {
|
|
691
789
|
if (error.code === "ENOENT") return void 0;
|
|
@@ -693,8 +791,8 @@ async function readLocalHash(target) {
|
|
|
693
791
|
}
|
|
694
792
|
}
|
|
695
793
|
async function computeFileDrift(cwd, aliases, lockEntry, file, paths) {
|
|
696
|
-
const target =
|
|
697
|
-
const relPath = toPosixPath(
|
|
794
|
+
const target = path8.join(cwd, resolveTargetPath(file.path, aliases, paths));
|
|
795
|
+
const relPath = toPosixPath(path8.relative(cwd, target));
|
|
698
796
|
const registryHash = file.hash ?? hashContent(file.content);
|
|
699
797
|
const installedHash = lockEntry?.files[relPath];
|
|
700
798
|
const localHash = await readLocalHash(target);
|
|
@@ -723,8 +821,8 @@ async function computeComponentDrift(params) {
|
|
|
723
821
|
}
|
|
724
822
|
|
|
725
823
|
// src/lib/file-diff-details.ts
|
|
726
|
-
import { readFile as
|
|
727
|
-
import
|
|
824
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
825
|
+
import path9 from "path";
|
|
728
826
|
|
|
729
827
|
// src/lib/text-diff.ts
|
|
730
828
|
var DEFAULT_CONTEXT = 3;
|
|
@@ -833,7 +931,7 @@ function looksBinary(buffer) {
|
|
|
833
931
|
async function readTextFileSafely(target) {
|
|
834
932
|
let buffer;
|
|
835
933
|
try {
|
|
836
|
-
buffer = await
|
|
934
|
+
buffer = await readFile6(target);
|
|
837
935
|
} catch (error) {
|
|
838
936
|
if (error.code === "ENOENT") return void 0;
|
|
839
937
|
throw error;
|
|
@@ -873,7 +971,7 @@ async function computeFileDiffDetails(params) {
|
|
|
873
971
|
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."
|
|
874
972
|
};
|
|
875
973
|
}
|
|
876
|
-
const target =
|
|
974
|
+
const target = path9.join(cwd, resolveTargetPath(registryFile.path, aliases, paths));
|
|
877
975
|
const localContent = await readTextFileSafely(target);
|
|
878
976
|
if (localContent === "binary") {
|
|
879
977
|
return {
|
|
@@ -1430,8 +1528,8 @@ function dependencyRiskLevel(highImpactComponents) {
|
|
|
1430
1528
|
}
|
|
1431
1529
|
|
|
1432
1530
|
// src/lib/repo-scanner.ts
|
|
1433
|
-
import { readFile as
|
|
1434
|
-
import
|
|
1531
|
+
import { readFile as readFile7, readdir } from "fs/promises";
|
|
1532
|
+
import path10 from "path";
|
|
1435
1533
|
import ts2 from "typescript";
|
|
1436
1534
|
|
|
1437
1535
|
// src/lib/ast-engine.ts
|
|
@@ -1506,11 +1604,11 @@ async function collectSourceFiles(root) {
|
|
|
1506
1604
|
for (const entry of entries) {
|
|
1507
1605
|
if (entry.isDirectory()) {
|
|
1508
1606
|
if (IGNORED_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
1509
|
-
await walk(
|
|
1607
|
+
await walk(path10.join(dir, entry.name));
|
|
1510
1608
|
continue;
|
|
1511
1609
|
}
|
|
1512
|
-
if (entry.isFile() && SCAN_EXTENSIONS.has(
|
|
1513
|
-
results.push(
|
|
1610
|
+
if (entry.isFile() && SCAN_EXTENSIONS.has(path10.extname(entry.name))) {
|
|
1611
|
+
results.push(path10.join(dir, entry.name));
|
|
1514
1612
|
}
|
|
1515
1613
|
}
|
|
1516
1614
|
}
|
|
@@ -1537,7 +1635,7 @@ async function scanRepositoryForComponent(params) {
|
|
|
1537
1635
|
for (const filePath of sourceFilePaths) {
|
|
1538
1636
|
let content;
|
|
1539
1637
|
try {
|
|
1540
|
-
content = await
|
|
1638
|
+
content = await readFile7(filePath, "utf8");
|
|
1541
1639
|
} catch {
|
|
1542
1640
|
continue;
|
|
1543
1641
|
}
|
|
@@ -1551,7 +1649,7 @@ async function scanRepositoryForComponent(params) {
|
|
|
1551
1649
|
const snippets = findJsxUsagesInSource(sourceFile, tagName);
|
|
1552
1650
|
if (snippets.length === 0) continue;
|
|
1553
1651
|
files.push({
|
|
1554
|
-
path: toPosixPath(
|
|
1652
|
+
path: toPosixPath(path10.relative(projectRoot, filePath)),
|
|
1555
1653
|
usages: snippets.length,
|
|
1556
1654
|
snippets
|
|
1557
1655
|
});
|
|
@@ -1957,12 +2055,74 @@ async function impactCommand(name, options) {
|
|
|
1957
2055
|
}
|
|
1958
2056
|
|
|
1959
2057
|
// src/commands/init.ts
|
|
2058
|
+
import { readFile as readFile10, writeFile as writeFile8 } from "fs/promises";
|
|
2059
|
+
import path14 from "path";
|
|
2060
|
+
|
|
2061
|
+
// src/lib/agent-instructions.ts
|
|
1960
2062
|
import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
|
|
1961
|
-
import
|
|
2063
|
+
import path11 from "path";
|
|
2064
|
+
var AGENT_CLIENTS = [
|
|
2065
|
+
{ value: "claude-code", label: "Claude Code (CLAUDE.md)" },
|
|
2066
|
+
{ value: "codex", label: "Codex (AGENTS.md)" },
|
|
2067
|
+
{ value: "cursor", label: "Cursor (AGENTS.md)" },
|
|
2068
|
+
{ value: "vscode", label: "VS Code (AGENTS.md)" },
|
|
2069
|
+
{ value: "gemini", label: "Gemini CLI (GEMINI.md)" }
|
|
2070
|
+
];
|
|
2071
|
+
var FONTS = [
|
|
2072
|
+
{ value: "iransans", label: "IRANSansX" },
|
|
2073
|
+
{ value: "yekan", label: "IRANYekan" },
|
|
2074
|
+
{ value: "vazirmatn", label: "Vazirmatn" }
|
|
2075
|
+
];
|
|
2076
|
+
var BLOCK_START = "<!-- dig:start";
|
|
2077
|
+
var BLOCK_END = "<!-- dig:end -->";
|
|
2078
|
+
async function fetchInstructionsFile(registryUrl, options) {
|
|
2079
|
+
const url = new URL("/api/agent-setup", registryUrl);
|
|
2080
|
+
if (options.agent) url.searchParams.set("client", options.agent);
|
|
2081
|
+
for (const key2 of ["brand", "font", "name"]) {
|
|
2082
|
+
const value = options[key2];
|
|
2083
|
+
if (value) url.searchParams.set(key2, value);
|
|
2084
|
+
}
|
|
2085
|
+
let response;
|
|
2086
|
+
try {
|
|
2087
|
+
response = await fetch(url);
|
|
2088
|
+
} catch {
|
|
2089
|
+
throw new DigError(`\u0627\u062A\u0635\u0627\u0644 \u0628\u0647 ${url.origin} \u0628\u0631\u0627\u06CC \u0633\u0627\u062E\u062A\u0646\u0650 \u0641\u0627\u06CC\u0644\u0650 \u0627\u06CC\u062C\u0646\u062A \u0628\u0631\u0642\u0631\u0627\u0631 \u0646\u0634\u062F.`);
|
|
2090
|
+
}
|
|
2091
|
+
const body = await response.json().catch(() => null);
|
|
2092
|
+
const file = body?.files?.find((item) => /\.md$/.test(item.path) && item.path !== "DESIGN.md");
|
|
2093
|
+
if (!response.ok || !file) {
|
|
2094
|
+
throw new DigError(`\u0641\u0627\u06CC\u0644\u0650 \u0627\u06CC\u062C\u0646\u062A \u0633\u0627\u062E\u062A\u0647 \u0646\u0634\u062F: ${body?.error ?? response.status}`);
|
|
2095
|
+
}
|
|
2096
|
+
return file;
|
|
2097
|
+
}
|
|
2098
|
+
function mergeInstructions(existing, block) {
|
|
2099
|
+
const trimmed = block.trim();
|
|
2100
|
+
if (existing === null || existing.trim() === "") return `${trimmed}
|
|
2101
|
+
`;
|
|
2102
|
+
const start = existing.indexOf(BLOCK_START);
|
|
2103
|
+
const end = existing.indexOf(BLOCK_END);
|
|
2104
|
+
if (start >= 0 && end > start) {
|
|
2105
|
+
return `${existing.slice(0, start)}${trimmed}${existing.slice(end + BLOCK_END.length)}`;
|
|
2106
|
+
}
|
|
2107
|
+
return `${existing.trimEnd()}
|
|
2108
|
+
|
|
2109
|
+
${trimmed}
|
|
2110
|
+
`;
|
|
2111
|
+
}
|
|
2112
|
+
async function writeAgentInstructions(cwd, registryUrl, options) {
|
|
2113
|
+
const file = await fetchInstructionsFile(registryUrl, options);
|
|
2114
|
+
const target = path11.join(cwd, file.path);
|
|
2115
|
+
const existing = await readFile8(target, "utf8").catch(() => null);
|
|
2116
|
+
await writeFile6(target, mergeInstructions(existing, file.content), "utf8");
|
|
2117
|
+
console.log(
|
|
2118
|
+
existing === null ? `\u2713 ${file.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 ${file.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.`
|
|
2119
|
+
);
|
|
2120
|
+
return file.path;
|
|
2121
|
+
}
|
|
1962
2122
|
|
|
1963
2123
|
// src/commands/theme.ts
|
|
1964
|
-
import { access as access5, mkdir as mkdir4, readFile as
|
|
1965
|
-
import
|
|
2124
|
+
import { access as access5, mkdir as mkdir4, readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
|
|
2125
|
+
import path13 from "path";
|
|
1966
2126
|
|
|
1967
2127
|
// src/lib/brand-ramp.ts
|
|
1968
2128
|
var RAMP_STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
|
|
@@ -2041,6 +2201,9 @@ function relativeLuminance(rgb) {
|
|
|
2041
2201
|
function contrastWithWhite(color) {
|
|
2042
2202
|
return 1.05 / (relativeLuminance(oklchToSrgb(color)) + 0.05);
|
|
2043
2203
|
}
|
|
2204
|
+
function contrastWithBlack(color) {
|
|
2205
|
+
return (relativeLuminance(oklchToSrgb(color)) + 0.05) / 0.05;
|
|
2206
|
+
}
|
|
2044
2207
|
function formatOklch({ l, c, h }) {
|
|
2045
2208
|
return `oklch(${l.toFixed(1)}% ${c.toFixed(3)} ${h.toFixed(1)})`;
|
|
2046
2209
|
}
|
|
@@ -2063,9 +2226,9 @@ function buildBrandRamp(hex) {
|
|
|
2063
2226
|
}
|
|
2064
2227
|
|
|
2065
2228
|
// src/lib/theme-files.ts
|
|
2066
|
-
import
|
|
2229
|
+
import path12 from "path";
|
|
2067
2230
|
function importPath(fromFile, toFile) {
|
|
2068
|
-
const rel = toPosixPath(
|
|
2231
|
+
const rel = toPosixPath(path12.relative(path12.dirname(fromFile), toFile));
|
|
2069
2232
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
2070
2233
|
}
|
|
2071
2234
|
function globalsCss(paths, extraStatements = []) {
|
|
@@ -2096,10 +2259,14 @@ var BRAND_HEADER = `/* =========================================================
|
|
|
2096
2259
|
`;
|
|
2097
2260
|
function brandRampBlock(ramp, hex) {
|
|
2098
2261
|
const lines = RAMP_STEPS.map((step) => ` --brand-${step}: ${formatOklch(ramp.steps[step])};`);
|
|
2262
|
+
const darkText = contrastWithWhite(ramp.steps[400]) >= contrastWithBlack(ramp.steps[400]) ? "--dig-white" : "--dig-black";
|
|
2099
2263
|
return `/* dig:brand-ramp-start \u2014 \u0633\u0627\u062E\u062A\u0647\u200C\u0634\u062F\u0647 \u0627\u0632 ${hex.toUpperCase()}\u061B \xABdig theme brand\xBB \u0647\u0645\u06CC\u0646 \u0628\u0644\u0648\u06A9 \u0631\u0627 \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0645\u06CC\u200C\u06A9\u0646\u062F */
|
|
2100
2264
|
:root {
|
|
2101
2265
|
${lines.join("\n")}
|
|
2102
2266
|
}
|
|
2267
|
+
.dark {
|
|
2268
|
+
--primary-foreground: var(${darkText});
|
|
2269
|
+
}
|
|
2103
2270
|
/* dig:brand-ramp-end */`;
|
|
2104
2271
|
}
|
|
2105
2272
|
function brandCss(options) {
|
|
@@ -2224,9 +2391,9 @@ function splitTheme(projectCss, digThemeCss) {
|
|
|
2224
2391
|
|
|
2225
2392
|
// src/commands/theme.ts
|
|
2226
2393
|
var THEME_ITEM = "theme";
|
|
2227
|
-
async function
|
|
2394
|
+
async function readIfExists2(target) {
|
|
2228
2395
|
try {
|
|
2229
|
-
return await
|
|
2396
|
+
return await readFile9(target, "utf8");
|
|
2230
2397
|
} catch {
|
|
2231
2398
|
return null;
|
|
2232
2399
|
}
|
|
@@ -2240,7 +2407,7 @@ async function exists(target) {
|
|
|
2240
2407
|
}
|
|
2241
2408
|
}
|
|
2242
2409
|
function themePaths(config) {
|
|
2243
|
-
const dir =
|
|
2410
|
+
const dir = path13.posix.dirname(toPosixPath(config.tailwind.css));
|
|
2244
2411
|
return {
|
|
2245
2412
|
css: config.tailwind.css,
|
|
2246
2413
|
theme: config.tailwind.theme ?? `${dir}/dig-theme.css`,
|
|
@@ -2274,9 +2441,9 @@ async function recordTheme(cwd, config, themeRel, item) {
|
|
|
2274
2441
|
await writeLock(lock, cwd);
|
|
2275
2442
|
}
|
|
2276
2443
|
async function writeText(cwd, rel, content) {
|
|
2277
|
-
const target =
|
|
2278
|
-
await mkdir4(
|
|
2279
|
-
await
|
|
2444
|
+
const target = path13.join(cwd, rel);
|
|
2445
|
+
await mkdir4(path13.dirname(target), { recursive: true });
|
|
2446
|
+
await writeFile7(target, content, "utf8");
|
|
2280
2447
|
}
|
|
2281
2448
|
async function installTheme(cwd, config, options) {
|
|
2282
2449
|
const paths = themePaths(config);
|
|
@@ -2284,7 +2451,7 @@ async function installTheme(cwd, config, options) {
|
|
|
2284
2451
|
const ramp = options.brand ? buildBrandRamp(options.brand) : void 0;
|
|
2285
2452
|
await writeText(cwd, paths.theme, item.content);
|
|
2286
2453
|
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).`);
|
|
2287
|
-
const existingBrand = await
|
|
2454
|
+
const existingBrand = await readIfExists2(path13.join(cwd, paths.brand));
|
|
2288
2455
|
if (existingBrand === null) {
|
|
2289
2456
|
await writeText(cwd, paths.brand, brandCss({ ramp, hex: options.brand }));
|
|
2290
2457
|
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).`);
|
|
@@ -2295,10 +2462,10 @@ async function installTheme(cwd, config, options) {
|
|
|
2295
2462
|
console.log(`- ${paths.brand} \u0627\u0632 \u0642\u0628\u0644 \u0647\u0633\u062A\u061B \u062F\u0633\u062A \u0646\u062E\u0648\u0631\u062F.`);
|
|
2296
2463
|
}
|
|
2297
2464
|
if (ramp) reportContrast(ramp.primaryContrast);
|
|
2298
|
-
const cssTarget =
|
|
2299
|
-
const existingCss = await
|
|
2465
|
+
const cssTarget = path13.join(cwd, paths.css);
|
|
2466
|
+
const existingCss = await readIfExists2(cssTarget);
|
|
2300
2467
|
if (!isBoilerplateCss(existingCss) && existingCss !== null) {
|
|
2301
|
-
await
|
|
2468
|
+
await writeFile7(`${cssTarget}.backup`, existingCss, "utf8");
|
|
2302
2469
|
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.`);
|
|
2303
2470
|
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.`);
|
|
2304
2471
|
}
|
|
@@ -2331,11 +2498,11 @@ async function themeSplitCommand(options) {
|
|
|
2331
2498
|
return;
|
|
2332
2499
|
}
|
|
2333
2500
|
const paths = themePaths(config);
|
|
2334
|
-
const current = await
|
|
2501
|
+
const current = await readIfExists2(path13.join(cwd, paths.css));
|
|
2335
2502
|
if (current === null || isBoilerplateCss(current)) {
|
|
2336
2503
|
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.`);
|
|
2337
2504
|
}
|
|
2338
|
-
if (await exists(
|
|
2505
|
+
if (await exists(path13.join(cwd, paths.brand))) {
|
|
2339
2506
|
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.`);
|
|
2340
2507
|
}
|
|
2341
2508
|
console.log(`\u062F\u0631\u06CC\u0627\u0641\u062A\u0650 \u062A\u0645\u0650 \u062F\u06CC\u06AF \u0627\u0632 ${config.registryUrl} \u2026`);
|
|
@@ -2354,7 +2521,7 @@ ${split.changed} \u062A\u0648\u06A9\u0646\u0650 \u0639\u0648\u0636\u200C\u0634\u
|
|
|
2354
2521
|
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)");
|
|
2355
2522
|
return;
|
|
2356
2523
|
}
|
|
2357
|
-
await
|
|
2524
|
+
await writeFile7(path13.join(cwd, `${paths.css}.backup`), current, "utf8");
|
|
2358
2525
|
await writeText(cwd, paths.theme, item.content);
|
|
2359
2526
|
await writeText(cwd, paths.brand, brandCss({ body: split.body }));
|
|
2360
2527
|
await writeText(cwd, paths.css, globalsCss(paths, split.extraStatements));
|
|
@@ -2378,11 +2545,12 @@ async function themeBrandCommand(hex) {
|
|
|
2378
2545
|
}
|
|
2379
2546
|
const paths = themePaths(config);
|
|
2380
2547
|
const ramp = buildBrandRamp(hex);
|
|
2381
|
-
const existing = await
|
|
2548
|
+
const existing = await readIfExists2(path13.join(cwd, paths.brand)) ?? brandCss({});
|
|
2382
2549
|
const next = replaceBrandRamp(existing, ramp, hex);
|
|
2383
2550
|
await writeText(cwd, paths.brand, next);
|
|
2384
2551
|
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).`);
|
|
2385
2552
|
reportContrast(ramp.primaryContrast);
|
|
2553
|
+
await refreshDesignMd(cwd, config.registryUrl, { brand: hex });
|
|
2386
2554
|
const outside = next.replace(/\/\* dig:brand-ramp-start[\s\S]*?\/\* dig:brand-ramp-end \*\//, "");
|
|
2387
2555
|
const shadowing = outside.match(/--brand-\d+\s*:/g);
|
|
2388
2556
|
if (shadowing) {
|
|
@@ -2393,16 +2561,16 @@ async function themeBrandCommand(hex) {
|
|
|
2393
2561
|
}
|
|
2394
2562
|
|
|
2395
2563
|
// src/commands/init.ts
|
|
2396
|
-
async function
|
|
2564
|
+
async function readIfExists3(target) {
|
|
2397
2565
|
try {
|
|
2398
|
-
return await
|
|
2566
|
+
return await readFile10(target, "utf8");
|
|
2399
2567
|
} catch {
|
|
2400
2568
|
return null;
|
|
2401
2569
|
}
|
|
2402
2570
|
}
|
|
2403
2571
|
async function writeTheme(config, cwd, options) {
|
|
2404
2572
|
const paths = themePaths(config);
|
|
2405
|
-
const existing = await
|
|
2573
|
+
const existing = await readIfExists3(path14.join(cwd, paths.css));
|
|
2406
2574
|
if (!isBoilerplateCss(existing) && !options.yes) {
|
|
2407
2575
|
console.log(`
|
|
2408
2576
|
${paths.css} \u0627\u0632 \u0642\u0628\u0644 \u0645\u062D\u062A\u0648\u0627 \u062F\u0627\u0631\u062F.`);
|
|
@@ -2427,8 +2595,8 @@ ${paths.css} \u0627\u0632 \u0642\u0628\u0644 \u0645\u062D\u062A\u0648\u0627 \u06
|
|
|
2427
2595
|
}
|
|
2428
2596
|
async function applyRtl(cwd) {
|
|
2429
2597
|
for (const candidate of ["src/app/layout.tsx", "app/layout.tsx"]) {
|
|
2430
|
-
const target =
|
|
2431
|
-
const source = await
|
|
2598
|
+
const target = path14.join(cwd, candidate);
|
|
2599
|
+
const source = await readIfExists3(target);
|
|
2432
2600
|
if (source === null) continue;
|
|
2433
2601
|
if (/<html[^>]*\sdir=/.test(source)) {
|
|
2434
2602
|
console.log(`- ${candidate} \u0627\u0632 \u0642\u0628\u0644 dir \u062F\u0627\u0631\u062F\u061B \u062F\u0633\u062A \u0646\u062E\u0648\u0631\u062F.`);
|
|
@@ -2440,7 +2608,7 @@ async function applyRtl(cwd) {
|
|
|
2440
2608
|
match[0],
|
|
2441
2609
|
`<html${match[1]} dir="rtl">`.replace(/\s+dir="rtl">$/, ' dir="rtl">')
|
|
2442
2610
|
);
|
|
2443
|
-
await
|
|
2611
|
+
await writeFile8(target, updated, "utf8");
|
|
2444
2612
|
console.log(`\u2713 dir="rtl" \u0628\u0647 ${candidate} \u0627\u0636\u0627\u0641\u0647 \u0634\u062F.`);
|
|
2445
2613
|
return;
|
|
2446
2614
|
}
|
|
@@ -2448,6 +2616,15 @@ async function applyRtl(cwd) {
|
|
|
2448
2616
|
'- \u0686\u06CC\u062F\u0645\u0627\u0646\u0650 \u0631\u06CC\u0634\u0647 \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F. \u062E\u0648\u062F\u062A\u0627\u0646 dir="rtl" \u0631\u0627 \u0631\u0648\u06CC \u062A\u06AF <html> \u0628\u06AF\u0630\u0627\u0631\u06CC\u062F.'
|
|
2449
2617
|
);
|
|
2450
2618
|
}
|
|
2619
|
+
async function askProjectDecisions(options) {
|
|
2620
|
+
if (options.yes) return { brand: options.brand, font: options.font, agent: options.agent };
|
|
2621
|
+
const brand = options.brand ?? ((await ask("\u0631\u0646\u06AF \u0628\u0631\u0646\u062F (\u06A9\u062F \u0647\u06AF\u0632\u061B Enter \u06CC\u0639\u0646\u06CC \u0631\u0646\u06AF \u067E\u06CC\u0634\u200C\u0641\u0631\u0636 \u062F\u06CC\u06AF)\u061F", "")).trim() || void 0);
|
|
2622
|
+
console.log("\n\u0641\u0648\u0646\u062A \u0641\u0627\u0631\u0633\u06CC:");
|
|
2623
|
+
const font = options.font ?? await select("\u0634\u0645\u0627\u0631\u0647\u0654 \u0641\u0648\u0646\u062A\u061F", FONTS, "iransans");
|
|
2624
|
+
console.log("\n\u0628\u0627 \u06A9\u062F\u0627\u0645 \u0627\u06CC\u062C\u0646\u062A \u06A9\u062F \u0645\u06CC\u200C\u0646\u0648\u06CC\u0633\u06CC\u062F\u061F");
|
|
2625
|
+
const agent = options.agent ?? await select("\u0634\u0645\u0627\u0631\u0647\u0654 \u0627\u06CC\u062C\u0646\u062A\u061F", AGENT_CLIENTS, "codex");
|
|
2626
|
+
return { brand, font, agent };
|
|
2627
|
+
}
|
|
2451
2628
|
async function initCommand(options) {
|
|
2452
2629
|
const cwd = process.cwd();
|
|
2453
2630
|
const base = defaultConfig();
|
|
@@ -2463,16 +2640,24 @@ async function initCommand(options) {
|
|
|
2463
2640
|
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),
|
|
2464
2641
|
tailwind: await (async () => {
|
|
2465
2642
|
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);
|
|
2466
|
-
const dir =
|
|
2643
|
+
const dir = path14.posix.dirname(css.split(path14.sep).join("/"));
|
|
2467
2644
|
return { ...base.tailwind, css, theme: `${dir}/dig-theme.css`, brand: `${dir}/brand.css` };
|
|
2468
2645
|
})()
|
|
2469
2646
|
};
|
|
2647
|
+
const decisions = await askProjectDecisions(options);
|
|
2648
|
+
options = { ...options, brand: decisions.brand };
|
|
2470
2649
|
await writeConfig(config, cwd);
|
|
2471
|
-
console.log(`\u2713 ${
|
|
2650
|
+
console.log(`\u2713 ${path14.join(cwd, "dig.json")} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F.`);
|
|
2472
2651
|
const theme2 = await writeTheme(config, cwd, options);
|
|
2473
2652
|
if (config.rtl) {
|
|
2474
2653
|
await applyRtl(cwd);
|
|
2475
2654
|
}
|
|
2655
|
+
await writeDesignMd(cwd, config.registryUrl, { brand: decisions.brand, font: decisions.font }).catch((error) => {
|
|
2656
|
+
console.log(`- DESIGN.md \u0633\u0627\u062E\u062A\u0647 \u0646\u0634\u062F (${error instanceof Error ? error.message : error}). \u0628\u0639\u062F\u0627\u064B: dig design-md`);
|
|
2657
|
+
});
|
|
2658
|
+
await writeAgentInstructions(cwd, config.registryUrl, decisions).catch((error) => {
|
|
2659
|
+
console.log(`- \u0641\u0627\u06CC\u0644\u0650 \u0627\u06CC\u062C\u0646\u062A \u0633\u0627\u062E\u062A\u0647 \u0646\u0634\u062F (${error instanceof Error ? error.message : error}). \u0628\u0639\u062F\u0627\u064B: dig init`);
|
|
2660
|
+
});
|
|
2476
2661
|
console.log("\n\u0622\u0645\u0627\u062F\u0647 \u0627\u0633\u062A. \u0642\u062F\u0645 \u0628\u0639\u062F\u06CC:");
|
|
2477
2662
|
console.log(" dig add button");
|
|
2478
2663
|
if (theme2 === "written") {
|
|
@@ -2486,9 +2671,102 @@ async function initCommand(options) {
|
|
|
2486
2671
|
}
|
|
2487
2672
|
}
|
|
2488
2673
|
|
|
2674
|
+
// src/commands/mcp.ts
|
|
2675
|
+
import { createInterface as createInterface2 } from "readline";
|
|
2676
|
+
|
|
2677
|
+
// src/lib/version.ts
|
|
2678
|
+
var CLI_VERSION = "0.6.1";
|
|
2679
|
+
|
|
2680
|
+
// src/commands/mcp.ts
|
|
2681
|
+
var DEFAULT_MCP_URL = "https://docs.digdesign.ir/api/mcp";
|
|
2682
|
+
function parseSse(body) {
|
|
2683
|
+
const messages = [];
|
|
2684
|
+
for (const event of body.split(/\r?\n\r?\n/)) {
|
|
2685
|
+
const data = event.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).replace(/^ /, "")).join("\n");
|
|
2686
|
+
if (!data) continue;
|
|
2687
|
+
try {
|
|
2688
|
+
messages.push(JSON.parse(data));
|
|
2689
|
+
} catch {
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
return messages;
|
|
2693
|
+
}
|
|
2694
|
+
function requestIds(message) {
|
|
2695
|
+
const list = Array.isArray(message) ? message : [message];
|
|
2696
|
+
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);
|
|
2697
|
+
}
|
|
2698
|
+
function createBridge(url, fetchImpl = fetch) {
|
|
2699
|
+
let sessionId;
|
|
2700
|
+
let protocolVersion;
|
|
2701
|
+
return {
|
|
2702
|
+
async handle(line) {
|
|
2703
|
+
let message;
|
|
2704
|
+
try {
|
|
2705
|
+
message = JSON.parse(line);
|
|
2706
|
+
} catch {
|
|
2707
|
+
return [{ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } }];
|
|
2708
|
+
}
|
|
2709
|
+
const ids = requestIds(message);
|
|
2710
|
+
const fail = (reason) => ids.map((id) => ({ jsonrpc: "2.0", id, error: { code: -32603, message: `Dig MCP (${url}): ${reason}` } }));
|
|
2711
|
+
let response;
|
|
2712
|
+
try {
|
|
2713
|
+
response = await fetchImpl(url, {
|
|
2714
|
+
method: "POST",
|
|
2715
|
+
headers: {
|
|
2716
|
+
"content-type": "application/json",
|
|
2717
|
+
accept: "application/json, text/event-stream",
|
|
2718
|
+
"user-agent": `digdesign-cli/${CLI_VERSION}`,
|
|
2719
|
+
...sessionId && { "mcp-session-id": sessionId },
|
|
2720
|
+
...protocolVersion && { "mcp-protocol-version": protocolVersion }
|
|
2721
|
+
},
|
|
2722
|
+
body: line
|
|
2723
|
+
});
|
|
2724
|
+
} catch (error) {
|
|
2725
|
+
return fail(`network error: ${error.message}`);
|
|
2726
|
+
}
|
|
2727
|
+
sessionId = response.headers.get("mcp-session-id") ?? sessionId;
|
|
2728
|
+
if (response.status === 202) return [];
|
|
2729
|
+
const body = await response.text();
|
|
2730
|
+
if (!response.ok) return fail(`HTTP ${response.status} ${body.slice(0, 200)}`);
|
|
2731
|
+
let replies;
|
|
2732
|
+
if ((response.headers.get("content-type") ?? "").includes("text/event-stream")) replies = parseSse(body);
|
|
2733
|
+
else {
|
|
2734
|
+
try {
|
|
2735
|
+
replies = body.trim() ? [JSON.parse(body)] : [];
|
|
2736
|
+
} catch {
|
|
2737
|
+
return fail(`invalid JSON response: ${body.slice(0, 200)}`);
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
for (const reply of replies.flat()) {
|
|
2741
|
+
const result = reply?.result;
|
|
2742
|
+
if (result?.protocolVersion) protocolVersion = result.protocolVersion;
|
|
2743
|
+
}
|
|
2744
|
+
return replies;
|
|
2745
|
+
}
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
async function mcpCommand(options) {
|
|
2749
|
+
const url = options.url ?? process.env.DIG_MCP_URL ?? DEFAULT_MCP_URL;
|
|
2750
|
+
const bridge = createBridge(url);
|
|
2751
|
+
const pending = /* @__PURE__ */ new Set();
|
|
2752
|
+
process.stderr.write(`dig mcp: stdio bridge to ${url}
|
|
2753
|
+
`);
|
|
2754
|
+
const input = createInterface2({ input: process.stdin, crlfDelay: Infinity });
|
|
2755
|
+
for await (const line of input) {
|
|
2756
|
+
if (!line.trim()) continue;
|
|
2757
|
+
const task = bridge.handle(line).then((replies) => {
|
|
2758
|
+
for (const reply of replies) process.stdout.write(`${JSON.stringify(reply)}
|
|
2759
|
+
`);
|
|
2760
|
+
});
|
|
2761
|
+
pending.add(task);
|
|
2762
|
+
void task.finally(() => pending.delete(task));
|
|
2763
|
+
}
|
|
2764
|
+
await Promise.all(pending);
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2489
2767
|
// src/lib/migration-apply.ts
|
|
2490
|
-
import { readFile as
|
|
2491
|
-
import
|
|
2768
|
+
import { readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
|
|
2769
|
+
import path15 from "path";
|
|
2492
2770
|
|
|
2493
2771
|
// src/lib/migration-strategy.ts
|
|
2494
2772
|
var TextReplaceStrategy = class {
|
|
@@ -2565,7 +2843,7 @@ function looksBinary2(buffer) {
|
|
|
2565
2843
|
async function readTextFileSafely2(target) {
|
|
2566
2844
|
let buffer;
|
|
2567
2845
|
try {
|
|
2568
|
-
buffer = await
|
|
2846
|
+
buffer = await readFile11(target);
|
|
2569
2847
|
} catch (error) {
|
|
2570
2848
|
if (error.code === "ENOENT") return void 0;
|
|
2571
2849
|
throw error;
|
|
@@ -2610,8 +2888,8 @@ async function planOrApplyMigrations(params) {
|
|
|
2610
2888
|
};
|
|
2611
2889
|
}
|
|
2612
2890
|
const fileTargets = registryItem.files.map((file) => {
|
|
2613
|
-
const target =
|
|
2614
|
-
return { relPath: toPosixPath(
|
|
2891
|
+
const target = path15.join(cwd, resolveTargetPath(file.path, aliases, paths));
|
|
2892
|
+
return { relPath: toPosixPath(path15.relative(cwd, target)), target };
|
|
2615
2893
|
});
|
|
2616
2894
|
const currentContents = /* @__PURE__ */ new Map();
|
|
2617
2895
|
for (const ft of fileTargets) {
|
|
@@ -2677,12 +2955,12 @@ async function planOrApplyMigrations(params) {
|
|
|
2677
2955
|
const written = [];
|
|
2678
2956
|
try {
|
|
2679
2957
|
for (const item of pending) {
|
|
2680
|
-
await
|
|
2958
|
+
await writeFile9(item.target, item.newContent, "utf8");
|
|
2681
2959
|
written.push(item);
|
|
2682
2960
|
}
|
|
2683
2961
|
} catch (error) {
|
|
2684
2962
|
for (const item of written) {
|
|
2685
|
-
await
|
|
2963
|
+
await writeFile9(item.target, item.originalContent, "utf8");
|
|
2686
2964
|
}
|
|
2687
2965
|
return {
|
|
2688
2966
|
name,
|
|
@@ -3295,7 +3573,7 @@ ${entry.migrationHint}`);
|
|
|
3295
3573
|
|
|
3296
3574
|
// src/lib/missing-dependencies.ts
|
|
3297
3575
|
import { access as access6 } from "fs/promises";
|
|
3298
|
-
import
|
|
3576
|
+
import path16 from "path";
|
|
3299
3577
|
async function exists2(target) {
|
|
3300
3578
|
try {
|
|
3301
3579
|
await access6(target);
|
|
@@ -3316,7 +3594,7 @@ async function findMissingDependencies(params) {
|
|
|
3316
3594
|
for (const item of tree.values()) {
|
|
3317
3595
|
if (requested.has(item.name) || lock.components[item.name]) continue;
|
|
3318
3596
|
for (const file of item.files) {
|
|
3319
|
-
const target =
|
|
3597
|
+
const target = path16.join(
|
|
3320
3598
|
cwd,
|
|
3321
3599
|
resolveTargetPath(file.path, config.aliases, targetPathOptions(config))
|
|
3322
3600
|
);
|
|
@@ -3330,8 +3608,8 @@ async function findMissingDependencies(params) {
|
|
|
3330
3608
|
}
|
|
3331
3609
|
|
|
3332
3610
|
// src/lib/update.ts
|
|
3333
|
-
import { mkdir as mkdir5, readFile as
|
|
3334
|
-
import
|
|
3611
|
+
import { mkdir as mkdir5, readFile as readFile12, rm as rm2, writeFile as writeFile10 } from "fs/promises";
|
|
3612
|
+
import path17 from "path";
|
|
3335
3613
|
function safeFileIndexes(drift) {
|
|
3336
3614
|
const indexes = [];
|
|
3337
3615
|
drift.files.forEach((file, index) => {
|
|
@@ -3351,7 +3629,7 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun, paths)
|
|
|
3351
3629
|
for (const index of safeIndexes) {
|
|
3352
3630
|
const file = drift.files[index];
|
|
3353
3631
|
const registryFile = registryItem.files[index];
|
|
3354
|
-
const target =
|
|
3632
|
+
const target = path17.join(cwd, resolveTargetPath(registryFile.path, aliases, paths));
|
|
3355
3633
|
const isNewFile = file.status === "new_upstream";
|
|
3356
3634
|
pending.push({
|
|
3357
3635
|
index,
|
|
@@ -3359,14 +3637,14 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun, paths)
|
|
|
3359
3637
|
newContent: registryFile.content,
|
|
3360
3638
|
newHash: registryFile.hash ?? hashContent(registryFile.content),
|
|
3361
3639
|
isNewFile,
|
|
3362
|
-
originalContent: isNewFile ? void 0 : await
|
|
3640
|
+
originalContent: isNewFile ? void 0 : await readFile12(target, "utf8")
|
|
3363
3641
|
});
|
|
3364
3642
|
}
|
|
3365
3643
|
const written = [];
|
|
3366
3644
|
try {
|
|
3367
3645
|
for (const item of pending) {
|
|
3368
|
-
if (item.isNewFile) await mkdir5(
|
|
3369
|
-
await
|
|
3646
|
+
if (item.isNewFile) await mkdir5(path17.dirname(item.target), { recursive: true });
|
|
3647
|
+
await writeFile10(item.target, item.newContent, "utf8");
|
|
3370
3648
|
written.push(item);
|
|
3371
3649
|
}
|
|
3372
3650
|
} catch (error) {
|
|
@@ -3374,7 +3652,7 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun, paths)
|
|
|
3374
3652
|
if (item.isNewFile) {
|
|
3375
3653
|
await rm2(item.target, { force: true });
|
|
3376
3654
|
} else {
|
|
3377
|
-
await
|
|
3655
|
+
await writeFile10(item.target, item.originalContent, "utf8");
|
|
3378
3656
|
}
|
|
3379
3657
|
}
|
|
3380
3658
|
return { actions, writtenHashes: {}, rollbackError: error.message };
|
|
@@ -3512,6 +3790,9 @@ async function updateCommand(components, options) {
|
|
|
3512
3790
|
console.log = log;
|
|
3513
3791
|
}
|
|
3514
3792
|
}
|
|
3793
|
+
if (!dryRun && entries.length) {
|
|
3794
|
+
await refreshDesignMd(cwd, config.registryUrl, {}, options.json ? console.error : console.log);
|
|
3795
|
+
}
|
|
3515
3796
|
if (options.json) {
|
|
3516
3797
|
console.log(
|
|
3517
3798
|
JSON.stringify(
|
|
@@ -3694,18 +3975,17 @@ ${result.name}`);
|
|
|
3694
3975
|
}
|
|
3695
3976
|
}
|
|
3696
3977
|
|
|
3697
|
-
// src/lib/version.ts
|
|
3698
|
-
var CLI_VERSION = "0.5.0";
|
|
3699
|
-
|
|
3700
3978
|
// src/index.ts
|
|
3701
3979
|
var program = new Command();
|
|
3702
3980
|
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);
|
|
3703
3981
|
program.command("init").description(
|
|
3704
|
-
'\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
|
|
3705
|
-
).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").action(initCommand);
|
|
3982
|
+
'\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'
|
|
3983
|
+
).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);
|
|
3984
|
+
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);
|
|
3706
3985
|
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");
|
|
3707
3986
|
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);
|
|
3708
3987
|
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);
|
|
3988
|
+
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);
|
|
3709
3989
|
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);
|
|
3710
3990
|
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);
|
|
3711
3991
|
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);
|