real-prototypes-skill 0.1.1 → 0.1.3

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.
@@ -18,7 +18,17 @@ const { execSync } = require('child_process');
18
18
 
19
19
  const SKILL_DIR = __dirname;
20
20
  const PROJECTS_DIR = path.resolve(SKILL_DIR, '../../../projects');
21
- const VERSION = '1.1.0';
21
+ const VERSION = '1.4.0';
22
+
23
+ // Import new modules
24
+ const { detectPrototype, formatResult } = require('./scripts/detect-prototype');
25
+ const { ColorValidator, validateColors } = require('./validation/color-validator');
26
+ const { HTMLToReactConverter, convertHTMLToReact, writeComponents } = require('./scripts/html-to-react');
27
+ const { CSSExtractor, extractCSS } = require('./scripts/extract-css');
28
+ const { VisualDiffComparator } = require('./scripts/visual-diff');
29
+ const { ComponentExtractor, extractComponents } = require('./scripts/extract-components');
30
+ const { PlanGenerator, generatePlan } = require('./scripts/generate-plan');
31
+ const { ProjectStructure } = require('./scripts/project-structure');
22
32
 
23
33
  function log(message, type = 'info') {
24
34
  const styles = {
@@ -51,17 +61,27 @@ function showHelp() {
51
61
  real-prototypes-skill <command> [options]
52
62
 
53
63
  \x1b[1mCOMMANDS\x1b[0m
54
- new Create a new project
55
- capture Capture a web platform
56
- validate Validate capture or prototype
57
- generate Generate prototype from capture
58
- pipeline Run full capture validate → generate pipeline
59
- init Initialize a new capture configuration
60
- list List all projects
64
+ new Create a new project
65
+ detect Detect existing prototype in project
66
+ capture Capture a web platform
67
+ validate Validate capture or prototype
68
+ validate-colors Validate colors against design tokens
69
+ convert Convert captured HTML to React components
70
+ extract-css Extract and analyze CSS from captured HTML
71
+ extract-lib Extract reusable component library from HTML
72
+ visual-diff Compare screenshots for visual accuracy
73
+ plan Generate implementation plan
74
+ generate Generate prototype from capture
75
+ pipeline Run full capture → validate → generate pipeline
76
+ init Initialize a new capture configuration
77
+ list List all projects
61
78
 
62
79
  \x1b[1mPROJECT OPTIONS\x1b[0m
63
80
  --project Project name (required for capture/validate/generate/pipeline)
64
81
 
82
+ \x1b[1mNEW PROJECT OPTIONS\x1b[0m
83
+ --force-create Required flag to create new project (blocks by default)
84
+
65
85
  \x1b[1mCAPTURE OPTIONS\x1b[0m
66
86
  --url Platform URL (required)
67
87
  --email Login email
@@ -118,7 +138,8 @@ function parseArgs(args) {
118
138
  phase: 'all',
119
139
  refs: null,
120
140
  proto: null,
121
- features: []
141
+ features: [],
142
+ forceCreate: false
122
143
  };
123
144
 
124
145
  for (let i = 1; i < args.length; i++) {
@@ -156,6 +177,9 @@ function parseArgs(args) {
156
177
  case '--feature':
157
178
  options.features.push(args[++i]);
158
179
  break;
180
+ case '--force-create':
181
+ options.forceCreate = true;
182
+ break;
159
183
  case '--help':
160
184
  case '-h':
161
185
  showHelp();
@@ -197,6 +221,24 @@ function runNew(options) {
197
221
  process.exit(1);
198
222
  }
199
223
 
224
+ // Block project creation by default - require explicit flag
225
+ if (!options.forceCreate) {
226
+ console.log('');
227
+ log('═══════════════════════════════════════════', 'warning');
228
+ log('PROJECT CREATION BLOCKED', 'warning');
229
+ log('═══════════════════════════════════════════', 'warning');
230
+ console.log('');
231
+ log('This skill is for capturing EXISTING platforms, not creating new designs.', 'info');
232
+ console.log('');
233
+ log('If you have an existing platform to capture:', 'info');
234
+ log(` 1. Create project: node cli.js new --project ${options.project} --force-create`, 'info');
235
+ log(` 2. Capture it: node cli.js capture --project ${options.project} --url <URL>`, 'info');
236
+ console.log('');
237
+ log('Use --force-create only if you understand this creates an EMPTY project', 'warning');
238
+ log('that must be populated by capturing an existing platform.', 'warning');
239
+ process.exit(1);
240
+ }
241
+
200
242
  const projectDir = getProjectDir(options.project);
201
243
  const refsDir = path.join(projectDir, 'references');
202
244
  const protoDir = path.join(projectDir, 'prototype');
@@ -384,8 +426,74 @@ async function runValidate(options) {
384
426
  }
385
427
  }
386
428
 
429
+ /**
430
+ * Pre-flight check - MANDATORY before any generation
431
+ * Validates that required captures exist before proceeding
432
+ */
433
+ function runPreflight(options) {
434
+ const errors = [];
435
+
436
+ // Required files
437
+ const required = [
438
+ { path: path.join(options.refs, 'design-tokens.json'), name: 'design-tokens.json' },
439
+ { path: path.join(options.refs, 'manifest.json'), name: 'manifest.json' }
440
+ ];
441
+
442
+ for (const { path: filePath, name } of required) {
443
+ if (!fs.existsSync(filePath)) {
444
+ errors.push(`${name} missing - captures required before generation`);
445
+ }
446
+ }
447
+
448
+ // Screenshots directory
449
+ const screenshotsDir = path.join(options.refs, 'screenshots');
450
+ if (!fs.existsSync(screenshotsDir)) {
451
+ errors.push('screenshots/ directory missing - run capture first');
452
+ } else {
453
+ const screenshots = fs.readdirSync(screenshotsDir).filter(f => f.endsWith('.png'));
454
+ if (screenshots.length === 0) {
455
+ errors.push('No screenshots found - run capture first');
456
+ }
457
+ }
458
+
459
+ if (errors.length > 0) {
460
+ console.log('');
461
+ log('═══════════════════════════════════════════', 'error');
462
+ log('PRE-FLIGHT CHECK FAILED - CANNOT PROCEED', 'error');
463
+ log('═══════════════════════════════════════════', 'error');
464
+ console.log('');
465
+ errors.forEach(e => log(e, 'error'));
466
+ console.log('');
467
+ log('You MUST capture the existing platform first:', 'warning');
468
+ log(` node cli.js capture --project ${options.project} --url <PLATFORM_URL>`, 'info');
469
+ console.log('');
470
+ log('This skill is for adding features to EXISTING platforms.', 'info');
471
+ log('It does NOT create new designs from scratch.', 'info');
472
+ process.exit(1);
473
+ }
474
+
475
+ log('Pre-flight check passed', 'success');
476
+ return true;
477
+ }
478
+
387
479
  async function runGenerate(options) {
388
480
  requireProject(options, 'generate');
481
+
482
+ // MANDATORY: Pre-flight check before ANY generation
483
+ runPreflight(options);
484
+
485
+ // Auto-detect existing prototype
486
+ const protoInfo = detectPrototype(options.proto);
487
+ if (protoInfo.exists) {
488
+ console.log('');
489
+ log('═══════════════════════════════════════════', 'warning');
490
+ log('EXTEND MODE ACTIVE - Existing prototype detected', 'warning');
491
+ log('═══════════════════════════════════════════', 'warning');
492
+ log(`Framework: ${protoInfo.framework}`, 'info');
493
+ log('All changes MUST modify existing files only', 'warning');
494
+ console.log('');
495
+ }
496
+
389
497
  log(`Generating prototype for project: ${options.project}`, 'title');
390
498
 
391
499
  // This would integrate with your prototype generation logic
@@ -408,24 +516,47 @@ async function runGenerate(options) {
408
516
  const tokens = JSON.parse(fs.readFileSync(tokensPath, 'utf-8'));
409
517
 
410
518
  console.log(`
411
- \x1b[1mCapture Summary:\x1b[0m
519
+ \x1b[1m════════════════════════════════════════════════════════════\x1b[0m
520
+ \x1b[1m PROJECT PATHS \x1b[0m
521
+ \x1b[1m════════════════════════════════════════════════════════════\x1b[0m
522
+
523
+ \x1b[1mPrototype Output Directory:\x1b[0m
524
+ ${options.proto}
525
+
526
+ \x1b[1mReference Files:\x1b[0m
527
+ Screenshots: ${path.join(options.refs, 'screenshots')}
528
+ HTML: ${path.join(options.refs, 'html')}
529
+ Tokens: ${tokensPath}
530
+ Manifest: ${manifestPath}
531
+
532
+ \x1b[1m════════════════════════════════════════════════════════════\x1b[0m
533
+ \x1b[1m CAPTURE SUMMARY \x1b[0m
534
+ \x1b[1m════════════════════════════════════════════════════════════\x1b[0m
535
+
412
536
  Platform: ${manifest.platform.name}
413
537
  Pages: ${manifest.pages.length}
414
538
  Colors: ${tokens.totalColorsFound}
415
539
  Primary Color: ${tokens.colors?.primary || 'Not identified'}
416
540
 
417
- \x1b[1mGeneration Instructions:\x1b[0m
418
- 1. Use ONLY colors from design-tokens.json
419
- 2. Match layout from screenshots exactly
420
- 3. Use inline styles for colors (Tailwind custom colors may not work)
421
- 4. Validate with: real-prototypes-skill validate --phase post-gen
422
-
423
- \x1b[1mRequired Colors:\x1b[0m
541
+ \x1b[1mRequired Colors (from design-tokens.json):\x1b[0m
424
542
  Primary: ${tokens.colors?.primary || 'N/A'}
425
543
  Text: ${tokens.colors?.text?.primary || 'N/A'}
426
544
  Background: ${tokens.colors?.background?.white || 'N/A'}
427
545
  Border: ${tokens.colors?.border?.default || 'N/A'}
428
546
 
547
+ \x1b[1m════════════════════════════════════════════════════════════\x1b[0m
548
+ \x1b[1m INSTRUCTIONS \x1b[0m
549
+ \x1b[1m════════════════════════════════════════════════════════════\x1b[0m
550
+
551
+ \x1b[33mALL prototype files MUST be created in:\x1b[0m
552
+ ${options.proto}
553
+
554
+ \x1b[1mGeneration Rules:\x1b[0m
555
+ 1. Use ONLY colors from design-tokens.json
556
+ 2. Match layout from screenshots exactly
557
+ 3. Use inline styles for colors (Tailwind custom colors may not work)
558
+ 4. Validate with: node cli.js validate-colors --project ${options.project}
559
+
429
560
  \x1b[1mFeatures to add:\x1b[0m
430
561
  ${options.features.length > 0 ? options.features.map(f => ` - ${f}`).join('\n') : ' (none specified)'}
431
562
  `);
@@ -492,6 +623,360 @@ async function runPipeline(options) {
492
623
  `);
493
624
  }
494
625
 
626
+ function runValidateColors(options) {
627
+ requireProject(options, 'validate-colors');
628
+ showBanner();
629
+ log(`Validating colors for project: ${options.project}`, 'title');
630
+
631
+ const tokensPath = path.join(options.refs, 'design-tokens.json');
632
+
633
+ if (!fs.existsSync(tokensPath)) {
634
+ log('design-tokens.json not found - run capture first', 'error');
635
+ process.exit(1);
636
+ }
637
+
638
+ if (!fs.existsSync(options.proto)) {
639
+ log('Prototype directory not found', 'error');
640
+ process.exit(1);
641
+ }
642
+
643
+ try {
644
+ const validator = validateColors(options.proto, tokensPath);
645
+ console.log('');
646
+ console.log(validator.formatViolations());
647
+
648
+ const summary = validator.getSummary();
649
+ if (summary.passed) {
650
+ log('All colors validated successfully!', 'success');
651
+ } else {
652
+ log(`Found ${summary.total} color violation(s)`, 'error');
653
+ process.exit(1);
654
+ }
655
+ } catch (error) {
656
+ log(`Validation failed: ${error.message}`, 'error');
657
+ process.exit(1);
658
+ }
659
+ }
660
+
661
+ function runConvert(options, args) {
662
+ requireProject(options, 'convert');
663
+ showBanner();
664
+
665
+ // Get page name from args
666
+ let pageName = null;
667
+ for (let i = 0; i < args.length; i++) {
668
+ if (args[i] === '--page') {
669
+ pageName = args[++i];
670
+ }
671
+ }
672
+
673
+ if (!pageName) {
674
+ log('--page is required for convert command', 'error');
675
+ log('Example: real-prototypes-skill convert --project my-app --page homepage', 'info');
676
+ process.exit(1);
677
+ }
678
+
679
+ log(`Converting HTML to React for: ${pageName}`, 'title');
680
+
681
+ const htmlPath = path.join(options.refs, 'html', `${pageName}.html`);
682
+ const outputDir = path.join(options.proto, 'src', 'components', 'extracted');
683
+
684
+ if (!fs.existsSync(htmlPath)) {
685
+ log(`HTML file not found: ${htmlPath}`, 'error');
686
+ process.exit(1);
687
+ }
688
+
689
+ try {
690
+ const result = convertHTMLToReact(htmlPath);
691
+
692
+ console.log(`\n\x1b[1mDetected Components (${result.boundaries.length}):\x1b[0m`);
693
+ for (const boundary of result.boundaries.slice(0, 10)) {
694
+ console.log(` ${boundary.suggestedName} (${boundary.type})`);
695
+ }
696
+
697
+ console.log(`\n\x1b[1mExtracted Components (${result.components.length}):\x1b[0m`);
698
+ for (const comp of result.components) {
699
+ console.log(` ${comp.name}`);
700
+ }
701
+
702
+ // Write components
703
+ const written = writeComponents(result.components, outputDir);
704
+ console.log(`\n\x1b[32m✓ Wrote ${written.length} components to: ${outputDir}\x1b[0m`);
705
+
706
+ } catch (error) {
707
+ log(`Conversion failed: ${error.message}`, 'error');
708
+ process.exit(1);
709
+ }
710
+ }
711
+
712
+ function runExtractCSS(options, args) {
713
+ requireProject(options, 'extract-css');
714
+ showBanner();
715
+
716
+ let pageName = null;
717
+ for (let i = 0; i < args.length; i++) {
718
+ if (args[i] === '--page') {
719
+ pageName = args[++i];
720
+ }
721
+ }
722
+
723
+ if (!pageName) {
724
+ log('--page is required for extract-css command', 'error');
725
+ log('Example: real-prototypes-skill extract-css --project my-app --page homepage', 'info');
726
+ process.exit(1);
727
+ }
728
+
729
+ log(`Extracting CSS from: ${pageName}`, 'title');
730
+
731
+ const htmlPath = path.join(options.refs, 'html', `${pageName}.html`);
732
+
733
+ if (!fs.existsSync(htmlPath)) {
734
+ log(`HTML file not found: ${htmlPath}`, 'error');
735
+ process.exit(1);
736
+ }
737
+
738
+ try {
739
+ const extractor = new CSSExtractor();
740
+ extractor.loadFromFile(htmlPath);
741
+
742
+ console.log('');
743
+ console.log(extractor.formatResults());
744
+
745
+ } catch (error) {
746
+ log(`Extraction failed: ${error.message}`, 'error');
747
+ process.exit(1);
748
+ }
749
+ }
750
+
751
+ function runVisualDiff(options, args) {
752
+ requireProject(options, 'visual-diff');
753
+ showBanner();
754
+
755
+ let pageName = null;
756
+ let listMode = false;
757
+ for (let i = 0; i < args.length; i++) {
758
+ if (args[i] === '--page') {
759
+ pageName = args[++i];
760
+ }
761
+ if (args[i] === '--list') {
762
+ listMode = true;
763
+ }
764
+ }
765
+
766
+ const screenshotsDir = path.join(options.refs, 'screenshots');
767
+
768
+ if (listMode) {
769
+ log('Available reference screenshots:', 'title');
770
+ if (fs.existsSync(screenshotsDir)) {
771
+ const files = fs.readdirSync(screenshotsDir).filter(f => f.endsWith('.png'));
772
+ for (const file of files) {
773
+ console.log(` ${file.replace('.png', '')}`);
774
+ }
775
+ } else {
776
+ log('No screenshots found', 'warning');
777
+ }
778
+ return;
779
+ }
780
+
781
+ if (!pageName) {
782
+ log('--page is required (or use --list to see available)', 'error');
783
+ log('Example: real-prototypes-skill visual-diff --project my-app --page homepage', 'info');
784
+ process.exit(1);
785
+ }
786
+
787
+ log(`Visual diff for: ${pageName}`, 'title');
788
+
789
+ // Find reference screenshot
790
+ const refScreenshots = fs.existsSync(screenshotsDir)
791
+ ? fs.readdirSync(screenshotsDir).filter(f => f.toLowerCase().includes(pageName.toLowerCase()) && f.endsWith('.png'))
792
+ : [];
793
+
794
+ if (refScreenshots.length === 0) {
795
+ log(`No reference screenshot found for: ${pageName}`, 'error');
796
+ log('Use --list to see available screenshots', 'info');
797
+ process.exit(1);
798
+ }
799
+
800
+ const refPath = path.join(screenshotsDir, refScreenshots[0]);
801
+ console.log(` Reference: ${refScreenshots[0]}`);
802
+
803
+ // Look for generated screenshot
804
+ const genScreenshotsDir = path.join(options.proto, 'screenshots');
805
+ const genScreenshots = fs.existsSync(genScreenshotsDir)
806
+ ? fs.readdirSync(genScreenshotsDir).filter(f => f.toLowerCase().includes(pageName.toLowerCase()) && f.endsWith('.png'))
807
+ : [];
808
+
809
+ if (genScreenshots.length === 0) {
810
+ log('No generated screenshot found to compare', 'warning');
811
+ console.log(`
812
+ \x1b[1mTo generate a screenshot:\x1b[0m
813
+ 1. Start your prototype: npm run dev
814
+ 2. Take a screenshot of the page
815
+ 3. Save it to: ${genScreenshotsDir}/${pageName}.png
816
+ `);
817
+ return;
818
+ }
819
+
820
+ const genPath = path.join(genScreenshotsDir, genScreenshots[0]);
821
+ console.log(` Generated: ${genScreenshots[0]}`);
822
+
823
+ // Run comparison
824
+ (async () => {
825
+ try {
826
+ const comparator = new VisualDiffComparator({ minSimilarity: 95 });
827
+ const diffPath = path.join(options.refs, 'diff', `${pageName}-diff.png`);
828
+
829
+ const result = await comparator.compare(refPath, genPath, diffPath);
830
+
831
+ console.log('');
832
+ console.log(comparator.formatResults());
833
+
834
+ } catch (error) {
835
+ log(`Visual diff failed: ${error.message}`, 'error');
836
+ process.exit(1);
837
+ }
838
+ })();
839
+ }
840
+
841
+ function runDetect(options) {
842
+ requireProject(options, 'detect');
843
+ showBanner();
844
+ log(`Detecting existing prototype for project: ${options.project}`, 'title');
845
+
846
+ const projectDir = getProjectDir(options.project);
847
+ const protoDir = path.join(projectDir, 'prototype');
848
+ const manifestPath = path.join(projectDir, 'references', 'manifest.json');
849
+
850
+ // First check if prototype directory exists
851
+ if (!fs.existsSync(protoDir)) {
852
+ log('No prototype directory found', 'warning');
853
+ log(`Expected at: ${protoDir}`, 'info');
854
+ console.log(`
855
+ \x1b[1mTo create a prototype:\x1b[0m
856
+ 1. Run capture first: real-prototypes-skill capture --project ${options.project} --url <URL>
857
+ 2. Then generate: real-prototypes-skill generate --project ${options.project}
858
+ `);
859
+ return { exists: false };
860
+ }
861
+
862
+ // Run detection
863
+ const result = detectPrototype(protoDir);
864
+
865
+ // Try to map pages if manifest exists
866
+ if (fs.existsSync(manifestPath)) {
867
+ try {
868
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
869
+ const { mapPages } = require('./scripts/detect-prototype');
870
+ result.mappedPages = mapPages(protoDir, manifest);
871
+ } catch (e) {
872
+ log(`Could not load manifest: ${e.message}`, 'warning');
873
+ }
874
+ }
875
+
876
+ console.log('');
877
+ console.log(formatResult(result));
878
+ console.log('');
879
+
880
+ if (result.exists) {
881
+ log('Existing prototype detected - use EXTEND mode', 'success');
882
+ console.log(`
883
+ \x1b[1mRecommendation:\x1b[0m
884
+ When generating new features, modify existing files instead of creating new ones.
885
+
886
+ \x1b[1mExisting prototype details:\x1b[0m
887
+ Framework: ${result.framework || 'Unknown'}
888
+ Styling: ${result.styling.join(', ')}
889
+ Pages: ${result.pages.length}
890
+ Components: ${result.components.length}
891
+ `);
892
+ } else {
893
+ log('No existing prototype found - safe to CREATE new', 'info');
894
+ }
895
+
896
+ return result;
897
+ }
898
+
899
+ function runExtractLib(options) {
900
+ requireProject(options, 'extract-lib');
901
+ showBanner();
902
+ log(`Extracting component library for project: ${options.project}`, 'title');
903
+
904
+ const htmlDir = path.join(options.refs, 'html');
905
+ const outputDir = path.join(options.proto, 'src', 'components', 'extracted');
906
+
907
+ if (!fs.existsSync(htmlDir)) {
908
+ log('HTML directory not found - run capture first', 'error');
909
+ process.exit(1);
910
+ }
911
+
912
+ try {
913
+ const extractor = new ComponentExtractor();
914
+ extractor.analyzeDirectory(htmlDir);
915
+
916
+ console.log('');
917
+ console.log(extractor.formatSummary());
918
+
919
+ // Write components
920
+ const written = extractor.writeComponents(outputDir);
921
+ console.log(`\n\x1b[32m✓ Wrote ${written.length} files to: ${outputDir}\x1b[0m`);
922
+ for (const file of written) {
923
+ console.log(` ${path.basename(file)}`);
924
+ }
925
+
926
+ } catch (error) {
927
+ log(`Extraction failed: ${error.message}`, 'error');
928
+ process.exit(1);
929
+ }
930
+ }
931
+
932
+ function runPlan(options, args) {
933
+ requireProject(options, 'plan');
934
+ showBanner();
935
+
936
+ let feature = '';
937
+ let targetPage = null;
938
+ let outputPath = null;
939
+
940
+ for (let i = 0; i < args.length; i++) {
941
+ if (args[i] === '--feature') {
942
+ feature = args[++i];
943
+ } else if (args[i] === '--target') {
944
+ targetPage = args[++i];
945
+ } else if (args[i] === '--output' || args[i] === '-o') {
946
+ outputPath = args[++i];
947
+ }
948
+ }
949
+
950
+ log(`Generating implementation plan for project: ${options.project}`, 'title');
951
+
952
+ const projectDir = getProjectDir(options.project);
953
+
954
+ try {
955
+ const generator = new PlanGenerator(projectDir, {
956
+ featureDescription: feature,
957
+ targetPage
958
+ });
959
+
960
+ generator.generate();
961
+ console.log('');
962
+ console.log(generator.formatPlan());
963
+
964
+ if (outputPath) {
965
+ generator.writePlan(outputPath);
966
+ console.log(`\n\x1b[32m✓ Plan written to: ${outputPath}\x1b[0m`);
967
+ } else {
968
+ // Write to project directory by default
969
+ const defaultPath = path.join(projectDir, 'plan.json');
970
+ generator.writePlan(defaultPath);
971
+ console.log(`\n\x1b[32m✓ Plan written to: ${defaultPath}\x1b[0m`);
972
+ }
973
+
974
+ } catch (error) {
975
+ log(`Plan generation failed: ${error.message}`, 'error');
976
+ process.exit(1);
977
+ }
978
+ }
979
+
495
980
  function runInit(options) {
496
981
  showBanner();
497
982
  log('Initializing capture configuration...', 'title');
@@ -565,6 +1050,9 @@ switch (options.command) {
565
1050
  case 'new':
566
1051
  runNew(options);
567
1052
  break;
1053
+ case 'detect':
1054
+ runDetect(options);
1055
+ break;
568
1056
  case 'list':
569
1057
  runList();
570
1058
  break;
@@ -574,6 +1062,24 @@ switch (options.command) {
574
1062
  case 'validate':
575
1063
  runValidate(options);
576
1064
  break;
1065
+ case 'validate-colors':
1066
+ runValidateColors(options);
1067
+ break;
1068
+ case 'convert':
1069
+ runConvert(options, args);
1070
+ break;
1071
+ case 'extract-css':
1072
+ runExtractCSS(options, args);
1073
+ break;
1074
+ case 'extract-lib':
1075
+ runExtractLib(options);
1076
+ break;
1077
+ case 'visual-diff':
1078
+ runVisualDiff(options, args);
1079
+ break;
1080
+ case 'plan':
1081
+ runPlan(options, args);
1082
+ break;
577
1083
  case 'generate':
578
1084
  runGenerate(options);
579
1085
  break;