← back to gregpriday__copytree

Function bodies 337 total

All specs Real LLM only Function bodies
GitHubUrlHandler.cloneRepository method · javascript · L110-L153 (44 LOC)
src/services/GitHubUrlHandler.js
  async cloneRepository() {
    // Detect default branch if needed
    if (!this.branch) {
      this.branch = await this.detectDefaultBranch();
      this.updateCacheKey();
      this.repoDir = path.join(this.cacheDir, this.cacheKey);
    }

    // Clean up existing directory if it exists
    if (fs.existsSync(this.repoDir)) {
      logger.warn('Repository cache already exists, removing...', {
        cacheDir: this.repoDir,
      });
      fs.rmSync(this.repoDir, { recursive: true, force: true });
    }

    const command = `git clone --branch ${this.branch} --single-branch ${this.repoUrl} ${this.repoDir}`;

    try {
      logger.info('Cloning repository', {
        repo: this.repoUrl,
        branch: this.branch,
      });

      execSync(command, { stdio: 'pipe' });

      logger.info('Repository cloned successfully', {
        repo: this.repoUrl,
        cacheDir: this.repoDir,
      });
    } catch (error) {
      // Handle specific errors
      if (error.message.includes('Authe
GitHubUrlHandler.updateRepository method · javascript · L158-L199 (42 LOC)
src/services/GitHubUrlHandler.js
  async updateRepository() {
    try {
      logger.info('Updating repository', {
        repo: this.repoUrl,
        cacheDir: this.repoDir,
      });

      // Fetch latest changes
      execSync('git fetch', { cwd: this.repoDir, stdio: 'pipe' });

      // Check if behind origin
      const behindCount = parseInt(
        execSync(`git rev-list HEAD..origin/${this.branch} --count`, {
          cwd: this.repoDir,
          encoding: 'utf8',
        }).trim(),
      );

      if (behindCount > 0) {
        logger.info(`Repository is ${behindCount} commits behind, updating...`);

        // Reset and clean to avoid conflicts
        execSync('git reset --hard HEAD', { cwd: this.repoDir, stdio: 'pipe' });
        execSync('git clean -fd', { cwd: this.repoDir, stdio: 'pipe' });

        // Pull latest changes
        execSync(`git pull origin ${this.branch}`, { cwd: this.repoDir, stdio: 'pipe' });

        logger.info('Repository updated successfully');
      } else {
        logger.info
GitHubUrlHandler.detectDefaultBranch method · javascript · L204-L222 (19 LOC)
src/services/GitHubUrlHandler.js
  async detectDefaultBranch() {
    try {
      const output = execSync(`git ls-remote --symref ${this.repoUrl} HEAD`, {
        encoding: 'utf8',
        stdio: 'pipe',
      });

      const match = output.match(/ref: refs\/heads\/([^\s]+)\s+HEAD/);
      const branch = match ? match[1] : 'main';

      logger.info('Detected default branch', { branch });
      return branch;
    } catch (error) {
      logger.warn('Failed to detect default branch, using "main"', {
        error: error.message,
      });
      return 'main';
    }
  }
GitHubUrlHandler.getCacheInfo method · javascript · L227-L236 (10 LOC)
src/services/GitHubUrlHandler.js
  getCacheInfo() {
    return {
      cacheKey: this.cacheKey,
      cacheDir: this.repoDir,
      exists: fs.existsSync(this.repoDir),
      repository: this.repoUrl,
      branch: this.branch,
      subPath: this.subPath,
    };
  }
GitHubUrlHandler.clearCache method · javascript · L241-L246 (6 LOC)
src/services/GitHubUrlHandler.js
  async clearCache() {
    if (fs.existsSync(this.repoDir)) {
      logger.info('Clearing repository cache', { cacheDir: this.repoDir });
      fs.removeSync(this.repoDir);
    }
  }
GitHubUrlHandler.getStats method · javascript · L251-L279 (29 LOC)
src/services/GitHubUrlHandler.js
  async getStats() {
    if (!fs.existsSync(this.repoDir)) {
      return null;
    }

    try {
      const stats = {
        lastFetch: fs.statSync(path.join(this.repoDir, '.git', 'FETCH_HEAD')).mtime,
        size: await this.getDirectorySize(this.repoDir),
        commitCount: parseInt(
          execSync('git rev-list --count HEAD', {
            cwd: this.repoDir,
            encoding: 'utf8',
          }).trim(),
        ),
        currentCommit: execSync('git rev-parse HEAD', {
          cwd: this.repoDir,
          encoding: 'utf8',
        })
          .trim()
          .substring(0, 7),
      };

      return stats;
    } catch (error) {
      logger.warn('Failed to get repository stats', { error: error.message });
      return null;
    }
  }
GitHubUrlHandler.execSync method · javascript · L261-L269 (9 LOC)
src/services/GitHubUrlHandler.js
          execSync('git rev-list --count HEAD', {
            cwd: this.repoDir,
            encoding: 'utf8',
          }).trim(),
        ),
        currentCommit: execSync('git rev-parse HEAD', {
          cwd: this.repoDir,
          encoding: 'utf8',
        })
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
GitHubUrlHandler.getDirectorySize method · javascript · L284-L300 (17 LOC)
src/services/GitHubUrlHandler.js
  async getDirectorySize(dirPath) {
    let size = 0;
    const files = await fs.readdir(dirPath);

    for (const file of files) {
      const filePath = path.join(dirPath, file);
      const stat = await fs.stat(filePath);

      if (stat.isDirectory() && file !== '.git') {
        size += await this.getDirectorySize(filePath);
      } else if (stat.isFile()) {
        size += stat.size;
      }
    }

    return size;
  }
GitleaksAdapter.constructor method · javascript · L27-L33 (7 LOC)
src/services/GitleaksAdapter.js
  constructor(options = {}) {
    this.binaryPath = options.binaryPath || 'gitleaks';
    this.configPath = options.configPath || null;
    this.extraArgs = options.extraArgs || [];
    this.logLevel = options.logLevel || 'fatal';
    this._available = null; // Cache availability check
  }
GitleaksAdapter.isAvailable method · javascript · L39-L52 (14 LOC)
src/services/GitleaksAdapter.js
  async isAvailable() {
    if (this._available !== null) {
      return this._available;
    }

    try {
      await execAsync(`"${this.binaryPath}" version`, { timeout: 5000 });
      this._available = true;
      return true;
    } catch (error) {
      this._available = false;
      return false;
    }
  }
GitleaksAdapter.scanString method · javascript · L62-L135 (74 LOC)
src/services/GitleaksAdapter.js
  async scanString(content, logicalPath = 'stdin') {
    // Check gitleaks version for compatibility
    const version = (await this.getVersion()) || '';
    const supportsStdin = /^8\.(19|[2-9]\d)\./.test(version) || /^9\./.test(version);
    const supportsRedactPercent = /^8\.(19|[2-9]\d)\./.test(version) || /^9\./.test(version);

    // Build command arguments with version-appropriate options
    const args = [];

    // Use stdin if available, otherwise use detect
    if (supportsStdin) {
      args.push('stdin');
    } else {
      args.push('detect', '--no-git');
    }

    args.push(
      '--report-format',
      'json',
      '--report-path',
      '-', // stdout
      '--no-banner',
      '--no-color',
      '--log-level',
      this.logLevel,
    );

    // Add redaction flag based on version
    if (supportsRedactPercent) {
      args.push('--redact=100'); // Newer versions support percentage
    } else {
      args.push('--redact'); // Older versions use boolean flag
    }
GitleaksAdapter._executeGitleaks method · javascript · L144-L212 (69 LOC)
src/services/GitleaksAdapter.js
  async _executeGitleaks(args, stdin) {
    return new Promise((resolve, reject) => {
      const child = spawn(this.binaryPath, args, {
        stdio: ['pipe', 'pipe', 'pipe'],
      });

      let stdout = '';
      let stderr = '';
      let timedOut = false;

      // Set timeout to kill hung scans
      const timeoutMs = 10000; // 10 seconds
      const timer = setTimeout(() => {
        timedOut = true;
        try {
          child.kill();
        } catch (e) {
          // Process may have already exited
        }
      }, timeoutMs);

      child.stdout.on('data', (data) => {
        stdout += data.toString();
      });

      child.stderr.on('data', (data) => {
        stderr += data.toString();
      });

      child.on('error', (error) => {
        clearTimeout(timer);
        reject(new Error(`Failed to spawn gitleaks: ${error.message}`));
      });

      child.on('close', (code) => {
        clearTimeout(timer);

        if (timedOut) {
          reject(new Error('Gitlea
GitleaksAdapter.getVersion method · javascript · L218-L227 (10 LOC)
src/services/GitleaksAdapter.js
  async getVersion() {
    try {
      const { stdout } = await execAsync(`"${this.binaryPath}" version`, { timeout: 5000 });
      // Parse version from output (format: "v8.19.0" or similar)
      const match = stdout.match(/v?(\d+\.\d+\.\d+)/);
      return match ? match[1] : stdout.trim();
    } catch (error) {
      return null;
    }
  }
InstructionsLoader.constructor method · javascript · L30-L40 (11 LOC)
src/services/InstructionsLoader.js
  constructor(basePath = process.cwd()) {
    this.basePath = basePath;
    this.logger = logger?.child ? logger.child('InstructionsLoader') : logger;

    // Instructions directories (in order of priority)
    this.userDir = path.join(os.homedir(), '.copytree/instructions');
    this.appDir = path.join(moduleDir, '..', 'templates', 'instructions');

    // Cache for loaded instructions
    this.instructionsCache = new Map();
  }
InstructionsLoader.load method · javascript · L47-L94 (48 LOC)
src/services/InstructionsLoader.js
  async load(name = 'default') {
    try {
      // Check cache first
      const cacheKey = name;
      if (this.instructionsCache.has(cacheKey)) {
        return this.instructionsCache.get(cacheKey);
      }

      // Search for instructions file
      const userPath = path.join(this.userDir, `${name}.md`);
      const appPath = path.join(this.appDir, `${name}.md`);

      let content;
      let foundPath;

      // Check user directory first (highest priority)
      if (await fs.pathExists(userPath)) {
        content = await fs.readFile(userPath, 'utf8');
        foundPath = userPath;
        if (this.logger?.debug) {
          this.logger.debug(`Loading instructions from user directory: ${userPath}`);
        }
      }
      // Then check app directory (built-in)
      else if (await fs.pathExists(appPath)) {
        content = await fs.readFile(appPath, 'utf8');
        foundPath = appPath;
      } else {
        const { InstructionsError, CopyTreeError } = await import('../utils/
Same scanner, your repo: https://repobility.com — Repobility
InstructionsLoader.list method · javascript · L100-L128 (29 LOC)
src/services/InstructionsLoader.js
  async list() {
    const instructionsSet = new Set();

    // Helper to add instructions from a directory
    const addInstructionsFromDir = async (dir) => {
      if (!(await fs.pathExists(dir))) return;

      try {
        const files = await fs.readdir(dir);
        for (const file of files) {
          const ext = path.extname(file).toLowerCase();
          if (ext === '.md') {
            const name = path.basename(file, path.extname(file)).toLowerCase();
            instructionsSet.add(name);
          }
        }
      } catch (error) {
        if (this.logger?.warn) {
          this.logger.warn(`Failed to read instructions directory ${dir}: ${error.message}`);
        }
      }
    };

    // Scan both directories
    await addInstructionsFromDir(this.userDir);
    await addInstructionsFromDir(this.appDir);

    return Array.from(instructionsSet).sort();
  }
InstructionsLoader.exists method · javascript · L135-L140 (6 LOC)
src/services/InstructionsLoader.js
  async exists(name = 'default') {
    const userPath = path.join(this.userDir, `${name}.md`);
    const appPath = path.join(this.appDir, `${name}.md`);

    return (await fs.pathExists(userPath)) || (await fs.pathExists(appPath));
  }
InstructionsLoader.clearCache method · javascript · L145-L150 (6 LOC)
src/services/InstructionsLoader.js
  clearCache() {
    this.instructionsCache.clear();
    if (this.logger?.debug) {
      this.logger.debug('Instructions cache cleared');
    }
  }
InstructionsLoader.getCacheStats method · javascript · L156-L161 (6 LOC)
src/services/InstructionsLoader.js
  getCacheStats() {
    return {
      size: this.instructionsCache.size,
      keys: Array.from(this.instructionsCache.keys()),
    };
  }
BaseTransformer.constructor method · javascript · L12-L30 (19 LOC)
src/transforms/BaseTransformer.js
  constructor(options = {}) {
    this.options = options;
    this.config = config();
    this.logger = options.logger || logger.child(this.constructor.name);

    // Enable caching based on configuration
    // Default to false for non-AI transformers; AI transformers should override
    this.cacheEnabled = options.noCache
      ? false
      : (options.cache ?? this.config.get('cache.transformations.enabled', false));
    this.cacheTTL = options.cacheTTL ?? this.config.get('cache.transformations.ttl', 86400);

    // Indicates whether this transformer performs heavy/slow operations
    // Heavy transformers will show progress during processing
    this.isHeavy = false;

    // Cache will be initialized on first use if caching is enabled
    this.cache = null;
  }
BaseTransformer.initializeCache method · javascript · L35-L45 (11 LOC)
src/transforms/BaseTransformer.js
  initializeCache() {
    if (!this.cache && this.cacheEnabled) {
      const transformerName = this.constructor.name.toLowerCase().replace('transformer', '');
      const specificConfig = `cache.transformations.${transformerName}`;

      this.cache = CacheService.create('transform', {
        enabled: this.config.get(`${specificConfig}.enabled`, this.cacheEnabled),
        defaultTtl: this.config.get(`${specificConfig}.ttl`, this.cacheTTL),
      });
    }
  }
BaseTransformer.transform method · javascript · L52-L98 (47 LOC)
src/transforms/BaseTransformer.js
  async transform(file) {
    try {
      // Validate input first
      this.validateInput(file);

      this.logger.debug(`Transforming ${file.path}`);
      const startTime = Date.now();

      // Initialize cache if needed
      this.initializeCache();

      // Check cache if enabled
      if (this.cacheEnabled && this.cache) {
        const cached = await this.getFromCache(file);
        if (cached) {
          this.logger.debug(`Cache hit for ${file.path}`);
          return cached;
        }
      }

      // Perform transformation
      const result = await this.doTransform(file);

      // Validate output
      this.validateOutput(result);

      // Cache result if enabled and cache exists
      if (this.cacheEnabled && this.cache) {
        await this.saveToCache(file, result);
      }

      const duration = Date.now() - startTime;
      this.logger.debug(`Transformed ${file.path} in ${duration}ms`);

      return result;
    } catch (error) {
      if (error instanceof Tran
BaseTransformer.validateInput method · javascript · L115-L122 (8 LOC)
src/transforms/BaseTransformer.js
  validateInput(file) {
    if (!file) {
      throw new TransformError('File object is required', this.constructor.name);
    }
    if (!file.path) {
      throw new TransformError('File path is required', this.constructor.name);
    }
  }
About: code-quality intelligence by Repobility · https://repobility.com
BaseTransformer.getCacheKey method · javascript · L140-L150 (11 LOC)
src/transforms/BaseTransformer.js
  getCacheKey(file) {
    const parts = [
      this.constructor.name,
      file.path,
      file.size || 0,
      file.modified ? file.modified.getTime() : 0,
      JSON.stringify(this.options),
    ];

    return crypto.createHash('sha256').update(parts.join(':')).digest('hex');
  }
BaseTransformer.getFromCache method · javascript · L157-L173 (17 LOC)
src/transforms/BaseTransformer.js
  async getFromCache(file) {
    if (!this.cache) return null;

    const cacheKey = this.getCacheKey(file);
    const cached = await this.cache.get(cacheKey);

    if (cached) {
      // Merge cached content back into file object
      return {
        ...file,
        ...cached,
        fromCache: true,
      };
    }

    return null;
  }
BaseTransformer.saveToCache method · javascript · L181-L199 (19 LOC)
src/transforms/BaseTransformer.js
  async saveToCache(file, result) {
    if (!this.cache) return;

    const cacheKey = this.getCacheKey(file);

    // Only cache successful transformations
    if (result.transformed) {
      // Don't cache the entire file object, just the transformation results
      const cacheData = {
        content: result.content,
        transformed: result.transformed,
        transformedBy: result.transformedBy,
        metadata: result.metadata,
        error: result.error,
      };

      await this.cache.set(cacheKey, cacheData, this.cacheTTL);
    }
  }
BaseTransformer.getMetadata method · javascript · L214-L221 (8 LOC)
src/transforms/BaseTransformer.js
  getMetadata() {
    return {
      name: this.constructor.name,
      description: this.description || 'No description available',
      supportedExtensions: this.supportedExtensions || [],
      options: this.options,
    };
  }
BaseTransformer.isTextContent method · javascript · L228-L241 (14 LOC)
src/transforms/BaseTransformer.js
  isTextContent(content) {
    if (typeof content === 'string') return true;

    if (Buffer.isBuffer(content)) {
      // Check first 512 bytes for null characters
      const sample = content.slice(0, Math.min(512, content.length));
      for (let i = 0; i < sample.length; i++) {
        if (sample[i] === 0) return false;
      }
      return true;
    }

    return false;
  }
BaseTransformer.detectEncoding method · javascript · L248-L263 (16 LOC)
src/transforms/BaseTransformer.js
  detectEncoding(buffer) {
    // Simple BOM detection
    if (buffer.length >= 3) {
      if (buffer[0] === 0xef && buffer[1] === 0xbb && buffer[2] === 0xbf) {
        return 'utf8';
      }
      if (buffer[0] === 0xff && buffer[1] === 0xfe) {
        return 'utf16le';
      }
      if (buffer[0] === 0xfe && buffer[1] === 0xff) {
        return 'utf16be';
      }
    }

    return 'utf8'; // Default
  }
BaseTransformer.formatBytes method · javascript · L270-L276 (7 LOC)
src/transforms/BaseTransformer.js
  formatBytes(bytes) {
    if (bytes === 0) return '0 B';
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }
TransformerRegistry.constructor method · javascript · L15-L28 (14 LOC)
src/transforms/TransformerRegistry.js
  constructor(options = {}) {
    this.transformers = new Map();
    this.extensionMap = new Map();
    this.mimeTypeMap = new Map();
    this.defaultTransformer = null;
    this.logger = logger.child('TransformerRegistry');

    // Traits system
    this.traits = new Map(); // transformer name -> traits
    this.validationEnabled = true;

    // Store config for potential use by transformers
    this.config = options.config || null;
  }
If a scraper extracted this row, it came from Repobility (https://repobility.com)
TransformerRegistry.register method · javascript · L37-L81 (45 LOC)
src/transforms/TransformerRegistry.js
  register(name, transformer, options = {}, traits = null) {
    if (this.transformers.has(name)) {
      this.logger.warn(`Overwriting existing transformer: ${name}`);
    }

    this.transformers.set(name, {
      transformer,
      options,
      priority: options.priority || 0,
    });

    // Register traits if provided
    if (traits) {
      this.traits.set(name, this._normalizeTraits(traits));
      this.logger.debug(`Registered traits for transformer: ${name}`, traits);
    }

    // Register extensions
    if (options.extensions) {
      options.extensions.forEach((ext) => {
        const normalizedExt = ext.startsWith('.') ? ext : `.${ext}`;
        if (!this.extensionMap.has(normalizedExt)) {
          this.extensionMap.set(normalizedExt, []);
        }
        this.extensionMap.get(normalizedExt).push(name);
      });
    }

    // Register MIME types
    if (options.mimeTypes) {
      options.mimeTypes.forEach((mimeType) => {
        if (!this.mimeTypeMap.has(mimeType)) {
TransformerRegistry.get method · javascript · L88-L94 (7 LOC)
src/transforms/TransformerRegistry.js
  get(name) {
    const entry = this.transformers.get(name);
    if (!entry) {
      throw new TransformError(`Transformer not found: ${name}`, name);
    }
    return entry.transformer;
  }
TransformerRegistry.getForFile method · javascript · L101-L131 (31 LOC)
src/transforms/TransformerRegistry.js
  getForFile(file) {
    const transformerNames = [];

    // Check by extension
    const ext = this.getExtension(file.path);
    if (ext && this.extensionMap.has(ext)) {
      transformerNames.push(...this.extensionMap.get(ext));
    }

    // Check by MIME type
    if (file.mimeType && this.mimeTypeMap.has(file.mimeType)) {
      transformerNames.push(...this.mimeTypeMap.get(file.mimeType));
    }

    // Get unique transformer names with highest priority
    const uniqueNames = [...new Set(transformerNames)];
    if (uniqueNames.length > 0) {
      const sorted = uniqueNames
        .map((name) => ({ name, ...this.transformers.get(name) }))
        .sort((a, b) => b.priority - a.priority);

      return this.get(sorted[0].name);
    }

    // Return default transformer
    if (this.defaultTransformer) {
      return this.get(this.defaultTransformer);
    }

    throw new TransformError(`No transformer found for file: ${file.path}`, 'unknown', file.path);
  }
TransformerRegistry.list method · javascript · L146-L159 (14 LOC)
src/transforms/TransformerRegistry.js
  list() {
    return Array.from(this.transformers.entries()).map(([name, entry]) => ({
      name,
      priority: entry.priority,
      extensions: Array.from(this.extensionMap.entries())
        .filter(([_, names]) => names.includes(name))
        .map(([ext]) => ext),
      mimeTypes: Array.from(this.mimeTypeMap.entries())
        .filter(([_, names]) => names.includes(name))
        .map(([mime]) => mime),
      isDefault: this.defaultTransformer === name,
      traits: this.traits.get(name) || null,
    }));
  }
TransformerRegistry.clear method · javascript · L172-L178 (7 LOC)
src/transforms/TransformerRegistry.js
  clear() {
    this.transformers.clear();
    this.extensionMap.clear();
    this.mimeTypeMap.clear();
    this.traits.clear();
    this.defaultTransformer = null;
  }
TransformerRegistry.validateDependencies method · javascript · L185-L240 (56 LOC)
src/transforms/TransformerRegistry.js
  validateDependencies() {
    const VISITING = 1;
    const DONE = 2;
    const state = new Map();
    const order = [];

    const getDeps = (name) => {
      const traits = this.traits.get(name);
      if (!traits || !traits.dependencies) {
        return [];
      }
      return Array.isArray(traits.dependencies) ? traits.dependencies : [];
    };

    const visit = (name, stack = []) => {
      const mark = state.get(name) || 0;

      if (mark === VISITING) {
        const cycle = [...stack, name].join(' -> ');
        throw new TransformError(`Circular dependency detected: ${cycle}`, 'CIRCULAR_DEPENDENCY');
      }

      if (mark === DONE) {
        return;
      }

      // Check if transformer exists
      if (!this.transformers.has(name)) {
        throw new TransformError(`Missing transformer dependency: ${name}`, 'MISSING_DEPENDENCY');
      }

      state.set(name, VISITING);
      stack.push(name);

      for (const dep of getDeps(name)) {
        // Dependencies can be 
TransformerRegistry.validatePlan method · javascript · L247-L296 (50 LOC)
src/transforms/TransformerRegistry.js
  validatePlan(stages) {
    if (!this.validationEnabled || !stages || stages.length === 0) {
      return { valid: true, issues: [], warnings: [] };
    }

    const issues = [];
    const warnings = [];

    // First, validate dependencies for circular references
    try {
      this.validateDependencies();
    } catch (error) {
      if (error.code === 'CIRCULAR_DEPENDENCY' || error.code === 'MISSING_DEPENDENCY') {
        issues.push({
          type: 'dependency_error',
          severity: 'error',
          message: error.message,
          transformers: stages,
        });
        return { valid: false, issues, warnings };
      }
      throw error;
    }

    // Check for conflicts between transformers
    for (let i = 0; i < stages.length; i++) {
      for (let j = i + 1; j < stages.length; j++) {
        const conflicts = this._checkConflicts(stages[i], stages[j]);
        issues.push(...conflicts);
      }
    }

    // Check ordering issues
    const orderingIssues = this._
TransformerRegistry.optimizePlan method · javascript · L303-L359 (57 LOC)
src/transforms/TransformerRegistry.js
  optimizePlan(stages) {
    if (!stages || stages.length <= 1) {
      return {
        optimized: stages || [],
        changes: [],
        reasoning: [],
      };
    }

    const optimized = [...stages];
    const changes = [];
    const reasoning = [];

    // Sort by dependency requirements (order-sensitive transformers first)
    const withTraits = optimized.map((name) => ({
      name,
      traits: this.traits.get(name) || {},
    }));

    // Move order-sensitive transformers to appropriate positions
    const orderSensitive = withTraits.filter((t) => t.traits.orderSensitive);
    const orderInsensitive = withTraits.filter((t) => !t.traits.orderSensitive);
    const heavy = withTraits.filter((t) => t.traits.heavy);

    // Rebuild order: order-sensitive first, then light operations, then heavy operations
    const reordered = [
      ...orderSensitive.filter((t) => !t.traits.heavy),
      ...orderInsensitive.filter((t) => !t.traits.heavy),
      ...heavy,
    ];

    const o
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
TransformerRegistry._normalizeTraits method · javascript · L382-L414 (33 LOC)
src/transforms/TransformerRegistry.js
  _normalizeTraits(traits) {
    const normalized = {
      inputTypes: traits.inputTypes || ['text'],
      outputTypes: traits.outputTypes || ['text'],
      idempotent: traits.idempotent ?? true,
      orderSensitive: traits.orderSensitive ?? false,
      dependencies: traits.dependencies || [],
      heavy: traits.heavy ?? false,
      stateful: traits.stateful ?? false,
      conflictsWith: traits.conflictsWith || [],
      requirements: traits.requirements || {},
      tags: traits.tags || [],
    };

    // Validate trait values
    if (!Array.isArray(normalized.inputTypes)) {
      normalized.inputTypes = [normalized.inputTypes];
    }
    if (!Array.isArray(normalized.outputTypes)) {
      normalized.outputTypes = [normalized.outputTypes];
    }
    if (!Array.isArray(normalized.dependencies)) {
      normalized.dependencies = [normalized.dependencies];
    }
    if (!Array.isArray(normalized.conflictsWith)) {
      normalized.conflictsWith = [normalized.conflictsWith];
    }
TransformerRegistry._checkConflicts method · javascript · L420-L467 (48 LOC)
src/transforms/TransformerRegistry.js
  _checkConflicts(transformer1, transformer2) {
    const issues = [];
    const traits1 = this.traits.get(transformer1);
    const traits2 = this.traits.get(transformer2);

    if (!traits1 || !traits2) {
      return issues; // Skip validation if traits not available
    }

    // Check explicit conflicts
    if (traits1.conflictsWith.includes(transformer2)) {
      issues.push({
        type: 'conflict',
        severity: 'error',
        message: `Transformer '${transformer1}' conflicts with '${transformer2}'`,
        transformers: [transformer1, transformer2],
      });
    }

    if (traits2.conflictsWith.includes(transformer1)) {
      issues.push({
        type: 'conflict',
        severity: 'error',
        message: `Transformer '${transformer2}' conflicts with '${transformer1}'`,
        transformers: [transformer1, transformer2],
      });
    }

    // Check input/output type compatibility
    const hasCompatibleTypes = traits1.outputTypes.some((output) =>
      traits2.in
TransformerRegistry._validateOrdering method · javascript · L473-L515 (43 LOC)
src/transforms/TransformerRegistry.js
  _validateOrdering(stages) {
    const issues = [];

    for (let i = 0; i < stages.length; i++) {
      const traits = this.traits.get(stages[i]);
      if (!traits) continue;

      // Check if order-sensitive transformer is placed appropriately
      if (traits.orderSensitive) {
        // Look for non-idempotent transformers before this one
        for (let j = 0; j < i; j++) {
          const prevTraits = this.traits.get(stages[j]);
          if (prevTraits && !prevTraits.idempotent) {
            issues.push({
              type: 'ordering',
              severity: 'warning',
              message: `Order-sensitive transformer '${stages[i]}' follows non-idempotent transformer '${stages[j]}', which may cause unpredictable results`,
              transformers: [stages[j], stages[i]],
            });
          }
        }
      }

      // Check if heavy transformer is placed optimally
      if (traits.heavy && i < stages.length - 2) {
        const remainingHeavy = stages.slice(i 
TransformerRegistry._validateResources method · javascript · L521-L550 (30 LOC)
src/transforms/TransformerRegistry.js
  _validateResources(stages) {
    const issues = [];
    const requiredResources = new Set();

    for (const stage of stages) {
      const traits = this.traits.get(stage);
      if (!traits) continue;

      // Check for required dependencies
      for (const dep of traits.dependencies) {
        requiredResources.add(dep);
      }

      // Check specific requirements
      if (traits.requirements.apiKey) {
        issues.push({
          type: 'missing_resource',
          severity: 'error',
          message: `Transformer '${stage}' requires an API key but none is configured`,
          transformers: [stage],
        });
      }

      if (traits.requirements.network) {
        // Could add network connectivity checks here
      }
    }

    return issues;
  }
TransformerRegistry._generateWarnings method · javascript · L556-L600 (45 LOC)
src/transforms/TransformerRegistry.js
  _generateWarnings(stages) {
    const warnings = [];

    // Check for too many heavy operations
    const heavyCount = stages.filter((name) => {
      const traits = this.traits.get(name);
      return traits && traits.heavy;
    }).length;

    if (heavyCount > 3) {
      warnings.push({
        type: 'performance',
        severity: 'warning',
        message: `Pipeline contains ${heavyCount} heavy transformers, which may impact performance`,
        suggestion: 'Consider reducing the number of AI or computationally intensive transformers',
      });
    }

    // Check for redundant transformers
    const duplicateTags = new Map();
    stages.forEach((name) => {
      const traits = this.traits.get(name);
      if (traits && traits.tags) {
        traits.tags.forEach((tag) => {
          if (!duplicateTags.has(tag)) {
            duplicateTags.set(tag, []);
          }
          duplicateTags.get(tag).push(name);
        });
      }
    });

    for (const [tag, transformers] of 
TransformerRegistry.createDefault method · javascript · L620-L712 (93 LOC)
src/transforms/TransformerRegistry.js
  static async createDefault(options = {}) {
    const registry = new TransformerRegistry();
    // Store config for potential use by transformers
    registry.config = options.config || null;

    // Register default transformers - using dynamic imports for better ESM compatibility
    const { default: FileLoaderTransformer } =
      await import('./transformers/FileLoaderTransformer.js');
    const { default: StreamingFileLoaderTransformer } =
      await import('./transformers/StreamingFileLoaderTransformer.js');
    const { default: BinaryTransformer } = await import('./transformers/BinaryTransformer.js');

    // File Loader - default transformer
    registry.register(
      'file-loader',
      new FileLoaderTransformer(),
      {
        isDefault: true,
        priority: 0,
      },
      {
        inputTypes: ['any'],
        outputTypes: ['text', 'binary'],
        idempotent: true,
        orderSensitive: false,
        heavy: false,
        stateful: false,
        dependen
BinaryTransformer.constructor method · javascript · L10-L58 (49 LOC)
src/transforms/transformers/BinaryTransformer.js
  constructor(options = {}) {
    super(options);
    this.description = 'Handles binary files with placeholder text or base64 encoding';
    this.supportedExtensions = [
      // Images
      '.jpg',
      '.jpeg',
      '.png',
      '.gif',
      '.bmp',
      '.webp',
      '.ico',
      '.svg',
      // Documents
      '.pdf',
      '.doc',
      '.docx',
      '.xls',
      '.xlsx',
      '.ppt',
      '.pptx',
      // Archives
      '.zip',
      '.tar',
      '.gz',
      '.rar',
      '.7z',
      '.bz2',
      // Executables
      '.exe',
      '.dll',
      '.so',
      '.dylib',
      '.app',
      // Media
      '.mp3',
      '.mp4',
      '.avi',
      '.mov',
      '.wmv',
      '.flv',
      '.webm',
      // Other
      '.db',
      '.sqlite',
      '.bin',
      '.dat',
    ];
  }
BinaryTransformer.doTransform method · javascript · L60-L83 (24 LOC)
src/transforms/transformers/BinaryTransformer.js
  async doTransform(file) {
    // Hard-exclude all non-image binary files
    const isImage = isImageExtension(file.path);

    if (!isImage) {
      // Non-image binary files are completely excluded (no output)
      return null;
    }

    // For image files, use configured action
    const action = this.config.get('copytree.binaryFileAction', 'placeholder');

    switch (action) {
      case 'skip':
        return null;

      case 'base64':
        return await this.transformToBase64(file);

      case 'placeholder':
      default:
        return this.transformToPlaceholder(file);
    }
  }
Same scanner, your repo: https://repobility.com — Repobility
BinaryTransformer.transformToPlaceholder method · javascript · L90-L110 (21 LOC)
src/transforms/transformers/BinaryTransformer.js
  transformToPlaceholder(file) {
    const placeholderText = this.config.get(
      'copytree.binaryPlaceholderText',
      '[Binary file not included]',
    );

    const fileInfo = [
      placeholderText,
      `Type: ${this.getFileType(file.path)}`,
      `Size: ${this.formatBytes(file.size || 0)}`,
    ].join('\n');

    return {
      ...file,
      content: fileInfo,
      transformed: true,
      transformedBy: this.constructor.name,
      isBinary: true,
      binaryAction: 'placeholder',
    };
  }
BinaryTransformer.transformToBase64 method · javascript · L117-L157 (41 LOC)
src/transforms/transformers/BinaryTransformer.js
  async transformToBase64(file) {
    const fs = await import('fs-extra');

    try {
      // Check size limit for base64 encoding
      const maxBase64Size = this.config.get('copytree.maxBase64Size', 1024 * 1024); // 1MB default

      if (file.size > maxBase64Size) {
        this.logger.warn(
          `File ${file.path} too large for base64 encoding (${this.formatBytes(file.size)})`,
        );
        return this.transformToPlaceholder(file);
      }

      const buffer = await fs.readFile(file.absolutePath);
      const base64 = buffer.toString('base64');

      // Add metadata comment
      const content = [
        '[Binary file encoded as base64]',
        `Type: ${this.getFileType(file.path)}`,
        `Size: ${this.formatBytes(file.size || 0)}`,
        'Encoding: base64',
        '',
        base64,
      ].join('\n');

      return {
        ...file,
        content,
        transformed: true,
        transformedBy: this.constructor.name,
        isBinary: true,
        bi
BinaryTransformer.getFileType method · javascript · L164-L208 (45 LOC)
src/transforms/transformers/BinaryTransformer.js
  getFileType(filePath) {
    const ext = path.extname(filePath).toLowerCase();

    const typeMap = {
      // Images
      '.jpg': 'JPEG Image',
      '.jpeg': 'JPEG Image',
      '.png': 'PNG Image',
      '.gif': 'GIF Image',
      '.bmp': 'Bitmap Image',
      '.webp': 'WebP Image',
      '.ico': 'Icon',
      '.svg': 'SVG Image',

      // Documents
      '.pdf': 'PDF Document',
      '.doc': 'Word Document',
      '.docx': 'Word Document',
      '.xls': 'Excel Spreadsheet',
      '.xlsx': 'Excel Spreadsheet',
      '.ppt': 'PowerPoint Presentation',
      '.pptx': 'PowerPoint Presentation',

      // Archives
      '.zip': 'ZIP Archive',
      '.tar': 'TAR Archive',
      '.gz': 'Gzip Archive',
      '.rar': 'RAR Archive',
      '.7z': '7-Zip Archive',

      // Media
      '.mp3': 'MP3 Audio',
      '.mp4': 'MP4 Video',
      '.avi': 'AVI Video',
      '.mov': 'QuickTime Video',

      // Other
      '.exe': 'Windows Executable',
      '.dll': 'Dynamic Link Library',
      '.so
‹ prevpage 4 / 7next ›