UNPKG

taylo

Version:

Make changes to a branch a plugin. A command-line tool to manage and apply plugins '.taylored'. Supports applying, removing, verifying plugins, and generating them from branch (GIT).

771 lines (770 loc) 44.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.handleAutomaticOperation = handleAutomaticOperation; const fs = __importStar(require("fs/promises")); const path = __importStar(require("path")); const child_process_1 = require("child_process"); const constants_1 = require("../constants"); const utils_1 = require("../utils"); const execOpts = { encoding: 'utf8', stdio: 'pipe', }; async function findFilesRecursive(dir, ext, allFiles = [], excludeDirs, CWD_ABS) { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { const relativePath = CWD_ABS ? path.relative(CWD_ABS, fullPath) : entry.name; if (entry.name !== '.git' && entry.name !== constants_1.TAYLORED_DIR_NAME && (!excludeDirs || !excludeDirs.some((excludedDir) => relativePath === excludedDir || relativePath.startsWith(excludedDir + path.sep)))) { await findFilesRecursive(fullPath, ext, allFiles, excludeDirs, CWD_ABS); } } else if (entry.isFile() && entry.name.endsWith(ext)) { allFiles.push(fullPath); } } return allFiles; } async function handleAutomaticOperation(extensionsInput, branchName, CWD, excludeDirs) { let originalBranchName; try { originalBranchName = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', { cwd: CWD, ...execOpts, }).trim(); if (originalBranchName === 'HEAD') { const errorMessage = 'CRITICAL ERROR: Repository is in a detached HEAD state. Please checkout a branch.'; console.error(errorMessage); throw new Error(errorMessage); } } catch (error) { const errorMessage = `CRITICAL ERROR: Failed to get current Git branch. Details: ${error.message}`; console.error(errorMessage); if (error.stderr) console.error('STDERR:\n' + error.stderr); if (error.stdout) console.error('STDOUT:\n' + error.stdout); throw new Error(errorMessage); } try { const gitStatus = (0, child_process_1.execSync)('git status --porcelain', { cwd: CWD, ...execOpts, }).trim(); if (gitStatus) { const errorMessage = 'CRITICAL ERROR: Uncommitted changes or untracked files in the repository. Please commit or stash them before running --automatic.'; console.error(errorMessage); console.error('Details:\n' + gitStatus); throw new Error(errorMessage); } } catch (error) { const errorMessage = `CRITICAL ERROR: Failed to check Git status. Details: ${error.message}`; console.error(errorMessage); if (error.stderr) console.error('STDERR:\n' + error.stderr); if (error.stdout) console.error('STDOUT:\n' + error.stdout); throw new Error(errorMessage); } console.log(`Starting automatic taylored block extraction for extensions '${extensionsInput}' in directory '${CWD}'. Original branch: '${originalBranchName}'`); const tayloredDir = path.join(CWD, constants_1.TAYLORED_DIR_NAME); try { await fs.mkdir(tayloredDir, { recursive: true }); } catch (error) { const errorMessage = `CRITICAL ERROR: Could not create directory '${tayloredDir}'. Details: ${error.message}`; console.error(errorMessage); throw new Error(errorMessage); } const extensions = extensionsInput.split(',').map((ext) => ext.trim()); const allFilesToScan = []; const CWD_ABS = path.resolve(CWD); for (const ext of extensions) { const normalizedExtension = ext.startsWith('.') ? ext : `.${ext}`; try { const filesForExtension = await findFilesRecursive(CWD_ABS, normalizedExtension, [], excludeDirs, CWD_ABS); allFilesToScan.push(...filesForExtension); } catch (error) { console.error(`Error while searching for files with extension '${normalizedExtension}': ${error.message}`); } } if (allFilesToScan.length === 0) { console.log(`No files found with specified extensions: ${extensionsInput}`); return; } console.log(`Found ${allFilesToScan.length} file(s) with specified extensions. Processing...`); const blockRegex = /[^\n]*?<taylored\s+number="(\d+)"([^>]*)>([\s\S]*?)[^\n]*?<\/taylored>/g; const jsonBlockRegex = /(?:const\s+\w+\s*=\s*)?({(?:[^{}]|{[^{}]*})*?"taylored"\s*:\s*(\d+)(?:[^{}]|{[^{}]*})*?});?/g; let totalBlocksProcessed = 0; const asyncScriptPromises = []; for (const originalFilePath of allFilesToScan) { let fileContent; try { fileContent = await fs.readFile(originalFilePath, 'utf-8'); } catch (readError) { console.warn(`Warning: Error reading file '${originalFilePath}': ${readError.message}. Skipping this file.`); continue; } const xmlMatchesRaw = Array.from(fileContent.matchAll(blockRegex)); const jsonMatchesRaw = Array.from(fileContent.matchAll(jsonBlockRegex)); const allMatches = []; for (const match of xmlMatchesRaw) { if (match.index !== undefined) { allMatches.push({ type: 'xml', match, index: match.index }); } } for (const match of jsonMatchesRaw) { if (match.index !== undefined) { allMatches.push({ type: 'json', match, index: match.index }); } } allMatches.sort((a, b) => a.index - b.index); if (allMatches.length === 0) { continue; } for (const matchInfo of allMatches) { let numero; let attributesString; let scriptContent; let scriptContentWithTags; let computeCharsToStrip; let asyncFlag = false; let isDisabled = false; if (matchInfo.type === 'xml') { const match = matchInfo.match; numero = match[1]; attributesString = match[2]; scriptContentWithTags = match[0]; scriptContent = match[3]; const computeMatch = attributesString.match(/compute=["']([^"']*)["']/); computeCharsToStrip = computeMatch ? computeMatch[1] : undefined; const asyncMatch = attributesString.match(/async=["'](true|false)["']/); asyncFlag = asyncMatch ? asyncMatch[1] === 'true' : false; const disabledMatch = attributesString.match(/disabled=["'](true|false)["']/); isDisabled = disabledMatch ? disabledMatch[1] === 'true' : false; } else { scriptContentWithTags = matchInfo.match[0]; const jsonBlockText = matchInfo.match[1]; numero = matchInfo.match[2]; try { const cleanedJsonText = jsonBlockText.replace(/\/\/.*$/gm, ''); const parsedJson = JSON.parse(cleanedJsonText); if (typeof parsedJson.content !== 'string') { console.warn(`Warning: JSON block ${numero} in ${originalFilePath} has invalid or missing 'content' string. Skipping.`); continue; } scriptContent = parsedJson.content; computeCharsToStrip = typeof parsedJson.compute === 'string' ? parsedJson.compute : undefined; asyncFlag = parsedJson.async === true; isDisabled = parsedJson.disabled === true; } catch (e) { console.warn(`Warning: Parsing of JSON block in ${originalFilePath} ... Skipping.`); continue; } } if (isDisabled) { console.log(`Skipping disabled block ${numero} from ${originalFilePath}.`); continue; } const targetTayloredFileName = `${numero}${constants_1.TAYLORED_FILE_EXTENSION}`; const targetTayloredFilePath = path.join(tayloredDir, targetTayloredFileName); const intermediateMainTayloredPath = path.join(tayloredDir, `main${constants_1.TAYLORED_FILE_EXTENSION}`); console.log(`Processing block ${numero} from ${originalFilePath}...`); if (computeCharsToStrip !== undefined) { const processComputeBlock = async (currentNumero, currentOriginalFilePath, currentScriptContent, currentComputeCharsToStrip, currentScriptContentWithTags, currentCWD, currentBranchName, currentOriginalBranchName, currentTargetTayloredFilePath) => { console.log(`Asynchronously processing computed block ${currentNumero} from ${currentOriginalFilePath}...`); try { await fs.access(currentTargetTayloredFilePath); const message = `CRITICAL ERROR: Target file ${currentTargetTayloredFilePath} for computed block already exists. Please remove or rename it.`; console.error(message); throw new Error(message); } catch (error) { if (error.code !== 'ENOENT') { throw error; } } try { await fs.access(intermediateMainTayloredPath); const message = `CRITICAL ERROR: Intermediate file ${intermediateMainTayloredPath} exists for a compute block. This file should not be present. Please remove or rename it.`; console.error(message); throw new Error(message); } catch (error) { if (error.code !== 'ENOENT') { throw error; } } let actualScriptContent; if (currentComputeCharsToStrip.length > 0) { let processedContent = currentScriptContent.trim(); const patterns = currentComputeCharsToStrip.split(','); for (const pattern of patterns) { const trimmedPattern = pattern.trim(); if (trimmedPattern.length > 0) { processedContent = processedContent.replaceAll(trimmedPattern, ''); } } actualScriptContent = processedContent.trim(); } else { actualScriptContent = currentScriptContent.trim(); } const tempScriptPath = path.join(currentCWD, `taylored-temp-script-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`); await fs.writeFile(tempScriptPath, actualScriptContent); let scriptResult = ''; try { await fs.chmod(tempScriptPath, 0o755); scriptResult = await new Promise((resolve, reject) => { const child = (0, child_process_1.spawn)(tempScriptPath, [], { cwd: currentCWD, stdio: 'pipe', shell: true, }); let scriptOutput = ''; let scriptErrorOutput = ''; child.stdout.on('data', (data) => { scriptOutput += data.toString(); process.stdout.write(data); }); child.stderr.on('data', (data) => { scriptErrorOutput += data.toString(); process.stderr.write(data); }); child.on('error', reject); child.on('close', (code) => { if (code === 0) { resolve(scriptOutput); } else { const error = new Error(`Script failed with code ${code}`); error.status = code; error.stdout = scriptOutput; error.stderr = scriptErrorOutput; reject(error); } }); }); } catch (error) { if (error.status !== undefined || error.stderr !== undefined || error.stdout !== undefined) { console.error(`ERROR: Script execution failed for block ${currentNumero} in ${currentOriginalFilePath}. Error: ${error.message}`); if (error.stderr) console.error('STDERR:\n' + error.stderr); if (error.stdout) console.error('STDOUT:\n' + error.stdout); } else { console.error(`ERROR: Failed to set execute permissions or other FS issue on temporary script file '${tempScriptPath}'. Details: ${error.message}`); } throw error; } finally { try { await fs.unlink(tempScriptPath); } catch (unlinkError) { console.warn(`Warning: Failed to delete temporary script file '${tempScriptPath}' during cleanup. Details: ${unlinkError.message}`); } } const relativeOriginalFilePath = path.relative(currentCWD, currentOriginalFilePath); const tempComputeBranchName = `temp-taylored-compute-${currentNumero}-${Date.now()}`; try { (0, child_process_1.execSync)(`git checkout -b "${tempComputeBranchName}" "${currentOriginalBranchName}"`, { cwd: currentCWD, ...execOpts }); const gitignorePath = path.join(currentCWD, '.gitignore'); await fs.writeFile(gitignorePath, constants_1.TAYLORED_DIR_NAME + '\n'); (0, child_process_1.execSync)(`git add .gitignore`, { cwd: currentCWD, ...execOpts }); const contentOnTempBranch = await fs.readFile(currentOriginalFilePath, 'utf-8'); const contentWithScriptResult = contentOnTempBranch.replace(currentScriptContentWithTags, scriptResult); await fs.writeFile(currentOriginalFilePath, contentWithScriptResult); (0, child_process_1.execSync)(`git add "${relativeOriginalFilePath}"`, { cwd: currentCWD, ...execOpts, }); (0, child_process_1.execSync)(`git commit --no-verify -m "AUTO: Apply computed block ${currentNumero} for ${path.basename(currentOriginalFilePath)}"`, { cwd: currentCWD, ...execOpts }); const diffAgainstBranchCommand = `git diff --exit-code "${currentBranchName}" HEAD -- "${relativeOriginalFilePath}"`; try { (0, child_process_1.execSync)(diffAgainstBranchCommand, { cwd: currentCWD, encoding: 'utf8', stdio: 'pipe', }); await fs.writeFile(currentTargetTayloredFilePath, ''); console.log(`No difference found for computed block ${currentNumero} from ${currentOriginalFilePath} when compared against branch '${currentBranchName}'. Empty taylored file created: ${currentTargetTayloredFilePath}`); } catch (e) { if (e.status === 1 && typeof e.stdout === 'string') { await fs.writeFile(currentTargetTayloredFilePath, e.stdout); console.log(`Successfully created ${currentTargetTayloredFilePath} for computed block ${currentNumero} from ${currentOriginalFilePath} (using branch diff against '${currentBranchName}')`); } else { console.error(`CRITICAL ERROR: Failed to generate diff for computed block ${currentNumero} from ${currentOriginalFilePath} against branch '${currentBranchName}'.`); if (e.message) console.error(` Error message: ${e.message}`); if (e.stderr) console.error(' STDERR:\n' + e.stderr.toString().trim()); if (e.stdout) console.error(' STDOUT:\n' + e.stdout.toString().trim()); throw e; } } } catch (error) { console.error(`CRITICAL ERROR: Failed to process computed block ${currentNumero} from ${currentOriginalFilePath} using branch diff method.`); if (error.message) console.error(` Error message: ${error.message}`); if (error.stderr) console.error(' STDERR:\n' + error.stderr.toString().trim()); if (error.stdout) console.error(' STDOUT:\n' + error.stdout.toString().trim()); throw error; } finally { const currentBranchAfterOps = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', { cwd: currentCWD, ...execOpts }).trim(); if (currentBranchAfterOps === tempComputeBranchName) { (0, child_process_1.execSync)(`git checkout -q "${currentOriginalBranchName}"`, { cwd: currentCWD, stdio: 'ignore', }); } else if (currentBranchAfterOps !== currentOriginalBranchName) { console.warn(`Warning: Unexpected current branch '${currentBranchAfterOps}' during cleanup for computed block. Attempting to return to '${currentOriginalBranchName}'.`); try { (0, child_process_1.execSync)(`git checkout -q "${currentOriginalBranchName}"`, { cwd: currentCWD, stdio: 'ignore', }); } catch (coErr) { console.warn(`Warning: Failed to checkout original branch '${currentOriginalBranchName}' during cleanup. Current branch: ${currentBranchAfterOps}. Error: ${coErr.message}`); } } try { const branchesRaw = (0, child_process_1.execSync)('git branch', { cwd: currentCWD, ...execOpts, }); const branchesList = branchesRaw .split('\n') .map((b) => b.trim().replace(/^\* /, '')); if (branchesList.includes(tempComputeBranchName)) { (0, child_process_1.execSync)(`git branch -q -D "${tempComputeBranchName}"`, { cwd: currentCWD, stdio: 'ignore', }); } } catch (deleteBranchError) { console.warn(`Warning: Failed to delete temporary branch '${tempComputeBranchName}' during cleanup for computed block. May require manual cleanup. ${deleteBranchError.message}`); } } }; if (asyncFlag) { asyncScriptPromises.push(processComputeBlock(numero, originalFilePath, scriptContent, computeCharsToStrip, scriptContentWithTags, CWD, branchName, originalBranchName, targetTayloredFilePath)); totalBlocksProcessed++; } else { try { await fs.access(targetTayloredFilePath); const message = `CRITICAL ERROR: Target file ${targetTayloredFilePath} for computed block already exists. Please remove or rename it.`; console.error(message); throw new Error(message); } catch (error) { if (error.code !== 'ENOENT') { throw error; } } try { await fs.access(intermediateMainTayloredPath); const message = `CRITICAL ERROR: Intermediate file ${intermediateMainTayloredPath} exists for a compute block. This file should not be present. Please remove or rename it.`; console.error(message); throw new Error(message); } catch (error) { if (error.code !== 'ENOENT') { throw error; } } let actualScriptContent; if (computeCharsToStrip !== undefined && computeCharsToStrip.length > 0) { let processedContent = scriptContent.trim(); const patterns = computeCharsToStrip.split(','); for (const pattern of patterns) { const trimmedPattern = pattern.trim(); if (trimmedPattern.length > 0) { processedContent = processedContent.replaceAll(trimmedPattern, ''); } } actualScriptContent = processedContent.trim(); } else { actualScriptContent = scriptContent.trim(); } const tempScriptPath = path.join(CWD, `taylored-temp-script-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`); await fs.writeFile(tempScriptPath, actualScriptContent); let scriptResult = ''; try { await fs.chmod(tempScriptPath, 0o755); scriptResult = await new Promise((resolve, reject) => { const child = (0, child_process_1.spawn)(tempScriptPath, [], { cwd: CWD, stdio: 'pipe', shell: true, }); let scriptOutput = ''; let scriptErrorOutput = ''; child.stdout.on('data', (data) => { scriptOutput += data.toString(); process.stdout.write(data); }); child.stderr.on('data', (data) => { scriptErrorOutput += data.toString(); process.stderr.write(data); }); child.on('error', (err) => { reject(err); }); child.on('close', (code) => { if (code === 0) { resolve(scriptOutput); } else { const error = new Error(`Script failed with code ${code}`); error.status = code; error.stdout = scriptOutput; error.stderr = scriptErrorOutput; reject(error); } }); }); } catch (error) { if (error.status !== undefined || error.stderr !== undefined || error.stdout !== undefined) { console.error(`ERROR: Script execution failed for block ${numero} in ${originalFilePath}. Error: ${error.message}`); if (error.stderr) console.error('STDERR:\n' + error.stderr); if (error.stdout) console.error('STDOUT:\n' + error.stdout); } else { console.error(`ERROR: Failed to set execute permissions or other FS issue on temporary script file '${tempScriptPath}'. Details: ${error.message}`); } throw error; } finally { try { await fs.unlink(tempScriptPath); } catch (unlinkError) { console.warn(`Warning: Failed to delete temporary script file '${tempScriptPath}' during cleanup. Details: ${unlinkError.message}`); } } const relativeOriginalFilePath = path.relative(CWD, originalFilePath); const tempComputeBranchName = `temp-taylored-compute-${numero}-${Date.now()}`; try { (0, child_process_1.execSync)(`git checkout -b "${tempComputeBranchName}" "${originalBranchName}"`, { cwd: CWD, ...execOpts }); const gitignorePath = path.join(CWD, '.gitignore'); await fs.writeFile(gitignorePath, constants_1.TAYLORED_DIR_NAME + '\n'); (0, child_process_1.execSync)(`git add .gitignore`, { cwd: CWD, ...execOpts }); const contentOnTempBranch = await fs.readFile(originalFilePath, 'utf-8'); const contentWithScriptResult = contentOnTempBranch.replace(scriptContentWithTags, scriptResult); await fs.writeFile(originalFilePath, contentWithScriptResult); (0, child_process_1.execSync)(`git add "${relativeOriginalFilePath}"`, { cwd: CWD, ...execOpts, }); (0, child_process_1.execSync)(`git commit --no-verify -m "AUTO: Apply computed block ${numero} for ${path.basename(originalFilePath)}"`, { cwd: CWD, ...execOpts }); const diffAgainstBranchCommand = `git diff --exit-code "${branchName}" HEAD -- "${relativeOriginalFilePath}"`; let diffOutputCommandResult; try { diffOutputCommandResult = (0, child_process_1.execSync)(diffAgainstBranchCommand, { cwd: CWD, encoding: 'utf8', stdio: 'pipe', }); await fs.writeFile(targetTayloredFilePath, ''); console.log(`No difference found for computed block ${numero} from ${originalFilePath} when compared against branch '${branchName}'. Empty taylored file created: ${targetTayloredFilePath}`); } catch (e) { if (e.status === 1 && typeof e.stdout === 'string') { diffOutputCommandResult = e.stdout; await fs.writeFile(targetTayloredFilePath, diffOutputCommandResult); console.log(`Successfully created ${targetTayloredFilePath} for computed block ${numero} from ${originalFilePath} (using branch diff against '${branchName}')`); } else { console.error(`CRITICAL ERROR: Failed to generate diff for computed block ${numero} from ${originalFilePath} against branch '${branchName}'.`); if (e.message) console.error(` Error message: ${e.message}`); if (e.stderr) console.error(' STDERR:\n' + e.stderr.toString().trim()); if (e.stdout) console.error(' STDOUT:\n' + e.stdout.toString().trim()); throw e; } } totalBlocksProcessed++; } catch (error) { console.error(`CRITICAL ERROR: Failed to process computed block ${numero} from ${originalFilePath} using branch diff method.`); if (error.message) console.error(` Error message: ${error.message}`); if (error.stderr) console.error(' STDERR:\n' + error.stderr.toString().trim()); if (error.stdout) console.error(' STDOUT:\n' + error.stdout.toString().trim()); throw error; } finally { const currentBranchAfterOps = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', { cwd: CWD, ...execOpts }).trim(); if (currentBranchAfterOps === tempComputeBranchName) { (0, child_process_1.execSync)(`git checkout -q "${originalBranchName}"`, { cwd: CWD, stdio: 'ignore', }); } else if (currentBranchAfterOps !== originalBranchName) { console.warn(`Warning: Unexpected current branch '${currentBranchAfterOps}' during cleanup for computed block. Attempting to return to '${originalBranchName}'.`); try { (0, child_process_1.execSync)(`git checkout -q "${originalBranchName}"`, { cwd: CWD, stdio: 'ignore', }); } catch (coErr) { console.warn(`Warning: Failed to checkout original branch '${originalBranchName}' during cleanup. Current branch: ${currentBranchAfterOps}. Error: ${coErr.message}`); } } try { const branchesRaw = (0, child_process_1.execSync)('git branch', { cwd: CWD, ...execOpts, }); const branchesList = branchesRaw .split('\n') .map((b) => b.trim().replace(/^\* /, '')); if (branchesList.includes(tempComputeBranchName)) { (0, child_process_1.execSync)(`git branch -q -D "${tempComputeBranchName}"`, { cwd: CWD, stdio: 'ignore', }); } } catch (deleteBranchError) { console.warn(`Warning: Failed to delete temporary branch '${tempComputeBranchName}' during cleanup for computed block. May require manual cleanup. ${deleteBranchError.message}`); } } } } else { const actualIntermediateFileName = `${branchName.replace(/[/\\]/g, '-')}${constants_1.TAYLORED_FILE_EXTENSION}`; const actualIntermediateFilePath = path.join(tayloredDir, actualIntermediateFileName); try { await fs.access(actualIntermediateFilePath); const message = `CRITICAL ERROR: Intermediate file ${actualIntermediateFilePath} (derived from branch name '${branchName}') already exists. 'handleSaveOperation' would overwrite this file. Please remove or rename it to ensure a clean state.`; console.error(message); throw new Error(message); } catch (error) { if (error.code !== 'ENOENT') { throw error; } } try { await fs.access(targetTayloredFilePath); const message = `CRITICAL ERROR: Target file ${targetTayloredFilePath} already exists. Please remove or rename it.`; console.error(message); throw new Error(message); } catch (error) { if (error.code !== 'ENOENT') { throw error; } } const contentUpToMatch = fileContent.substring(0, matchInfo.index); const startLineNum = contentUpToMatch.split('\n').length; const matchLinesCount = scriptContentWithTags.split('\n').length; const tempBranchName = `temp-taylored-${numero}-${Date.now()}`; try { (0, child_process_1.execSync)(`git checkout -b ${tempBranchName}`, { cwd: CWD, ...execOpts, }); const gitignorePath = path.join(CWD, '.gitignore'); await fs.writeFile(gitignorePath, constants_1.TAYLORED_DIR_NAME + '\n'); (0, child_process_1.execSync)(`git add .gitignore`, { cwd: CWD, ...execOpts }); const currentFileLines = (await fs.readFile(originalFilePath, 'utf-8')).split('\n'); currentFileLines.splice(startLineNum - 1, matchLinesCount); await fs.writeFile(originalFilePath, currentFileLines.join('\n')); (0, child_process_1.execSync)(`git add "${originalFilePath}"`, { cwd: CWD, ...execOpts }); (0, child_process_1.execSync)(`git commit -m "Temporary: Remove block ${numero} from ${path.basename(originalFilePath)}"`, { cwd: CWD, ...execOpts }); const relativeOriginalFilePath = path.relative(CWD, originalFilePath); const diffCommand = `git diff --exit-code "${originalBranchName}" HEAD -- "${relativeOriginalFilePath}"`; let diffContentForFile = ''; try { (0, child_process_1.execSync)(diffCommand, { cwd: CWD, encoding: 'utf8', stdio: 'pipe', }); } catch (e) { if (e.status === 1 && typeof e.stdout === 'string') { diffContentForFile = e.stdout; } else { console.error(`CRITICAL ERROR: Failed to generate diff for non-compute block ${numero} (removal vs original branch '${originalBranchName}').`); if (e.message) console.error(` Error message: ${e.message}`); if (e.stderr) console.error(' STDERR:\n' + e.stderr.toString().trim()); if (e.stdout) console.error(' STDOUT:\n' + e.stdout.toString().trim()); throw e; } } const analysis = (0, utils_1.analyzeDiffContent)(diffContentForFile); if (!analysis.success) { console.error(`CRITICAL ERROR: Failed to analyze diff content for non-compute block ${numero}. ${analysis.errorMessage}`); throw new Error(`Diff analysis failed for non-compute block ${numero}.`); } if (analysis.isPure && analysis.deletions > 0 && analysis.additions === 0) { await fs.writeFile(targetTayloredFilePath, diffContentForFile); console.log(`Successfully created ${targetTayloredFilePath} for block ${numero} from ${originalFilePath} (block removal vs original branch '${originalBranchName}')`); } else if (analysis.isPure && analysis.additions === 0 && analysis.deletions === 0) { await fs.writeFile(targetTayloredFilePath, ''); console.log(`Block removal for ${numero} in ${originalFilePath} resulted in no textual changes against original branch '${originalBranchName}'. Empty taylored file created: ${targetTayloredFilePath}`); } else { console.error(`CRITICAL ERROR: Diff for non-compute block ${numero} (removal vs original branch '${originalBranchName}') was not as expected (purely deletions or no change).`); console.error(` Additions: ${analysis.additions}, Deletions: ${analysis.deletions}, IsPure: ${analysis.isPure}`); const maxDiffPreviewLength = 1000; const diffPreview = diffContentForFile.length > maxDiffPreviewLength ? diffContentForFile.substring(0, maxDiffPreviewLength) + '\n... (diff truncated)' : diffContentForFile; if (diffContentForFile.trim()) console.error(` Diff content:\n${diffPreview}`); throw new Error(`Unexpected diff characteristics for non-compute block ${numero}.`); } totalBlocksProcessed++; } catch (error) { console.error(`CRITICAL ERROR: Failed to process block ${numero} from ${originalFilePath}.`); if (error.message && !error.message.includes('Unexpected diff characteristics')) console.error(`Error message: ${error.message}`); throw error; } finally { try { (0, child_process_1.execSync)(`git checkout "${originalBranchName}"`, { cwd: CWD, stdio: 'ignore', }); } catch (checkoutError) { console.warn(`Warning: Failed to checkout original branch '${originalBranchName}' during cleanup. May require manual cleanup. ${checkoutError.message}`); } try { (0, child_process_1.execSync)(`git branch -D "${tempBranchName}"`, { cwd: CWD, stdio: 'ignore', }); } catch (deleteBranchError) { console.warn(`Warning: Failed to delete temporary branch '${tempBranchName}' during cleanup. May require manual cleanup. ${deleteBranchError.message}`); } } } } } if (asyncScriptPromises.length > 0) { console.log(`Executing ${asyncScriptPromises.length} asynchronous compute block(s) in parallel...`); const results = await Promise.allSettled(asyncScriptPromises); let succeededCount = 0; let failedCount = 0; results.forEach((result, index) => { const blockIdentifier = `async block (index ${index})`; if (result.status === 'fulfilled') { console.log(`Asynchronous task for ${blockIdentifier} completed successfully.`); succeededCount++; } else { console.error(`Asynchronous task for ${blockIdentifier} failed: ${result.reason}`); failedCount++; } }); console.log(`All asynchronous tasks have completed. Succeeded: ${succeededCount}, Failed: ${failedCount}.`); } if (totalBlocksProcessed === 0) { console.log('No taylored blocks found matching the criteria in any of the scanned files.'); } else { if (asyncScriptPromises.length > 0) { console.log(`Finished processing. Initiated ${totalBlocksProcessed} taylored block(s). See async summary for completion details.`); } else { console.log(`Finished processing. Successfully created ${totalBlocksProcessed} taylored file(s).`); } } }