@@ -12,6 +12,8 @@ import chalk from 'chalk';
1212import inquirer from 'inquirer' ;
1313import { ToolGenerator , createToolGenerator , Tool } from './lib/tool-generator.js' ;
1414import { ToolRegistryManager , createRegistryManager } from './lib/tool-registry.js' ;
15+ import { BatchToolImporter , createBatchToolImporter } from './lib/batch-importer.js' ;
16+ import { createLibraryManager , getConversionRecommendations , generateLibraryMatrix } from './lib/library-integration.js' ;
1517import path from 'path' ;
1618
1719const DEFAULT_TOOLS_DIR = './apps/tools/app' ;
@@ -329,4 +331,212 @@ program
329331 }
330332 } ) ;
331333
334+ // Batch import command
335+ program
336+ . command ( 'import' )
337+ . description ( 'Batch import tools from a file or input' )
338+ . option ( '-f, --file <file>' , 'Import from file' )
339+ . option ( '-i, --input <input>' , 'Import from direct input string' )
340+ . option ( '--dry-run' , 'Analyze without creating tools' )
341+ . option ( '--skip-existing' , 'Skip tools that already exist' )
342+ . option ( '--generate-content' , 'Generate basic content for new tools' )
343+ . option ( '--report <file>' , 'Save report to file' )
344+ . action ( async ( options ) => {
345+ try {
346+ console . log ( chalk . blue ( '📥 Batch Tool Import' ) ) ;
347+
348+ if ( ! options . file && ! options . input ) {
349+ console . error ( chalk . red ( '❌ Please provide either --file or --input' ) ) ;
350+ process . exit ( 1 ) ;
351+ }
352+
353+ const registryManager = createRegistryManager ( DEFAULT_REGISTRY_PATH ) ;
354+ const importer = createBatchToolImporter ( registryManager ) ;
355+
356+ let input : string ;
357+ if ( options . file ) {
358+ console . log ( chalk . gray ( `Reading from file: ${ options . file } ` ) ) ;
359+ const fs = await import ( 'fs/promises' ) ;
360+ input = await fs . readFile ( options . file , 'utf-8' ) ;
361+ } else {
362+ input = options . input ;
363+ }
364+
365+ // Parse input
366+ console . log ( chalk . gray ( 'Parsing import requests...' ) ) ;
367+ const requests = importer . parseImportList ( input ) ;
368+ console . log ( chalk . cyan ( `Found ${ requests . length } conversion requests` ) ) ;
369+
370+ // Analyze requests
371+ console . log ( chalk . gray ( 'Analyzing against existing tools...' ) ) ;
372+ const analysis = await importer . analyzeImportRequests ( requests ) ;
373+
374+ console . log ( chalk . cyan ( `\n📊 Analysis Results:` ) ) ;
375+ console . log ( ` Total requests: ${ analysis . total } ` ) ;
376+ console . log ( ` Existing tools: ${ analysis . existing . length } ` ) ;
377+ console . log ( ` New tools: ${ analysis . new . length } ` ) ;
378+ console . log ( ` Conflicts: ${ analysis . conflicts . length } ` ) ;
379+
380+ // Show existing tools
381+ if ( analysis . existing . length > 0 ) {
382+ console . log ( chalk . yellow ( `\n⚠️ Existing Tools (${ analysis . existing . length } ):` ) ) ;
383+ analysis . existing . slice ( 0 , 5 ) . forEach ( ( { request, existingTool, match } ) => {
384+ console . log ( ` ${ match === 'exact' ? '🎯' : '🔍' } ${ request . from } → ${ request . to } (${ match } match: ${ existingTool . name } )` ) ;
385+ } ) ;
386+ if ( analysis . existing . length > 5 ) {
387+ console . log ( ` ... and ${ analysis . existing . length - 5 } more` ) ;
388+ }
389+ }
390+
391+ // Show conflicts
392+ if ( analysis . conflicts . length > 0 ) {
393+ console . log ( chalk . red ( `\n❌ Conflicts (${ analysis . conflicts . length } ):` ) ) ;
394+ analysis . conflicts . forEach ( ( { request, issue } ) => {
395+ console . log ( ` ${ request . from } → ${ request . to } : ${ issue } ` ) ;
396+ } ) ;
397+ }
398+
399+ // Execute import if not dry run
400+ let execution ;
401+ if ( ! options . dryRun && analysis . new . length > 0 ) {
402+ console . log ( chalk . blue ( `\n🚀 Creating ${ analysis . new . length } new tools...` ) ) ;
403+
404+ execution = await importer . executeImport ( analysis . new , {
405+ skipExisting : options . skipExisting ,
406+ generateContent : options . generateContent ,
407+ dryRun : false
408+ } ) ;
409+
410+ console . log ( chalk . green ( `✅ Import completed:` ) ) ;
411+ console . log ( ` Created: ${ execution . created . length } ` ) ;
412+ console . log ( ` Skipped: ${ execution . skipped . length } ` ) ;
413+ console . log ( ` Errors: ${ execution . errors . length } ` ) ;
414+
415+ if ( execution . errors . length > 0 ) {
416+ console . log ( chalk . red ( `\n❌ Errors:` ) ) ;
417+ execution . errors . forEach ( ( { tool, error } ) => {
418+ console . log ( ` ${ tool . from } → ${ tool . to } : ${ error } ` ) ;
419+ } ) ;
420+ }
421+ } else if ( options . dryRun ) {
422+ console . log ( chalk . yellow ( '\n🔍 Dry run completed - no tools were created' ) ) ;
423+ }
424+
425+ // Generate and save report
426+ const report = importer . generateImportReport ( analysis , execution ) ;
427+
428+ if ( options . report ) {
429+ const fs = await import ( 'fs/promises' ) ;
430+ await fs . writeFile ( options . report , report ) ;
431+ console . log ( chalk . green ( `\n📝 Report saved to: ${ options . report } ` ) ) ;
432+ } else {
433+ console . log ( '\n' + report ) ;
434+ }
435+
436+ } catch ( error ) {
437+ console . error ( chalk . red ( '❌ Import failed:' ) , error ) ;
438+ process . exit ( 1 ) ;
439+ }
440+ } ) ;
441+
442+ // Libraries command
443+ program
444+ . command ( 'libraries' )
445+ . description ( 'Show available conversion libraries and their capabilities' )
446+ . option ( '--check-availability' , 'Check which libraries are currently available' )
447+ . option ( '--matrix' , 'Show conversion compatibility matrix' )
448+ . option ( '--recommend <conversion>' , 'Get library recommendations for a conversion (format: "jpg:png")' )
449+ . action ( async ( options ) => {
450+ try {
451+ console . log ( chalk . blue ( '📚 Conversion Libraries' ) ) ;
452+
453+ const manager = createLibraryManager ( ) ;
454+ const libraries = manager . getAllLibraries ( ) ;
455+
456+ if ( options . recommend ) {
457+ const [ from , to ] = options . recommend . split ( ':' ) ;
458+ if ( ! from || ! to ) {
459+ console . error ( chalk . red ( '❌ Conversion format should be "from:to" (e.g., "jpg:png")' ) ) ;
460+ process . exit ( 1 ) ;
461+ }
462+
463+ console . log ( chalk . cyan ( `\n🎯 Recommendations for ${ from } → ${ to } :` ) ) ;
464+ const recommendations = await getConversionRecommendations ( from , to ) ;
465+
466+ if ( recommendations . recommended ) {
467+ console . log ( chalk . green ( `\n✅ Recommended: ${ recommendations . recommended . name } ` ) ) ;
468+ console . log ( ` ${ recommendations . recommended . description } ` ) ;
469+ console . log ( ` Platform: ${ recommendations . recommended . capabilities . platform } ` ) ;
470+ console . log ( ` License: ${ recommendations . recommended . capabilities . license } ` ) ;
471+ }
472+
473+ if ( recommendations . alternatives . length > 0 ) {
474+ console . log ( chalk . yellow ( `\n🔄 Alternatives:` ) ) ;
475+ recommendations . alternatives . forEach ( lib => {
476+ console . log ( ` • ${ lib . name } (${ lib . capabilities . platform } )` ) ;
477+ } ) ;
478+ }
479+
480+ if ( recommendations . unsupported ) {
481+ console . log ( chalk . red ( '\n❌ This conversion is not supported by any available library' ) ) ;
482+ }
483+
484+ return ;
485+ }
486+
487+ if ( options . matrix ) {
488+ console . log ( chalk . cyan ( '\n📊 Conversion Compatibility Matrix:' ) ) ;
489+ const { matrix } = generateLibraryMatrix ( ) ;
490+
491+ // Show a sample of the matrix (top formats)
492+ const topFormats = [ 'jpg' , 'png' , 'gif' , 'webp' , 'mp4' , 'pdf' ] ;
493+ console . log ( '\nFormat compatibility (sample):' ) ;
494+ console . log ( 'FROM \\ TO ' + topFormats . map ( f => f . padEnd ( 8 ) ) . join ( '' ) ) ;
495+ console . log ( '─' . repeat ( 80 ) ) ;
496+
497+ topFormats . forEach ( from => {
498+ const row = from . padEnd ( 10 ) + topFormats . map ( to => {
499+ const libs = matrix [ from ] ?. [ to ] ;
500+ return libs ? `${ libs . length } libs` . padEnd ( 8 ) : '─' . padEnd ( 8 ) ;
501+ } ) . join ( '' ) ;
502+ console . log ( row ) ;
503+ } ) ;
504+ console . log ( '\nNote: Numbers indicate available libraries for each conversion' ) ;
505+ return ;
506+ }
507+
508+ console . log ( chalk . cyan ( `\n📋 Available Libraries (${ libraries . length } ):` ) ) ;
509+
510+ for ( const library of libraries ) {
511+ console . log ( `\n${ chalk . bold ( library . name ) } (${ library . id } )` ) ;
512+ console . log ( ` ${ library . description } ` ) ;
513+ console . log ( ` Platform: ${ library . capabilities . platform } ` ) ;
514+ console . log ( ` License: ${ library . capabilities . license } ` ) ;
515+ console . log ( ` Input formats: ${ library . capabilities . supportedFormats . input . slice ( 0 , 10 ) . join ( ', ' ) } ${ library . capabilities . supportedFormats . input . length > 10 ? '...' : '' } ` ) ;
516+ console . log ( ` Output formats: ${ library . capabilities . supportedFormats . output . slice ( 0 , 10 ) . join ( ', ' ) } ${ library . capabilities . supportedFormats . output . length > 10 ? '...' : '' } ` ) ;
517+ console . log ( ` Operations: ${ library . capabilities . operations . join ( ', ' ) } ` ) ;
518+
519+ if ( library . capabilities . homepage ) {
520+ console . log ( ` Homepage: ${ library . capabilities . homepage } ` ) ;
521+ }
522+ }
523+
524+ if ( options . checkAvailability ) {
525+ console . log ( chalk . cyan ( '\n🔍 Checking library availability...' ) ) ;
526+ const availability = await manager . checkLibraryAvailability ( ) ;
527+
528+ console . log ( '\nAvailability Status:' ) ;
529+ for ( const [ id , available ] of availability ) {
530+ const library = manager . getLibrary ( id ) ;
531+ const status = available ? chalk . green ( '✅ Available' ) : chalk . red ( '❌ Unavailable' ) ;
532+ console . log ( ` ${ library ?. name } : ${ status } ` ) ;
533+ }
534+ }
535+
536+ } catch ( error ) {
537+ console . error ( chalk . red ( '❌ Libraries command failed:' ) , error ) ;
538+ process . exit ( 1 ) ;
539+ }
540+ } ) ;
541+
332542program . parse ( ) ;
0 commit comments