UNPKG

project-context

Version:

Собирает структуру проекта и содержимое файлов в Markdown для формирования контекстных подсказок (prompts).

127 lines 5.24 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.projectContext = projectContext; const promises_1 = __importDefault(require("node:fs/promises")); const node_path_1 = __importDefault(require("node:path")); const micromatch_1 = __importDefault(require("micromatch")); const DEFAULT_INCLUDE = ['**/*.ts', '**/*.tsx', '**/*.vue', '**/*.json', '**/*.md', '.editorconfig']; const DEFAULT_EXCLUDE = ['node_modules', 'dist', 'build', 'out', 'coverage', '.git', 'package-lock.json', 'LICENSE']; function collectFiles(root) { return __awaiter(this, void 0, void 0, function* () { const files = []; function walk(current) { return __awaiter(this, void 0, void 0, function* () { const entries = yield promises_1.default.readdir(current, { withFileTypes: true }); for (const ent of entries) { const res = node_path_1.default.join(current, ent.name); if (ent.isDirectory()) { yield walk(res); } else if (ent.isFile()) { files.push(res); } } }); } yield walk(root); return files; }); } function toPosix(p) { return p.split(node_path_1.default.sep).join('/'); } function isPlainPattern(p) { // consider it "plain" if it contains no glob metacharacters return !/[*?\[\]{}]/.test(p); } function shouldInclude(relPath, include, exclude) { // exclude wins for (const ex of exclude) { if (!ex) continue; // direct micromatch support try { if (micromatch_1.default.isMatch(relPath, ex, { dot: true })) return false; } catch (_) { // ignore bad patterns } // treat plain patterns (no glob meta) as path prefixes if (isPlainPattern(ex)) { const normalized = ex.replace(/\\/g, '/'); if (relPath === normalized || relPath.startsWith(normalized + '/')) return false; } } // if include list is empty -> include everything (unless excluded) if (!include || include.length === 0) return true; for (const inc of include) { if (!inc) continue; try { if (micromatch_1.default.isMatch(relPath, inc, { dot: true })) return true; } catch (_) { // ignore } if (isPlainPattern(inc)) { const normalized = inc.replace(/\\/g, '/'); if (relPath === normalized || relPath.startsWith(normalized + '/')) return true; } } return false; } function fileSection(filePath, root) { return __awaiter(this, void 0, void 0, function* () { const rel = toPosix(node_path_1.default.relative(root, filePath)); const content = yield promises_1.default.readFile(filePath, 'utf8'); return `## ${rel}\n\n\`\`\`\n${content}\n\`\`\`\n`; }); } function joinParts(parts) { return parts.join('\n'); } // Reads directory recursively and returns markdown with filenames and contents. // Supports include/exclude similar to tsconfig (using micromatch for matching). function projectContext(dir_1) { return __awaiter(this, arguments, void 0, function* (dir, options = {}) { var _a; const root = dir || process.cwd(); const limit = (_a = options.limit) !== null && _a !== void 0 ? _a : 10000; const include = options.include ? options.include.slice() : DEFAULT_INCLUDE.slice(); const exclude = options.exclude ? options.exclude.slice() : DEFAULT_EXCLUDE.slice(); const files = yield collectFiles(root); const parts = []; // sort for deterministic output files.sort(); for (const f of files) { const rel = toPosix(node_path_1.default.relative(root, f)); if (!shouldInclude(rel, include, exclude)) continue; const section = yield fileSection(f, root); parts.push(section); } const out = joinParts(parts); if (out.length > limit) { throw new Error(`Output exceeds limit of ${limit} characters. Length is ${out.length}`); } return out; }); } //# sourceMappingURL=projectContext.js.map