@umec/core 0.1.0-alpha.11 → 0.1.0-alpha.12

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/cli.js CHANGED
@@ -14,8 +14,8 @@ import {
14
14
  } from "./chunk-T2PO3YLZ.js";
15
15
 
16
16
  // src/cli/index.ts
17
- import { readFileSync as readFileSync5 } from "fs";
18
- import { join as join5 } from "path";
17
+ import { readFileSync as readFileSync7 } from "fs";
18
+ import { join as join7 } from "path";
19
19
 
20
20
  // src/cli/doctor.ts
21
21
  import { existsSync } from "fs";
@@ -582,10 +582,839 @@ function coreVersionFromPackage(root = findPackageRoot()) {
582
582
  return pkg.version;
583
583
  }
584
584
 
585
+ // src/cli/registry.ts
586
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
587
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join5, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
588
+
589
+ // src/cli/template-doctor.ts
590
+ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync } from "fs";
591
+ import { isAbsolute, join as join4, relative, resolve, sep } from "path";
592
+ var SHA256_HEX = /^[0-9a-f]{64}$/;
593
+ var SKIP_DIRS = /* @__PURE__ */ new Set([
594
+ "node_modules",
595
+ ".git",
596
+ "dist",
597
+ ".astro",
598
+ ".wrangler",
599
+ "coverage",
600
+ ".output",
601
+ ".cache",
602
+ ".pnpm-store"
603
+ ]);
604
+ function isZone(value) {
605
+ return value === "managed" || value === "brand" || value === "generated";
606
+ }
607
+ function isRecord(value) {
608
+ return typeof value === "object" && value !== null && !Array.isArray(value);
609
+ }
610
+ function isHttpsUrl(value) {
611
+ return /^https:\/\//i.test(value);
612
+ }
613
+ function isSafeRelPath(posixPath) {
614
+ if (!posixPath || posixPath.startsWith("/") || posixPath.includes("\\") || posixPath.includes("\0")) {
615
+ return false;
616
+ }
617
+ const parts = posixPath.split("/");
618
+ return parts.every((part) => part !== "" && part !== "." && part !== "..");
619
+ }
620
+ function resolveInstancePath(cwd, posixPath) {
621
+ if (!isSafeRelPath(posixPath)) return null;
622
+ const resolved = resolve(cwd, ...posixPath.split("/"));
623
+ const rel = relative(resolve(cwd), resolved);
624
+ if (!rel || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) return null;
625
+ return resolved;
626
+ }
627
+ function parseTemplateManifest(text) {
628
+ let parsed;
629
+ try {
630
+ parsed = JSON.parse(text);
631
+ } catch {
632
+ throw new Error("manifest \u306E JSON \u304C\u4E0D\u6B63\u3067\u3059\u3002");
633
+ }
634
+ if (!isRecord(parsed)) throw new Error("manifest \u306E JSON \u304C\u4E0D\u6B63\u3067\u3059\u3002");
635
+ if (parsed.schemaVersion !== 1) {
636
+ throw new Error(`manifest schemaVersion ${String(parsed.schemaVersion)} \u306B\u306F\u672A\u5BFE\u5FDC\u3067\u3059\uFF08\u5BFE\u5FDC: 1\uFF09\u3002`);
637
+ }
638
+ if (typeof parsed.templateVersion !== "string" || parsed.templateVersion.length === 0) {
639
+ throw new Error("manifest.templateVersion \u304C\u5FC5\u8981\u3067\u3059\u3002");
640
+ }
641
+ if (typeof parsed.generatedAt !== "string" || parsed.generatedAt.length === 0) {
642
+ throw new Error("manifest.generatedAt \u304C\u5FC5\u8981\u3067\u3059\u3002");
643
+ }
644
+ if (!Array.isArray(parsed.files)) {
645
+ throw new Error("manifest.files \u304C\u5FC5\u8981\u3067\u3059\u3002");
646
+ }
647
+ const files = [];
648
+ const seen = /* @__PURE__ */ new Set();
649
+ for (const entry of parsed.files) {
650
+ if (!isRecord(entry)) throw new Error("manifest.files \u306E\u8981\u7D20\u304C\u4E0D\u6B63\u3067\u3059\u3002");
651
+ if (typeof entry.path !== "string" || !isSafeRelPath(entry.path)) {
652
+ throw new Error(`manifest \u306E path \u304C\u4E0D\u6B63\u3067\u3059: ${String(entry.path)}`);
653
+ }
654
+ if (seen.has(entry.path)) {
655
+ throw new Error(`manifest \u306E path \u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059: ${entry.path}`);
656
+ }
657
+ if (typeof entry.sha256 !== "string" || !SHA256_HEX.test(entry.sha256.toLowerCase())) {
658
+ throw new Error(`manifest \u306E sha256 \u304C\u4E0D\u6B63\u3067\u3059: ${entry.path}`);
659
+ }
660
+ if (!isZone(entry.zone)) {
661
+ throw new Error(`manifest \u306E zone \u304C\u4E0D\u6B63\u3067\u3059: ${entry.path} (${String(entry.zone)})`);
662
+ }
663
+ seen.add(entry.path);
664
+ files.push({
665
+ path: entry.path,
666
+ sha256: entry.sha256.toLowerCase(),
667
+ zone: entry.zone
668
+ });
669
+ }
670
+ return {
671
+ schemaVersion: 1,
672
+ templateVersion: parsed.templateVersion,
673
+ generatedAt: parsed.generatedAt,
674
+ files
675
+ };
676
+ }
677
+ async function loadManifestText(source, cwd, fetchImpl = globalThis.fetch) {
678
+ if (isHttpsUrl(source)) {
679
+ const response = await fetchImpl(source, { signal: AbortSignal.timeout(15e3) });
680
+ if (!response.ok) {
681
+ throw new Error(`manifest \u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093 (${response.status}): ${source}`);
682
+ }
683
+ return { text: await response.text(), origin: source };
684
+ }
685
+ if (/^https?:\/\//i.test(source)) {
686
+ throw new Error("manifest URL \u306F https:// \u306E\u307F\u53D7\u3051\u4ED8\u3051\u307E\u3059\u3002");
687
+ }
688
+ const path = isAbsolute(source) ? source : resolve(cwd, source);
689
+ if (!existsSync3(path) || !statSync(path).isFile()) {
690
+ throw new Error(`manifest \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${source}`);
691
+ }
692
+ return { text: readFileSync4(path, "utf8"), origin: path };
693
+ }
694
+ function toPosix(rel) {
695
+ return rel.split(sep).join("/");
696
+ }
697
+ function listInstanceFiles(cwd) {
698
+ const root = resolve(cwd);
699
+ const out = [];
700
+ const walk = (dir) => {
701
+ for (const entry of readdirSync2(dir, { withFileTypes: true })) {
702
+ if (entry.name === ".DS_Store") continue;
703
+ const full = join4(dir, entry.name);
704
+ if (entry.isDirectory()) {
705
+ if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
706
+ walk(full);
707
+ continue;
708
+ }
709
+ if (!entry.isFile()) continue;
710
+ out.push(toPosix(relative(root, full)));
711
+ }
712
+ };
713
+ walk(root);
714
+ out.sort();
715
+ return out;
716
+ }
717
+ function classifyTemplate(input) {
718
+ const files = [];
719
+ const tracked = new Set(input.manifest.files.map((file) => file.path));
720
+ for (const entry of input.manifest.files) {
721
+ if (entry.zone === "generated") continue;
722
+ const resolved = resolveInstancePath(input.cwd, entry.path);
723
+ if (!resolved || !existsSync3(resolved) || !statSync(resolved).isFile()) {
724
+ files.push({ path: entry.path, status: "missing", zone: entry.zone, expected: entry.sha256 });
725
+ continue;
726
+ }
727
+ const actual = sha256File(resolved);
728
+ if (actual === entry.sha256) {
729
+ files.push({ path: entry.path, status: "match", zone: entry.zone, actual });
730
+ } else {
731
+ files.push({
732
+ path: entry.path,
733
+ status: "modified",
734
+ zone: entry.zone,
735
+ expected: entry.sha256,
736
+ actual
737
+ });
738
+ }
739
+ }
740
+ for (const path of listInstanceFiles(input.cwd)) {
741
+ if (tracked.has(path)) continue;
742
+ files.push({ path, status: "untracked" });
743
+ }
744
+ const counts = {
745
+ match: files.filter((file) => file.status === "match").length,
746
+ modified: files.filter((file) => file.status === "modified").length,
747
+ missing: files.filter((file) => file.status === "missing").length,
748
+ untracked: files.filter((file) => file.status === "untracked").length
749
+ };
750
+ const ok = !files.some(
751
+ (file) => file.zone === "managed" && (file.status === "modified" || file.status === "missing")
752
+ );
753
+ return {
754
+ schemaVersion: 1,
755
+ templateVersion: input.manifest.templateVersion,
756
+ generatedAt: input.manifest.generatedAt,
757
+ manifest: input.origin,
758
+ ok,
759
+ counts,
760
+ files
761
+ };
762
+ }
763
+ function formatTemplateReport(report) {
764
+ const lines = [
765
+ `template version: ${report.templateVersion}`,
766
+ `generated at: ${report.generatedAt}`,
767
+ `match: ${report.counts.match}`,
768
+ `modified: ${report.counts.modified}`,
769
+ `missing: ${report.counts.missing}`,
770
+ `untracked: ${report.counts.untracked}`
771
+ ];
772
+ for (const file of report.files) {
773
+ if (file.status === "match") continue;
774
+ if (file.status === "untracked") {
775
+ lines.push(`untracked: ${file.path}`);
776
+ continue;
777
+ }
778
+ if (file.zone === "managed" && (file.status === "modified" || file.status === "missing")) {
779
+ lines.push(`error: ${file.status} managed ${file.path}`);
780
+ continue;
781
+ }
782
+ lines.push(`${file.status} ${file.zone} ${file.path}`);
783
+ }
784
+ return lines;
785
+ }
786
+ async function runTemplateDoctor(options) {
787
+ const print = options.print ?? (() => {
788
+ });
789
+ const lines = [];
790
+ const say = (line) => {
791
+ lines.push(line);
792
+ print(line);
793
+ };
794
+ try {
795
+ const loaded = await loadManifestText(options.manifest, options.cwd, options.fetch);
796
+ const manifest = parseTemplateManifest(loaded.text);
797
+ const report = classifyTemplate({
798
+ cwd: options.cwd,
799
+ manifest,
800
+ origin: loaded.origin
801
+ });
802
+ if (options.json) {
803
+ say(JSON.stringify(report, null, 2));
804
+ } else {
805
+ for (const line of formatTemplateReport(report)) say(line);
806
+ }
807
+ return { exitCode: report.ok ? 0 : 1, lines, report };
808
+ } catch (error) {
809
+ const message = error instanceof Error ? error.message : String(error);
810
+ return { exitCode: 1, lines, error: message };
811
+ }
812
+ }
813
+
814
+ // src/cli/registry.ts
815
+ var INSTALLED_RECORD_REL = ".umec/installed-registry-items.json";
816
+ var ITEM_ID = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
817
+ var SEMVER = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
818
+ function isRecord2(value) {
819
+ return typeof value === "object" && value !== null && !Array.isArray(value);
820
+ }
821
+ function isRisk(value) {
822
+ return value === "low" || value === "medium" || value === "high";
823
+ }
824
+ function isRegistryItemId(value) {
825
+ return ITEM_ID.test(value);
826
+ }
827
+ function defaultRegistryRoot(root = findPackageRoot()) {
828
+ return join5(root, "registry");
829
+ }
830
+ function toPosix2(rel) {
831
+ return rel.split(sep2).join("/");
832
+ }
833
+ function stripInlineComment(line) {
834
+ let quote = null;
835
+ for (let i = 0; i < line.length; i += 1) {
836
+ const char = line[i];
837
+ if (quote) {
838
+ if (char === quote && line[i - 1] !== "\\") quote = null;
839
+ continue;
840
+ }
841
+ if (char === '"' || char === "'") {
842
+ quote = char;
843
+ continue;
844
+ }
845
+ if (char === "#") return line.slice(0, i).trimEnd();
846
+ }
847
+ return line;
848
+ }
849
+ function splitTopLevel(input, sep3) {
850
+ const parts = [];
851
+ let buf = "";
852
+ let depthBrace = 0;
853
+ let depthBracket = 0;
854
+ let quote = null;
855
+ for (let i = 0; i < input.length; i += 1) {
856
+ const char = input[i] ?? "";
857
+ if (quote) {
858
+ buf += char;
859
+ if (char === quote && input[i - 1] !== "\\") quote = null;
860
+ continue;
861
+ }
862
+ if (char === '"' || char === "'") {
863
+ quote = char;
864
+ buf += char;
865
+ continue;
866
+ }
867
+ if (char === "{") depthBrace += 1;
868
+ if (char === "}") depthBrace -= 1;
869
+ if (char === "[") depthBracket += 1;
870
+ if (char === "]") depthBracket -= 1;
871
+ if (char === sep3 && depthBrace === 0 && depthBracket === 0) {
872
+ if (buf.trim()) parts.push(buf.trim());
873
+ buf = "";
874
+ continue;
875
+ }
876
+ buf += char;
877
+ }
878
+ if (buf.trim()) parts.push(buf.trim());
879
+ return parts;
880
+ }
881
+ function splitKeyValue(input) {
882
+ let quote = null;
883
+ for (let i = 0; i < input.length; i += 1) {
884
+ const char = input[i];
885
+ if (quote) {
886
+ if (char === quote) quote = null;
887
+ continue;
888
+ }
889
+ if (char === '"' || char === "'") {
890
+ quote = char;
891
+ continue;
892
+ }
893
+ if (char === ":") return [input.slice(0, i).trim(), input.slice(i + 1).trim()];
894
+ }
895
+ return null;
896
+ }
897
+ function unquote(input) {
898
+ if (input.startsWith('"') && input.endsWith('"') && input.length >= 2 || input.startsWith("'") && input.endsWith("'") && input.length >= 2) {
899
+ return input.slice(1, -1);
900
+ }
901
+ return input;
902
+ }
903
+ function parseYamlValue(raw) {
904
+ const value = raw.trim();
905
+ if (value === "") return "";
906
+ if (value === "true") return true;
907
+ if (value === "false") return false;
908
+ if (value === "null") return null;
909
+ if (value.startsWith("{") && value.endsWith("}")) {
910
+ const entries = splitTopLevel(value.slice(1, -1), ",");
911
+ const record = {};
912
+ for (const entry of entries) {
913
+ const kv = splitKeyValue(entry);
914
+ if (!kv) throw new Error(`frontmatter \u306E object \u304C\u4E0D\u6B63\u3067\u3059: ${entry}`);
915
+ record[unquote(kv[0])] = parseYamlValue(kv[1]);
916
+ }
917
+ return record;
918
+ }
919
+ if (value.startsWith("[") && value.endsWith("]")) {
920
+ return splitTopLevel(value.slice(1, -1), ",").map((entry) => parseYamlValue(entry));
921
+ }
922
+ return unquote(value);
923
+ }
924
+ function parseFrontmatter(text) {
925
+ const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(text);
926
+ if (!match) throw new Error("manifest.md \u306B YAML frontmatter \u304C\u3042\u308A\u307E\u305B\u3093\u3002");
927
+ const data = {};
928
+ for (const rawLine of match[1].split(/\r?\n/)) {
929
+ const line = stripInlineComment(rawLine).trim();
930
+ if (!line) continue;
931
+ const kv = splitKeyValue(line);
932
+ if (!kv || !kv[0]) throw new Error(`frontmatter \u306E\u884C\u304C\u4E0D\u6B63\u3067\u3059: ${rawLine}`);
933
+ data[kv[0]] = parseYamlValue(kv[1]);
934
+ }
935
+ return { data, body: match[2] };
936
+ }
937
+ function parseStringArray(value, field) {
938
+ if (value === void 0) return [];
939
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.length === 0)) {
940
+ throw new Error(`${field} \u306F\u6587\u5B57\u5217\u306E\u914D\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002`);
941
+ }
942
+ return value;
943
+ }
944
+ function parseRequires(value) {
945
+ if (value === void 0) return {};
946
+ if (!isRecord2(value)) throw new Error("requires \u306F object \u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002");
947
+ const requires = {};
948
+ for (const key of Object.keys(value)) {
949
+ if (key !== "core" && key !== "admin" && key !== "items") {
950
+ throw new Error(`requires.${key} \u306B\u306F\u672A\u5BFE\u5FDC\u3067\u3059\uFF08\u5BFE\u5FDC: core, admin, items\uFF09\u3002`);
951
+ }
952
+ }
953
+ if (value.core !== void 0) {
954
+ if (typeof value.core !== "string" || value.core.length === 0) {
955
+ throw new Error("requires.core \u306F\u30D0\u30FC\u30B8\u30E7\u30F3\u7BC4\u56F2\u306E\u6587\u5B57\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002");
956
+ }
957
+ requires.core = value.core;
958
+ }
959
+ if (value.admin !== void 0) {
960
+ if (typeof value.admin !== "string" || value.admin.length === 0) {
961
+ throw new Error("requires.admin \u306F\u30D0\u30FC\u30B8\u30E7\u30F3\u7BC4\u56F2\u306E\u6587\u5B57\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002");
962
+ }
963
+ requires.admin = value.admin;
964
+ }
965
+ if (value.items !== void 0) {
966
+ requires.items = parseStringArray(value.items, "requires.items");
967
+ for (const id of requires.items) {
968
+ if (!isRegistryItemId(id)) throw new Error(`requires.items \u306E id \u304C\u4E0D\u6B63\u3067\u3059: ${id}`);
969
+ }
970
+ }
971
+ return requires;
972
+ }
973
+ function parseSemVer(input) {
974
+ const match = SEMVER.exec(input.trim());
975
+ if (!match) return null;
976
+ return {
977
+ major: Number(match[1]),
978
+ minor: Number(match[2]),
979
+ patch: Number(match[3]),
980
+ prerelease: match[4] ? match[4].split(".") : []
981
+ };
982
+ }
983
+ function compareIdentifier(a, b) {
984
+ const aNum = /^\d+$/.test(a);
985
+ const bNum = /^\d+$/.test(b);
986
+ if (aNum && bNum) return Number(a) - Number(b);
987
+ if (aNum !== bNum) return aNum ? -1 : 1;
988
+ return a < b ? -1 : a > b ? 1 : 0;
989
+ }
990
+ function compareSemVer(a, b) {
991
+ if (a.major !== b.major) return a.major - b.major;
992
+ if (a.minor !== b.minor) return a.minor - b.minor;
993
+ if (a.patch !== b.patch) return a.patch - b.patch;
994
+ if (a.prerelease.length === 0 && b.prerelease.length === 0) return 0;
995
+ if (a.prerelease.length === 0) return 1;
996
+ if (b.prerelease.length === 0) return -1;
997
+ const n = Math.max(a.prerelease.length, b.prerelease.length);
998
+ for (let i = 0; i < n; i += 1) {
999
+ const left = a.prerelease[i];
1000
+ const right = b.prerelease[i];
1001
+ if (left === void 0) return -1;
1002
+ if (right === void 0) return 1;
1003
+ const cmp = compareIdentifier(left, right);
1004
+ if (cmp !== 0) return cmp;
1005
+ }
1006
+ return 0;
1007
+ }
1008
+ function satisfiesVersionRange(version, range) {
1009
+ const parsed = parseSemVer(version);
1010
+ if (!parsed) throw new Error(`\u30D0\u30FC\u30B8\u30E7\u30F3\u304C\u4E0D\u6B63\u3067\u3059: ${version}`);
1011
+ const trimmed = range.trim();
1012
+ const operators = [">=", "<=", ">", "<", "="];
1013
+ const operator = operators.find((item) => trimmed.startsWith(item));
1014
+ const spec = operator ? trimmed.slice(operator.length).trim() : trimmed;
1015
+ const target = parseSemVer(spec);
1016
+ if (!target) throw new Error(`\u30D0\u30FC\u30B8\u30E7\u30F3\u7BC4\u56F2\u304C\u4E0D\u6B63\u3067\u3059: ${range}`);
1017
+ const cmp = compareSemVer(parsed, target);
1018
+ if (operator === ">=") return cmp >= 0;
1019
+ if (operator === ">") return cmp > 0;
1020
+ if (operator === "<=") return cmp <= 0;
1021
+ if (operator === "<") return cmp < 0;
1022
+ return cmp === 0;
1023
+ }
1024
+ function titleFromBody(body, fallback) {
1025
+ const heading = /^#\s+(.+)$/m.exec(body);
1026
+ const title = heading?.[1]?.trim();
1027
+ return title && title.length > 0 ? title : fallback;
1028
+ }
1029
+ function listFilesRecursive(root) {
1030
+ const out = [];
1031
+ const walk = (dir) => {
1032
+ for (const entry of readdirSync3(dir, { withFileTypes: true })) {
1033
+ if (entry.name === ".DS_Store" || entry.name.startsWith(".")) continue;
1034
+ const full = join5(dir, entry.name);
1035
+ if (entry.isDirectory()) {
1036
+ walk(full);
1037
+ continue;
1038
+ }
1039
+ if (!entry.isFile()) continue;
1040
+ const rel = toPosix2(relative2(root, full));
1041
+ if (!isSafeRelPath(rel)) throw new Error(`registry \u306E path \u304C\u4E0D\u6B63\u3067\u3059: ${rel}`);
1042
+ out.push(rel);
1043
+ }
1044
+ };
1045
+ walk(root);
1046
+ out.sort();
1047
+ return out;
1048
+ }
1049
+ function loadItemFiles(itemDir) {
1050
+ const filesRoot = join5(itemDir, "files");
1051
+ if (!existsSync4(filesRoot) || !statSync2(filesRoot).isDirectory()) {
1052
+ throw new Error("files/ \u304C\u3042\u308A\u307E\u305B\u3093\u3002");
1053
+ }
1054
+ const paths = listFilesRecursive(filesRoot);
1055
+ if (paths.length === 0) throw new Error("files/ \u304C\u7A7A\u3067\u3059\u3002");
1056
+ return paths.map((path) => {
1057
+ const sourcePath = join5(filesRoot, ...path.split("/"));
1058
+ return { path, sourcePath, sha256: sha256File(sourcePath) };
1059
+ });
1060
+ }
1061
+ function parseRegistryManifest(text) {
1062
+ const { data, body } = parseFrontmatter(text);
1063
+ if (typeof data.id !== "string" || !isRegistryItemId(data.id)) {
1064
+ throw new Error(`manifest.md \u306E id \u304C\u4E0D\u6B63\u3067\u3059: ${String(data.id)}`);
1065
+ }
1066
+ if (typeof data.version !== "string" || !parseSemVer(data.version)) {
1067
+ throw new Error(`manifest.md \u306E version \u304C\u4E0D\u6B63\u3067\u3059: ${String(data.version)}`);
1068
+ }
1069
+ if (!isRisk(data.risk)) {
1070
+ throw new Error(`manifest.md \u306E risk \u304C\u4E0D\u6B63\u3067\u3059: ${String(data.risk)}`);
1071
+ }
1072
+ return {
1073
+ id: data.id,
1074
+ version: data.version,
1075
+ title: titleFromBody(body, data.id),
1076
+ risk: data.risk,
1077
+ requires: parseRequires(data.requires),
1078
+ touches: parseStringArray(data.touches, "touches")
1079
+ };
1080
+ }
1081
+ function loadRegistryItem(registryRoot, itemId) {
1082
+ if (!isRegistryItemId(itemId)) {
1083
+ throw new Error(`registry item id \u304C\u4E0D\u6B63\u3067\u3059: ${itemId}`);
1084
+ }
1085
+ const dir = resolve2(registryRoot, itemId);
1086
+ const rel = relative2(resolve2(registryRoot), dir);
1087
+ if (!rel || rel === ".." || rel.startsWith(`..${sep2}`) || isAbsolute2(rel) || rel.includes(sep2)) {
1088
+ throw new Error(`registry item id \u304C\u4E0D\u6B63\u3067\u3059: ${itemId}`);
1089
+ }
1090
+ const manifestPath = join5(dir, "manifest.md");
1091
+ if (!existsSync4(dir) || !statSync2(dir).isDirectory() || !existsSync4(manifestPath) || !statSync2(manifestPath).isFile()) {
1092
+ throw new Error(`registry item \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${itemId}`);
1093
+ }
1094
+ const parsed = parseRegistryManifest(readFileSync5(manifestPath, "utf8"));
1095
+ if (parsed.id !== itemId) {
1096
+ throw new Error(`manifest.md \u306E id (${parsed.id}) \u304C\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u540D (${itemId}) \u3068\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002`);
1097
+ }
1098
+ return {
1099
+ ...parsed,
1100
+ files: loadItemFiles(dir),
1101
+ dir,
1102
+ manifestPath
1103
+ };
1104
+ }
1105
+ function listRegistryItems(registryRoot) {
1106
+ if (!existsSync4(registryRoot)) return [];
1107
+ if (!statSync2(registryRoot).isDirectory()) {
1108
+ throw new Error(`registry \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u4E0D\u6B63\u3067\u3059: ${registryRoot}`);
1109
+ }
1110
+ const ids = readdirSync3(registryRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && existsSync4(join5(registryRoot, entry.name, "manifest.md"))).map((entry) => entry.name).sort();
1111
+ return ids.map((id) => loadRegistryItem(registryRoot, id));
1112
+ }
1113
+ function catalogEntry(item) {
1114
+ return {
1115
+ id: item.id,
1116
+ version: item.version,
1117
+ title: item.title,
1118
+ risk: item.risk,
1119
+ requires: item.requires,
1120
+ touches: item.touches
1121
+ };
1122
+ }
1123
+ function formatRequires(requires) {
1124
+ const parts = [];
1125
+ if (requires.core) parts.push(`core ${requires.core}`);
1126
+ if (requires.admin) parts.push(`admin ${requires.admin}`);
1127
+ if (requires.items?.length) parts.push(`items ${requires.items.join(", ")}`);
1128
+ return parts.length > 0 ? parts.join("; ") : "(none)";
1129
+ }
1130
+ function sayResult(print, lines, line) {
1131
+ lines.push(line);
1132
+ print(line);
1133
+ }
1134
+ function finishJson(print, lines, payload, exitCode) {
1135
+ sayResult(print, lines, JSON.stringify(payload, null, 2));
1136
+ return { exitCode, lines };
1137
+ }
1138
+ function finishError(options, error, extra = {}) {
1139
+ if (options.json) return finishJson(options.print, options.lines, { ok: false, error, ...extra }, 1);
1140
+ return { exitCode: 1, lines: options.lines, error };
1141
+ }
1142
+ function readCustomerPackageVersion(cwd, packageName) {
1143
+ const path = join5(cwd, "node_modules", ...packageName.split("/"), "package.json");
1144
+ if (!existsSync4(path) || !statSync2(path).isFile()) return null;
1145
+ try {
1146
+ const pkg = JSON.parse(readFileSync5(path, "utf8"));
1147
+ return typeof pkg.version === "string" ? pkg.version : null;
1148
+ } catch {
1149
+ return null;
1150
+ }
1151
+ }
1152
+ function readInstalledRecord(cwd) {
1153
+ const path = resolveInstancePath(cwd, INSTALLED_RECORD_REL);
1154
+ if (!path || !existsSync4(path) || !statSync2(path).isFile()) {
1155
+ return { schemaVersion: 1, items: [] };
1156
+ }
1157
+ let parsed;
1158
+ try {
1159
+ parsed = JSON.parse(readFileSync5(path, "utf8"));
1160
+ } catch {
1161
+ throw new Error(`${INSTALLED_RECORD_REL} \u306E JSON \u304C\u4E0D\u6B63\u3067\u3059\u3002`);
1162
+ }
1163
+ if (!isRecord2(parsed) || parsed.schemaVersion !== 1 || !Array.isArray(parsed.items)) {
1164
+ throw new Error(`${INSTALLED_RECORD_REL} \u306E schemaVersion \u306F 1 \u306E\u307F\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u3059\u3002`);
1165
+ }
1166
+ const items = [];
1167
+ for (const entry of parsed.items) {
1168
+ if (!isRecord2(entry)) throw new Error(`${INSTALLED_RECORD_REL} \u306E items \u304C\u4E0D\u6B63\u3067\u3059\u3002`);
1169
+ if (typeof entry.id !== "string" || !isRegistryItemId(entry.id)) {
1170
+ throw new Error(`${INSTALLED_RECORD_REL} \u306E id \u304C\u4E0D\u6B63\u3067\u3059: ${String(entry.id)}`);
1171
+ }
1172
+ if (typeof entry.version !== "string" || !parseSemVer(entry.version)) {
1173
+ throw new Error(`${INSTALLED_RECORD_REL} \u306E version \u304C\u4E0D\u6B63\u3067\u3059: ${entry.id}`);
1174
+ }
1175
+ if (typeof entry.appliedAt !== "string" || entry.appliedAt.length === 0) {
1176
+ throw new Error(`${INSTALLED_RECORD_REL} \u306E appliedAt \u304C\u4E0D\u6B63\u3067\u3059: ${entry.id}`);
1177
+ }
1178
+ items.push({ id: entry.id, version: entry.version, appliedAt: entry.appliedAt });
1179
+ }
1180
+ return { schemaVersion: 1, items };
1181
+ }
1182
+ function writeInstalledRecord(cwd, record) {
1183
+ const path = resolveInstancePath(cwd, INSTALLED_RECORD_REL);
1184
+ if (!path) throw new Error(`${INSTALLED_RECORD_REL} \u3092\u66F8\u304D\u8FBC\u3081\u307E\u305B\u3093\u3002`);
1185
+ mkdirSync2(dirname2(path), { recursive: true });
1186
+ writeFileSync2(path, `${JSON.stringify(record, null, 2)}
1187
+ `);
1188
+ return INSTALLED_RECORD_REL;
1189
+ }
1190
+ function upsertInstalledItem(record, item) {
1191
+ const items = record.items.slice();
1192
+ const index = items.findIndex((entry) => entry.id === item.id);
1193
+ if (index >= 0) items[index] = item;
1194
+ else items.push(item);
1195
+ return { schemaVersion: 1, items };
1196
+ }
1197
+ function classifyRegistryFiles(cwd, files) {
1198
+ return files.map((file) => {
1199
+ const resolved = resolveInstancePath(cwd, file.path);
1200
+ if (!resolved || !existsSync4(resolved) || !statSync2(resolved).isFile()) {
1201
+ return { path: file.path, status: "missing", expected: file.sha256 };
1202
+ }
1203
+ const actual = sha256File(resolved);
1204
+ if (actual === file.sha256) return { path: file.path, status: "match", actual };
1205
+ return { path: file.path, status: "modified", expected: file.sha256, actual };
1206
+ });
1207
+ }
1208
+ function unmetRequires(input) {
1209
+ const messages = [];
1210
+ const { item } = input;
1211
+ if (item.requires.core) {
1212
+ try {
1213
+ if (!satisfiesVersionRange(input.coreVersion, item.requires.core)) {
1214
+ messages.push(
1215
+ `@umec/core ${item.requires.core} \u304C\u5FC5\u8981\u3067\u3059\uFF08\u73FE\u5728 ${input.coreVersion}\uFF09\u3002pnpm update @umec/core \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
1216
+ );
1217
+ }
1218
+ } catch (error) {
1219
+ messages.push(error instanceof Error ? error.message : String(error));
1220
+ }
1221
+ }
1222
+ if (item.requires.admin) {
1223
+ if (!input.adminVersion) {
1224
+ messages.push(
1225
+ `@umec/admin ${item.requires.admin} \u304C\u5FC5\u8981\u3067\u3059\u3002\u5148\u306B @umec/admin \u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
1226
+ );
1227
+ } else {
1228
+ try {
1229
+ if (!satisfiesVersionRange(input.adminVersion, item.requires.admin)) {
1230
+ messages.push(
1231
+ `@umec/admin ${item.requires.admin} \u304C\u5FC5\u8981\u3067\u3059\uFF08\u73FE\u5728 ${input.adminVersion}\uFF09\u3002`
1232
+ );
1233
+ }
1234
+ } catch (error) {
1235
+ messages.push(error instanceof Error ? error.message : String(error));
1236
+ }
1237
+ }
1238
+ }
1239
+ for (const requiredId of item.requires.items ?? []) {
1240
+ if (!input.installed.items.some((entry) => entry.id === requiredId)) {
1241
+ messages.push(`\u5148\u306B umec add ${requiredId} \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`);
1242
+ }
1243
+ }
1244
+ return messages;
1245
+ }
1246
+ async function runRegistryList(options) {
1247
+ const print = options.print ?? (() => {
1248
+ });
1249
+ const lines = [];
1250
+ try {
1251
+ const registryRoot = options.registryRoot ?? defaultRegistryRoot();
1252
+ const items = listRegistryItems(registryRoot).map(catalogEntry);
1253
+ if (options.json) {
1254
+ return finishJson(print, lines, { schemaVersion: 1, items }, 0);
1255
+ }
1256
+ if (items.length === 0) {
1257
+ sayResult(print, lines, "(no registry items)");
1258
+ return { exitCode: 0, lines };
1259
+ }
1260
+ for (const item of items) {
1261
+ sayResult(print, lines, `${item.id} ${item.version} ${item.risk}`);
1262
+ sayResult(print, lines, ` ${item.title}`);
1263
+ sayResult(print, lines, ` requires: ${formatRequires(item.requires)}`);
1264
+ }
1265
+ return { exitCode: 0, lines };
1266
+ } catch (error) {
1267
+ const message = error instanceof Error ? error.message : String(error);
1268
+ return finishError({ json: options.json, print, lines }, message);
1269
+ }
1270
+ }
1271
+ async function runRegistryAdd(options) {
1272
+ const print = options.print ?? (() => {
1273
+ });
1274
+ const lines = [];
1275
+ const itemId = options.itemId;
1276
+ if (!itemId) {
1277
+ return finishError({ json: options.json, print, lines }, "umec add \u306B\u306F <item-id> \u307E\u305F\u306F --list \u304C\u5FC5\u8981\u3067\u3059\u3002");
1278
+ }
1279
+ try {
1280
+ const registryRoot = options.registryRoot ?? defaultRegistryRoot();
1281
+ const item = loadRegistryItem(registryRoot, itemId);
1282
+ const installed = readInstalledRecord(options.cwd);
1283
+ const coreVersion = options.coreVersion ?? coreVersionFromPackage();
1284
+ const adminVersion = options.adminVersion !== void 0 ? options.adminVersion : readCustomerPackageVersion(options.cwd, "@umec/admin");
1285
+ const unmet = unmetRequires({ item, coreVersion, adminVersion, installed });
1286
+ if (unmet.length > 0) {
1287
+ return finishError(
1288
+ { json: options.json, print, lines },
1289
+ unmet.join("\n"),
1290
+ { id: item.id, version: item.version, unmet }
1291
+ );
1292
+ }
1293
+ const findings = classifyRegistryFiles(options.cwd, item.files);
1294
+ const modified = findings.filter((file) => file.status === "modified");
1295
+ if (modified.length > 0 && options.force !== true) {
1296
+ const error = `\u6539\u5909\u6E08\u307F\u306E\u30D5\u30A1\u30A4\u30EB\u304C\u3042\u308B\u305F\u3081 ${item.id} \u3092\u66F8\u304D\u8FBC\u307F\u307E\u305B\u3093\u3002umec diff ${item.id} \u3067\u5DEE\u5206\u3092\u78BA\u8A8D\u3057\u3001\u4E0A\u66F8\u304D\u3059\u308B\u5834\u5408\u306F umec add ${item.id} --force \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`;
1297
+ if (options.json) {
1298
+ return finishJson(
1299
+ print,
1300
+ lines,
1301
+ { ok: false, id: item.id, version: item.version, error, files: findings },
1302
+ 1
1303
+ );
1304
+ }
1305
+ for (const file of modified) sayResult(print, lines, `modified: ${file.path}`);
1306
+ return { exitCode: 1, lines, error };
1307
+ }
1308
+ const written = [];
1309
+ const unchanged = [];
1310
+ for (const file of item.files) {
1311
+ const dest = resolveInstancePath(options.cwd, file.path);
1312
+ if (!dest) throw new Error(`\u66F8\u304D\u8FBC\u307F\u5148\u304C\u4E0D\u6B63\u3067\u3059: ${file.path}`);
1313
+ if (existsSync4(dest) && !statSync2(dest).isFile()) {
1314
+ throw new Error(`${file.path} \u306F\u30D5\u30A1\u30A4\u30EB\u3067\u306F\u306A\u3044\u305F\u3081\u66F8\u304D\u8FBC\u3081\u307E\u305B\u3093\u3002`);
1315
+ }
1316
+ const finding = findings.find((entry) => entry.path === file.path);
1317
+ if (finding?.status === "match") {
1318
+ unchanged.push(file.path);
1319
+ continue;
1320
+ }
1321
+ mkdirSync2(dirname2(dest), { recursive: true });
1322
+ writeFileSync2(dest, readFileSync5(file.sourcePath));
1323
+ written.push(file.path);
1324
+ }
1325
+ const appliedAt = new Date(options.now ?? Date.now()).toISOString();
1326
+ const record = upsertInstalledItem(installed, { id: item.id, version: item.version, appliedAt });
1327
+ writeInstalledRecord(options.cwd, record);
1328
+ if (options.json) {
1329
+ return finishJson(
1330
+ print,
1331
+ lines,
1332
+ {
1333
+ ok: true,
1334
+ id: item.id,
1335
+ version: item.version,
1336
+ written,
1337
+ unchanged,
1338
+ record: INSTALLED_RECORD_REL,
1339
+ appliedAt
1340
+ },
1341
+ 0
1342
+ );
1343
+ }
1344
+ sayResult(print, lines, `applied ${item.id}@${item.version}`);
1345
+ for (const path of written) sayResult(print, lines, `wrote ${path}`);
1346
+ for (const path of unchanged) sayResult(print, lines, `unchanged ${path}`);
1347
+ sayResult(print, lines, `recorded ${INSTALLED_RECORD_REL}`);
1348
+ return { exitCode: 0, lines };
1349
+ } catch (error) {
1350
+ const message = error instanceof Error ? error.message : String(error);
1351
+ return finishError({ json: options.json, print, lines }, message, itemId ? { id: itemId } : {});
1352
+ }
1353
+ }
1354
+ function formatDiffReport(item, files) {
1355
+ const counts = {
1356
+ match: files.filter((file) => file.status === "match").length,
1357
+ modified: files.filter((file) => file.status === "modified").length,
1358
+ missing: files.filter((file) => file.status === "missing").length
1359
+ };
1360
+ const lines = [
1361
+ `registry item: ${item.id} ${item.version}`,
1362
+ `match: ${counts.match}`,
1363
+ `modified: ${counts.modified}`,
1364
+ `missing: ${counts.missing}`
1365
+ ];
1366
+ for (const file of files) {
1367
+ if (file.status === "match") continue;
1368
+ lines.push(`${file.status}: ${file.path}`);
1369
+ }
1370
+ return lines;
1371
+ }
1372
+ async function runRegistryDiff(options) {
1373
+ const print = options.print ?? (() => {
1374
+ });
1375
+ const lines = [];
1376
+ const itemId = options.itemId;
1377
+ if (!itemId) {
1378
+ return finishError({ json: options.json, print, lines }, "umec diff \u306B\u306F <item-id> \u304C\u5FC5\u8981\u3067\u3059\u3002");
1379
+ }
1380
+ try {
1381
+ const registryRoot = options.registryRoot ?? defaultRegistryRoot();
1382
+ const item = loadRegistryItem(registryRoot, itemId);
1383
+ const files = classifyRegistryFiles(options.cwd, item.files);
1384
+ const ok = files.every((file) => file.status === "match");
1385
+ const counts = {
1386
+ match: files.filter((file) => file.status === "match").length,
1387
+ modified: files.filter((file) => file.status === "modified").length,
1388
+ missing: files.filter((file) => file.status === "missing").length
1389
+ };
1390
+ if (options.json) {
1391
+ return finishJson(
1392
+ print,
1393
+ lines,
1394
+ {
1395
+ schemaVersion: 1,
1396
+ ok,
1397
+ id: item.id,
1398
+ version: item.version,
1399
+ title: item.title,
1400
+ counts,
1401
+ files
1402
+ },
1403
+ ok ? 0 : 1
1404
+ );
1405
+ }
1406
+ for (const line of formatDiffReport(item, files)) sayResult(print, lines, line);
1407
+ return { exitCode: ok ? 0 : 1, lines };
1408
+ } catch (error) {
1409
+ const message = error instanceof Error ? error.message : String(error);
1410
+ return finishError({ json: options.json, print, lines }, message, { id: itemId });
1411
+ }
1412
+ }
1413
+
585
1414
  // src/cli/wrangler.ts
586
1415
  import { spawn } from "child_process";
587
- import { readFileSync as readFileSync4 } from "fs";
588
- import { join as join4 } from "path";
1416
+ import { readFileSync as readFileSync6 } from "fs";
1417
+ import { join as join6 } from "path";
589
1418
 
590
1419
  // src/cli/executor.ts
591
1420
  function sqlLiteral(value) {
@@ -605,7 +1434,7 @@ function bindSql(sql, params = []) {
605
1434
 
606
1435
  // src/cli/wrangler.ts
607
1436
  function defaultRunCommand(command, args, cwd) {
608
- return new Promise((resolve, reject) => {
1437
+ return new Promise((resolve3, reject) => {
609
1438
  const child = spawn(command, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
610
1439
  let stdout = "";
611
1440
  let stderr = "";
@@ -617,7 +1446,7 @@ function defaultRunCommand(command, args, cwd) {
617
1446
  });
618
1447
  child.on("error", reject);
619
1448
  child.on("close", (code) => {
620
- resolve({ code: code ?? 1, stdout, stderr });
1449
+ resolve3({ code: code ?? 1, stdout, stderr });
621
1450
  });
622
1451
  });
623
1452
  }
@@ -627,9 +1456,9 @@ function parseJsonc(text) {
627
1456
  }
628
1457
  function readD1DatabaseName(cwd, override) {
629
1458
  if (override) return override;
630
- const path = join4(cwd, "wrangler.jsonc");
1459
+ const path = join6(cwd, "wrangler.jsonc");
631
1460
  try {
632
- const parsed = parseJsonc(readFileSync4(path, "utf8"));
1461
+ const parsed = parseJsonc(readFileSync6(path, "utf8"));
633
1462
  return parsed.d1_databases?.[0]?.database_name ?? null;
634
1463
  } catch {
635
1464
  return null;
@@ -655,7 +1484,7 @@ function createWranglerExecutor(options) {
655
1484
  const execute = async (sql) => {
656
1485
  const result = await runCommand(
657
1486
  "wrangler",
658
- ["d1", "execute", options.database, location, "--json", "--command", sql],
1487
+ ["d1", "execute", options.database, location, "--json", `--command=${sql}`],
659
1488
  options.cwd
660
1489
  );
661
1490
  if (result.code !== 0) {
@@ -703,8 +1532,21 @@ function createWranglerExecutor(options) {
703
1532
  }
704
1533
 
705
1534
  // src/cli/index.ts
1535
+ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
1536
+ "dry-run",
1537
+ "adopt",
1538
+ "repair",
1539
+ "remote",
1540
+ "local",
1541
+ "template",
1542
+ "json",
1543
+ "help",
1544
+ "force",
1545
+ "list"
1546
+ ]);
706
1547
  function parseArgs(argv) {
707
1548
  const flags = {};
1549
+ const positionals = [];
708
1550
  let command = null;
709
1551
  for (let i = 0; i < argv.length; i += 1) {
710
1552
  const arg = argv[i];
@@ -717,7 +1559,7 @@ function parseArgs(argv) {
717
1559
  }
718
1560
  const name = arg.slice(2);
719
1561
  const next = argv[i + 1];
720
- if (next && !next.startsWith("--") && name !== "dry-run" && name !== "adopt" && name !== "repair" && name !== "remote" && name !== "local") {
1562
+ if (next && !next.startsWith("--") && !BOOLEAN_FLAGS.has(name)) {
721
1563
  flags[name] = next;
722
1564
  i += 1;
723
1565
  } else {
@@ -726,24 +1568,83 @@ function parseArgs(argv) {
726
1568
  continue;
727
1569
  }
728
1570
  if (!command) command = arg;
1571
+ else positionals.push(arg);
729
1572
  }
730
- return { command, flags };
1573
+ return { command, flags, positionals };
731
1574
  }
732
1575
  function usage() {
733
1576
  return `Usage:
734
1577
  umec migrate [--dry-run] [--local|--remote] [--adopt] [--repair] [--database NAME]
735
1578
  umec doctor [--local|--remote] [--database NAME]
1579
+ umec doctor --template --manifest <path-or-url> [--json]
1580
+ umec add --list [--json]
1581
+ umec add <item-id> [--force] [--json]
1582
+ umec diff <item-id> [--json]
736
1583
  umec extend [--key NAME] [--type boolean|string|number]`;
737
1584
  }
738
1585
  async function runCli(argv, io = {}) {
739
1586
  const cwd = io.cwd ?? process.cwd();
740
1587
  const stdout = io.stdout ?? ((message) => console.log(message));
741
1588
  const stderr = io.stderr ?? ((message) => console.error(message));
742
- const { command, flags } = parseArgs(argv);
1589
+ const { command, flags, positionals } = parseArgs(argv);
743
1590
  if (!command || command === "help" || flags.help) {
744
1591
  stdout(usage());
745
1592
  return command ? 0 : 1;
746
1593
  }
1594
+ if (command === "add" || command === "diff") {
1595
+ if (command === "add" && flags.list === true) {
1596
+ if (positionals.length > 0) {
1597
+ stderr("umec add --list \u306F item-id \u3092\u53D6\u308A\u307E\u305B\u3093\u3002");
1598
+ return 1;
1599
+ }
1600
+ const listed = await runRegistryList({
1601
+ cwd,
1602
+ json: flags.json === true,
1603
+ print: stdout,
1604
+ registryRoot: io.registryRoot
1605
+ });
1606
+ if (listed.error) stderr(listed.error);
1607
+ return listed.exitCode;
1608
+ }
1609
+ if (positionals.length > 1) {
1610
+ stderr(`umec ${command} \u306F item-id \u30921\u3064\u3060\u3051\u53D7\u3051\u53D6\u308A\u307E\u3059\u3002`);
1611
+ return 1;
1612
+ }
1613
+ const result = command === "add" ? await runRegistryAdd({
1614
+ cwd,
1615
+ itemId: positionals[0],
1616
+ force: flags.force === true,
1617
+ json: flags.json === true,
1618
+ print: stdout,
1619
+ registryRoot: io.registryRoot,
1620
+ coreVersion: io.coreVersion,
1621
+ adminVersion: io.adminVersion,
1622
+ now: io.now
1623
+ }) : await runRegistryDiff({
1624
+ cwd,
1625
+ itemId: positionals[0],
1626
+ json: flags.json === true,
1627
+ print: stdout,
1628
+ registryRoot: io.registryRoot
1629
+ });
1630
+ if (result.error) stderr(result.error);
1631
+ return result.exitCode;
1632
+ }
1633
+ if (command === "doctor" && flags.template === true) {
1634
+ const manifest = flags.manifest;
1635
+ if (typeof manifest !== "string" || manifest.length === 0) {
1636
+ stderr("umec doctor --template \u306B\u306F --manifest <path-or-url> \u304C\u5FC5\u8981\u3067\u3059\u3002");
1637
+ return 1;
1638
+ }
1639
+ const result = await runTemplateDoctor({
1640
+ cwd,
1641
+ manifest,
1642
+ json: flags.json === true,
1643
+ print: stdout
1644
+ });
1645
+ if (result.error) stderr(result.error);
1646
+ return result.exitCode;
1647
+ }
747
1648
  const snapshot = loadSnapshotJson();
748
1649
  const remote = flags.remote === true;
749
1650
  const database = typeof flags.database === "string" ? flags.database : readD1DatabaseName(cwd);
@@ -801,10 +1702,10 @@ async function runCli(argv, io = {}) {
801
1702
  function isDirectRun() {
802
1703
  const invoked = process.argv[1];
803
1704
  if (!invoked) return false;
804
- return /(?:^|[\\/])cli(?:\.js)?$/.test(invoked) || invoked.includes(`${join5("dist", "cli")}`);
1705
+ return /(?:^|[\\/])cli(?:\.js)?$/.test(invoked) || invoked.includes(`${join7("dist", "cli")}`);
805
1706
  }
806
1707
  if (isDirectRun()) {
807
- const pkg = JSON.parse(readFileSync5(join5(findPackageRoot(), "package.json"), "utf8"));
1708
+ const pkg = JSON.parse(readFileSync7(join7(findPackageRoot(), "package.json"), "utf8"));
808
1709
  if (process.argv.includes("--version") || process.argv.includes("-V")) {
809
1710
  console.log(pkg.version);
810
1711
  process.exit(0);