glotfile 0.5.0 → 0.5.2
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/server/cli.js +331 -63
- package/dist/server/server.js +367 -141
- package/dist/ui/assets/{index-DC89onXX.js → index-B1UwtMSs.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/package.json +3 -2
- package/skill/SKILL.md +66 -0
- package/skill/references/cli-reference.md +84 -0
- package/skill/references/conventions.md +50 -0
- package/skill/references/schema.md +99 -0
- package/skill/references/workflows.md +84 -0
package/dist/server/cli.js
CHANGED
|
@@ -1445,8 +1445,58 @@ var init_apple_stringsdict = __esm({
|
|
|
1445
1445
|
}
|
|
1446
1446
|
});
|
|
1447
1447
|
|
|
1448
|
+
// src/server/adapters/apple-strings.ts
|
|
1449
|
+
function escape(s) {
|
|
1450
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\t/g, "\\t").replace(/\r/g, "\\r");
|
|
1451
|
+
}
|
|
1452
|
+
var DEFAULT_LOCALE_CASE6, appleStrings;
|
|
1453
|
+
var init_apple_strings = __esm({
|
|
1454
|
+
"src/server/adapters/apple-strings.ts"() {
|
|
1455
|
+
"use strict";
|
|
1456
|
+
init_adapters();
|
|
1457
|
+
init_options();
|
|
1458
|
+
DEFAULT_LOCALE_CASE6 = "bcp47-hyphen";
|
|
1459
|
+
appleStrings = {
|
|
1460
|
+
name: "apple-strings",
|
|
1461
|
+
capabilities: {
|
|
1462
|
+
// Plurals belong in .stringsdict (apple-stringsdict), not the scalar table.
|
|
1463
|
+
plural: "none",
|
|
1464
|
+
select: "none",
|
|
1465
|
+
nesting: "flat",
|
|
1466
|
+
metadata: false,
|
|
1467
|
+
placeholderStyle: "printf",
|
|
1468
|
+
fileGrouping: "per-locale"
|
|
1469
|
+
},
|
|
1470
|
+
defaultLocaleCase: DEFAULT_LOCALE_CASE6,
|
|
1471
|
+
export(state, output) {
|
|
1472
|
+
const files = [];
|
|
1473
|
+
const warnings = [];
|
|
1474
|
+
warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE6));
|
|
1475
|
+
const emptyAs = resolveEmptyAs(output, "source");
|
|
1476
|
+
const keys = Object.keys(state.keys).sort();
|
|
1477
|
+
for (const locale of state.config.locales) {
|
|
1478
|
+
const lines = [];
|
|
1479
|
+
for (const key of keys) {
|
|
1480
|
+
const entry = state.keys[key];
|
|
1481
|
+
if (entry.plural) continue;
|
|
1482
|
+
const value = resolveScalar(entry, locale, state.config.sourceLocale, emptyAs);
|
|
1483
|
+
if (value === null) continue;
|
|
1484
|
+
lines.push(`"${escape(key)}" = "${escape(value)}";`);
|
|
1485
|
+
}
|
|
1486
|
+
const contents = lines.length ? lines.join("\n") + "\n" : "";
|
|
1487
|
+
files.push({
|
|
1488
|
+
path: resolvePath(output.path, resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE6)),
|
|
1489
|
+
contents
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
return { files, warnings };
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
});
|
|
1497
|
+
|
|
1448
1498
|
// src/server/adapters/vue-i18n-json.ts
|
|
1449
|
-
var
|
|
1499
|
+
var DEFAULT_LOCALE_CASE7, vueI18nJson;
|
|
1450
1500
|
var init_vue_i18n_json = __esm({
|
|
1451
1501
|
"src/server/adapters/vue-i18n-json.ts"() {
|
|
1452
1502
|
"use strict";
|
|
@@ -1456,7 +1506,7 @@ var init_vue_i18n_json = __esm({
|
|
|
1456
1506
|
init_format();
|
|
1457
1507
|
init_placeholders();
|
|
1458
1508
|
init_schema();
|
|
1459
|
-
|
|
1509
|
+
DEFAULT_LOCALE_CASE7 = "lower-hyphen";
|
|
1460
1510
|
vueI18nJson = {
|
|
1461
1511
|
name: "vue-i18n-json",
|
|
1462
1512
|
capabilities: {
|
|
@@ -1467,11 +1517,11 @@ var init_vue_i18n_json = __esm({
|
|
|
1467
1517
|
placeholderStyle: "named",
|
|
1468
1518
|
fileGrouping: "per-locale"
|
|
1469
1519
|
},
|
|
1470
|
-
defaultLocaleCase:
|
|
1520
|
+
defaultLocaleCase: DEFAULT_LOCALE_CASE7,
|
|
1471
1521
|
export(state, output) {
|
|
1472
1522
|
const files = [];
|
|
1473
1523
|
const warnings = [];
|
|
1474
|
-
warnings.push(...localeCollisionWarnings(output, state.config.locales,
|
|
1524
|
+
warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE7));
|
|
1475
1525
|
const { indent, finalNewline } = resolveFormat(state, output);
|
|
1476
1526
|
const fmt = { indent, sortKeys: true, finalNewline };
|
|
1477
1527
|
const emptyAs = resolveEmptyAs(output, "omit");
|
|
@@ -1511,7 +1561,7 @@ var init_vue_i18n_json = __esm({
|
|
|
1511
1561
|
}
|
|
1512
1562
|
payload = tree;
|
|
1513
1563
|
}
|
|
1514
|
-
files.push({ path: resolvePath(output.path, resolveLocaleToken(output, locale,
|
|
1564
|
+
files.push({ path: resolvePath(output.path, resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE7)), contents: serializeJson(payload, fmt) });
|
|
1515
1565
|
}
|
|
1516
1566
|
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
1517
1567
|
return { files, warnings };
|
|
@@ -1557,7 +1607,7 @@ function renderEmbeddedIcu(value) {
|
|
|
1557
1607
|
function renderScalar(value, ids) {
|
|
1558
1608
|
return isIcuPluralOrSelect(value) ? renderEmbeddedIcu(value) : renderInterpolations(value, ids);
|
|
1559
1609
|
}
|
|
1560
|
-
var
|
|
1610
|
+
var DEFAULT_LOCALE_CASE8, angularXliff;
|
|
1561
1611
|
var init_angular_xliff = __esm({
|
|
1562
1612
|
"src/server/adapters/angular-xliff.ts"() {
|
|
1563
1613
|
"use strict";
|
|
@@ -1565,7 +1615,7 @@ var init_angular_xliff = __esm({
|
|
|
1565
1615
|
init_options();
|
|
1566
1616
|
init_placeholders();
|
|
1567
1617
|
init_schema();
|
|
1568
|
-
|
|
1618
|
+
DEFAULT_LOCALE_CASE8 = "bcp47-hyphen";
|
|
1569
1619
|
angularXliff = {
|
|
1570
1620
|
name: "angular-xliff",
|
|
1571
1621
|
capabilities: {
|
|
@@ -1576,17 +1626,17 @@ var init_angular_xliff = __esm({
|
|
|
1576
1626
|
placeholderStyle: "icu",
|
|
1577
1627
|
fileGrouping: "per-locale"
|
|
1578
1628
|
},
|
|
1579
|
-
defaultLocaleCase:
|
|
1629
|
+
defaultLocaleCase: DEFAULT_LOCALE_CASE8,
|
|
1580
1630
|
export(state, output) {
|
|
1581
1631
|
const files = [];
|
|
1582
1632
|
const warnings = [];
|
|
1583
|
-
warnings.push(...localeCollisionWarnings(output, state.config.locales,
|
|
1633
|
+
warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE8));
|
|
1584
1634
|
const sourceLocale = state.config.sourceLocale;
|
|
1585
|
-
const sourceToken = resolveLocaleToken(output, sourceLocale,
|
|
1635
|
+
const sourceToken = resolveLocaleToken(output, sourceLocale, DEFAULT_LOCALE_CASE8);
|
|
1586
1636
|
const emptyAs = resolveEmptyAs(output, "source");
|
|
1587
1637
|
const keys = Object.keys(state.keys).sort();
|
|
1588
1638
|
for (const locale of state.config.locales) {
|
|
1589
|
-
const token = resolveLocaleToken(output, locale,
|
|
1639
|
+
const token = resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE8);
|
|
1590
1640
|
const units = [];
|
|
1591
1641
|
for (const key of keys) {
|
|
1592
1642
|
const entry = state.keys[key];
|
|
@@ -1650,7 +1700,7 @@ function yamlMap(node, indent, level) {
|
|
|
1650
1700
|
}
|
|
1651
1701
|
return lines;
|
|
1652
1702
|
}
|
|
1653
|
-
var RESERVED_KEYS,
|
|
1703
|
+
var RESERVED_KEYS, DEFAULT_LOCALE_CASE9, railsYaml;
|
|
1654
1704
|
var init_rails_yaml = __esm({
|
|
1655
1705
|
"src/server/adapters/rails-yaml.ts"() {
|
|
1656
1706
|
"use strict";
|
|
@@ -1660,7 +1710,7 @@ var init_rails_yaml = __esm({
|
|
|
1660
1710
|
init_placeholders();
|
|
1661
1711
|
init_schema();
|
|
1662
1712
|
RESERVED_KEYS = /* @__PURE__ */ new Set(["true", "false", "yes", "no", "on", "off", "null", "y", "n"]);
|
|
1663
|
-
|
|
1713
|
+
DEFAULT_LOCALE_CASE9 = "bcp47-hyphen";
|
|
1664
1714
|
railsYaml = {
|
|
1665
1715
|
name: "rails-yaml",
|
|
1666
1716
|
capabilities: {
|
|
@@ -1671,10 +1721,10 @@ var init_rails_yaml = __esm({
|
|
|
1671
1721
|
placeholderStyle: "named",
|
|
1672
1722
|
fileGrouping: "per-locale"
|
|
1673
1723
|
},
|
|
1674
|
-
defaultLocaleCase:
|
|
1724
|
+
defaultLocaleCase: DEFAULT_LOCALE_CASE9,
|
|
1675
1725
|
export(state, output) {
|
|
1676
1726
|
const warnings = [];
|
|
1677
|
-
warnings.push(...localeCollisionWarnings(output, state.config.locales,
|
|
1727
|
+
warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE9));
|
|
1678
1728
|
const { indent, finalNewline } = resolveFormat(state, output);
|
|
1679
1729
|
const emptyAs = resolveEmptyAs(output, "omit");
|
|
1680
1730
|
const files = [];
|
|
@@ -1706,7 +1756,7 @@ var init_rails_yaml = __esm({
|
|
|
1706
1756
|
for (const c of collisions) {
|
|
1707
1757
|
warnings.push({ code: "key-collision", key: c, locale, message: "key is both a leaf and a parent; dropped from nested output" });
|
|
1708
1758
|
}
|
|
1709
|
-
const token = resolveLocaleToken(output, locale,
|
|
1759
|
+
const token = resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE9);
|
|
1710
1760
|
const body = [`${yamlKey(token)}:`, ...yamlMap(nested, indent, 1)].join("\n");
|
|
1711
1761
|
files.push({ path: resolvePath(output.path, token), contents: finalNewline ? body + "\n" : body });
|
|
1712
1762
|
}
|
|
@@ -1748,6 +1798,7 @@ function getRegistry() {
|
|
|
1748
1798
|
[i18nextJson.name]: i18nextJson,
|
|
1749
1799
|
[gettextPo.name]: gettextPo,
|
|
1750
1800
|
[appleStringsdict.name]: appleStringsdict,
|
|
1801
|
+
[appleStrings.name]: appleStrings,
|
|
1751
1802
|
[vueI18nJson.name]: vueI18nJson,
|
|
1752
1803
|
[angularXliff.name]: angularXliff,
|
|
1753
1804
|
[railsYaml.name]: railsYaml
|
|
@@ -1768,6 +1819,7 @@ var init_adapters = __esm({
|
|
|
1768
1819
|
init_i18next_json();
|
|
1769
1820
|
init_gettext_po();
|
|
1770
1821
|
init_apple_stringsdict();
|
|
1822
|
+
init_apple_strings();
|
|
1771
1823
|
init_vue_i18n_json();
|
|
1772
1824
|
init_angular_xliff();
|
|
1773
1825
|
init_rails_yaml();
|
|
@@ -2806,7 +2858,7 @@ function attachScreenshotsForProvider(reqs, state, projectRoot, supportsVision)
|
|
|
2806
2858
|
const keys = new Set(reqs.filter((r) => state.keys[r.key]?.screenshot).map((r) => r.key));
|
|
2807
2859
|
return { skipped: keys.size };
|
|
2808
2860
|
}
|
|
2809
|
-
async function runLocaleParallel(reqs, provider, hooks = {}, concurrency = DEFAULT_LOCALE_CONCURRENCY, signal) {
|
|
2861
|
+
async function runLocaleParallel(reqs, provider, hooks = {}, concurrency = DEFAULT_LOCALE_CONCURRENCY, signal, batchSize = Infinity) {
|
|
2810
2862
|
if (!reqs.length) return [];
|
|
2811
2863
|
const byLocale = /* @__PURE__ */ new Map();
|
|
2812
2864
|
for (const req of reqs) {
|
|
@@ -2817,26 +2869,42 @@ async function runLocaleParallel(reqs, provider, hooks = {}, concurrency = DEFAU
|
|
|
2817
2869
|
}
|
|
2818
2870
|
group.push(req);
|
|
2819
2871
|
}
|
|
2872
|
+
const localeBatches = [...byLocale.entries()].map(([locale, group]) => ({
|
|
2873
|
+
locale,
|
|
2874
|
+
batches: chunk(group, Math.max(1, batchSize))
|
|
2875
|
+
}));
|
|
2876
|
+
const jobs = [];
|
|
2877
|
+
const maxBatches = Math.max(...localeBatches.map((g) => g.batches.length));
|
|
2878
|
+
for (let i = 0; i < maxBatches; i++) {
|
|
2879
|
+
for (const g of localeBatches) {
|
|
2880
|
+
if (i < g.batches.length) jobs.push({ locale: g.locale, batch: g.batches[i] });
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
const remaining = new Map(localeBatches.map((g) => [g.locale, g.batches.length]));
|
|
2884
|
+
const started = /* @__PURE__ */ new Set();
|
|
2820
2885
|
const total = reqs.length;
|
|
2821
2886
|
let done = 0;
|
|
2822
2887
|
const allResults = [];
|
|
2823
|
-
const groups = [...byLocale.values()];
|
|
2824
2888
|
let next = 0;
|
|
2825
2889
|
async function worker() {
|
|
2826
|
-
while (next <
|
|
2890
|
+
while (next < jobs.length) {
|
|
2827
2891
|
if (signal?.aborted) break;
|
|
2828
|
-
const
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2892
|
+
const { locale, batch } = jobs[next++];
|
|
2893
|
+
if (!started.has(locale)) {
|
|
2894
|
+
started.add(locale);
|
|
2895
|
+
hooks.onLocaleStart?.(locale);
|
|
2896
|
+
}
|
|
2897
|
+
const batchResults = await provider.translate(batch, (_localeDone, _localeTotal, results) => {
|
|
2898
|
+
done += results.length;
|
|
2899
|
+
hooks.onBatchComplete?.(done, total, results, locale);
|
|
2900
|
+
}, signal, (raw, size) => hooks.onMalformedReply?.(raw, size, locale));
|
|
2901
|
+
allResults.push(...batchResults);
|
|
2902
|
+
const left = remaining.get(locale) - 1;
|
|
2903
|
+
remaining.set(locale, left);
|
|
2904
|
+
if (left === 0 && !signal?.aborted) hooks.onLocaleDone?.(locale);
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
const workers = Array.from({ length: Math.min(concurrency, jobs.length) }, worker);
|
|
2840
2908
|
await Promise.all(workers);
|
|
2841
2909
|
return allResults;
|
|
2842
2910
|
}
|
|
@@ -2872,6 +2940,7 @@ var init_run = __esm({
|
|
|
2872
2940
|
init_plurals();
|
|
2873
2941
|
init_state();
|
|
2874
2942
|
init_glob();
|
|
2943
|
+
init_batch();
|
|
2875
2944
|
MEDIA_TYPES = {
|
|
2876
2945
|
".png": "image/png",
|
|
2877
2946
|
".jpg": "image/jpeg",
|
|
@@ -3292,7 +3361,11 @@ var init_scanner = __esm({
|
|
|
3292
3361
|
apple: [
|
|
3293
3362
|
/NSLocalizedString\s*\(\s*@?"([^"]+)"/g,
|
|
3294
3363
|
/String\s*\(\s*localized:\s*"([^"]+)"/g,
|
|
3295
|
-
/localizedString\s*\(\s*forKey:\s*"([^"]+)"/g
|
|
3364
|
+
/localizedString\s*\(\s*forKey:\s*"([^"]+)"/g,
|
|
3365
|
+
// The "key".localized / "key".localised String-extension idiom, where the
|
|
3366
|
+
// literal IS the key (common when keys are natural-language source text).
|
|
3367
|
+
/"([^"]+)"\s*\.\s*localized\b/g,
|
|
3368
|
+
/"([^"]+)"\s*\.\s*localised\b/g
|
|
3296
3369
|
]
|
|
3297
3370
|
};
|
|
3298
3371
|
PREFIX_PATTERNS = {
|
|
@@ -3310,7 +3383,7 @@ var init_scanner = __esm({
|
|
|
3310
3383
|
/(?<!\.)(?<![a-zA-Z0-9_$])\bt\s*\(\s*`([^`$]*)\$\{/g
|
|
3311
3384
|
]
|
|
3312
3385
|
};
|
|
3313
|
-
CACHE_VERSION =
|
|
3386
|
+
CACHE_VERSION = 5;
|
|
3314
3387
|
EXT_SCANNER = {
|
|
3315
3388
|
".php": "laravel",
|
|
3316
3389
|
".vue": "js-i18n",
|
|
@@ -3950,6 +4023,32 @@ function detectArb(root) {
|
|
|
3950
4023
|
}
|
|
3951
4024
|
return null;
|
|
3952
4025
|
}
|
|
4026
|
+
function lprojLocales(dir) {
|
|
4027
|
+
return listDirs(dir).map((d) => d.match(/^(.+)\.lproj$/)?.[1]).filter((l) => !!l && LOCALE_RE.test(l) && existsSync10(join4(dir, `${l}.lproj`, "Localizable.strings")));
|
|
4028
|
+
}
|
|
4029
|
+
function detectApple(root) {
|
|
4030
|
+
const candidates = [root, ...listDirs(root).map((d) => join4(root, d))];
|
|
4031
|
+
let best = null;
|
|
4032
|
+
for (const dir of candidates) {
|
|
4033
|
+
const locales = lprojLocales(dir);
|
|
4034
|
+
if (locales.length === 0) continue;
|
|
4035
|
+
if (!best || locales.length > best.locales.length) {
|
|
4036
|
+
best = {
|
|
4037
|
+
format: "apple-strings",
|
|
4038
|
+
localeRoot: dir,
|
|
4039
|
+
locales,
|
|
4040
|
+
sourceLocale: pickSource(locales, (loc) => {
|
|
4041
|
+
try {
|
|
4042
|
+
return statSync3(join4(dir, `${loc}.lproj`, "Localizable.strings")).size;
|
|
4043
|
+
} catch {
|
|
4044
|
+
return 0;
|
|
4045
|
+
}
|
|
4046
|
+
})
|
|
4047
|
+
};
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
return best;
|
|
4051
|
+
}
|
|
3953
4052
|
function detect(root, formatOverride) {
|
|
3954
4053
|
if (!existsSync10(root)) return null;
|
|
3955
4054
|
if (formatOverride) {
|
|
@@ -3969,11 +4068,12 @@ var init_detect = __esm({
|
|
|
3969
4068
|
"use strict";
|
|
3970
4069
|
LOCALE_RE = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
|
|
3971
4070
|
VUE_DIR_CANDIDATES = ["src/locale", "src/locales", "src/i18n/locales", "locales", "lang"];
|
|
3972
|
-
DETECTORS = [detectLaravel, detectVue, detectArb];
|
|
4071
|
+
DETECTORS = [detectLaravel, detectVue, detectArb, detectApple];
|
|
3973
4072
|
BY_FORMAT = {
|
|
3974
4073
|
"laravel-php": detectLaravel,
|
|
3975
4074
|
"vue-i18n-json": (root) => detectVue(root, true),
|
|
3976
|
-
"flutter-arb": detectArb
|
|
4075
|
+
"flutter-arb": detectArb,
|
|
4076
|
+
"apple-strings": detectApple
|
|
3977
4077
|
};
|
|
3978
4078
|
}
|
|
3979
4079
|
});
|
|
@@ -4199,6 +4299,139 @@ var init_flutter_arb2 = __esm({
|
|
|
4199
4299
|
}
|
|
4200
4300
|
});
|
|
4201
4301
|
|
|
4302
|
+
// src/server/import/parsers/apple-strings.ts
|
|
4303
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync5 } from "fs";
|
|
4304
|
+
import { join as join8 } from "path";
|
|
4305
|
+
function localeFromLproj(dir) {
|
|
4306
|
+
const m = dir.match(/^(.+)\.lproj$/);
|
|
4307
|
+
if (!m) return null;
|
|
4308
|
+
return LOCALE_RE4.test(m[1]) ? m[1] : null;
|
|
4309
|
+
}
|
|
4310
|
+
function unescape(body) {
|
|
4311
|
+
return body.replace(/\\(U[0-9a-fA-F]{4}|u[0-9a-fA-F]{4}|.)/g, (_m, esc) => {
|
|
4312
|
+
const c = esc[0];
|
|
4313
|
+
if (c === "U" || c === "u") return String.fromCharCode(parseInt(esc.slice(1), 16));
|
|
4314
|
+
if (c === "n") return "\n";
|
|
4315
|
+
if (c === "t") return " ";
|
|
4316
|
+
if (c === "r") return "\r";
|
|
4317
|
+
return esc;
|
|
4318
|
+
});
|
|
4319
|
+
}
|
|
4320
|
+
function parseStrings(text, file, warnings) {
|
|
4321
|
+
const pairs = [];
|
|
4322
|
+
let i = 0;
|
|
4323
|
+
const n = text.length;
|
|
4324
|
+
const skipTrivia = () => {
|
|
4325
|
+
while (i < n) {
|
|
4326
|
+
const c = text[i];
|
|
4327
|
+
if (c === " " || c === " " || c === "\n" || c === "\r") {
|
|
4328
|
+
i++;
|
|
4329
|
+
continue;
|
|
4330
|
+
}
|
|
4331
|
+
if (c === "/" && text[i + 1] === "/") {
|
|
4332
|
+
i += 2;
|
|
4333
|
+
while (i < n && text[i] !== "\n") i++;
|
|
4334
|
+
continue;
|
|
4335
|
+
}
|
|
4336
|
+
if (c === "/" && text[i + 1] === "*") {
|
|
4337
|
+
i += 2;
|
|
4338
|
+
while (i < n && !(text[i] === "*" && text[i + 1] === "/")) i++;
|
|
4339
|
+
i += 2;
|
|
4340
|
+
continue;
|
|
4341
|
+
}
|
|
4342
|
+
break;
|
|
4343
|
+
}
|
|
4344
|
+
};
|
|
4345
|
+
const readToken = () => {
|
|
4346
|
+
if (i >= n) return null;
|
|
4347
|
+
if (text[i] === '"') {
|
|
4348
|
+
i++;
|
|
4349
|
+
let raw2 = "";
|
|
4350
|
+
while (i < n) {
|
|
4351
|
+
const c = text[i];
|
|
4352
|
+
if (c === "\\") {
|
|
4353
|
+
raw2 += c + (text[i + 1] ?? "");
|
|
4354
|
+
i += 2;
|
|
4355
|
+
continue;
|
|
4356
|
+
}
|
|
4357
|
+
if (c === '"') {
|
|
4358
|
+
i++;
|
|
4359
|
+
return unescape(raw2);
|
|
4360
|
+
}
|
|
4361
|
+
raw2 += c;
|
|
4362
|
+
i++;
|
|
4363
|
+
}
|
|
4364
|
+
return null;
|
|
4365
|
+
}
|
|
4366
|
+
let raw = "";
|
|
4367
|
+
while (i < n && !/[\s=;]/.test(text[i])) raw += text[i++];
|
|
4368
|
+
return raw.length ? raw : null;
|
|
4369
|
+
};
|
|
4370
|
+
while (true) {
|
|
4371
|
+
skipTrivia();
|
|
4372
|
+
if (i >= n) break;
|
|
4373
|
+
const key = readToken();
|
|
4374
|
+
if (key === null) {
|
|
4375
|
+
warnings.push(`apple-strings: malformed entry in ${file} near offset ${i}`);
|
|
4376
|
+
break;
|
|
4377
|
+
}
|
|
4378
|
+
skipTrivia();
|
|
4379
|
+
if (text[i] !== "=") {
|
|
4380
|
+
warnings.push(`apple-strings: expected '=' after key "${key}" in ${file}`);
|
|
4381
|
+
break;
|
|
4382
|
+
}
|
|
4383
|
+
i++;
|
|
4384
|
+
skipTrivia();
|
|
4385
|
+
const value = readToken();
|
|
4386
|
+
if (value === null) {
|
|
4387
|
+
warnings.push(`apple-strings: missing value for key "${key}" in ${file}`);
|
|
4388
|
+
break;
|
|
4389
|
+
}
|
|
4390
|
+
skipTrivia();
|
|
4391
|
+
if (text[i] === ";") i++;
|
|
4392
|
+
pairs.push({ key, value });
|
|
4393
|
+
}
|
|
4394
|
+
return pairs;
|
|
4395
|
+
}
|
|
4396
|
+
var LOCALE_RE4, TABLE, appleStrings2;
|
|
4397
|
+
var init_apple_strings2 = __esm({
|
|
4398
|
+
"src/server/import/parsers/apple-strings.ts"() {
|
|
4399
|
+
"use strict";
|
|
4400
|
+
LOCALE_RE4 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
|
|
4401
|
+
TABLE = "Localizable.strings";
|
|
4402
|
+
appleStrings2 = {
|
|
4403
|
+
name: "apple-strings",
|
|
4404
|
+
parse(localeRoot, opts) {
|
|
4405
|
+
const warnings = [];
|
|
4406
|
+
const keys = {};
|
|
4407
|
+
const locales = [];
|
|
4408
|
+
for (const dir of readdirSync8(localeRoot).sort()) {
|
|
4409
|
+
const locale = localeFromLproj(dir);
|
|
4410
|
+
if (!locale) continue;
|
|
4411
|
+
if (opts?.locales && !opts.locales.includes(locale)) continue;
|
|
4412
|
+
const file = join8(localeRoot, dir, TABLE);
|
|
4413
|
+
let text;
|
|
4414
|
+
try {
|
|
4415
|
+
if (!statSync5(file).isFile()) continue;
|
|
4416
|
+
text = readFileSync13(file, "utf8");
|
|
4417
|
+
} catch {
|
|
4418
|
+
continue;
|
|
4419
|
+
}
|
|
4420
|
+
locales.push(locale);
|
|
4421
|
+
const others = readdirSync8(join8(localeRoot, dir)).filter((f) => f.endsWith(".strings") && f !== TABLE);
|
|
4422
|
+
if (others.length) {
|
|
4423
|
+
warnings.push(`apple-strings: ${dir} has other .strings tables (${others.join(", ")}); only ${TABLE} is imported`);
|
|
4424
|
+
}
|
|
4425
|
+
for (const { key, value } of parseStrings(text, file, warnings)) {
|
|
4426
|
+
(keys[key] ??= { values: {} }).values[locale] = value;
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
return { locales, keys, warnings };
|
|
4430
|
+
}
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4433
|
+
});
|
|
4434
|
+
|
|
4202
4435
|
// src/server/import/parsers/index.ts
|
|
4203
4436
|
function getParser(name) {
|
|
4204
4437
|
const p = REGISTRY[name];
|
|
@@ -4212,10 +4445,12 @@ var init_parsers = __esm({
|
|
|
4212
4445
|
init_vue_i18n_json2();
|
|
4213
4446
|
init_laravel_php2();
|
|
4214
4447
|
init_flutter_arb2();
|
|
4448
|
+
init_apple_strings2();
|
|
4215
4449
|
REGISTRY = {
|
|
4216
4450
|
[vueI18nJson2.name]: vueI18nJson2,
|
|
4217
4451
|
[laravelPhp2.name]: laravelPhp2,
|
|
4218
|
-
[flutterArb2.name]: flutterArb2
|
|
4452
|
+
[flutterArb2.name]: flutterArb2,
|
|
4453
|
+
[appleStrings2.name]: appleStrings2
|
|
4219
4454
|
};
|
|
4220
4455
|
}
|
|
4221
4456
|
});
|
|
@@ -4225,10 +4460,13 @@ function assemble2(parsed, opts) {
|
|
|
4225
4460
|
const warnings = [...parsed.warnings];
|
|
4226
4461
|
const base = OUTPUT_BY_FORMAT[opts.format];
|
|
4227
4462
|
if (!base) throw new Error(`No output mapping for format "${opts.format}"`);
|
|
4463
|
+
const prefix = (opts.localeRootRel ?? "").replace(/\\/g, "/").replace(/\/+$/, "");
|
|
4464
|
+
const path = base.rootRelative && prefix ? `${prefix}/${base.path}` : base.path;
|
|
4228
4465
|
const rawLocales = [.../* @__PURE__ */ new Set([opts.sourceLocale, ...parsed.locales])];
|
|
4229
4466
|
const pairs = rawLocales.map((obs) => [canonLocale(obs), obs]);
|
|
4230
4467
|
const inferred = inferLocaleStyle(pairs, getAdapter(base.adapter).defaultLocaleCase);
|
|
4231
|
-
const
|
|
4468
|
+
const { rootRelative: _rootRelative, ...baseOutput } = base;
|
|
4469
|
+
const output = { ...baseOutput, path, ...inferred };
|
|
4232
4470
|
const sourceLocale = canonLocale(opts.sourceLocale);
|
|
4233
4471
|
const locales = [...new Set(rawLocales.map(canonLocale))].sort();
|
|
4234
4472
|
const keys = {};
|
|
@@ -4297,7 +4535,8 @@ var init_assemble = __esm({
|
|
|
4297
4535
|
OUTPUT_BY_FORMAT = {
|
|
4298
4536
|
"laravel-php": { adapter: "laravel-php", path: "lang/{locale}/{namespace}.php" },
|
|
4299
4537
|
"vue-i18n-json": { adapter: "vue-i18n-json", path: "src/locale/{locale}.json" },
|
|
4300
|
-
"flutter-arb": { adapter: "flutter-arb", path: "lib/l10n/app_{locale}.arb" }
|
|
4538
|
+
"flutter-arb": { adapter: "flutter-arb", path: "lib/l10n/app_{locale}.arb" },
|
|
4539
|
+
"apple-strings": { adapter: "apple-strings", path: "{locale}.lproj/Localizable.strings", rootRelative: true }
|
|
4301
4540
|
};
|
|
4302
4541
|
}
|
|
4303
4542
|
});
|
|
@@ -4308,6 +4547,7 @@ __export(run_exports, {
|
|
|
4308
4547
|
previewImport: () => previewImport,
|
|
4309
4548
|
runImport: () => runImport
|
|
4310
4549
|
});
|
|
4550
|
+
import { relative as relative3 } from "path";
|
|
4311
4551
|
function previewImport(projectRoot, format) {
|
|
4312
4552
|
const det = detect(projectRoot, format);
|
|
4313
4553
|
if (!det) return null;
|
|
@@ -4341,7 +4581,8 @@ function runImport(opts) {
|
|
|
4341
4581
|
const assembled = assemble2(parsed, {
|
|
4342
4582
|
sourceLocale: opts.sourceLocale ?? det.sourceLocale,
|
|
4343
4583
|
format: det.format,
|
|
4344
|
-
cldr: opts.cldr
|
|
4584
|
+
cldr: opts.cldr,
|
|
4585
|
+
localeRootRel: relative3(opts.projectRoot, det.localeRoot)
|
|
4345
4586
|
});
|
|
4346
4587
|
const { warnings, ...rest } = assembled;
|
|
4347
4588
|
const state = validate(rest);
|
|
@@ -4717,12 +4958,12 @@ var init_checks = __esm({
|
|
|
4717
4958
|
});
|
|
4718
4959
|
|
|
4719
4960
|
// src/server/ui-prefs.ts
|
|
4720
|
-
import { readFileSync as
|
|
4961
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
4721
4962
|
import { homedir } from "os";
|
|
4722
|
-
import { join as
|
|
4963
|
+
import { join as join9 } from "path";
|
|
4723
4964
|
function readJson2(path) {
|
|
4724
4965
|
try {
|
|
4725
|
-
const parsed = JSON.parse(
|
|
4966
|
+
const parsed = JSON.parse(readFileSync14(path, "utf8"));
|
|
4726
4967
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
4727
4968
|
} catch {
|
|
4728
4969
|
return {};
|
|
@@ -4747,7 +4988,7 @@ var init_ui_prefs = __esm({
|
|
|
4747
4988
|
THEMES = ["system", "light", "dark"];
|
|
4748
4989
|
isThemeMode = (v) => THEMES.includes(v);
|
|
4749
4990
|
isPanelWidth = (v) => typeof v === "number" && Number.isFinite(v) && v >= 120 && v <= 1200;
|
|
4750
|
-
defaultUiPrefsPath = () =>
|
|
4991
|
+
defaultUiPrefsPath = () => join9(homedir(), ".glotfile", "ui.json");
|
|
4751
4992
|
DEFAULTS = { theme: "system" };
|
|
4752
4993
|
}
|
|
4753
4994
|
});
|
|
@@ -4755,13 +4996,13 @@ var init_ui_prefs = __esm({
|
|
|
4755
4996
|
// src/server/api.ts
|
|
4756
4997
|
import { Hono } from "hono";
|
|
4757
4998
|
import { streamSSE } from "hono/streaming";
|
|
4758
|
-
import { readFileSync as
|
|
4759
|
-
import { dirname as dirname3, resolve as resolve9, basename, relative as
|
|
4999
|
+
import { readFileSync as readFileSync15, existsSync as existsSync11, readdirSync as readdirSync9, statSync as statSync6, rmSync as rmSync4 } from "fs";
|
|
5000
|
+
import { dirname as dirname3, resolve as resolve9, basename, relative as relative4, sep as sep2 } from "path";
|
|
4760
5001
|
function projectName(root) {
|
|
4761
5002
|
const nameFile = resolve9(root, ".idea", ".name");
|
|
4762
5003
|
if (existsSync11(nameFile)) {
|
|
4763
5004
|
try {
|
|
4764
|
-
const name =
|
|
5005
|
+
const name = readFileSync15(nameFile, "utf8").trim();
|
|
4765
5006
|
if (name) return name;
|
|
4766
5007
|
} catch {
|
|
4767
5008
|
}
|
|
@@ -4876,7 +5117,7 @@ function createApi(deps) {
|
|
|
4876
5117
|
app.get("/file", (c) => c.json({ path: deps.statePath, name: basename(deps.statePath), dir: projectRoot, project: basename(projectRoot) }));
|
|
4877
5118
|
app.get("/files", (c) => {
|
|
4878
5119
|
const found = /* @__PURE__ */ new Map();
|
|
4879
|
-
const activeRel =
|
|
5120
|
+
const activeRel = relative4(projectRoot, deps.statePath);
|
|
4880
5121
|
found.set(deps.statePath, {
|
|
4881
5122
|
name: basename(deps.statePath),
|
|
4882
5123
|
path: deps.statePath,
|
|
@@ -4886,7 +5127,7 @@ function createApi(deps) {
|
|
|
4886
5127
|
if (depth > 4) return;
|
|
4887
5128
|
let entries = [];
|
|
4888
5129
|
try {
|
|
4889
|
-
entries =
|
|
5130
|
+
entries = readdirSync9(dir);
|
|
4890
5131
|
} catch {
|
|
4891
5132
|
return;
|
|
4892
5133
|
}
|
|
@@ -4900,7 +5141,7 @@ function createApi(deps) {
|
|
|
4900
5141
|
filePath = abs;
|
|
4901
5142
|
} else {
|
|
4902
5143
|
try {
|
|
4903
|
-
if (
|
|
5144
|
+
if (statSync6(abs).isDirectory()) walk(abs, depth + 1);
|
|
4904
5145
|
} catch {
|
|
4905
5146
|
}
|
|
4906
5147
|
continue;
|
|
@@ -4908,7 +5149,7 @@ function createApi(deps) {
|
|
|
4908
5149
|
if (found.has(filePath)) continue;
|
|
4909
5150
|
try {
|
|
4910
5151
|
loadState(filePath);
|
|
4911
|
-
const rel =
|
|
5152
|
+
const rel = relative4(projectRoot, filePath);
|
|
4912
5153
|
found.set(filePath, { name: basename(filePath), path: filePath, relDir: rel !== basename(filePath) ? dirname3(rel) : void 0 });
|
|
4913
5154
|
} catch {
|
|
4914
5155
|
}
|
|
@@ -4987,7 +5228,7 @@ function createApi(deps) {
|
|
|
4987
5228
|
for (const e of Object.values(s.keys)) if (e.screenshot === screenshot) return;
|
|
4988
5229
|
const root = dirname3(resolve9(deps.statePath));
|
|
4989
5230
|
const abs = resolve9(root, screenshot);
|
|
4990
|
-
const rel =
|
|
5231
|
+
const rel = relative4(root, abs);
|
|
4991
5232
|
const seg0 = rel.split(sep2)[0] ?? "";
|
|
4992
5233
|
if (!rel.startsWith("..") && seg0.endsWith("-screenshots") && existsSync11(abs)) {
|
|
4993
5234
|
try {
|
|
@@ -5453,7 +5694,7 @@ function createApi(deps) {
|
|
|
5453
5694
|
raw
|
|
5454
5695
|
});
|
|
5455
5696
|
}
|
|
5456
|
-
}, aiCfg.concurrency, signal);
|
|
5697
|
+
}, aiCfg.concurrency, signal, aiCfg.batchSize);
|
|
5457
5698
|
if (!signal?.aborted) {
|
|
5458
5699
|
console.log(`[translate] done \u2014 wrote ${totalWritten}, ${allErrors.length} error(s)`);
|
|
5459
5700
|
await stream.writeSSE({ event: "done", data: JSON.stringify({ written: totalWritten, errors: allErrors }) });
|
|
@@ -5496,7 +5737,7 @@ function createApi(deps) {
|
|
|
5496
5737
|
raw
|
|
5497
5738
|
});
|
|
5498
5739
|
}
|
|
5499
|
-
}, aiCfg.concurrency);
|
|
5740
|
+
}, aiCfg.concurrency, void 0, aiCfg.batchSize);
|
|
5500
5741
|
const latest = load();
|
|
5501
5742
|
({ written, errors } = applyResults(latest, toTranslate, results, void 0, force));
|
|
5502
5743
|
const entry = {
|
|
@@ -5718,7 +5959,7 @@ __export(server_exports, {
|
|
|
5718
5959
|
import { Hono as Hono2 } from "hono";
|
|
5719
5960
|
import { serve } from "@hono/node-server";
|
|
5720
5961
|
import { fileURLToPath } from "url";
|
|
5721
|
-
import { dirname as dirname4, join as
|
|
5962
|
+
import { dirname as dirname4, join as join10, resolve as resolve10, extname as extname3, sep as sep3 } from "path";
|
|
5722
5963
|
import { readFile, stat } from "fs/promises";
|
|
5723
5964
|
import { createServer } from "net";
|
|
5724
5965
|
import open from "open";
|
|
@@ -5761,7 +6002,7 @@ function buildApp(opts) {
|
|
|
5761
6002
|
const file = await readFileResponse(target);
|
|
5762
6003
|
if (file) return file;
|
|
5763
6004
|
}
|
|
5764
|
-
const index = await readFileResponse(
|
|
6005
|
+
const index = await readFileResponse(join10(root, "index.html"));
|
|
5765
6006
|
if (index) return index;
|
|
5766
6007
|
return c.notFound();
|
|
5767
6008
|
});
|
|
@@ -5819,7 +6060,7 @@ var init_server = __esm({
|
|
|
5819
6060
|
init_scan();
|
|
5820
6061
|
init_scanner();
|
|
5821
6062
|
here = dirname4(fileURLToPath(import.meta.url));
|
|
5822
|
-
DEFAULT_UI_DIR =
|
|
6063
|
+
DEFAULT_UI_DIR = join10(here, "..", "ui");
|
|
5823
6064
|
MIME = {
|
|
5824
6065
|
".html": "text/html; charset=utf-8",
|
|
5825
6066
|
".js": "text/javascript; charset=utf-8",
|
|
@@ -5863,8 +6104,9 @@ init_scanner();
|
|
|
5863
6104
|
init_context();
|
|
5864
6105
|
init_run2();
|
|
5865
6106
|
init_outputs();
|
|
5866
|
-
import { resolve as resolve11, dirname as dirname5 } from "path";
|
|
5867
|
-
import { readFileSync as
|
|
6107
|
+
import { resolve as resolve11, dirname as dirname5, join as join11 } from "path";
|
|
6108
|
+
import { readFileSync as readFileSync16, existsSync as existsSync12, mkdirSync as mkdirSync4, cpSync } from "fs";
|
|
6109
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5868
6110
|
|
|
5869
6111
|
// src/server/lint/locate.ts
|
|
5870
6112
|
function locate(rawText, key) {
|
|
@@ -5930,8 +6172,7 @@ function formatSarif(report, rawText) {
|
|
|
5930
6172
|
}
|
|
5931
6173
|
|
|
5932
6174
|
// src/server/cli.ts
|
|
5933
|
-
|
|
5934
|
-
var COMMANDS = ["serve", "export", "translate", "lint", "check", "import", "build-context", "scan", "prune", "split"];
|
|
6175
|
+
var COMMANDS = ["serve", "export", "translate", "lint", "check", "import", "build-context", "scan", "prune", "split", "skill"];
|
|
5935
6176
|
var isCommand = (s) => s != null && COMMANDS.includes(s);
|
|
5936
6177
|
function parseArgs(argv) {
|
|
5937
6178
|
const statePath = resolve11(process.cwd(), "glotfile.json");
|
|
@@ -5998,6 +6239,7 @@ function parseArgs(argv) {
|
|
|
5998
6239
|
else if (flag === "--unused") args.unused = true;
|
|
5999
6240
|
else if (flag === "--write") args.write = true;
|
|
6000
6241
|
else if (flag === "--estimate") args.estimate = true;
|
|
6242
|
+
else if (flag === "--print") args.print = true;
|
|
6001
6243
|
}
|
|
6002
6244
|
return args;
|
|
6003
6245
|
}
|
|
@@ -6123,7 +6365,7 @@ async function runTranslate(args) {
|
|
|
6123
6365
|
raw
|
|
6124
6366
|
});
|
|
6125
6367
|
}
|
|
6126
|
-
});
|
|
6368
|
+
}, ai.concurrency, void 0, ai.batchSize);
|
|
6127
6369
|
process.stdout.write("\n");
|
|
6128
6370
|
if (!batchCallbackFired) {
|
|
6129
6371
|
({ written, errors } = applyResults(state, toTranslate, results));
|
|
@@ -6171,7 +6413,7 @@ async function runLintCmd(args) {
|
|
|
6171
6413
|
}
|
|
6172
6414
|
return;
|
|
6173
6415
|
}
|
|
6174
|
-
const rawText = existsSync12(args.statePath) ?
|
|
6416
|
+
const rawText = existsSync12(args.statePath) ? readFileSync16(args.statePath, "utf8") : "";
|
|
6175
6417
|
const report = await runLint(state, {
|
|
6176
6418
|
locales: args.locales,
|
|
6177
6419
|
ruleIds: args.ruleIds,
|
|
@@ -6195,7 +6437,7 @@ async function runCheck(args) {
|
|
|
6195
6437
|
process.exitCode = 1;
|
|
6196
6438
|
return;
|
|
6197
6439
|
}
|
|
6198
|
-
const rawText = existsSync12(args.statePath) ?
|
|
6440
|
+
const rawText = existsSync12(args.statePath) ? readFileSync16(args.statePath, "utf8") : "";
|
|
6199
6441
|
const root = dirname5(resolve11(args.statePath));
|
|
6200
6442
|
const lint = await runLint(state, {});
|
|
6201
6443
|
const findings = sortFindings([...lint.findings, ...checkOutputs(state, root)]);
|
|
@@ -6356,6 +6598,22 @@ function runSplit(args) {
|
|
|
6356
6598
|
`Split catalog into ${splitDirFor(args.statePath)}/ (config.json, keys.json, locales/ \u2014 up to ${state.config.locales.length} locale files). Removed ${args.statePath}.`
|
|
6357
6599
|
);
|
|
6358
6600
|
}
|
|
6601
|
+
var SKILL_SRC = join11(dirname5(fileURLToPath2(import.meta.url)), "..", "..", "skill");
|
|
6602
|
+
function runSkill(args) {
|
|
6603
|
+
if (args.print) {
|
|
6604
|
+
console.log(readFileSync16(join11(SKILL_SRC, "SKILL.md"), "utf8").trimEnd());
|
|
6605
|
+
return;
|
|
6606
|
+
}
|
|
6607
|
+
const dest = resolve11(process.cwd(), ".claude", "skills", "glotfile");
|
|
6608
|
+
if (existsSync12(dest) && !args.importForce) {
|
|
6609
|
+
console.error(`${dest} already exists; pass --force to overwrite`);
|
|
6610
|
+
process.exitCode = 1;
|
|
6611
|
+
return;
|
|
6612
|
+
}
|
|
6613
|
+
mkdirSync4(dirname5(dest), { recursive: true });
|
|
6614
|
+
cpSync(SKILL_SRC, dest, { recursive: true });
|
|
6615
|
+
console.log(`Installed the glotfile skill to ${dest}. Restart Claude Code to pick it up.`);
|
|
6616
|
+
}
|
|
6359
6617
|
var GLOBAL_OPTS = [
|
|
6360
6618
|
["-f, --file <path>", "State file to use (default: ./glotfile.json)"],
|
|
6361
6619
|
["-h, --help", "Show this help"]
|
|
@@ -6441,6 +6699,14 @@ var COMMAND_HELP = {
|
|
|
6441
6699
|
summary: "Convert glotfile.json into a glotfile/ directory of per-locale files (faster, reviewable git diffs).",
|
|
6442
6700
|
usage: "glotfile split",
|
|
6443
6701
|
options: []
|
|
6702
|
+
},
|
|
6703
|
+
skill: {
|
|
6704
|
+
summary: "Install the Claude Code skill for managing glotfile into ./.claude/skills/glotfile/.",
|
|
6705
|
+
usage: "glotfile skill [--print] [--force]",
|
|
6706
|
+
options: [
|
|
6707
|
+
["--print", "Write SKILL.md to stdout instead of installing"],
|
|
6708
|
+
["--force", "Overwrite an existing installed skill"]
|
|
6709
|
+
]
|
|
6444
6710
|
}
|
|
6445
6711
|
};
|
|
6446
6712
|
function formatOpts(opts) {
|
|
@@ -6491,6 +6757,7 @@ async function main(argv) {
|
|
|
6491
6757
|
if (args.command === "scan") return runScanCmd(args);
|
|
6492
6758
|
if (args.command === "prune") return runPrune(args);
|
|
6493
6759
|
if (args.command === "split") return runSplit(args);
|
|
6760
|
+
if (args.command === "skill") return runSkill(args);
|
|
6494
6761
|
const { startServer: startServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
6495
6762
|
const { url } = await startServer2({ statePath: args.statePath, dev: args.dev });
|
|
6496
6763
|
if (args.dev) console.log(`Glotfile dev API on ${url} \u2014 open the UI at the Vite "Local:" URL above`);
|
|
@@ -6506,5 +6773,6 @@ export {
|
|
|
6506
6773
|
main,
|
|
6507
6774
|
parseArgs,
|
|
6508
6775
|
runPrune,
|
|
6776
|
+
runSkill,
|
|
6509
6777
|
watchTargetFor
|
|
6510
6778
|
};
|