UNPKG

@codewithmehmet/paul-cli

Version:

Intelligent project file scanner and Git change tracker with interactive interface

138 lines (134 loc) 4.59 kB
import path from 'path'; import { getFileContent } from './file.js'; import { Scanner } from '../core/scanner.js'; import { LANGUAGE_MAP } from '../core/patterns.js'; export function formatSize(bytes) { if (bytes === 0) return '0B'; const units = ['B', 'KB', 'MB', 'GB']; const k = 1024; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${(bytes / k ** i).toFixed(i === 0 ? 0 : 1)}${units[i]}`; } export function formatDate(date = new Date()) { return date.toISOString().split('T')[0]; } export function getLanguage(filename) { const ext = path.extname(filename); return LANGUAGE_MAP[ext] || ext.slice(1) || 'txt'; } export async function generateOutput(options) { const { selectedFiles = [], includeContent = true, includeTree = true, allFiles = null, rootPath = process.cwd() } = options; const output = []; const projectName = path.basename(rootPath); output.push(`# Project Context: ${projectName}`); output.push(`Generated: ${formatDate()}`); output.push(''); // Project structure if (includeTree) { // Si on a allFiles, faire un arbre avec marqueurs if (allFiles && allFiles.length > 0) { output.push('## Complete Project Structure'); output.push('*[✓] = included, [✗] = excluded*'); output.push(''); const selectedSet = new Set(selectedFiles); const tree = generateTreeWithMarkers(allFiles, selectedSet); output.push('```'); output.push(...tree); output.push('```'); output.push(''); } else { // Sinon, arbre standard output.push('## Project Structure'); output.push(''); const scanner = new Scanner({ path: rootPath, includeContent: false }); const items = await scanner.scan(); const tree = generateTree(items); output.push('```'); output.push(...tree); output.push('```'); output.push(''); } } // Selected files list if (selectedFiles.length > 0) { output.push('## Selected Files'); output.push(''); output.push('```'); for (const filePath of selectedFiles) { const relativePath = path.relative(rootPath, filePath); output.push(`- ${relativePath}`); } output.push('```'); output.push(''); } // File contents if (includeContent && selectedFiles.length > 0) { output.push('## File Contents'); output.push(''); for (const filePath of selectedFiles) { const relativePath = path.relative(rootPath, filePath); output.push(`### ${relativePath}`); output.push(''); const content = getFileContent(filePath); const language = getLanguage(filePath); output.push(`\`\`\`${language}`); output.push(content); output.push('```'); output.push(''); } } return output.join('\n'); } export function generateTree(items, prefix = '') { const tree = []; items.forEach((item, index) => { const isLast = index === items.length - 1; const connector = isLast ? '└── ' : '├── '; const extension = isLast ? ' ' : '│ '; const sizeStr = item.size ? ` (${formatSize(item.size)})` : ''; const icon = item.type === 'directory' ? '📁' : '📄'; tree.push(`${prefix}${connector}${icon} ${item.name}${sizeStr}`); if (item.children) { const childTree = generateTree(item.children, prefix + extension); tree.push(...childTree); } }); return tree; } export function generateTreeWithMarkers(items, selectedSet, prefix = '') { const tree = []; // Helper pour vérifier si un item ou ses enfants sont sélectionnés const isSelectedOrHasSelectedChildren = item => { if (selectedSet.has(item.path)) return true; if (item.children) { return item.children.some(child => isSelectedOrHasSelectedChildren(child)); } return false; }; items.forEach((item, index) => { const isLast = index === items.length - 1; const connector = isLast ? '└── ' : '├── '; const extension = isLast ? ' ' : '│ '; const icon = item.type === 'directory' ? '📁' : '📄'; const marker = isSelectedOrHasSelectedChildren(item) ? ' [✓]' : ' [✗]'; tree.push(`${prefix}${connector}${icon} ${item.name}${marker}`); if (item.children) { const childTree = generateTreeWithMarkers(item.children, selectedSet, prefix + extension); tree.push(...childTree); } }); return tree; } export function generateOutputFilename(prefix = 'context') { const timestamp = formatDate().replace(/[:.]/g, '-'); return `${prefix}-${timestamp}.md`; }