import fs from 'fs/promises';
import path from 'path';
import { z } from 'zod';
import { detectLanguages, getSignatures } from '../detectors/language.js';
import { detectEntryPoints } from '../detectors/entrypoint.js';
import { scanTree } from '../scanners/tree.js';
import { createError, ErrorCodes } from '../utils/errors.js';
/**
* Input schema for list_key_files tool
*/
export const listKeyFilesSchema = {
path: z.string().describe("Path to the project directory")
};
/**
* List key files in a project
* Returns categorized list of entry points, config files, and documentation
*/
export async function listKeyFiles({ path: projectPath }) {
// Validate path exists
try {
const stats = await fs.stat(projectPath);
if (!stats.isDirectory()) {
return createError(ErrorCodes.PATH_NOT_DIRECTORY);
}
} catch (err) {
if (err.code === 'ENOENT') {
return createError(ErrorCodes.PATH_NOT_FOUND);
}
if (err.code === 'EACCES') {
return createError(ErrorCodes.ACCESS_DENIED);
}
return createError(ErrorCodes.SCAN_ERROR, err.message);
}
try {
// Quick scan
const { files } = await scanTree(projectPath, {
maxDepth: 2,
maxFiles: 500
});
const sigs = await getSignatures();
// Detect languages for entry point detection
const languages = await detectLanguages(projectPath, files);
// Find entry points
const entryPoints = await detectEntryPoints(projectPath, files, languages);
// Find config files
const configFiles = files.filter(f => {
const basename = path.basename(f);
return sigs.configFiles.some(cf => basename === cf || f.endsWith(cf));
});
// Find doc files
const docFiles = files.filter(f => {
const basename = path.basename(f).toLowerCase();
return sigs.docFiles.some(df => {
if (df.endsWith('/')) {
// Directory pattern
return f.toLowerCase().startsWith(df.slice(0, -1));
}
return basename === df.toLowerCase();
});
});
// Build result (omit empty arrays)
const result = {};
if (entryPoints.length > 0) {
result.entry = entryPoints;
}
if (configFiles.length > 0) {
result.config = configFiles;
}
if (docFiles.length > 0) {
result.docs = docFiles;
}
return result;
} catch (err) {
return createError(ErrorCodes.SCAN_ERROR, err.message);
}
}