UNPKG

llm-json-fix

Version:

Fix malformed JSON outputs from Large Language Models (LLMs)

131 lines (114 loc) 3.83 kB
#!/usr/bin/env node const fs = require('fs') const path = require('path') const { pipeline } = require('stream') const { program } = require('commander') const { createLLMJsonFixStream } = require('../lib/cjs/stream') const packageJson = require('../package.json') // Parse command line arguments program .name('llm-json-fix') .description('Fix malformed JSON outputs from Large Language Models (LLMs)') .version(packageJson.version, '-v, --version') .argument('[filename]', 'Input file (if not provided, read from stdin)') .option('-o, --output <filename>', 'Output file (if not provided, write to stdout)') .option('--overwrite', 'Overwrite the input file') .option('--buffer <size>', 'Buffer size in bytes, for example 64K (default) or 1M', '64K') .option('--model <name>', 'Specify the LLM model (openai, anthropic, general)', 'general') .option('--preserve-comments', 'Preserve comments in the output', false) .option('--verbose', 'Show detailed repair information', false) .helpOption('-h, --help', 'Display help for command') .parse(process.argv) // Get options const options = program.opts() const inputFile = program.args[0] if (options.overwrite && !inputFile) { console.error('Error: --overwrite option requires an input file') process.exit(1) } if (options.output && options.overwrite) { console.error('Error: cannot use both --output and --overwrite options') process.exit(1) } // Parse buffer size let bufferSize = 65536 // Default 64K if (options.buffer) { const sizeStr = options.buffer.toUpperCase() if (sizeStr.endsWith('K')) { bufferSize = parseInt(sizeStr.slice(0, -1), 10) * 1024 } else if (sizeStr.endsWith('M')) { bufferSize = parseInt(sizeStr.slice(0, -1), 10) * 1024 * 1024 } else if (sizeStr.endsWith('G')) { bufferSize = parseInt(sizeStr.slice(0, -1), 10) * 1024 * 1024 * 1024 } else { bufferSize = parseInt(sizeStr, 10) } if (isNaN(bufferSize) || bufferSize <= 0) { console.error(`Error: Invalid buffer size: ${options.buffer}`) process.exit(1) } } // Determine output file let outputFile = options.output if (options.overwrite) { outputFile = inputFile } // Create input stream let inputStream if (inputFile) { try { inputStream = fs.createReadStream(inputFile) } catch (err) { console.error(`Error reading input file: ${err.message}`) process.exit(1) } } else { // Read from stdin inputStream = process.stdin // Check if stdin is a TTY (terminal) - if it is and no file was specified, show help if (inputStream.isTTY) { program.help() } } // Create transform stream const fixStream = createLLMJsonFixStream({ bufferSize, model: options.model, preserveComments: options.preserveComments, verbose: options.verbose }) // Create output stream let outputStream if (outputFile) { try { // If overwriting, write to a temporary file first const actualOutputFile = options.overwrite ? `${outputFile}.tmp` : outputFile outputStream = fs.createWriteStream(actualOutputFile) } catch (err) { console.error(`Error creating output file: ${err.message}`) process.exit(1) } } else { // Write to stdout outputStream = process.stdout } // Run the pipeline pipeline(inputStream, fixStream, outputStream, (err) => { if (err) { console.error(`Error: ${err.message}`) // Clean up temporary file if we were overwriting if (options.overwrite && fs.existsSync(`${outputFile}.tmp`)) { fs.unlinkSync(`${outputFile}.tmp`) } process.exit(1) } // If overwriting, replace the original file with the fixed version if (options.overwrite) { try { fs.renameSync(`${outputFile}.tmp`, outputFile) } catch (err) { console.error(`Error replacing original file: ${err.message}`) process.exit(1) } } })