@codewithmehmet/paul-cli
Version:
Intelligent project file scanner and Git change tracker with interactive interface
138 lines (134 loc) • 4.59 kB
JavaScript
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`;
}