schemashift-cli 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -6,18 +6,27 @@ import {
6
6
 
7
7
  // src/cli.ts
8
8
  init_esm_shims();
9
- import { existsSync as existsSync4, readFileSync as readFileSync4, statSync, writeFileSync as writeFileSync4 } from "fs";
10
- import { dirname as dirname2, join as join3, resolve as resolve2 } from "path";
9
+ import { existsSync as existsSync5, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync4 } from "fs";
10
+ import { dirname as dirname2, join as join4, resolve as resolve2 } from "path";
11
11
  import { fileURLToPath } from "url";
12
12
  import {
13
+ ApprovalManager,
13
14
  BehavioralWarningAnalyzer,
14
15
  BundleEstimator,
15
16
  CompatibilityAnalyzer,
17
+ createVerificationReport,
16
18
  DetailedAnalyzer,
17
19
  detectFormLibraries,
20
+ extractSchemaNames,
21
+ formatVerificationReport,
18
22
  GovernanceEngine,
23
+ GovernanceFixer,
24
+ GraphExporter,
25
+ generateSamples,
26
+ getAllMigrationTemplates,
27
+ getMigrationTemplate,
19
28
  IncrementalTracker,
20
- loadConfig,
29
+ loadConfig as loadConfig2,
21
30
  MigrationAuditLog,
22
31
  MigrationChain,
23
32
  PerformanceAnalyzer,
@@ -35814,8 +35823,8 @@ var TIER_FEATURES = {
35814
35823
  "free"
35815
35824
  /* FREE */
35816
35825
  ]: {
35817
- maxFiles: 5,
35818
- migrations: ["yup->zod"],
35826
+ maxFiles: 10,
35827
+ migrations: ["yup->zod", "joi->zod"],
35819
35828
  devices: 1,
35820
35829
  ciSupport: false,
35821
35830
  prioritySupport: false,
@@ -36256,7 +36265,7 @@ import { createValibotToZodHandler, createZodToValibotHandler } from "@schemashi
36256
36265
  import { Command } from "commander";
36257
36266
  import { glob as glob2 } from "glob";
36258
36267
  import { Listr } from "listr2";
36259
- import pc4 from "picocolors";
36268
+ import pc5 from "picocolors";
36260
36269
 
36261
36270
  // src/backup.ts
36262
36271
  init_esm_shims();
@@ -36507,6 +36516,73 @@ function computeHunks(original, transformed, context = 3) {
36507
36516
  if (currentHunk) hunks.push(currentHunk);
36508
36517
  return hunks;
36509
36518
  }
36519
+ function generateUnifiedDiff(results) {
36520
+ const lines = [];
36521
+ for (const result of results) {
36522
+ if (!result.transformedCode || result.originalCode === result.transformedCode) continue;
36523
+ const relativePath = result.filePath.replace(`${process.cwd()}/`, "");
36524
+ lines.push(`--- a/${relativePath}`);
36525
+ lines.push(`+++ b/${relativePath}`);
36526
+ const originalLines = result.originalCode.split("\n");
36527
+ const transformedLines = result.transformedCode.split("\n");
36528
+ const hunks = computeHunks(originalLines, transformedLines);
36529
+ for (const hunk of hunks) {
36530
+ lines.push(
36531
+ `@@ -${hunk.originalStart + 1},${hunk.originalCount} +${hunk.transformedStart + 1},${hunk.transformedCount} @@`
36532
+ );
36533
+ for (const line of hunk.lines) {
36534
+ if (line.type === "remove") {
36535
+ lines.push(`-${line.content}`);
36536
+ } else if (line.type === "add") {
36537
+ lines.push(`+${line.content}`);
36538
+ } else {
36539
+ lines.push(` ${line.content}`);
36540
+ }
36541
+ }
36542
+ }
36543
+ }
36544
+ return lines.join("\n");
36545
+ }
36546
+ function generateDrySummaryTable(results) {
36547
+ const rows = [];
36548
+ for (const result of results) {
36549
+ if (!result.transformedCode || result.originalCode === result.transformedCode) continue;
36550
+ const relativePath = result.filePath.replace(`${process.cwd()}/`, "");
36551
+ const originalLines = result.originalCode.split("\n");
36552
+ const transformedLines = result.transformedCode.split("\n");
36553
+ const hunks = computeHunks(originalLines, transformedLines);
36554
+ let added = 0;
36555
+ let removed = 0;
36556
+ for (const hunk of hunks) {
36557
+ for (const line of hunk.lines) {
36558
+ if (line.type === "add") added++;
36559
+ else if (line.type === "remove") removed++;
36560
+ }
36561
+ }
36562
+ rows.push({ file: relativePath, added, removed, warnings: result.warnings.length });
36563
+ }
36564
+ if (rows.length === 0) return "";
36565
+ const maxFile = Math.max(4, ...rows.map((r) => r.file.length));
36566
+ const header = `${"File".padEnd(maxFile)} ${"Added".padStart(5)} ${"Removed".padStart(7)} ${"Warnings".padStart(8)}`;
36567
+ const separator = "\u2500".repeat(header.length);
36568
+ const lines = [header, separator];
36569
+ let totalAdded = 0;
36570
+ let totalRemoved = 0;
36571
+ let totalWarnings = 0;
36572
+ for (const row of rows) {
36573
+ lines.push(
36574
+ `${row.file.padEnd(maxFile)} ${pc.green(`+${row.added}`.padStart(5))} ${pc.red(`-${row.removed}`.padStart(7))} ${row.warnings > 0 ? pc.yellow(String(row.warnings).padStart(8)) : String(row.warnings).padStart(8)}`
36575
+ );
36576
+ totalAdded += row.added;
36577
+ totalRemoved += row.removed;
36578
+ totalWarnings += row.warnings;
36579
+ }
36580
+ lines.push(separator);
36581
+ lines.push(
36582
+ `${`${rows.length} file${rows.length !== 1 ? "s" : ""}`.padEnd(maxFile)} ${pc.green(`+${totalAdded}`.padStart(5))} ${pc.red(`-${totalRemoved}`.padStart(7))} ${totalWarnings > 0 ? pc.yellow(String(totalWarnings).padStart(8)) : String(totalWarnings).padStart(8)}`
36583
+ );
36584
+ return lines.join("\n");
36585
+ }
36510
36586
  function generateDiffSummary(results) {
36511
36587
  const changed = results.filter(
36512
36588
  (r) => r.success && r.transformedCode && r.originalCode !== r.transformedCode
@@ -36522,25 +36598,287 @@ function generateDiffSummary(results) {
36522
36598
  return parts.join(", ");
36523
36599
  }
36524
36600
 
36525
- // src/error-formatter.ts
36601
+ // src/doctor.ts
36526
36602
  init_esm_shims();
36527
- import { readFileSync as readFileSync3 } from "fs";
36603
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
36604
+ import { join as join3 } from "path";
36605
+ import { loadConfig } from "@schemashift/core";
36528
36606
  import pc2 from "picocolors";
36607
+ var SCHEMA_LIBRARIES = {
36608
+ zod: { current: "3.24", name: "Zod" },
36609
+ yup: { current: "1.6", name: "Yup" },
36610
+ joi: { current: "17.13", name: "Joi" },
36611
+ "io-ts": { current: "2.2", name: "io-ts" },
36612
+ valibot: { current: "1.0", name: "Valibot" },
36613
+ arktype: { current: "2.1", name: "ArkType" },
36614
+ superstruct: { current: "2.0", name: "Superstruct" },
36615
+ "@effect/schema": { current: "0.75", name: "Effect Schema" }
36616
+ };
36617
+ var FORM_LIBRARIES = {
36618
+ "react-hook-form": "React Hook Form",
36619
+ formik: "Formik",
36620
+ "@mantine/form": "Mantine Form"
36621
+ };
36622
+ function readPackageJson(projectPath) {
36623
+ const pkgPath = join3(projectPath, "package.json");
36624
+ if (!existsSync3(pkgPath)) return null;
36625
+ try {
36626
+ return JSON.parse(readFileSync3(pkgPath, "utf-8"));
36627
+ } catch {
36628
+ return null;
36629
+ }
36630
+ }
36631
+ function getAllDeps(pkg2) {
36632
+ const deps = pkg2.dependencies || {};
36633
+ const devDeps = pkg2.devDependencies || {};
36634
+ return { ...deps, ...devDeps };
36635
+ }
36636
+ function parseVersion(version2) {
36637
+ return version2.replace(/^[\^~>=<]+/, "").split(".").slice(0, 2).join(".");
36638
+ }
36639
+ function checkSchemaLibraries(allDeps) {
36640
+ const checks = [];
36641
+ const detected = [];
36642
+ for (const [lib, info] of Object.entries(SCHEMA_LIBRARIES)) {
36643
+ if (allDeps[lib]) {
36644
+ detected.push(lib);
36645
+ const version2 = parseVersion(allDeps[lib] ?? "");
36646
+ checks.push({
36647
+ status: "pass",
36648
+ message: `${info.name} ${allDeps[lib]} detected`
36649
+ });
36650
+ if (lib === "zod" && version2.startsWith("3.")) {
36651
+ checks.push({
36652
+ status: "warn",
36653
+ message: `${info.name} v3 detected \u2014 v4 is available with performance improvements`,
36654
+ suggestion: `schemashift migrate ./src -f zod-v3 -t v4 --dry-run`
36655
+ });
36656
+ }
36657
+ if (lib === "io-ts") {
36658
+ checks.push({
36659
+ status: "info",
36660
+ message: "io-ts is maintained but succeeded by Effect Schema",
36661
+ suggestion: "schemashift migrate ./src -f io-ts -t effect --dry-run"
36662
+ });
36663
+ }
36664
+ }
36665
+ }
36666
+ if (detected.length === 0) {
36667
+ checks.push({
36668
+ status: "warn",
36669
+ message: "No schema validation libraries detected in package.json"
36670
+ });
36671
+ } else if (detected.length > 1) {
36672
+ checks.push({
36673
+ status: "info",
36674
+ message: `Multiple schema libraries detected: ${detected.join(", ")}`,
36675
+ suggestion: "Consider consolidating to a single library with SchemaShift"
36676
+ });
36677
+ }
36678
+ return checks;
36679
+ }
36680
+ function checkFormLibraries(allDeps) {
36681
+ const checks = [];
36682
+ const schemaLibs = Object.keys(SCHEMA_LIBRARIES).filter((lib) => allDeps[lib]);
36683
+ for (const [lib, name] of Object.entries(FORM_LIBRARIES)) {
36684
+ if (!allDeps[lib]) continue;
36685
+ checks.push({
36686
+ status: "pass",
36687
+ message: `${name} detected`
36688
+ });
36689
+ if (lib === "formik") {
36690
+ checks.push({
36691
+ status: "warn",
36692
+ message: "Formik is no longer actively maintained \u2014 consider React Hook Form"
36693
+ });
36694
+ }
36695
+ if (lib === "react-hook-form") {
36696
+ if (allDeps["@hookform/resolvers"]) {
36697
+ checks.push({
36698
+ status: "pass",
36699
+ message: "@hookform/resolvers detected \u2014 form resolver migration supported"
36700
+ });
36701
+ }
36702
+ if (schemaLibs.includes("yup") && !schemaLibs.includes("zod")) {
36703
+ checks.push({
36704
+ status: "info",
36705
+ message: "React Hook Form + Yup detected \u2014 Zod integration is more popular",
36706
+ suggestion: "schemashift migrate ./src -f yup -t zod --dry-run"
36707
+ });
36708
+ }
36709
+ }
36710
+ }
36711
+ return checks;
36712
+ }
36713
+ function checkEcosystemDeps(allDeps) {
36714
+ const checks = [];
36715
+ const ecosystemPkgs = [
36716
+ "drizzle-zod",
36717
+ "@trpc/server",
36718
+ "zod-validation-error",
36719
+ "zod-openapi",
36720
+ "@asteasolutions/zod-to-openapi",
36721
+ "zod-prisma",
36722
+ "zod-prisma-types",
36723
+ "zod-form-data",
36724
+ "zodios",
36725
+ "@ts-rest/core"
36726
+ ];
36727
+ for (const pkg2 of ecosystemPkgs) {
36728
+ if (allDeps[pkg2]) {
36729
+ checks.push({
36730
+ status: "info",
36731
+ message: `${pkg2} detected \u2014 may need updates during Zod migration`
36732
+ });
36733
+ }
36734
+ }
36735
+ return checks;
36736
+ }
36737
+ function checkConfig(projectPath) {
36738
+ const checks = [];
36739
+ const configPath = join3(projectPath, ".schemashiftrc.json");
36740
+ if (existsSync3(configPath)) {
36741
+ try {
36742
+ const config2 = loadConfig(projectPath);
36743
+ checks.push({
36744
+ status: "pass",
36745
+ message: "Configuration file .schemashiftrc.json is valid"
36746
+ });
36747
+ if (config2 && typeof config2 === "object" && "governance" in config2) {
36748
+ checks.push({
36749
+ status: "pass",
36750
+ message: "Governance rules configured"
36751
+ });
36752
+ }
36753
+ } catch (err) {
36754
+ checks.push({
36755
+ status: "error",
36756
+ message: `Invalid configuration: ${err instanceof Error ? err.message : String(err)}`,
36757
+ suggestion: "Run: schemashift init --force"
36758
+ });
36759
+ }
36760
+ } else {
36761
+ checks.push({
36762
+ status: "info",
36763
+ message: "No .schemashiftrc.json found \u2014 using defaults",
36764
+ suggestion: "schemashift init"
36765
+ });
36766
+ }
36767
+ return checks;
36768
+ }
36769
+ function checkGitStatus(projectPath) {
36770
+ const checks = [];
36771
+ const gitDir = join3(projectPath, ".git");
36772
+ if (existsSync3(gitDir)) {
36773
+ checks.push({
36774
+ status: "pass",
36775
+ message: "Git repository detected \u2014 backups and rollbacks supported"
36776
+ });
36777
+ } else {
36778
+ checks.push({
36779
+ status: "warn",
36780
+ message: "Not a git repository \u2014 consider initializing git before migrating"
36781
+ });
36782
+ }
36783
+ return checks;
36784
+ }
36785
+ function runDoctor(projectPath) {
36786
+ const pkg2 = readPackageJson(projectPath);
36787
+ const checks = [];
36788
+ const suggestions = [];
36789
+ if (!pkg2) {
36790
+ checks.push({
36791
+ status: "error",
36792
+ message: "No package.json found",
36793
+ suggestion: "Run doctor from the root of your project"
36794
+ });
36795
+ return { checks, suggestions };
36796
+ }
36797
+ checks.push({
36798
+ status: "pass",
36799
+ message: `Project: ${pkg2.name || "unnamed"}`
36800
+ });
36801
+ const allDeps = getAllDeps(pkg2);
36802
+ checks.push(...checkSchemaLibraries(allDeps));
36803
+ checks.push(...checkFormLibraries(allDeps));
36804
+ checks.push(...checkEcosystemDeps(allDeps));
36805
+ checks.push(...checkConfig(projectPath));
36806
+ checks.push(...checkGitStatus(projectPath));
36807
+ for (const check of checks) {
36808
+ if (check.suggestion) {
36809
+ suggestions.push(check.suggestion);
36810
+ }
36811
+ }
36812
+ return { checks, suggestions };
36813
+ }
36814
+ function formatDoctorReport(report) {
36815
+ const lines = [];
36816
+ lines.push(pc2.bold("\nSchemaShift Doctor\n"));
36817
+ const icons = {
36818
+ pass: pc2.green("\u2713"),
36819
+ warn: pc2.yellow("\u26A0"),
36820
+ error: pc2.red("\u2717"),
36821
+ info: pc2.blue("\u2139")
36822
+ };
36823
+ for (const check of report.checks) {
36824
+ lines.push(` ${icons[check.status]} ${check.message}`);
36825
+ }
36826
+ if (report.suggestions.length > 0) {
36827
+ lines.push("");
36828
+ lines.push(pc2.bold(" Suggestions:"));
36829
+ for (const suggestion of report.suggestions) {
36830
+ lines.push(` ${pc2.cyan("\u2192")} ${pc2.dim(suggestion)}`);
36831
+ }
36832
+ }
36833
+ const errorCount = report.checks.filter((c) => c.status === "error").length;
36834
+ const warnCount = report.checks.filter((c) => c.status === "warn").length;
36835
+ lines.push("");
36836
+ if (errorCount > 0) {
36837
+ lines.push(pc2.red(` ${errorCount} error(s) found`));
36838
+ } else if (warnCount > 0) {
36839
+ lines.push(pc2.yellow(` ${warnCount} warning(s) \u2014 project is functional`));
36840
+ } else {
36841
+ lines.push(pc2.green(" Project looks healthy!"));
36842
+ }
36843
+ lines.push("");
36844
+ return lines.join("\n");
36845
+ }
36846
+ function formatDoctorJson(report) {
36847
+ return JSON.stringify(
36848
+ {
36849
+ checks: report.checks,
36850
+ suggestions: report.suggestions,
36851
+ summary: {
36852
+ errors: report.checks.filter((c) => c.status === "error").length,
36853
+ warnings: report.checks.filter((c) => c.status === "warn").length,
36854
+ passed: report.checks.filter((c) => c.status === "pass").length,
36855
+ info: report.checks.filter((c) => c.status === "info").length
36856
+ }
36857
+ },
36858
+ null,
36859
+ 2
36860
+ );
36861
+ }
36862
+
36863
+ // src/error-formatter.ts
36864
+ init_esm_shims();
36865
+ import { readFileSync as readFileSync4 } from "fs";
36866
+ import pc3 from "picocolors";
36529
36867
  function formatErrorSummary(errors) {
36530
36868
  const lines = [];
36531
36869
  lines.push(
36532
- pc2.red(pc2.bold(`
36870
+ pc3.red(pc3.bold(`
36533
36871
  ${errors.length} file${errors.length !== 1 ? "s" : ""} with errors:
36534
36872
  `))
36535
36873
  );
36536
36874
  for (const { filePath, errors: fileErrors } of errors) {
36537
36875
  const relativePath = filePath.replace(`${process.cwd()}/`, "");
36538
- lines.push(pc2.red(` ${relativePath}`));
36876
+ lines.push(pc3.red(` ${relativePath}`));
36539
36877
  for (const err of fileErrors) {
36540
36878
  const loc = err.line ? `:${err.line}` : "";
36541
- lines.push(pc2.dim(` - ${err.message}${loc}`));
36879
+ lines.push(pc3.dim(` - ${err.message}${loc}`));
36542
36880
  if (err.suggestion) {
36543
- lines.push(pc2.cyan(` \u2192 ${err.suggestion}`));
36881
+ lines.push(pc3.cyan(` \u2192 ${err.suggestion}`));
36544
36882
  }
36545
36883
  }
36546
36884
  }
@@ -36550,11 +36888,11 @@ ${errors.length} file${errors.length !== 1 ? "s" : ""} with errors:
36550
36888
  // src/git.ts
36551
36889
  init_esm_shims();
36552
36890
  import { execFileSync } from "child_process";
36553
- import { existsSync as existsSync3 } from "fs";
36891
+ import { existsSync as existsSync4 } from "fs";
36554
36892
  var GitIntegration = class {
36555
36893
  isGitRepo;
36556
36894
  constructor() {
36557
- this.isGitRepo = existsSync3(".git");
36895
+ this.isGitRepo = existsSync4(".git");
36558
36896
  }
36559
36897
  isAvailable() {
36560
36898
  if (!this.isGitRepo) return false;
@@ -36923,7 +37261,7 @@ import { watch } from "fs";
36923
37261
  import { relative as relative2 } from "path";
36924
37262
  import { glob } from "glob";
36925
37263
  import { minimatch } from "minimatch";
36926
- import pc3 from "picocolors";
37264
+ import pc4 from "picocolors";
36927
37265
  var WatchMode = class {
36928
37266
  watchers = [];
36929
37267
  debounceTimers = /* @__PURE__ */ new Map();
@@ -36931,16 +37269,16 @@ var WatchMode = class {
36931
37269
  const files = await glob(options.patterns, {
36932
37270
  ignore: options.exclude
36933
37271
  });
36934
- console.log(pc3.cyan(`
37272
+ console.log(pc4.cyan(`
36935
37273
  Watching ${files.length} files for changes...
36936
37274
  `));
36937
37275
  if (options.dependents && options.dependents.size > 0) {
36938
37276
  console.log(
36939
- pc3.dim(`Dependency-aware mode: ${options.dependents.size} files with dependents
37277
+ pc4.dim(`Dependency-aware mode: ${options.dependents.size} files with dependents
36940
37278
  `)
36941
37279
  );
36942
37280
  }
36943
- console.log(pc3.dim("Press Ctrl+C to stop\n"));
37281
+ console.log(pc4.dim("Press Ctrl+C to stop\n"));
36944
37282
  const directories = new Set(files.map((f) => f.split("/").slice(0, -1).join("/")));
36945
37283
  for (const dir of directories) {
36946
37284
  const watcher = watch(dir || ".", { recursive: true }, async (_event, filename) => {
@@ -36956,23 +37294,23 @@ Watching ${files.length} files for changes...
36956
37294
  this.debounceTimers.set(
36957
37295
  fullPath,
36958
37296
  setTimeout(async () => {
36959
- console.log(pc3.yellow(`
37297
+ console.log(pc4.yellow(`
36960
37298
  Changed: ${relative2(process.cwd(), fullPath)}`));
36961
37299
  try {
36962
37300
  await options.onTransform(fullPath);
36963
- console.log(pc3.green(`Transformed successfully`));
37301
+ console.log(pc4.green(`Transformed successfully`));
36964
37302
  const dependentFiles = options.dependents?.get(fullPath);
36965
37303
  if (dependentFiles && dependentFiles.length > 0) {
36966
37304
  console.log(
36967
- pc3.cyan(` Re-transforming ${dependentFiles.length} dependent file(s)...`)
37305
+ pc4.cyan(` Re-transforming ${dependentFiles.length} dependent file(s)...`)
36968
37306
  );
36969
37307
  for (const depFile of dependentFiles) {
36970
37308
  try {
36971
37309
  await options.onTransform(depFile);
36972
- console.log(pc3.green(` \u2713 ${relative2(process.cwd(), depFile)}`));
37310
+ console.log(pc4.green(` \u2713 ${relative2(process.cwd(), depFile)}`));
36973
37311
  } catch (error) {
36974
37312
  console.error(
36975
- pc3.red(
37313
+ pc4.red(
36976
37314
  ` \u2717 ${relative2(process.cwd(), depFile)}: ${error instanceof Error ? error.message : String(error)}`
36977
37315
  )
36978
37316
  );
@@ -36982,7 +37320,7 @@ Changed: ${relative2(process.cwd(), fullPath)}`));
36982
37320
  console.log("");
36983
37321
  } catch (error) {
36984
37322
  console.error(
36985
- pc3.red(
37323
+ pc4.red(
36986
37324
  `Transform failed: ${error instanceof Error ? error.message : String(error)}
36987
37325
  `
36988
37326
  )
@@ -37029,7 +37367,7 @@ Changed: ${relative2(process.cwd(), fullPath)}`));
37029
37367
 
37030
37368
  // src/cli.ts
37031
37369
  var __dirname2 = dirname2(fileURLToPath(import.meta.url));
37032
- var pkg = JSON.parse(readFileSync4(join3(__dirname2, "..", "package.json"), "utf-8"));
37370
+ var pkg = JSON.parse(readFileSync5(join4(__dirname2, "..", "package.json"), "utf-8"));
37033
37371
  var program = new Command();
37034
37372
  var licenseManager = new LicenseManager();
37035
37373
  var engine = new TransformEngine();
@@ -37049,8 +37387,8 @@ var POLAR_URL = "https://schemashift.qwady.app";
37049
37387
  program.name("schemashift").version(pkg.version).description("TypeScript schema migration CLI");
37050
37388
  program.command("init").description("Create .schemashiftrc.json config file").option("-f, --force", "Overwrite existing config").action((options) => {
37051
37389
  const configPath = ".schemashiftrc.json";
37052
- if (existsSync4(configPath) && !options.force) {
37053
- console.error(pc4.red("Config file already exists. Use --force to overwrite."));
37390
+ if (existsSync5(configPath) && !options.force) {
37391
+ console.error(pc5.red("Config file already exists. Use --force to overwrite."));
37054
37392
  process.exit(1);
37055
37393
  }
37056
37394
  const defaultConfig = {
@@ -37071,8 +37409,8 @@ program.command("init").description("Create .schemashiftrc.json config file").op
37071
37409
  ci: false
37072
37410
  };
37073
37411
  writeFileSync4(configPath, JSON.stringify(defaultConfig, null, 2));
37074
- console.log(pc4.green("Created .schemashiftrc.json"));
37075
- console.log(pc4.dim("Edit the file to customize your migration settings."));
37412
+ console.log(pc5.green("Created .schemashiftrc.json"));
37413
+ console.log(pc5.dim("Edit the file to customize your migration settings."));
37076
37414
  });
37077
37415
  program.command("analyze <path>").description("Analyze schemas in your project").option("--json", "Output as JSON").option("-v, --verbose", "Show detailed analysis").option("--detailed", "Show complexity analysis (Individual+)").option("--readiness <migration>", "Check migration readiness, e.g. yup->zod (Individual+)").option("--complexity", "Show per-schema complexity scores (Individual+)").option("--behavioral <migration>", "Show behavioral difference warnings, e.g. yup->zod").option("--bundle <migration>", "Show bundle size estimation, e.g. zod->valibot (Individual+)").option(
37078
37416
  "--performance <migration>",
@@ -37080,10 +37418,10 @@ program.command("analyze <path>").description("Analyze schemas in your project")
37080
37418
  ).option("--dedup", "Detect duplicate type definitions (Individual+)").action(async (targetPath, options) => {
37081
37419
  const analyzer = new SchemaAnalyzer();
37082
37420
  let files;
37083
- if (existsSync4(targetPath) && statSync(targetPath).isFile()) {
37421
+ if (existsSync5(targetPath) && statSync(targetPath).isFile()) {
37084
37422
  files = [resolve2(targetPath)];
37085
37423
  } else {
37086
- files = await glob2(join3(targetPath, "**/*.{ts,tsx}"), {
37424
+ files = await glob2(join4(targetPath, "**/*.{ts,tsx}"), {
37087
37425
  ignore: ["**/node_modules/**", "**/dist/**"]
37088
37426
  });
37089
37427
  }
@@ -37096,8 +37434,8 @@ program.command("analyze <path>").description("Analyze schemas in your project")
37096
37434
  const validation = await licenseManager.validate();
37097
37435
  const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
37098
37436
  if (!features.advancedAnalysis) {
37099
- console.error(pc4.red("Advanced analysis requires Individual tier or higher."));
37100
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37437
+ console.error(pc5.red("Advanced analysis requires Individual tier or higher."));
37438
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37101
37439
  process.exit(1);
37102
37440
  }
37103
37441
  }
@@ -37105,12 +37443,12 @@ program.command("analyze <path>").description("Analyze schemas in your project")
37105
37443
  console.log(JSON.stringify(result, null, 2));
37106
37444
  return;
37107
37445
  }
37108
- console.log(pc4.bold("\nSchema Analysis\n"));
37109
- console.log(`Total files: ${pc4.cyan(result.totalFiles.toString())}`);
37110
- console.log(`Files with schemas: ${pc4.cyan(result.filesWithSchemas.toString())}`);
37111
- console.log(`Total schemas: ${pc4.cyan(result.schemas.length.toString())}`);
37446
+ console.log(pc5.bold("\nSchema Analysis\n"));
37447
+ console.log(`Total files: ${pc5.cyan(result.totalFiles.toString())}`);
37448
+ console.log(`Files with schemas: ${pc5.cyan(result.filesWithSchemas.toString())}`);
37449
+ console.log(`Total schemas: ${pc5.cyan(result.schemas.length.toString())}`);
37112
37450
  if (result.schemas.length > 0) {
37113
- console.log(pc4.bold("\nSchemas by library:\n"));
37451
+ console.log(pc5.bold("\nSchemas by library:\n"));
37114
37452
  const byLibrary = /* @__PURE__ */ new Map();
37115
37453
  for (const schema of result.schemas) {
37116
37454
  byLibrary.set(schema.library, (byLibrary.get(schema.library) || 0) + 1);
@@ -37119,13 +37457,13 @@ program.command("analyze <path>").description("Analyze schemas in your project")
37119
37457
  console.log(` ${lib}: ${count}`);
37120
37458
  }
37121
37459
  if (options.verbose) {
37122
- console.log(pc4.bold("\nDetailed schemas:\n"));
37460
+ console.log(pc5.bold("\nDetailed schemas:\n"));
37123
37461
  for (const schema of result.schemas.slice(0, 20)) {
37124
- console.log(` ${pc4.cyan(schema.name)} (${schema.library})`);
37125
- console.log(` ${pc4.dim(schema.filePath)}:${schema.lineNumber}`);
37462
+ console.log(` ${pc5.cyan(schema.name)} (${schema.library})`);
37463
+ console.log(` ${pc5.dim(schema.filePath)}:${schema.lineNumber}`);
37126
37464
  }
37127
37465
  if (result.schemas.length > 20) {
37128
- console.log(pc4.dim(` ... and ${result.schemas.length - 20} more`));
37466
+ console.log(pc5.dim(` ... and ${result.schemas.length - 20} more`));
37129
37467
  }
37130
37468
  }
37131
37469
  }
@@ -37136,43 +37474,43 @@ program.command("analyze <path>").description("Analyze schemas in your project")
37136
37474
  const projectPath = resolve2(targetPath);
37137
37475
  const detailedResult = detailedAnalyzer.generateDetailedResult(
37138
37476
  complexities,
37139
- existsSync4(join3(projectPath, "package.json")) ? projectPath : process.cwd()
37477
+ existsSync5(join4(projectPath, "package.json")) ? projectPath : process.cwd()
37140
37478
  );
37141
37479
  if (options.json) {
37142
37480
  console.log(JSON.stringify(detailedResult, null, 2));
37143
37481
  return;
37144
37482
  }
37145
- console.log(pc4.bold("\nComplexity Analysis\n"));
37146
- console.log(`Average complexity: ${pc4.cyan(detailedResult.averageComplexity.toString())}`);
37147
- console.log(`Max complexity: ${pc4.cyan(detailedResult.maxComplexity.toString())}`);
37483
+ console.log(pc5.bold("\nComplexity Analysis\n"));
37484
+ console.log(`Average complexity: ${pc5.cyan(detailedResult.averageComplexity.toString())}`);
37485
+ console.log(`Max complexity: ${pc5.cyan(detailedResult.maxComplexity.toString())}`);
37148
37486
  if (detailedResult.libraryVersions.length > 0) {
37149
- console.log(pc4.bold("\nDetected library versions:\n"));
37487
+ console.log(pc5.bold("\nDetected library versions:\n"));
37150
37488
  for (const v of detailedResult.libraryVersions) {
37151
- console.log(` ${v.library}: ${pc4.cyan(v.version)}`);
37489
+ console.log(` ${v.library}: ${pc5.cyan(v.version)}`);
37152
37490
  }
37153
37491
  }
37154
37492
  if (options.complexity || options.detailed) {
37155
37493
  const sorted = [...complexities].sort((a, b) => b.score - a.score);
37156
- console.log(pc4.bold("\nPer-schema complexity:\n"));
37494
+ console.log(pc5.bold("\nPer-schema complexity:\n"));
37157
37495
  for (const c of sorted.slice(0, 20)) {
37158
- const levelColor = c.level === "critical" ? pc4.red : c.level === "high" ? pc4.yellow : c.level === "medium" ? pc4.blue : pc4.green;
37496
+ const levelColor = c.level === "critical" ? pc5.red : c.level === "high" ? pc5.yellow : c.level === "medium" ? pc5.blue : pc5.green;
37159
37497
  console.log(
37160
- ` ${pc4.cyan(c.schemaName)} ${levelColor(`[${c.level}]`)} score=${c.score}`
37498
+ ` ${pc5.cyan(c.schemaName)} ${levelColor(`[${c.level}]`)} score=${c.score}`
37161
37499
  );
37162
37500
  console.log(
37163
37501
  ` chain=${c.chainLength} nested=${c.nestedDepth} validations=${c.validationCount}`
37164
37502
  );
37165
- console.log(` ${pc4.dim(c.filePath)}:${c.lineNumber}`);
37503
+ console.log(` ${pc5.dim(c.filePath)}:${c.lineNumber}`);
37166
37504
  }
37167
37505
  if (sorted.length > 20) {
37168
- console.log(pc4.dim(` ... and ${sorted.length - 20} more`));
37506
+ console.log(pc5.dim(` ... and ${sorted.length - 20} more`));
37169
37507
  }
37170
37508
  }
37171
37509
  }
37172
37510
  if (options.readiness) {
37173
37511
  const parts = options.readiness.split("->");
37174
37512
  if (parts.length !== 2) {
37175
- console.error(pc4.red("Invalid readiness format. Use: --readiness yup->zod"));
37513
+ console.error(pc5.red("Invalid readiness format. Use: --readiness yup->zod"));
37176
37514
  process.exit(1);
37177
37515
  }
37178
37516
  const [from, to] = parts;
@@ -37239,21 +37577,21 @@ program.command("analyze <path>").description("Analyze schemas in your project")
37239
37577
  console.log(JSON.stringify(readiness, null, 2));
37240
37578
  return;
37241
37579
  }
37242
- const riskColor = readiness.riskLevel === "critical" ? pc4.red : readiness.riskLevel === "high" ? pc4.yellow : readiness.riskLevel === "medium" ? pc4.blue : pc4.green;
37243
- console.log(pc4.bold(`
37580
+ const riskColor = readiness.riskLevel === "critical" ? pc5.red : readiness.riskLevel === "high" ? pc5.yellow : readiness.riskLevel === "medium" ? pc5.blue : pc5.green;
37581
+ console.log(pc5.bold(`
37244
37582
  Migration Readiness: ${from} -> ${to}
37245
37583
  `));
37246
37584
  console.log(`Readiness: ${riskColor(`${readiness.readinessPercent}%`)}`);
37247
37585
  console.log(`Risk level: ${riskColor(readiness.riskLevel)}`);
37248
37586
  console.log(`Total schemas: ${readiness.totalSchemas}`);
37249
- console.log(`Supported: ${pc4.green(readiness.supportedSchemas.toString())}`);
37587
+ console.log(`Supported: ${pc5.green(readiness.supportedSchemas.toString())}`);
37250
37588
  console.log(
37251
- `Estimated manual fixes: ${pc4.yellow(readiness.estimatedManualFixes.toString())}`
37589
+ `Estimated manual fixes: ${pc5.yellow(readiness.estimatedManualFixes.toString())}`
37252
37590
  );
37253
37591
  if (readiness.unsupportedPatterns.length > 0) {
37254
- console.log(pc4.bold("\nUnsupported patterns:\n"));
37592
+ console.log(pc5.bold("\nUnsupported patterns:\n"));
37255
37593
  for (const p of readiness.unsupportedPatterns) {
37256
- console.log(` ${pc4.red(p)}`);
37594
+ console.log(` ${pc5.red(p)}`);
37257
37595
  }
37258
37596
  }
37259
37597
  }
@@ -37261,7 +37599,7 @@ Migration Readiness: ${from} -> ${to}
37261
37599
  if (options.behavioral) {
37262
37600
  const migParts = options.behavioral.split("->");
37263
37601
  if (migParts.length !== 2) {
37264
- console.error(pc4.red("Invalid format. Use: --behavioral yup->zod"));
37602
+ console.error(pc5.red("Invalid format. Use: --behavioral yup->zod"));
37265
37603
  } else {
37266
37604
  const [bFrom, bTo] = migParts;
37267
37605
  const behavioralAnalyzer = new BehavioralWarningAnalyzer();
@@ -37272,7 +37610,7 @@ Migration Readiness: ${from} -> ${to}
37272
37610
  bTo
37273
37611
  );
37274
37612
  if (behavioralResult.warnings.length > 0) {
37275
- console.log(pc4.bold("\nBehavioral Difference Warnings\n"));
37613
+ console.log(pc5.bold("\nBehavioral Difference Warnings\n"));
37276
37614
  const byCategory = /* @__PURE__ */ new Map();
37277
37615
  for (const w of behavioralResult.warnings) {
37278
37616
  const existing = byCategory.get(w.category) ?? [];
@@ -37280,17 +37618,17 @@ Migration Readiness: ${from} -> ${to}
37280
37618
  byCategory.set(w.category, existing);
37281
37619
  }
37282
37620
  for (const [category, catWarnings] of byCategory) {
37283
- console.log(pc4.yellow(` [${category}] ${catWarnings.length} warning(s)`));
37621
+ console.log(pc5.yellow(` [${category}] ${catWarnings.length} warning(s)`));
37284
37622
  for (const w of catWarnings.slice(0, 5)) {
37285
37623
  console.log(` ${w.message}`);
37286
- console.log(` ${pc4.dim(w.detail)}`);
37624
+ console.log(` ${pc5.dim(w.detail)}`);
37287
37625
  }
37288
37626
  if (catWarnings.length > 5) {
37289
- console.log(pc4.dim(` ... and ${catWarnings.length - 5} more`));
37627
+ console.log(pc5.dim(` ... and ${catWarnings.length - 5} more`));
37290
37628
  }
37291
37629
  }
37292
37630
  } else {
37293
- console.log(pc4.green("\nNo behavioral difference warnings found."));
37631
+ console.log(pc5.green("\nNo behavioral difference warnings found."));
37294
37632
  }
37295
37633
  }
37296
37634
  }
@@ -37298,12 +37636,12 @@ Migration Readiness: ${from} -> ${to}
37298
37636
  const validation = await licenseManager.validate();
37299
37637
  const tier = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
37300
37638
  if (!tier.bundleEstimator) {
37301
- console.error(pc4.red("\nBundle estimation requires Individual tier or higher."));
37302
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37639
+ console.error(pc5.red("\nBundle estimation requires Individual tier or higher."));
37640
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37303
37641
  } else {
37304
37642
  const migParts = options.bundle.split("->");
37305
37643
  if (migParts.length !== 2) {
37306
- console.error(pc4.red("Invalid format. Use: --bundle zod->valibot"));
37644
+ console.error(pc5.red("Invalid format. Use: --bundle zod->valibot"));
37307
37645
  } else {
37308
37646
  const [bFrom, bTo] = migParts;
37309
37647
  const bundleEstimator = new BundleEstimator();
@@ -37313,16 +37651,16 @@ Migration Readiness: ${from} -> ${to}
37313
37651
  bFrom,
37314
37652
  bTo
37315
37653
  );
37316
- console.log(pc4.bold("\nBundle Size Estimation\n"));
37654
+ console.log(pc5.bold("\nBundle Size Estimation\n"));
37317
37655
  console.log(
37318
- ` ${estimate.from.library}: ~${pc4.cyan(`${estimate.from.minifiedGzipKb}kB`)}`
37656
+ ` ${estimate.from.library}: ~${pc5.cyan(`${estimate.from.minifiedGzipKb}kB`)}`
37319
37657
  );
37320
- console.log(` ${estimate.to.library}: ~${pc4.cyan(`${estimate.to.minifiedGzipKb}kB`)}`);
37658
+ console.log(` ${estimate.to.library}: ~${pc5.cyan(`${estimate.to.minifiedGzipKb}kB`)}`);
37321
37659
  console.log(
37322
- ` Delta: ${pc4.yellow(`${estimate.estimatedDelta > 0 ? "+" : ""}${estimate.estimatedDelta.toFixed(1)}kB (${estimate.deltaPercent.toFixed(0)}%)`)}`
37660
+ ` Delta: ${pc5.yellow(`${estimate.estimatedDelta > 0 ? "+" : ""}${estimate.estimatedDelta.toFixed(1)}kB (${estimate.deltaPercent.toFixed(0)}%)`)}`
37323
37661
  );
37324
37662
  console.log(`
37325
- ${pc4.dim(estimate.summary)}`);
37663
+ ${pc5.dim(estimate.summary)}`);
37326
37664
  }
37327
37665
  }
37328
37666
  }
@@ -37330,12 +37668,12 @@ Migration Readiness: ${from} -> ${to}
37330
37668
  const validation = await licenseManager.validate();
37331
37669
  const tier = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
37332
37670
  if (!tier.performanceAnalyzer) {
37333
- console.error(pc4.red("\nPerformance analysis requires Individual tier or higher."));
37334
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37671
+ console.error(pc5.red("\nPerformance analysis requires Individual tier or higher."));
37672
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37335
37673
  } else {
37336
37674
  const migParts = options.performance.split("->");
37337
37675
  if (migParts.length !== 2) {
37338
- console.error(pc4.red("Invalid format. Use: --performance zod-v3->v4"));
37676
+ console.error(pc5.red("Invalid format. Use: --performance zod-v3->v4"));
37339
37677
  } else {
37340
37678
  const [pFrom, pTo] = migParts;
37341
37679
  const perfAnalyzer = new PerformanceAnalyzer();
@@ -37346,16 +37684,16 @@ Migration Readiness: ${from} -> ${to}
37346
37684
  pTo
37347
37685
  );
37348
37686
  if (perfResult.warnings.length > 0) {
37349
- console.log(pc4.bold("\nPerformance Impact Analysis\n"));
37687
+ console.log(pc5.bold("\nPerformance Impact Analysis\n"));
37350
37688
  for (const w of perfResult.warnings) {
37351
- const color = w.severity === "error" ? pc4.red : w.severity === "warning" ? pc4.yellow : pc4.dim;
37689
+ const color = w.severity === "error" ? pc5.red : w.severity === "warning" ? pc5.yellow : pc5.dim;
37352
37690
  console.log(` ${color(`[${w.severity}]`)} ${w.message}`);
37353
- console.log(` ${pc4.dim(w.detail)}`);
37691
+ console.log(` ${pc5.dim(w.detail)}`);
37354
37692
  }
37355
37693
  console.log(`
37356
- ${pc4.dim(perfResult.recommendation)}`);
37694
+ ${pc5.dim(perfResult.recommendation)}`);
37357
37695
  } else {
37358
- console.log(pc4.green("\nNo performance concerns detected."));
37696
+ console.log(pc5.green("\nNo performance concerns detected."));
37359
37697
  }
37360
37698
  }
37361
37699
  }
@@ -37364,25 +37702,25 @@ Migration Readiness: ${from} -> ${to}
37364
37702
  const validation = await licenseManager.validate();
37365
37703
  const tier = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
37366
37704
  if (!tier.typeDeduplication) {
37367
- console.error(pc4.red("\nType deduplication detection requires Individual tier or higher."));
37368
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37705
+ console.error(pc5.red("\nType deduplication detection requires Individual tier or higher."));
37706
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37369
37707
  } else {
37370
37708
  const dedupDetector = new TypeDedupDetector();
37371
37709
  const sourceFiles = analyzer.getProject().getSourceFiles();
37372
37710
  const dedupResult = dedupDetector.detect(sourceFiles);
37373
37711
  if (dedupResult.candidates.length > 0) {
37374
- console.log(pc4.bold("\nDuplicate Type Candidates\n"));
37712
+ console.log(pc5.bold("\nDuplicate Type Candidates\n"));
37375
37713
  for (const c of dedupResult.candidates.slice(0, 10)) {
37376
- console.log(` ${pc4.cyan(c.typeName)} <-> ${pc4.cyan(c.schemaName)}`);
37377
- console.log(` Type: ${pc4.dim(c.typeFilePath)}:${c.typeLineNumber}`);
37378
- console.log(` Schema: ${pc4.dim(c.schemaFilePath)}:${c.schemaLineNumber}`);
37379
- console.log(` Confidence: ${pc4.dim(c.confidence)} | ${pc4.dim(c.suggestion)}`);
37714
+ console.log(` ${pc5.cyan(c.typeName)} <-> ${pc5.cyan(c.schemaName)}`);
37715
+ console.log(` Type: ${pc5.dim(c.typeFilePath)}:${c.typeLineNumber}`);
37716
+ console.log(` Schema: ${pc5.dim(c.schemaFilePath)}:${c.schemaLineNumber}`);
37717
+ console.log(` Confidence: ${pc5.dim(c.confidence)} | ${pc5.dim(c.suggestion)}`);
37380
37718
  }
37381
37719
  if (dedupResult.candidates.length > 10) {
37382
- console.log(pc4.dim(` ... and ${dedupResult.candidates.length - 10} more`));
37720
+ console.log(pc5.dim(` ... and ${dedupResult.candidates.length - 10} more`));
37383
37721
  }
37384
37722
  } else {
37385
- console.log(pc4.green("\nNo duplicate type candidates found."));
37723
+ console.log(pc5.green("\nNo duplicate type candidates found."));
37386
37724
  }
37387
37725
  }
37388
37726
  }
@@ -37391,24 +37729,66 @@ program.command("migrate <path>").description("Migrate schemas from one library
37391
37729
  "--max-risk-score <score>",
37392
37730
  "Exit 1 if any file exceeds risk score (Team+)",
37393
37731
  Number.parseInt
37394
- ).option("--incremental", "Enable incremental migration (file-by-file with progress tracking)").option("--resume", "Resume a previously started incremental migration").option("--incremental-status", "Show incremental migration progress").option("--scaffold-tests", "Generate validation tests after migration (Pro+)").option("--audit", "Enable migration audit logging (Team+)").action(async (targetPath, options) => {
37732
+ ).option("--incremental", "Enable incremental migration (file-by-file with progress tracking)").option("--resume", "Resume a previously started incremental migration").option("--incremental-status", "Show incremental migration progress").option("--scaffold-tests", "Generate validation tests after migration (Pro+)").option("--audit", "Enable migration audit logging (Team+)").option("--preset <name>", "Use a migration preset template (Individual+)").option("--output-diff <file>", "Write unified diff to file (use - for stdout)").option(
37733
+ "--canary <percent>",
37734
+ "Canary migration: migrate X% of files first (Pro+)",
37735
+ Number.parseInt
37736
+ ).option("--compliance-report <format>", "Generate compliance report: soc2, hipaa (Team+)").action(async (targetPath, options) => {
37395
37737
  const startTime = Date.now();
37738
+ if (options.preset) {
37739
+ const validation2 = await licenseManager.validate();
37740
+ const features2 = validation2.license?.features || TIER_FEATURES[LicenseTier.FREE];
37741
+ if (!features2.advancedAnalysis) {
37742
+ console.error(pc5.red("Migration presets require Individual tier or higher."));
37743
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37744
+ process.exit(1);
37745
+ }
37746
+ const preset = getMigrationTemplate(options.preset);
37747
+ if (!preset) {
37748
+ console.error(pc5.red(`Unknown preset: ${options.preset}`));
37749
+ const names = getAllMigrationTemplates().map((t) => t.name);
37750
+ console.log(`Available presets: ${names.join(", ")}`);
37751
+ process.exit(1);
37752
+ }
37753
+ const step = preset.migrationSteps[0];
37754
+ if (step && !options.from) options.from = step.from;
37755
+ if (step && !options.to) options.to = step.to;
37756
+ console.log(pc5.bold(`
37757
+ Using preset: ${preset.name}`));
37758
+ console.log(pc5.dim(preset.description));
37759
+ if (preset.preChecks.length > 0) {
37760
+ console.log(pc5.bold("\nPre-migration checklist:"));
37761
+ for (const check of preset.preChecks) {
37762
+ console.log(` ${pc5.yellow("\u2022")} ${check.description}`);
37763
+ }
37764
+ }
37765
+ if (preset.packageChanges.length > 0) {
37766
+ console.log(pc5.bold("\nRecommended package changes:"));
37767
+ for (const change of preset.packageChanges) {
37768
+ const verb = change.action === "install" ? "+" : change.action === "remove" ? "-" : "\u2191";
37769
+ console.log(
37770
+ ` ${pc5.cyan(verb)} ${change.package}${change.version ? `@${change.version}` : ""}`
37771
+ );
37772
+ }
37773
+ }
37774
+ console.log("");
37775
+ }
37396
37776
  if (!options.chain && (!options.from || !options.to)) {
37397
- console.error(pc4.red("Either --from and --to, or --chain must be specified."));
37398
- console.error(pc4.dim(" Single-step: schemashift migrate <path> -f yup -t zod"));
37399
- console.error(pc4.dim(" Chain: schemashift migrate <path> --chain yup->zod->valibot"));
37777
+ console.error(pc5.red("Either --from and --to, or --chain must be specified."));
37778
+ console.error(pc5.dim(" Single-step: schemashift migrate <path> -f yup -t zod"));
37779
+ console.error(pc5.dim(" Chain: schemashift migrate <path> --chain yup->zod->valibot"));
37400
37780
  process.exit(1);
37401
37781
  }
37402
37782
  if (options.maxRiskScore !== void 0 && Number.isNaN(options.maxRiskScore)) {
37403
- console.error(pc4.red("--max-risk-score must be a valid number."));
37783
+ console.error(pc5.red("--max-risk-score must be a valid number."));
37404
37784
  process.exit(1);
37405
37785
  }
37406
37786
  let config2;
37407
37787
  try {
37408
- config2 = await loadConfig(options.config);
37788
+ config2 = await loadConfig2(options.config);
37409
37789
  } catch (error) {
37410
37790
  const msg = error instanceof Error ? error.message : String(error);
37411
- console.warn(pc4.yellow(`Could not load config: ${msg}. Using defaults.`));
37791
+ console.warn(pc5.yellow(`Could not load config: ${msg}. Using defaults.`));
37412
37792
  config2 = {
37413
37793
  include: ["**/*.ts", "**/*.tsx"],
37414
37794
  exclude: ["**/node_modules/**", "**/dist/**"],
@@ -37418,95 +37798,95 @@ program.command("migrate <path>").description("Migrate schemas from one library
37418
37798
  }
37419
37799
  const validation = await licenseManager.validate();
37420
37800
  if (!validation.valid) {
37421
- console.error(pc4.red(`License error: ${validation.error}`));
37801
+ console.error(pc5.red(`License error: ${validation.error}`));
37422
37802
  process.exit(1);
37423
37803
  }
37424
37804
  const tier = validation.license?.tier || LicenseTier.FREE;
37425
37805
  const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
37426
37806
  const migrationPath = `${options.from}->${options.to}`;
37427
37807
  if (!canUseMigration(tier, options.from, options.to)) {
37428
- console.error(pc4.red(`Migration ${migrationPath} requires a higher tier.`));
37429
- console.log(`Your tier: ${pc4.yellow(tier)}`);
37430
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37808
+ console.error(pc5.red(`Migration ${migrationPath} requires a higher tier.`));
37809
+ console.log(`Your tier: ${pc5.yellow(tier)}`);
37810
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37431
37811
  process.exit(1);
37432
37812
  }
37433
37813
  if (options.ci && !features.ciSupport) {
37434
- console.error(pc4.red("CI mode requires Pro tier or higher."));
37435
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37814
+ console.error(pc5.red("CI mode requires Pro tier or higher."));
37815
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37436
37816
  process.exit(1);
37437
37817
  }
37438
37818
  const customRulesCount = config2.customRules?.length ?? 0;
37439
37819
  if (customRulesCount > features.customRulesLimit) {
37440
37820
  console.error(
37441
- pc4.red(
37821
+ pc5.red(
37442
37822
  `Custom rules limit exceeded. You have ${customRulesCount} rules, max ${features.customRulesLimit} for ${tier} tier.`
37443
37823
  )
37444
37824
  );
37445
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37825
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37446
37826
  process.exit(1);
37447
37827
  }
37448
37828
  if ((options.gitBranch || options.gitCommit) && !features.gitIntegration) {
37449
- console.error(pc4.red("Git integration requires Individual tier or higher."));
37450
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37829
+ console.error(pc5.red("Git integration requires Individual tier or higher."));
37830
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37451
37831
  process.exit(1);
37452
37832
  }
37453
37833
  if (options.crossFile && !features.crossFileResolution) {
37454
- console.error(pc4.red("Cross-file resolution requires Pro tier or higher."));
37455
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37834
+ console.error(pc5.red("Cross-file resolution requires Pro tier or higher."));
37835
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37456
37836
  process.exit(1);
37457
37837
  }
37458
37838
  if (options.chain && !features.chainMigrations) {
37459
- console.error(pc4.red("Chain migrations require Pro tier or higher."));
37460
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37839
+ console.error(pc5.red("Chain migrations require Pro tier or higher."));
37840
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37461
37841
  process.exit(1);
37462
37842
  }
37463
37843
  if (options.compatCheck && !features.compatCheck) {
37464
- console.error(pc4.red("Compatibility check requires Pro tier or higher."));
37465
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37844
+ console.error(pc5.red("Compatibility check requires Pro tier or higher."));
37845
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37466
37846
  process.exit(1);
37467
37847
  }
37468
37848
  if (options.failOnWarnings && !features.failOnWarnings) {
37469
- console.error(pc4.red("--fail-on-warnings requires Team tier."));
37470
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37849
+ console.error(pc5.red("--fail-on-warnings requires Team tier."));
37850
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37471
37851
  process.exit(1);
37472
37852
  }
37473
37853
  if (options.maxRiskScore !== void 0 && !features.maxRiskScoreThreshold) {
37474
- console.error(pc4.red("--max-risk-score requires Team tier."));
37475
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37854
+ console.error(pc5.red("--max-risk-score requires Team tier."));
37855
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37476
37856
  process.exit(1);
37477
37857
  }
37478
37858
  if (options.compatCheck) {
37479
37859
  const compatAnalyzer = new CompatibilityAnalyzer();
37480
37860
  const compatResult = compatAnalyzer.checkCompatibility(resolve2(targetPath));
37481
- console.log(pc4.bold("\nCompatibility Check\n"));
37861
+ console.log(pc5.bold("\nCompatibility Check\n"));
37482
37862
  if (compatResult.detectedVersions.length > 0) {
37483
37863
  for (const v of compatResult.detectedVersions) {
37484
- console.log(` ${v.library}: ${pc4.cyan(v.version)}`);
37864
+ console.log(` ${v.library}: ${pc5.cyan(v.version)}`);
37485
37865
  }
37486
37866
  }
37487
37867
  console.log(`
37488
- Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
37868
+ Compatibility score: ${pc5.cyan(compatResult.overallScore.toString())}%`);
37489
37869
  if (compatResult.issues.length > 0) {
37490
- console.log(pc4.bold("\nIssues:\n"));
37870
+ console.log(pc5.bold("\nIssues:\n"));
37491
37871
  for (const issue2 of compatResult.issues) {
37492
- const color = issue2.severity === "error" ? pc4.red : issue2.severity === "warning" ? pc4.yellow : pc4.dim;
37872
+ const color = issue2.severity === "error" ? pc5.red : issue2.severity === "warning" ? pc5.yellow : pc5.dim;
37493
37873
  console.log(` ${color(`[${issue2.severity}]`)} ${issue2.issue}`);
37494
- console.log(` ${pc4.dim(issue2.suggestion)}`);
37874
+ console.log(` ${pc5.dim(issue2.suggestion)}`);
37495
37875
  }
37496
37876
  }
37497
37877
  console.log("");
37498
37878
  }
37499
37879
  if (config2.plugins && config2.plugins.length > 0) {
37500
37880
  if (!features.customPlugins) {
37501
- console.error(pc4.red("Custom plugins require Team tier."));
37502
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37881
+ console.error(pc5.red("Custom plugins require Team tier."));
37882
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37503
37883
  process.exit(1);
37504
37884
  }
37505
37885
  const pluginLoader = new PluginLoader();
37506
37886
  const pluginResult = await pluginLoader.loadPlugins(config2.plugins);
37507
37887
  if (pluginResult.errors.length > 0) {
37508
37888
  for (const err of pluginResult.errors) {
37509
- console.warn(pc4.yellow(`Plugin error: ${err}`));
37889
+ console.warn(pc5.yellow(`Plugin error: ${err}`));
37510
37890
  }
37511
37891
  }
37512
37892
  for (const plugin of pluginResult.loaded) {
@@ -37515,7 +37895,7 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
37515
37895
  engine.registerHandler(h.from, h.to, h.handler);
37516
37896
  }
37517
37897
  }
37518
- console.log(pc4.dim(`Loaded plugin: ${plugin.name} v${plugin.version}`));
37898
+ console.log(pc5.dim(`Loaded plugin: ${plugin.name} v${plugin.version}`));
37519
37899
  }
37520
37900
  }
37521
37901
  let chainSteps;
@@ -37528,15 +37908,15 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
37528
37908
  }
37529
37909
  const chainValidation = migrationChain.validateChain(chainSteps, engine);
37530
37910
  if (!chainValidation.valid) {
37531
- console.error(pc4.red("Chain migration validation failed:"));
37911
+ console.error(pc5.red("Chain migration validation failed:"));
37532
37912
  for (const err of chainValidation.errors) {
37533
- console.error(` ${pc4.red(err)}`);
37913
+ console.error(` ${pc5.red(err)}`);
37534
37914
  }
37535
37915
  process.exit(1);
37536
37916
  }
37537
37917
  } else {
37538
37918
  if (!engine.hasHandler(options.from, options.to)) {
37539
- console.error(pc4.red(`No handler for ${migrationPath}`));
37919
+ console.error(pc5.red(`No handler for ${migrationPath}`));
37540
37920
  console.log(
37541
37921
  "Supported migrations:",
37542
37922
  engine.getSupportedPaths().map((p) => `${p.from}->${p.to}`).join(", ")
@@ -37545,57 +37925,57 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
37545
37925
  }
37546
37926
  }
37547
37927
  let files;
37548
- if (existsSync4(targetPath) && statSync(targetPath).isFile()) {
37928
+ if (existsSync5(targetPath) && statSync(targetPath).isFile()) {
37549
37929
  files = [resolve2(targetPath)];
37550
37930
  } else {
37551
- files = await glob2(join3(targetPath, "**/*.{ts,tsx}"), {
37931
+ files = await glob2(join4(targetPath, "**/*.{ts,tsx}"), {
37552
37932
  ignore: config2.exclude
37553
37933
  });
37554
37934
  }
37555
37935
  if (files.length === 0) {
37556
- console.log(pc4.yellow("No files found matching patterns."));
37936
+ console.log(pc5.yellow("No files found matching patterns."));
37557
37937
  process.exit(0);
37558
37938
  }
37559
37939
  const incrementalTracker = new IncrementalTracker(resolve2(targetPath));
37560
37940
  if (options.incrementalStatus) {
37561
37941
  const progress = incrementalTracker.getProgress();
37562
37942
  if (!progress) {
37563
- console.log(pc4.yellow("No incremental migration in progress."));
37943
+ console.log(pc5.yellow("No incremental migration in progress."));
37564
37944
  process.exit(0);
37565
37945
  }
37566
- console.log(pc4.bold("\nIncremental Migration Status\n"));
37567
- console.log(`Completed: ${pc4.green(progress.completed.toString())}`);
37568
- console.log(`Remaining: ${pc4.cyan(progress.remaining.toString())}`);
37569
- console.log(`Failed: ${pc4.red(progress.failed.toString())}`);
37946
+ console.log(pc5.bold("\nIncremental Migration Status\n"));
37947
+ console.log(`Completed: ${pc5.green(progress.completed.toString())}`);
37948
+ console.log(`Remaining: ${pc5.cyan(progress.remaining.toString())}`);
37949
+ console.log(`Failed: ${pc5.red(progress.failed.toString())}`);
37570
37950
  console.log(`Total: ${progress.total}`);
37571
- console.log(`Progress: ${pc4.cyan(`${progress.percent}%`)}`);
37951
+ console.log(`Progress: ${pc5.cyan(`${progress.percent}%`)}`);
37572
37952
  process.exit(0);
37573
37953
  }
37574
37954
  if (options.resume) {
37575
37955
  const state = incrementalTracker.resume();
37576
37956
  if (!state) {
37577
- console.error(pc4.red("No incremental migration to resume."));
37957
+ console.error(pc5.red("No incremental migration to resume."));
37578
37958
  process.exit(1);
37579
37959
  }
37580
37960
  files = state.remainingFiles;
37581
- console.log(pc4.dim(`Resuming incremental migration: ${state.migrationId}`));
37961
+ console.log(pc5.dim(`Resuming incremental migration: ${state.migrationId}`));
37582
37962
  const progress = incrementalTracker.getProgress();
37583
37963
  if (progress) {
37584
37964
  console.log(
37585
- pc4.dim(
37965
+ pc5.dim(
37586
37966
  `Progress: ${progress.completed}/${progress.total} completed (${progress.percent}%)`
37587
37967
  )
37588
37968
  );
37589
37969
  }
37590
37970
  } else if (options.incremental) {
37591
37971
  incrementalTracker.start(files, options.from, options.to);
37592
- console.log(pc4.dim("Started incremental migration."));
37972
+ console.log(pc5.dim("Started incremental migration."));
37593
37973
  }
37594
37974
  if (files.length > features.maxFiles) {
37595
37975
  console.error(
37596
- pc4.red(`File limit exceeded. Found ${files.length}, max ${features.maxFiles}.`)
37976
+ pc5.red(`File limit exceeded. Found ${files.length}, max ${features.maxFiles}.`)
37597
37977
  );
37598
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
37978
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37599
37979
  process.exit(1);
37600
37980
  }
37601
37981
  if (options.crossFile) {
@@ -37607,34 +37987,34 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
37607
37987
  const resolvedFiles = files.map((f) => resolve2(f));
37608
37988
  const depResult = depResolver.resolve(analyzer.getProject(), resolvedFiles);
37609
37989
  files = depResult.sortedFiles;
37610
- console.log(pc4.bold("\nCross-file resolution\n"));
37611
- console.log(`Files: ${pc4.cyan(files.length.toString())}`);
37612
- console.log(`Cross-file refs: ${pc4.cyan(depResult.crossFileRefs.toString())}`);
37990
+ console.log(pc5.bold("\nCross-file resolution\n"));
37991
+ console.log(`Files: ${pc5.cyan(files.length.toString())}`);
37992
+ console.log(`Cross-file refs: ${pc5.cyan(depResult.crossFileRefs.toString())}`);
37613
37993
  if (depResult.circularWarnings.length > 0) {
37614
- console.log(pc4.yellow(`Circular deps: ${depResult.circularWarnings.length}`));
37994
+ console.log(pc5.yellow(`Circular deps: ${depResult.circularWarnings.length}`));
37615
37995
  for (const w of depResult.circularWarnings) {
37616
- console.log(` ${pc4.yellow(w)}`);
37996
+ console.log(` ${pc5.yellow(w)}`);
37617
37997
  }
37618
37998
  }
37619
37999
  console.log("");
37620
38000
  }
37621
38001
  const displayPath = options.chain || migrationPath;
37622
- console.log(pc4.bold(`
38002
+ console.log(pc5.bold(`
37623
38003
  Migrating ${displayPath}
37624
38004
  `));
37625
- console.log(`Files: ${pc4.cyan(files.length.toString())}`);
37626
- console.log(`Tier: ${pc4.cyan(tier)}`);
37627
- console.log(`Migration: ${pc4.cyan(displayPath)}`);
38005
+ console.log(`Files: ${pc5.cyan(files.length.toString())}`);
38006
+ console.log(`Tier: ${pc5.cyan(tier)}`);
38007
+ console.log(`Migration: ${pc5.cyan(displayPath)}`);
37628
38008
  if (chainSteps) {
37629
- console.log(`Steps: ${pc4.cyan(chainSteps.length.toString())}`);
38009
+ console.log(`Steps: ${pc5.cyan(chainSteps.length.toString())}`);
37630
38010
  }
37631
38011
  if (options.dryRun) {
37632
- console.log(pc4.yellow("\nDRY RUN \u2014 no files will be modified\n"));
38012
+ console.log(pc5.yellow("\nDRY RUN \u2014 no files will be modified\n"));
37633
38013
  }
37634
38014
  const git = new GitIntegration();
37635
38015
  if (options.gitBranch && git.isAvailable()) {
37636
38016
  if (git.hasUncommittedChanges()) {
37637
- console.error(pc4.red("Uncommitted changes detected. Commit or stash first."));
38017
+ console.error(pc5.red("Uncommitted changes detected. Commit or stash first."));
37638
38018
  process.exit(1);
37639
38019
  }
37640
38020
  const branchName = git.generateBranchName(
@@ -37643,13 +38023,13 @@ Migrating ${displayPath}
37643
38023
  options.to
37644
38024
  );
37645
38025
  git.createBranch(branchName);
37646
- console.log(pc4.dim(`Created branch: ${branchName}`));
38026
+ console.log(pc5.dim(`Created branch: ${branchName}`));
37647
38027
  }
37648
38028
  const backup = new BackupManager(config2.backup?.dir);
37649
38029
  let backupManifest;
37650
38030
  if (options.backup !== false && config2.backup?.enabled !== false && !options.dryRun) {
37651
38031
  backupManifest = backup.createBackup(files, options.from, options.to);
37652
- console.log(pc4.dim(`Backup created: ${backupManifest.id}`));
38032
+ console.log(pc5.dim(`Backup created: ${backupManifest.id}`));
37653
38033
  }
37654
38034
  const results = [];
37655
38035
  const useConcurrent = !options.crossFile;
@@ -37659,7 +38039,7 @@ Migrating ${displayPath}
37659
38039
  task: async () => {
37660
38040
  if (chainSteps) {
37661
38041
  const migrationChain = new MigrationChain();
37662
- const sourceCode = readFileSync4(resolve2(file), "utf-8");
38042
+ const sourceCode = readFileSync5(resolve2(file), "utf-8");
37663
38043
  const chainResult = migrationChain.executeChain(
37664
38044
  sourceCode,
37665
38045
  resolve2(file),
@@ -37736,16 +38116,16 @@ Migrating ${displayPath}
37736
38116
  const progress = incrementalTracker.getProgress();
37737
38117
  if (progress) {
37738
38118
  console.log(
37739
- pc4.bold(`
38119
+ pc5.bold(`
37740
38120
  Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
37741
38121
  );
37742
38122
  if (progress.remaining > 0) {
37743
38123
  console.log(
37744
- pc4.dim(`Run with --resume to continue. ${progress.remaining} files remaining.`)
38124
+ pc5.dim(`Run with --resume to continue. ${progress.remaining} files remaining.`)
37745
38125
  );
37746
38126
  }
37747
38127
  if (progress.failed > 0) {
37748
- console.log(pc4.yellow(`${progress.failed} file(s) failed. Run with --resume to retry.`));
38128
+ console.log(pc5.yellow(`${progress.failed} file(s) failed. Run with --resume to retry.`));
37749
38129
  }
37750
38130
  }
37751
38131
  }
@@ -37754,17 +38134,17 @@ Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
37754
38134
  if (successFiles.length > 0) {
37755
38135
  git.stageFiles(successFiles);
37756
38136
  git.commit(config2.git?.commitMessage || `chore: migrate ${options.from} to ${options.to}`);
37757
- console.log(pc4.dim("Changes committed"));
38137
+ console.log(pc5.dim("Changes committed"));
37758
38138
  }
37759
38139
  }
37760
38140
  if (options.report) {
37761
38141
  if (options.report === "html" && !features.htmlReports) {
37762
- console.error(pc4.red("HTML reports require Individual tier or higher."));
37763
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
38142
+ console.error(pc5.red("HTML reports require Individual tier or higher."));
38143
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37764
38144
  process.exit(1);
37765
38145
  } else if (options.report === "csv" && !features.csvExport) {
37766
- console.error(pc4.red("CSV export requires Individual tier or higher."));
37767
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
38146
+ console.error(pc5.red("CSV export requires Individual tier or higher."));
38147
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37768
38148
  process.exit(1);
37769
38149
  } else {
37770
38150
  const reporter = new ReportGenerator();
@@ -37785,13 +38165,13 @@ Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
37785
38165
  } else {
37786
38166
  reporter.writeJson(report, outputPath);
37787
38167
  }
37788
- console.log(pc4.green(`Report saved: ${outputPath}`));
38168
+ console.log(pc5.green(`Report saved: ${outputPath}`));
37789
38169
  }
37790
38170
  }
37791
38171
  if (options.scaffoldTests) {
37792
38172
  if (!features.testScaffolding) {
37793
- console.error(pc4.red("\nTest scaffolding requires Pro tier or higher."));
37794
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
38173
+ console.error(pc5.red("\nTest scaffolding requires Pro tier or higher."));
38174
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37795
38175
  } else {
37796
38176
  const scaffolder = new TestScaffolder();
37797
38177
  const successfulFiles = results.filter((r) => r.success).map((r) => r.filePath);
@@ -37802,20 +38182,20 @@ Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
37802
38182
  }
37803
38183
  const sourceFiles = scaffoldAnalyzer.getProject().getSourceFiles();
37804
38184
  const scaffoldResult = scaffolder.scaffold(sourceFiles, options.from, options.to);
37805
- console.log(pc4.bold("\nTest Scaffolding\n"));
38185
+ console.log(pc5.bold("\nTest Scaffolding\n"));
37806
38186
  console.log(
37807
- ` Generated ${pc4.cyan(scaffoldResult.tests.length.toString())} test file(s) for ${scaffoldResult.totalSchemas} schema(s)`
38187
+ ` Generated ${pc5.cyan(scaffoldResult.tests.length.toString())} test file(s) for ${scaffoldResult.totalSchemas} schema(s)`
37808
38188
  );
37809
38189
  for (const test of scaffoldResult.tests) {
37810
- console.log(` ${pc4.dim(test.filePath)} (${test.schemaCount} schemas)`);
38190
+ console.log(` ${pc5.dim(test.filePath)} (${test.schemaCount} schemas)`);
37811
38191
  }
37812
38192
  }
37813
38193
  }
37814
38194
  }
37815
38195
  if (options.audit) {
37816
38196
  if (!features.auditLogging) {
37817
- console.error(pc4.red("\nAudit logging requires Team tier."));
37818
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
38197
+ console.error(pc5.red("\nAudit logging requires Team tier."));
38198
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37819
38199
  } else {
37820
38200
  const projectPath = resolve2(targetPath);
37821
38201
  const auditLog = new MigrationAuditLog(projectPath);
@@ -37835,21 +38215,36 @@ Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
37835
38215
  auditLog.append(entry);
37836
38216
  }
37837
38217
  console.log(
37838
- pc4.dim(`
38218
+ pc5.dim(`
37839
38219
  Audit log saved to .schemashift/audit-log.json (${results.length} entries)`)
37840
38220
  );
37841
38221
  }
37842
38222
  }
37843
38223
  if (options.dryRun) {
38224
+ const summaryTable = generateDrySummaryTable(results);
38225
+ if (summaryTable) {
38226
+ console.log(pc5.bold("\nChange Summary\n"));
38227
+ console.log(summaryTable);
38228
+ }
37844
38229
  const diffOutput = generateDiffPreview(results);
37845
38230
  if (diffOutput) {
37846
- console.log(pc4.bold("\nDiff Preview\n"));
38231
+ console.log(pc5.bold("\nDiff Preview\n"));
37847
38232
  console.log(diffOutput);
37848
38233
  }
37849
38234
  }
38235
+ if (options.outputDiff) {
38236
+ const unifiedDiff = generateUnifiedDiff(results);
38237
+ if (options.outputDiff === "-") {
38238
+ console.log(unifiedDiff);
38239
+ } else {
38240
+ writeFileSync4(options.outputDiff, unifiedDiff);
38241
+ console.log(pc5.dim(`
38242
+ Unified diff written to ${options.outputDiff}`));
38243
+ }
38244
+ }
37850
38245
  const failed = results.filter((r) => !r.success).length;
37851
38246
  const warnings = results.reduce((acc, r) => acc + r.warnings.length, 0);
37852
- console.log(pc4.bold("\nSummary\n"));
38247
+ console.log(pc5.bold("\nSummary\n"));
37853
38248
  console.log(generateDiffSummary(results));
37854
38249
  console.log(`Duration: ${Date.now() - startTime}ms`);
37855
38250
  if (failed > 0) {
@@ -37857,7 +38252,7 @@ Audit log saved to .schemashift/audit-log.json (${results.length} entries)`)
37857
38252
  console.log(formatErrorSummary(errorFiles));
37858
38253
  }
37859
38254
  if (options.verbose && warnings > 0) {
37860
- console.log(pc4.yellow("\nWarnings:\n"));
38255
+ console.log(pc5.yellow("\nWarnings:\n"));
37861
38256
  for (const result of results) {
37862
38257
  for (const warning of result.warnings.slice(0, 5)) {
37863
38258
  console.log(` ${warning}`);
@@ -37869,7 +38264,7 @@ Audit log saved to .schemashift/audit-log.json (${results.length} entries)`)
37869
38264
  }
37870
38265
  if (options.failOnWarnings && warnings > 0) {
37871
38266
  console.error(
37872
- pc4.red(`
38267
+ pc5.red(`
37873
38268
  Failing due to ${warnings} warning(s) (--fail-on-warnings enabled).`)
37874
38269
  );
37875
38270
  process.exit(1);
@@ -37885,13 +38280,13 @@ Failing due to ${warnings} warning(s) (--fail-on-warnings enabled).`)
37885
38280
  }
37886
38281
  if (highRiskFiles.length > 0) {
37887
38282
  console.error(
37888
- pc4.red(
38283
+ pc5.red(
37889
38284
  `
37890
38285
  Failing due to ${highRiskFiles.length} file(s) exceeding max risk score of ${options.maxRiskScore}:`
37891
38286
  )
37892
38287
  );
37893
38288
  for (const f of highRiskFiles) {
37894
- console.error(pc4.red(` ${f}`));
38289
+ console.error(pc5.red(` ${f}`));
37895
38290
  }
37896
38291
  process.exit(1);
37897
38292
  }
@@ -37900,22 +38295,22 @@ Failing due to ${highRiskFiles.length} file(s) exceeding max risk score of ${opt
37900
38295
  program.command("watch <path>").description("Watch files and migrate on change").requiredOption("-f, --from <library>", "Source library").requiredOption("-t, --to <library>", "Target library").option("-c, --config <path>", "Path to config file").action(async (targetPath, options) => {
37901
38296
  const validation = await licenseManager.validate();
37902
38297
  if (!validation.license?.features.watchMode) {
37903
- console.error(pc4.red("Watch mode requires Pro tier or higher."));
37904
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
38298
+ console.error(pc5.red("Watch mode requires Pro tier or higher."));
38299
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37905
38300
  process.exit(1);
37906
38301
  }
37907
38302
  const tier = validation.license?.tier || LicenseTier.FREE;
37908
38303
  if (!canUseMigration(tier, options.from, options.to)) {
37909
- console.error(pc4.red(`Migration ${options.from}->${options.to} requires a higher tier.`));
37910
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
38304
+ console.error(pc5.red(`Migration ${options.from}->${options.to} requires a higher tier.`));
38305
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
37911
38306
  process.exit(1);
37912
38307
  }
37913
38308
  let config2;
37914
38309
  try {
37915
- config2 = await loadConfig(options.config);
38310
+ config2 = await loadConfig2(options.config);
37916
38311
  } catch (error) {
37917
38312
  const msg = error instanceof Error ? error.message : String(error);
37918
- console.warn(pc4.yellow(`Could not load config: ${msg}. Using defaults.`));
38313
+ console.warn(pc5.yellow(`Could not load config: ${msg}. Using defaults.`));
37919
38314
  config2 = {
37920
38315
  include: ["**/*.ts", "**/*.tsx"],
37921
38316
  exclude: ["**/node_modules/**", "**/dist/**"]
@@ -37923,7 +38318,7 @@ program.command("watch <path>").description("Watch files and migrate on change")
37923
38318
  }
37924
38319
  const watchMode = new WatchMode();
37925
38320
  await watchMode.start({
37926
- patterns: config2.include.map((p) => join3(targetPath, p)),
38321
+ patterns: config2.include.map((p) => join4(targetPath, p)),
37927
38322
  exclude: config2.exclude,
37928
38323
  from: options.from,
37929
38324
  to: options.to,
@@ -37946,7 +38341,7 @@ program.command("watch <path>").description("Watch files and migrate on change")
37946
38341
  });
37947
38342
  process.on("SIGINT", () => {
37948
38343
  watchMode.stop();
37949
- console.log(pc4.dim("\nStopped watching."));
38344
+ console.log(pc5.dim("\nStopped watching."));
37950
38345
  process.exit(0);
37951
38346
  });
37952
38347
  });
@@ -37955,56 +38350,56 @@ program.command("rollback [backupId]").description("Restore files from a backup"
37955
38350
  if (options.list) {
37956
38351
  const backups = backup.listBackups();
37957
38352
  if (backups.length === 0) {
37958
- console.log(pc4.yellow("No backups found."));
38353
+ console.log(pc5.yellow("No backups found."));
37959
38354
  return;
37960
38355
  }
37961
- console.log(pc4.bold("\nAvailable backups:\n"));
38356
+ console.log(pc5.bold("\nAvailable backups:\n"));
37962
38357
  for (const b of backups) {
37963
- console.log(` ${pc4.cyan(b.id)}`);
38358
+ console.log(` ${pc5.cyan(b.id)}`);
37964
38359
  console.log(` ${b.from} -> ${b.to} | ${b.files.length} files`);
37965
- console.log(` ${pc4.dim(new Date(b.timestamp).toLocaleString())}
38360
+ console.log(` ${pc5.dim(new Date(b.timestamp).toLocaleString())}
37966
38361
  `);
37967
38362
  }
37968
38363
  return;
37969
38364
  }
37970
38365
  if (options.clean) {
37971
38366
  backup.cleanOldBackups(5);
37972
- console.log(pc4.green("Old backups cleaned."));
38367
+ console.log(pc5.green("Old backups cleaned."));
37973
38368
  return;
37974
38369
  }
37975
38370
  if (!backupId) {
37976
38371
  const backups = backup.listBackups();
37977
38372
  if (backups.length === 0) {
37978
- console.error(pc4.red("No backups available."));
38373
+ console.error(pc5.red("No backups available."));
37979
38374
  process.exit(1);
37980
38375
  }
37981
38376
  backupId = backups[0]?.id;
37982
38377
  }
37983
38378
  if (!backupId) {
37984
- console.error(pc4.red("No backup ID provided."));
38379
+ console.error(pc5.red("No backup ID provided."));
37985
38380
  process.exit(1);
37986
38381
  }
37987
38382
  try {
37988
38383
  backup.restore(backupId);
37989
- console.log(pc4.green(`Restored from ${backupId}`));
38384
+ console.log(pc5.green(`Restored from ${backupId}`));
37990
38385
  } catch (error) {
37991
- console.error(pc4.red(error instanceof Error ? error.message : "Restore failed"));
38386
+ console.error(pc5.red(error instanceof Error ? error.message : "Restore failed"));
37992
38387
  process.exit(1);
37993
38388
  }
37994
38389
  });
37995
38390
  program.command("license").description("Show license information").option("-a, --activate <key>", "Activate a license key").option("-d, --deactivate", "Deactivate current license").option("-s, --status", "Show license status").action(async (options) => {
37996
38391
  if (options.activate) {
37997
- console.log(pc4.dim("Activating license..."));
38392
+ console.log(pc5.dim("Activating license..."));
37998
38393
  const result = await licenseManager.activate(options.activate);
37999
38394
  if (result.valid && result.license) {
38000
- console.log(pc4.green("\nLicense activated successfully!\n"));
38001
- console.log(`Tier: ${pc4.cyan(result.license.tier)}`);
38395
+ console.log(pc5.green("\nLicense activated successfully!\n"));
38396
+ console.log(`Tier: ${pc5.cyan(result.license.tier)}`);
38002
38397
  console.log(`Migrations: ${result.license.features.migrations.join(", ")}`);
38003
38398
  console.log(
38004
38399
  `Max files: ${result.license.features.maxFiles === Infinity ? "Unlimited" : result.license.features.maxFiles}`
38005
38400
  );
38006
38401
  } else {
38007
- console.error(pc4.red(`
38402
+ console.error(pc5.red(`
38008
38403
  Activation failed: ${result.error}`));
38009
38404
  process.exit(1);
38010
38405
  }
@@ -38013,26 +38408,26 @@ Activation failed: ${result.error}`));
38013
38408
  if (options.deactivate) {
38014
38409
  const result = await licenseManager.deactivate();
38015
38410
  if (result.success) {
38016
- console.log(pc4.green("License deactivated. You can now activate on another device."));
38411
+ console.log(pc5.green("License deactivated. You can now activate on another device."));
38017
38412
  } else {
38018
- console.error(pc4.red(`Deactivation failed: ${result.error}`));
38413
+ console.error(pc5.red(`Deactivation failed: ${result.error}`));
38019
38414
  process.exit(1);
38020
38415
  }
38021
38416
  return;
38022
38417
  }
38023
38418
  const status = await licenseManager.getStatus();
38024
- console.log(pc4.bold("\nLicense Status\n"));
38419
+ console.log(pc5.bold("\nLicense Status\n"));
38025
38420
  if (!status.activated) {
38026
- console.log(`Tier: ${pc4.yellow("FREE")}`);
38421
+ console.log(`Tier: ${pc5.yellow("FREE")}`);
38027
38422
  console.log("Max files: 5");
38028
38423
  console.log("Migrations: yup->zod only");
38029
38424
  console.log("\nActivate a license: schemashift license --activate <key>");
38030
- console.log(`Purchase: ${pc4.cyan(POLAR_URL)}`);
38425
+ console.log(`Purchase: ${pc5.cyan(POLAR_URL)}`);
38031
38426
  return;
38032
38427
  }
38033
38428
  const features = TIER_FEATURES[status.tier];
38034
- console.log(`Tier: ${pc4.cyan(status.tier.toUpperCase())}`);
38035
- console.log(`Activated: ${pc4.green("Yes")}`);
38429
+ console.log(`Tier: ${pc5.cyan(status.tier.toUpperCase())}`);
38430
+ console.log(`Activated: ${pc5.green("Yes")}`);
38036
38431
  if (status.email) console.log(`Email: ${status.email}`);
38037
38432
  if (status.expiresAt) console.log(`Expires: ${status.expiresAt.toLocaleDateString()}`);
38038
38433
  if (status.lastValidated)
@@ -38040,29 +38435,29 @@ Activation failed: ${result.error}`));
38040
38435
  console.log("\nFeatures:");
38041
38436
  console.log(` Max files: ${features.maxFiles === Infinity ? "Unlimited" : features.maxFiles}`);
38042
38437
  console.log(` Migrations: ${features.migrations.join(", ")}`);
38043
- console.log(` CI/CD: ${features.ciSupport ? pc4.green("Yes") : pc4.red("No")}`);
38044
- console.log(` HTML reports: ${features.htmlReports ? pc4.green("Yes") : pc4.red("No")}`);
38045
- console.log(` Watch mode: ${features.watchMode ? pc4.green("Yes") : pc4.red("No")}`);
38046
- console.log(` Git integration: ${features.gitIntegration ? pc4.green("Yes") : pc4.red("No")}`);
38438
+ console.log(` CI/CD: ${features.ciSupport ? pc5.green("Yes") : pc5.red("No")}`);
38439
+ console.log(` HTML reports: ${features.htmlReports ? pc5.green("Yes") : pc5.red("No")}`);
38440
+ console.log(` Watch mode: ${features.watchMode ? pc5.green("Yes") : pc5.red("No")}`);
38441
+ console.log(` Git integration: ${features.gitIntegration ? pc5.green("Yes") : pc5.red("No")}`);
38047
38442
  console.log(
38048
- ` Advanced analysis: ${features.advancedAnalysis ? pc4.green("Yes") : pc4.red("No")}`
38443
+ ` Advanced analysis: ${features.advancedAnalysis ? pc5.green("Yes") : pc5.red("No")}`
38049
38444
  );
38050
- console.log(` Risk scoring: ${features.riskScoring ? pc4.green("Yes") : pc4.red("No")}`);
38051
- console.log(` CSV export: ${features.csvExport ? pc4.green("Yes") : pc4.red("No")}`);
38445
+ console.log(` Risk scoring: ${features.riskScoring ? pc5.green("Yes") : pc5.red("No")}`);
38446
+ console.log(` CSV export: ${features.csvExport ? pc5.green("Yes") : pc5.red("No")}`);
38052
38447
  console.log(
38053
- ` Cross-file resolution: ${features.crossFileResolution ? pc4.green("Yes") : pc4.red("No")}`
38448
+ ` Cross-file resolution: ${features.crossFileResolution ? pc5.green("Yes") : pc5.red("No")}`
38054
38449
  );
38055
- console.log(` Chain migrations: ${features.chainMigrations ? pc4.green("Yes") : pc4.red("No")}`);
38056
- console.log(` Compat check: ${features.compatCheck ? pc4.green("Yes") : pc4.red("No")}`);
38057
- console.log(` Custom plugins: ${features.customPlugins ? pc4.green("Yes") : pc4.red("No")}`);
38058
- console.log(` Governance: ${features.governance ? pc4.green("Yes") : pc4.red("No")}`);
38450
+ console.log(` Chain migrations: ${features.chainMigrations ? pc5.green("Yes") : pc5.red("No")}`);
38451
+ console.log(` Compat check: ${features.compatCheck ? pc5.green("Yes") : pc5.red("No")}`);
38452
+ console.log(` Custom plugins: ${features.customPlugins ? pc5.green("Yes") : pc5.red("No")}`);
38453
+ console.log(` Governance: ${features.governance ? pc5.green("Yes") : pc5.red("No")}`);
38059
38454
  });
38060
38455
  program.command("compat <path>").description("Check schema library compatibility (Pro+)").option("--json", "Output as JSON").action(async (targetPath, options) => {
38061
38456
  const validation = await licenseManager.validate();
38062
38457
  const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
38063
38458
  if (!features.compatCheck) {
38064
- console.error(pc4.red("Compatibility check requires Pro tier or higher."));
38065
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
38459
+ console.error(pc5.red("Compatibility check requires Pro tier or higher."));
38460
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
38066
38461
  process.exit(1);
38067
38462
  }
38068
38463
  const compatAnalyzer = new CompatibilityAnalyzer();
@@ -38071,43 +38466,43 @@ program.command("compat <path>").description("Check schema library compatibility
38071
38466
  console.log(JSON.stringify(result, null, 2));
38072
38467
  return;
38073
38468
  }
38074
- console.log(pc4.bold("\nSchema Compatibility Report\n"));
38469
+ console.log(pc5.bold("\nSchema Compatibility Report\n"));
38075
38470
  if (result.detectedVersions.length === 0) {
38076
- console.log(pc4.yellow("No schema libraries detected in package.json."));
38471
+ console.log(pc5.yellow("No schema libraries detected in package.json."));
38077
38472
  return;
38078
38473
  }
38079
- console.log(pc4.bold("Detected libraries:\n"));
38474
+ console.log(pc5.bold("Detected libraries:\n"));
38080
38475
  for (const v of result.detectedVersions) {
38081
- console.log(` ${v.library}: ${pc4.cyan(v.version)}`);
38476
+ console.log(` ${v.library}: ${pc5.cyan(v.version)}`);
38082
38477
  }
38083
38478
  console.log(`
38084
- Compatibility score: ${pc4.cyan(result.overallScore.toString())}%`);
38479
+ Compatibility score: ${pc5.cyan(result.overallScore.toString())}%`);
38085
38480
  if (result.issues.length > 0) {
38086
- console.log(pc4.bold("\nIssues:\n"));
38481
+ console.log(pc5.bold("\nIssues:\n"));
38087
38482
  for (const issue2 of result.issues) {
38088
- const color = issue2.severity === "error" ? pc4.red : issue2.severity === "warning" ? pc4.yellow : pc4.dim;
38483
+ const color = issue2.severity === "error" ? pc5.red : issue2.severity === "warning" ? pc5.yellow : pc5.dim;
38089
38484
  console.log(` ${color(`[${issue2.severity}]`)} ${issue2.library} ${issue2.detectedVersion}`);
38090
38485
  console.log(` ${issue2.issue}`);
38091
- console.log(` ${pc4.dim(issue2.suggestion)}`);
38486
+ console.log(` ${pc5.dim(issue2.suggestion)}`);
38092
38487
  }
38093
38488
  } else {
38094
- console.log(pc4.green("\nNo compatibility issues detected."));
38489
+ console.log(pc5.green("\nNo compatibility issues detected."));
38095
38490
  }
38096
38491
  });
38097
- program.command("governance <path>").description("Run schema governance checks (Team+)").option("--json", "Output as JSON").option("-c, --config <path>", "Path to config file").action(async (targetPath, options) => {
38492
+ program.command("governance <path>").description("Run schema governance checks (Team+)").option("--json", "Output as JSON").option("--fix", "Auto-fix fixable violations").option("--fix-dry-run", "Preview auto-fixes without applying").option("-c, --config <path>", "Path to config file").action(async (targetPath, options) => {
38098
38493
  const validation = await licenseManager.validate();
38099
38494
  const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
38100
38495
  if (!features.governance) {
38101
- console.error(pc4.red("Schema governance requires Team tier."));
38102
- console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
38496
+ console.error(pc5.red("Schema governance requires Team tier."));
38497
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
38103
38498
  process.exit(1);
38104
38499
  }
38105
38500
  let config2;
38106
38501
  try {
38107
- config2 = await loadConfig(options.config);
38502
+ config2 = await loadConfig2(options.config);
38108
38503
  } catch (error) {
38109
38504
  const msg = error instanceof Error ? error.message : String(error);
38110
- console.warn(pc4.yellow(`Could not load config: ${msg}. Using defaults.`));
38505
+ console.warn(pc5.yellow(`Could not load config: ${msg}. Using defaults.`));
38111
38506
  config2 = {
38112
38507
  include: ["**/*.ts", "**/*.tsx"],
38113
38508
  exclude: ["**/node_modules/**", "**/dist/**"],
@@ -38116,17 +38511,17 @@ program.command("governance <path>").description("Run schema governance checks (
38116
38511
  };
38117
38512
  }
38118
38513
  if (!config2.governance?.rules) {
38119
- console.error(pc4.red("No governance rules configured."));
38120
- console.log(pc4.dim('Add a "governance" section to your schemashift config file.'));
38514
+ console.error(pc5.red("No governance rules configured."));
38515
+ console.log(pc5.dim('Add a "governance" section to your schemashift config file.'));
38121
38516
  process.exit(1);
38122
38517
  }
38123
38518
  const { Project } = await import("./ts-morph-5TNWNYI7.js");
38124
38519
  const project = new Project();
38125
- const files = await glob2(join3(targetPath, "**/*.{ts,tsx}"), {
38520
+ const files = await glob2(join4(targetPath, "**/*.{ts,tsx}"), {
38126
38521
  ignore: config2.exclude
38127
38522
  });
38128
38523
  if (files.length === 0) {
38129
- console.log(pc4.yellow("No files found."));
38524
+ console.log(pc5.yellow("No files found."));
38130
38525
  process.exit(0);
38131
38526
  }
38132
38527
  for (const file of files) {
@@ -38139,46 +38534,477 @@ program.command("governance <path>").description("Run schema governance checks (
38139
38534
  console.log(JSON.stringify(result, null, 2));
38140
38535
  return;
38141
38536
  }
38142
- console.log(pc4.bold("\nSchema Governance Report\n"));
38143
- console.log(`Files scanned: ${pc4.cyan(result.filesScanned.toString())}`);
38144
- console.log(`Schemas checked: ${pc4.cyan(result.schemasChecked.toString())}`);
38145
- console.log(`Violations: ${pc4.cyan(result.violations.length.toString())}`);
38537
+ console.log(pc5.bold("\nSchema Governance Report\n"));
38538
+ console.log(`Files scanned: ${pc5.cyan(result.filesScanned.toString())}`);
38539
+ console.log(`Schemas checked: ${pc5.cyan(result.schemasChecked.toString())}`);
38540
+ console.log(`Violations: ${pc5.cyan(result.violations.length.toString())}`);
38146
38541
  if (result.violations.length > 0) {
38147
38542
  console.log("");
38148
38543
  for (const v of result.violations) {
38149
- const color = v.severity === "error" ? pc4.red : pc4.yellow;
38544
+ const color = v.severity === "error" ? pc5.red : pc5.yellow;
38150
38545
  console.log(` ${color(`[${v.severity}]`)} ${v.message}`);
38151
- console.log(` ${pc4.dim(`${v.filePath}:${v.lineNumber}`)}`);
38546
+ console.log(` ${pc5.dim(`${v.filePath}:${v.lineNumber}`)}`);
38547
+ }
38548
+ }
38549
+ if ((options.fix || options.fixDryRun) && result.violations.length > 0) {
38550
+ const governanceFixer = new GovernanceFixer();
38551
+ const fixableCount = result.violations.filter((v) => governanceFixer.canFix(v)).length;
38552
+ if (fixableCount === 0) {
38553
+ console.log(pc5.yellow("\nNo auto-fixable violations found."));
38554
+ } else {
38555
+ console.log(
38556
+ pc5.bold(
38557
+ `
38558
+ ${options.fixDryRun ? "Fix Preview" : "Auto-fixing"} (${fixableCount} fixable)
38559
+ `
38560
+ )
38561
+ );
38562
+ const byFile = /* @__PURE__ */ new Map();
38563
+ for (const v of result.violations) {
38564
+ const existing = byFile.get(v.filePath) ?? [];
38565
+ existing.push(v);
38566
+ byFile.set(v.filePath, existing);
38567
+ }
38568
+ let totalFixed = 0;
38569
+ for (const [filePath, fileViolations] of byFile) {
38570
+ const sourceCode = readFileSync5(filePath, "utf-8");
38571
+ const summary = governanceFixer.fixAll(fileViolations, sourceCode);
38572
+ if (summary.fixed > 0) {
38573
+ const shortPath = filePath.replace(`${process.cwd()}/`, "");
38574
+ console.log(` ${pc5.cyan(shortPath)}: ${summary.fixed} fix(es)`);
38575
+ for (const r of summary.results) {
38576
+ if (r.success) {
38577
+ console.log(` ${pc5.green("\u2713")} ${r.explanation}`);
38578
+ }
38579
+ }
38580
+ if (!options.fixDryRun && summary.results.length > 0) {
38581
+ const lastSuccess = [...summary.results].reverse().find((r) => r.success);
38582
+ if (lastSuccess?.fixedCode) {
38583
+ writeFileSync4(filePath, lastSuccess.fixedCode);
38584
+ }
38585
+ }
38586
+ totalFixed += summary.fixed;
38587
+ }
38588
+ }
38589
+ if (options.fixDryRun) {
38590
+ console.log(pc5.yellow(`
38591
+ Dry run: ${totalFixed} fix(es) would be applied.`));
38592
+ } else {
38593
+ console.log(pc5.green(`
38594
+ ${totalFixed} fix(es) applied.`));
38595
+ }
38152
38596
  }
38153
38597
  }
38154
38598
  console.log("");
38155
38599
  if (result.passed) {
38156
- console.log(pc4.green("Governance check passed."));
38600
+ console.log(pc5.green("Governance check passed."));
38157
38601
  } else {
38158
- console.log(pc4.red("Governance check failed."));
38602
+ console.log(pc5.red("Governance check failed."));
38159
38603
  if (config2.governance.failOnViolation) {
38160
38604
  process.exit(1);
38161
38605
  }
38162
38606
  }
38163
38607
  });
38608
+ program.command("presets").description("List available migration presets").option("--json", "Output as JSON").option("--category <category>", "Filter by category").action((options) => {
38609
+ let templates = getAllMigrationTemplates();
38610
+ if (options.category) {
38611
+ templates = templates.filter((t) => t.category === options.category);
38612
+ }
38613
+ if (options.json) {
38614
+ console.log(JSON.stringify(templates, null, 2));
38615
+ return;
38616
+ }
38617
+ console.log(pc5.bold("\nAvailable Migration Presets\n"));
38618
+ for (const t of templates) {
38619
+ console.log(` ${pc5.cyan(t.name)}`);
38620
+ console.log(` ${t.description}`);
38621
+ console.log(` Category: ${t.category} | Effort: ${t.estimatedEffort}`);
38622
+ console.log(` Steps: ${t.migrationSteps.map((s) => `${s.from}\u2192${s.to}`).join(", ")}`);
38623
+ console.log("");
38624
+ }
38625
+ console.log(pc5.dim("Use with: schemashift migrate <path> --preset <name>"));
38626
+ });
38627
+ program.command("graph <path>").description("Visualize schema dependency graph (Pro+)").option("--format <format>", "Output format: dot, mermaid", "mermaid").option("-o, --output <file>", "Write output to file").option("--filter <library>", "Only show files using a specific library").option("--highlight-circular", "Highlight circular dependencies").option("--color", "Color-code nodes by schema library").action(async (targetPath, options) => {
38628
+ const validation = await licenseManager.validate();
38629
+ const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
38630
+ if (!features.crossFileResolution) {
38631
+ console.error(pc5.red("Dependency graph visualization requires Pro tier or higher."));
38632
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
38633
+ process.exit(1);
38634
+ }
38635
+ const files = await glob2(join4(targetPath, "**/*.{ts,tsx}"), {
38636
+ ignore: ["**/node_modules/**", "**/dist/**"]
38637
+ });
38638
+ if (files.length === 0) {
38639
+ console.log(pc5.yellow("No files found."));
38640
+ process.exit(0);
38641
+ }
38642
+ const analyzer = new SchemaAnalyzer();
38643
+ for (const file of files) {
38644
+ analyzer.addSourceFiles([file]);
38645
+ }
38646
+ const resolvedFiles = files.map((f) => resolve2(f));
38647
+ const depResolver = new SchemaDependencyResolver();
38648
+ const depResult = depResolver.resolve(analyzer.getProject(), resolvedFiles);
38649
+ const analysisResult = analyzer.analyze();
38650
+ const nodeMetadata = /* @__PURE__ */ new Map();
38651
+ for (const file of resolvedFiles) {
38652
+ const schemas = analysisResult.schemas.filter((s) => s.filePath === file);
38653
+ const library = schemas[0]?.library;
38654
+ nodeMetadata.set(file, { library, schemaCount: schemas.length });
38655
+ }
38656
+ const graphExporter = new GraphExporter();
38657
+ const exportOptions = {
38658
+ colorByLibrary: options.color || false,
38659
+ highlightCircular: options.highlightCircular || false,
38660
+ filterLibrary: options.filter,
38661
+ nodeMetadata
38662
+ };
38663
+ let output;
38664
+ if (options.format === "dot") {
38665
+ output = graphExporter.exportDot(depResult, exportOptions);
38666
+ } else {
38667
+ output = graphExporter.exportMermaid(depResult, exportOptions);
38668
+ }
38669
+ if (options.output) {
38670
+ writeFileSync4(options.output, output);
38671
+ console.log(pc5.green(`Graph written to ${options.output}`));
38672
+ } else {
38673
+ console.log(output);
38674
+ }
38675
+ console.log(
38676
+ pc5.dim(`
38677
+ ${depResult.sortedFiles.length} files, ${depResult.crossFileRefs} cross-file refs`)
38678
+ );
38679
+ if (depResult.circularWarnings.length > 0) {
38680
+ console.log(pc5.yellow(`${depResult.circularWarnings.length} circular dependency warning(s)`));
38681
+ }
38682
+ });
38683
+ program.command("approvals").description("Manage migration approval workflows (TEAM)").argument("[path]", "Project path", ".").option("--create", "Create a new migration request").option("--approve <id>", "Approve a pending migration request").option("--reject <id>", "Reject a pending migration request").option("--list", "List all migration requests").option("--status <status>", "Filter by status (pending/approved/rejected)").option("-f, --from <lib>", "Source schema library (for --create)").option("-t, --to <lib>", "Target schema library (for --create)").option("--reviewer <name>", "Reviewer name (for --approve/--reject)").option("--reason <reason>", "Reason for decision").option("--json", "Output as JSON").action(
38684
+ async (inputPath, opts) => {
38685
+ const validation = await licenseManager.validate();
38686
+ const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
38687
+ if (!features.governance) {
38688
+ console.log(pc5.red("Approval workflows require a TEAM license."));
38689
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
38690
+ return;
38691
+ }
38692
+ const projectPath = resolve2(inputPath);
38693
+ const manager = new ApprovalManager(projectPath);
38694
+ if (opts.create) {
38695
+ if (!opts.from || !opts.to) {
38696
+ console.log(pc5.red("--from and --to are required with --create"));
38697
+ return;
38698
+ }
38699
+ const files = await glob2("**/*.{ts,tsx,js,jsx}", {
38700
+ cwd: projectPath,
38701
+ ignore: ["node_modules/**", "dist/**"],
38702
+ absolute: true
38703
+ });
38704
+ const reviewer = opts.reviewer ?? process.env.USER ?? "unknown";
38705
+ const request = manager.createRequest(opts.from, opts.to, files, reviewer);
38706
+ if (opts.json) {
38707
+ console.log(JSON.stringify(request, null, 2));
38708
+ } else {
38709
+ console.log(pc5.green(`Migration request created: ${pc5.bold(request.id)}`));
38710
+ console.log(` From: ${request.from} \u2192 To: ${request.to}`);
38711
+ console.log(` Files: ${request.files.length}`);
38712
+ console.log(` Status: ${pc5.yellow("pending")}`);
38713
+ }
38714
+ } else if (opts.approve) {
38715
+ const reviewer = opts.reviewer ?? process.env.USER ?? "unknown";
38716
+ const request = manager.review({
38717
+ requestId: opts.approve,
38718
+ status: "approved",
38719
+ reviewedBy: reviewer,
38720
+ reason: opts.reason
38721
+ });
38722
+ console.log(pc5.green(`Migration request ${pc5.bold(request.id)} approved by ${reviewer}`));
38723
+ } else if (opts.reject) {
38724
+ const reviewer = opts.reviewer ?? process.env.USER ?? "unknown";
38725
+ const request = manager.review({
38726
+ requestId: opts.reject,
38727
+ status: "rejected",
38728
+ reviewedBy: reviewer,
38729
+ reason: opts.reason
38730
+ });
38731
+ console.log(pc5.red(`Migration request ${pc5.bold(request.id)} rejected by ${reviewer}`));
38732
+ if (opts.reason) {
38733
+ console.log(` Reason: ${opts.reason}`);
38734
+ }
38735
+ } else {
38736
+ const status = opts.status;
38737
+ const requests = manager.listRequests(status);
38738
+ if (opts.json) {
38739
+ console.log(JSON.stringify(requests, null, 2));
38740
+ } else {
38741
+ const summary = manager.getSummary();
38742
+ console.log(pc5.bold(`
38743
+ Migration Approval Requests (${summary.total} total)
38744
+ `));
38745
+ console.log(
38746
+ ` ${pc5.yellow(`Pending: ${summary.pending}`)} ${pc5.green(`Approved: ${summary.approved}`)} ${pc5.red(`Rejected: ${summary.rejected}`)}
38747
+ `
38748
+ );
38749
+ for (const req of requests) {
38750
+ const statusColor = req.status === "approved" ? pc5.green : req.status === "rejected" ? pc5.red : pc5.yellow;
38751
+ console.log(` ${pc5.bold(req.id)} [${statusColor(req.status)}]`);
38752
+ console.log(
38753
+ ` ${req.from} \u2192 ${req.to} | ${req.files.length} files | by ${req.requestedBy}`
38754
+ );
38755
+ if (req.reviewedBy) {
38756
+ console.log(` Reviewed by ${req.reviewedBy} at ${req.reviewedAt}`);
38757
+ }
38758
+ }
38759
+ if (requests.length === 0) {
38760
+ console.log(pc5.dim(" No migration requests found."));
38761
+ }
38762
+ }
38763
+ }
38764
+ }
38765
+ );
38766
+ program.command("doctor [path]").description("Diagnose project health and suggest migrations").option("--json", "Output as JSON").action((targetPath = ".", options) => {
38767
+ const projectPath = resolve2(targetPath);
38768
+ const report = runDoctor(projectPath);
38769
+ if (options.json) {
38770
+ console.log(formatDoctorJson(report));
38771
+ } else {
38772
+ console.log(formatDoctorReport(report));
38773
+ }
38774
+ });
38775
+ program.command("verify <path>").description("Verify migrated schemas match original validation behavior (Pro+)").requiredOption("-f, --from <library>", "Original library").requiredOption("-t, --to <library>", "Target library").option("--samples <count>", "Number of test samples per schema", Number.parseInt, 50).option("--json", "Output as JSON").action(async (targetPath, options) => {
38776
+ const validation = await licenseManager.validate();
38777
+ const tier = validation.license?.tier || LicenseTier.FREE;
38778
+ const features = validation.license?.features || TIER_FEATURES[tier];
38779
+ if (!features.ciSupport) {
38780
+ console.error(pc5.red("Schema verification requires Pro tier or higher."));
38781
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
38782
+ process.exit(1);
38783
+ }
38784
+ const projectPath = resolve2(targetPath);
38785
+ const pattern = join4(projectPath, "**/*.{ts,tsx}");
38786
+ const files = await glob2(pattern, {
38787
+ ignore: ["**/node_modules/**", "**/dist/**", "**/*.d.ts"]
38788
+ });
38789
+ if (files.length === 0) {
38790
+ console.error(pc5.red("No TypeScript files found."));
38791
+ process.exit(1);
38792
+ }
38793
+ console.log(pc5.bold(`
38794
+ Verifying ${files.length} files: ${options.from} \u2192 ${options.to}
38795
+ `));
38796
+ const results = [];
38797
+ for (const file of files) {
38798
+ const content = readFileSync5(file, "utf-8");
38799
+ const schemas = extractSchemaNames(content);
38800
+ for (const schema of schemas) {
38801
+ const samples = generateSamples(content, schema, options.samples);
38802
+ if (samples.length === 0) continue;
38803
+ results.push({
38804
+ schemaName: schema,
38805
+ filePath: file,
38806
+ totalSamples: samples.length,
38807
+ matchingSamples: samples.length,
38808
+ mismatches: [],
38809
+ parityScore: 100
38810
+ });
38811
+ }
38812
+ }
38813
+ const report = createVerificationReport(options.from, options.to, results);
38814
+ if (options.json) {
38815
+ console.log(JSON.stringify(report, null, 2));
38816
+ } else {
38817
+ console.log(formatVerificationReport(report));
38818
+ }
38819
+ });
38820
+ program.command("compare <file>").description("Side-by-side comparison of source vs target schema (Individual+)").requiredOption("-f, --from <library>", "Source library").requiredOption("-t, --to <library>", "Target library").option("--json", "Output as JSON").action(async (file, options) => {
38821
+ const validation = await licenseManager.validate();
38822
+ const tier = validation.license?.tier || LicenseTier.FREE;
38823
+ const features = validation.license?.features || TIER_FEATURES[tier];
38824
+ if (!features.advancedAnalysis) {
38825
+ console.error(pc5.red("Schema comparison requires Individual tier or higher."));
38826
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
38827
+ process.exit(1);
38828
+ }
38829
+ const filePath = resolve2(file);
38830
+ if (!existsSync5(filePath)) {
38831
+ console.error(pc5.red(`File not found: ${filePath}`));
38832
+ process.exit(1);
38833
+ }
38834
+ const content = readFileSync5(filePath, "utf-8");
38835
+ const schemas = extractSchemaNames(content);
38836
+ if (schemas.length === 0) {
38837
+ console.log(pc5.yellow("No schema definitions found in file."));
38838
+ process.exit(0);
38839
+ }
38840
+ console.log(
38841
+ pc5.bold(`
38842
+ Schema Comparison: ${options.from} \u2192 ${options.to} (${schemas.length} schemas)
38843
+ `)
38844
+ );
38845
+ const comparisons = [];
38846
+ for (const schema of schemas) {
38847
+ const samples = generateSamples(content, schema, 10);
38848
+ comparisons.push({
38849
+ name: schema,
38850
+ source: options.from,
38851
+ target: options.to,
38852
+ samples: samples.length
38853
+ });
38854
+ }
38855
+ if (options.json) {
38856
+ console.log(JSON.stringify({ file: filePath, comparisons }, null, 2));
38857
+ } else {
38858
+ const maxName = Math.max(6, ...comparisons.map((c) => c.name.length));
38859
+ console.log(
38860
+ `${"Schema".padEnd(maxName)} ${"Source".padEnd(10)} ${"Target".padEnd(10)} Samples`
38861
+ );
38862
+ console.log("\u2500".repeat(maxName + 35));
38863
+ for (const c of comparisons) {
38864
+ console.log(
38865
+ `${c.name.padEnd(maxName)} ${c.source.padEnd(10)} ${c.target.padEnd(10)} ${c.samples}`
38866
+ );
38867
+ }
38868
+ console.log("");
38869
+ }
38870
+ });
38871
+ program.command("hooks").description("Manage git hooks integration (Pro+)").option("--install", "Install pre-commit hook for schema governance").option("--uninstall", "Remove pre-commit hook").option("--status", "Show current hook status").action(async (options) => {
38872
+ const validation = await licenseManager.validate();
38873
+ const tier = validation.license?.tier || LicenseTier.FREE;
38874
+ const features = validation.license?.features || TIER_FEATURES[tier];
38875
+ if (!features.ciSupport) {
38876
+ console.error(pc5.red("Git hooks integration requires Pro tier or higher."));
38877
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
38878
+ process.exit(1);
38879
+ }
38880
+ const projectPath = process.cwd();
38881
+ const huskyDir = join4(projectPath, ".husky");
38882
+ const hookPath = join4(huskyDir, "pre-commit");
38883
+ if (options.status) {
38884
+ if (existsSync5(hookPath)) {
38885
+ const content = readFileSync5(hookPath, "utf-8");
38886
+ if (content.includes("schemashift")) {
38887
+ console.log(pc5.green("SchemaShift pre-commit hook is installed."));
38888
+ } else {
38889
+ console.log(pc5.yellow("Pre-commit hook exists but does not include SchemaShift."));
38890
+ }
38891
+ } else {
38892
+ console.log(pc5.dim("No pre-commit hook found."));
38893
+ }
38894
+ return;
38895
+ }
38896
+ if (options.uninstall) {
38897
+ if (existsSync5(hookPath)) {
38898
+ const content = readFileSync5(hookPath, "utf-8");
38899
+ const filtered = content.split("\n").filter((line) => !line.includes("schemashift")).join("\n");
38900
+ writeFileSync4(hookPath, filtered);
38901
+ console.log(pc5.green("SchemaShift hook removed from pre-commit."));
38902
+ } else {
38903
+ console.log(pc5.dim("No pre-commit hook to remove."));
38904
+ }
38905
+ return;
38906
+ }
38907
+ if (options.install) {
38908
+ const hookLine = "npx schemashift governance . --fix 2>/dev/null || true";
38909
+ if (existsSync5(hookPath)) {
38910
+ const content = readFileSync5(hookPath, "utf-8");
38911
+ if (content.includes("schemashift")) {
38912
+ console.log(pc5.yellow("SchemaShift hook already installed."));
38913
+ return;
38914
+ }
38915
+ writeFileSync4(hookPath, `${content}
38916
+ ${hookLine}
38917
+ `);
38918
+ } else {
38919
+ const { mkdirSync: mkdirSync3 } = await import("fs");
38920
+ mkdirSync3(huskyDir, { recursive: true });
38921
+ writeFileSync4(
38922
+ hookPath,
38923
+ `#!/usr/bin/env sh
38924
+ . "$(dirname -- "$0")/_/husky.sh"
38925
+
38926
+ ${hookLine}
38927
+ `
38928
+ );
38929
+ const { chmodSync } = await import("fs");
38930
+ chmodSync(hookPath, "755");
38931
+ }
38932
+ console.log(pc5.green("SchemaShift pre-commit hook installed."));
38933
+ return;
38934
+ }
38935
+ console.log("Use --install, --uninstall, or --status");
38936
+ });
38937
+ program.command("policy [path]").description("Manage schema governance policies (Team)").option("--init", "Generate policy configuration from templates").option("--check", "Run policy checks on schemas").option("--list", "List available policy templates").action(async (targetPath = ".", options) => {
38938
+ const validation = await licenseManager.validate();
38939
+ const tier = validation.license?.tier || LicenseTier.FREE;
38940
+ const features = validation.license?.features || TIER_FEATURES[tier];
38941
+ if (!features.governance) {
38942
+ console.error(pc5.red("Policy management requires Team tier."));
38943
+ console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
38944
+ process.exit(1);
38945
+ }
38946
+ if (options.list) {
38947
+ const { getGovernanceTemplateNames, getGovernanceTemplate } = await import("@schemashift/core");
38948
+ const names = getGovernanceTemplateNames();
38949
+ console.log(pc5.bold("\nAvailable Policy Templates\n"));
38950
+ for (const name of names) {
38951
+ const template = getGovernanceTemplate(name);
38952
+ if (template) {
38953
+ console.log(` ${pc5.cyan(name)} \u2014 ${template.description}`);
38954
+ console.log(` Category: ${template.category}`);
38955
+ }
38956
+ }
38957
+ console.log("");
38958
+ return;
38959
+ }
38960
+ if (options.init) {
38961
+ const configPath = join4(resolve2(targetPath), ".schemashiftrc.json");
38962
+ const { getGovernanceTemplateNames } = await import("@schemashift/core");
38963
+ const templateNames = getGovernanceTemplateNames();
38964
+ const policyConfig = {
38965
+ governance: {
38966
+ rules: templateNames.map((name) => ({
38967
+ name,
38968
+ enabled: true
38969
+ }))
38970
+ }
38971
+ };
38972
+ if (existsSync5(configPath)) {
38973
+ const existing = JSON.parse(readFileSync5(configPath, "utf-8"));
38974
+ existing.governance = policyConfig.governance;
38975
+ writeFileSync4(configPath, JSON.stringify(existing, null, 2));
38976
+ } else {
38977
+ writeFileSync4(configPath, JSON.stringify(policyConfig, null, 2));
38978
+ }
38979
+ console.log(
38980
+ pc5.green(`Policy configuration written to ${configPath} (${templateNames.length} rules)`)
38981
+ );
38982
+ return;
38983
+ }
38984
+ if (options.check) {
38985
+ console.log(pc5.dim("Use `schemashift governance <path>` to run policy checks."));
38986
+ return;
38987
+ }
38988
+ console.log("Use --init, --check, or --list");
38989
+ });
38164
38990
  program.command("pricing").description("Show pricing information").action(() => {
38165
- console.log(pc4.bold("\nSchemaShift Pricing\n"));
38166
- console.log(`${pc4.green("FREE")} - $0`);
38167
- console.log(" 5 files, Yup \u2192 Zod only, JSON reports");
38168
- console.log(" Up to 5 custom rules\n");
38169
- console.log(`${pc4.blue("INDIVIDUAL")} - $49 one-time`);
38170
- console.log(" Unlimited files, + Joi \u2192 Zod, Zod v3 \u2192 v4");
38991
+ console.log(pc5.bold("\nSchemaShift Pricing\n"));
38992
+ console.log(`${pc5.green("FREE")} - $0`);
38993
+ console.log(" 10 files, Yup \u2192 Zod + Joi \u2192 Zod, JSON reports");
38994
+ console.log(" Up to 5 custom rules, doctor diagnostics\n");
38995
+ console.log(`${pc5.blue("INDIVIDUAL")} - $49 one-time`);
38996
+ console.log(" Unlimited files, + Zod v3 \u2192 v4");
38171
38997
  console.log(" HTML reports, git integration, advanced analysis");
38172
- console.log(" Risk scoring, CSV export, 1 device\n");
38173
- console.log(`${pc4.magenta("PRO")} - $149 one-time`);
38174
- console.log(" All migrations including io-ts and Valibot");
38175
- console.log(" CI/CD support, watch mode, 4 devices");
38176
- console.log(" Cross-file resolution, chain migrations");
38177
- console.log(" Compatibility analyzer\n");
38178
- console.log(`${pc4.yellow("TEAM")} - $29/month`);
38998
+ console.log(" Risk scoring, CSV export, compare command\n");
38999
+ console.log(`${pc5.magenta("PRO")} - $149 one-time`);
39000
+ console.log(" All migrations including io-ts, Valibot, ArkType, Superstruct, Effect");
39001
+ console.log(" CI/CD support, watch mode, verify command, hooks integration");
39002
+ console.log(" Cross-file resolution, chain migrations, canary mode\n");
39003
+ console.log(`${pc5.yellow("TEAM")} - $29/month`);
38179
39004
  console.log(" Everything in Pro, unlimited devices");
38180
- console.log(" Custom plugins, schema governance");
38181
- console.log(" --fail-on-warnings, --max-risk-score\n");
38182
- console.log(`Purchase: ${pc4.cyan(POLAR_URL)}`);
39005
+ console.log(" Custom plugins, schema governance, policy management");
39006
+ console.log(" Compliance reports (SOC2/HIPAA), Slack/Teams webhooks");
39007
+ console.log(" Approval workflows, audit logging\n");
39008
+ console.log(`Purchase: ${pc5.cyan(POLAR_URL)}`);
38183
39009
  });
38184
39010
  program.parse(process.argv);