/
kochenov
/
universal-modular
Обзор
Документация
Войти
/
kochenov
/
universal-modular
Код
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
scripts/extract_section.py
207 строк
7 KB
Kochenov Dmitry
feat(foundation): Шаг 1.5 — Создать pyproject.toml + ruff.toml + pre-commit конфиг
10 июл 2026, 16:06
10 июл 2026, 16:06
e52daa8
Код
Авторство
О чём код?
#!/usr/bin/env python3 """ Извлечение секций из больших Markdown-файлов (аудит v9). Проблема: 00-agent-protocol.md разросся до 77KB / 1324 строк. Чтение его целиком переполняет контекстное окно AI-агента. Решение: агент читает ТОЛЬКО нужные секции (§), а не весь файл. Запуск: # Извлечь §7.4 (стратегия main) uv run python scripts/extract_section.py .agent/rules/00-agent-protocol.md 7.4 # Извлечь §15 (полностью — все подсекции) uv run python scripts/extract_section.py .agent/rules/00-agent-protocol.md 15 # Извлечь несколько секций uv run python scripts/extract_section.py .agent/rules/00-agent-protocol.md 7.4 7.5 # Найти секцию по ключевому слову uv run python scripts/extract_section.py .agent/rules/00-agent-protocol.md --grep "loop detector" # Показать оглавление файла (список всех ## и ### заголовков) uv run python scripts/extract_section.py .agent/rules/00-agent-protocol.md --toc Exit codes: 0 — секция найдена и выведена 1 — секция не найдена 2 — файл не существует / ошибка """ import argparse import re import sys from pathlib import Path def extract_section(filepath: Path, section_nums: list[str]) -> str: """Извлечь секции по номерам (например, ['7.4'] или ['15']).""" try: content = filepath.read_text(encoding='utf-8') except Exception as e: print(f'ERROR: {e}', file=sys.stderr) return '' lines = content.split('\n') result = [] current_section = None capturing = False for line in lines: # Найти заголовки вида ## 7.4. ... или ### 7.4.1. ... m = re.match(r'^(#{1,6})\s+(\d+(?:\.\d+)*)(?:\.\s|\s)', line) if m: num = m.group(2) # Определяем, нужно ли захватывать эту секцию capturing = False for target in section_nums: # Точное совпадение (например, 7.4) или подсекция (например, 7.4.1, 7.4.2) if num == target or num.startswith(target + '.'): capturing = True # Если target = "15" и num = "15.1" — это подсекция, захватываем break if capturing: current_section = num result.append(line) continue else: # Текущая секция закончилась current_section = None continue # Если мы в захватываемой секции — добавляем строку if current_section is not None: result.append(line) return '\n'.join(result).rstrip() + '\n' def grep_sections(filepath: Path, pattern: str) -> str: """Найти секции, содержащие ключевое слово.""" try: content = filepath.read_text(encoding='utf-8') except Exception as e: print(f'ERROR: {e}', file=sys.stderr) return '' lines = content.split('\n') current_section_header = None current_section_lines = [] matching_sections = [] for line in lines: # Заголовок секции m = re.match(r'^(#{1,6})\s+(\d+(?:\.\d+)*)(?:\.\s|\s)(.+)$', line) if m: # Сохраняем предыдущую секцию if current_section_lines: section_text = '\n'.join(current_section_lines) if pattern.lower() in section_text.lower(): matching_sections.append(section_text) current_section_header = line current_section_lines = [line] else: if current_section_header: current_section_lines.append(line) # Последняя секция if current_section_lines: section_text = '\n'.join(current_section_lines) if pattern.lower() in section_text.lower(): matching_sections.append(section_text) return '\n\n---\n\n'.join(matching_sections) + '\n' if matching_sections else '' def show_toc(filepath: Path) -> str: """Показать оглавление файла (все заголовки с номерами секций).""" try: content = filepath.read_text(encoding='utf-8') except Exception as e: print(f'ERROR: {e}', file=sys.stderr) return '' lines = content.split('\n') toc = [] for line in lines: m = re.match(r'^(#{1,6})\s+(.+)$', line) if m: level = len(m.group(1)) title = m.group(2) indent = ' ' * (level - 1) toc.append(f'{indent}{title}') return '\n'.join(toc) + '\n' def main(): parser = argparse.ArgumentParser( description='Извлечение секций из больших MD-файлов', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Примеры: # Извлечь §7.4 из 00-agent-protocol.md %(prog)s .agent/rules/00-agent-protocol.md 7.4 # Извлечь §15 целиком (со всеми подсекциями 15.1, 15.2, ...) %(prog)s .agent/rules/00-agent-protocol.md 15 # Извлечь несколько секций %(prog)s .agent/rules/00-agent-protocol.md 7.4 7.5 16 # Найти секции со словом "loop" %(prog)s .agent/rules/00-agent-protocol.md --grep "loop detector" # Показать оглавление %(prog)s .agent/rules/00-agent-protocol.md --toc """) parser.add_argument('file', help='Путь к MD-файлу') parser.add_argument('sections', nargs='*', help='Номера секций (например, 7.4, 15)') parser.add_argument('--grep', help='Найти секции по ключевому слову') parser.add_argument('--toc', action='store_true', help='Показать оглавление') args = parser.parse_args() filepath = Path(args.file) if not filepath.is_absolute(): filepath = Path.cwd() / filepath if not filepath.exists(): print(f'ERROR: файл {filepath} не существует', file=sys.stderr) sys.exit(2) if args.toc: result = show_toc(filepath) if result: print(result) sys.exit(0) else: print('Файл пустой или нет заголовков.', file=sys.stderr) sys.exit(1) if args.grep: result = grep_sections(filepath, args.grep) if result: print(result) sys.exit(0) else: print(f'Секции со словом "{args.grep}" не найдены.', file=sys.stderr) sys.exit(1) if not args.sections: parser.print_help() sys.exit(2) result = extract_section(filepath, args.sections) if result.strip(): print(result) sys.exit(0) else: print(f'Секции {args.sections} не найдены в {filepath}.', file=sys.stderr) print('Используйте --toc для просмотра оглавления.', file=sys.stderr) sys.exit(1) if __name__ == '__main__': main()