Я много работаю с проектами на веб-стеке и параллельно активно использую нейросети.
Со временем стало ясно: чтобы ИИ помогал не «вообще по PHP», а по конкретному проекту, ему нужен нормальный контекст. Не один файл, не случайный фрагмент, а полноценный срез кода.

В какой-то момент меня это достало настолько, что я сел и сделал отдельный инструмент — scan2json.
Это маленький PHP-репозиторий, который:

  • в одну сторону: сканирует проект и превращает его в JSON/JSONL;

  • в другую: по этому JSONL умеет собрать обратно структуру папок и файлов.

Репозиторий лежит на GitHub: https://github.com/simai/scan2json. В статье расскажу, зачем я его делал, как он устроен и как им можно пользоваться у себя.

Зачем вообще городить JSON вокруг кода

Если вы хоть раз пытались обсуждать реальный проект с ИИ, сценарий примерно такой.

  1. Открываем ChatGPT или другой LLM.

  2. Копируем в чат один файл.

  3. Получаем ответ.

  4. Выясняется, что нужен ещё конфиг, ещё шаблон, ещё один класс.

  5. Начинается бесконечный копипаст и объяснения «где что лежит».

Проблема в этом месте не в модели.
Проблема в том, что мы даём ей мозаику из обрывков, а не картину проекта.

Я хотел другого:

  • один раз собрать целостный снимок проекта;

  • чтобы в этом снимке были:

    • пути до файлов,

    • структура папок,

    • сам код;

  • и дальше уже разговаривать с ИИ, опираясь на этот снимок, а не таская куски руками.

Плагины в IDE помогают, но у них есть ограничения:

  • они живут внутри конкретного редактора;

  • не всегда удобно делиться этим контекстом с кем-то ещё (менеджером, аналитиком, подрядчиком);

  • не всегда просто привязать это к ассистентам GPT, которые работают через веб-интерфейс и файлы.

Мне хотелось иметь отдельный артефакт — файл или набор файлов, который:

  • можно положить в репозиторий,

  • отправить ассистенту,

  • отдать другому разработчику,

  • скормить своему RAG-сервису.

Так родилась идея scan2json.

Что такое scan2json

Вся логика собрана в два файла:

  • scan.php — сканирует проект в JSON/JSONL;

  • restore.php — по JSONL разворачивает проект обратно в отдельную папку.

Оба скрипта работают через браузер и живут там же, где живёт ваш проект (под DOCUMENT_ROOT).
Bitrix обязателен не всегда: если он есть, можно использовать проверку на админа; если нет — используется обычный пароль.

Идеология простая:

  • scan.php — я запускаю, чтобы «сфотографировать» проект:

    • выбираю, какую папку сканировать;

    • при необходимости выкидываю лишнее;

    • получаю JSONL/JSON + архивы;

  • restore.php — я запускаю, когда:

    • хочу из этого JSONL собрать отдельную «лёгкую» копию проекта;

    • или передать кому-то набор JSONL + restore и дать ему возможность развернуть структуру у себя.

Дальше этим JSON можно кормить ассистентов, использовать в своих инструментах или сохранять как снапшот релиза.

Основные части инструмента

Я пойду от пользовательского сценария: что видит человек на экране и какие сущности за этим стоят.

Выбор корневой папки

Первое, что нужно сделать в scan.php, — выбрать, откуда сканировать.

Я сделал два варианта.

1. Ввести путь руками

Простой режим: у меня есть поле scan_folder.
Я пишу туда, например:

  • / — весь сайт целиком;

  • /local — кастомный код в Bitrix;

  • /bitrix/modules/fileman;

  • /project/app — если это не Bitrix, а условный PHP-проект.

Этот способ удобен, когда я уже знаю, где лежит нужный модуль.

2. Выбрать из списка

Если путь не помню или лень вспоминать, включаю режим «Select from list».
Там появляется мини-навигация:

  • строка «Current folder: …»;

  • кнопка «Go up one level»;

  • выпадающий список подпапок текущей директории.

Я просто кликаю по подпапкам и «проваливаюсь» туда, пока не дойду до нужного уровня.
Текущее положение хранится в сессии, так что можно туда-сюда ходить и подбирать правильный корень.

В обоих случаях в итоге получаю одну конкретную папку, которая будет корнем скана.

Тонкая настройка: кнопка Choose items…

Следующая фишка — возможность точно сказать, что включать, а что нет.

По умолчанию сканер идёт по всем вложенным папкам. Плюс в коде есть глобальные исключения (vendor, upload, какие-то служебные директории, бинарные файлы и т.п.).

Но бывают ситуации:

  • нужно сканировать весь модуль, но без конкретной тяжёлой папки;

  • нужно исключить пару больших файлов;

  • хочется аккуратно «обрезать» верхний уровень.

Для этого я добавил кнопку Choose items….

Как это работает:

  1. Я выбираю корневую папку (любой из двух способов).

  2. Нажимаю Choose items….

  3. Открывается панель со списком всех подпапок и файлов первого уровня внутри корня.

  4. Напротив каждого пункта — чекбокс.

  5. По умолчанию всё включено.

  6. Я снимаю галочки с того, что не хочу отдавать в JSON:

    • отдельные папки,

    • отдельные файлы.

  7. Нажимаю Apply selection.

Дальше сканер уже учитывает этот выбор:

  • на первом уровне он обходит только выбранные элементы,

  • внутрь выбранной папки заходит целиком и дальше работает как раньше,

  • поверх этого работают глобальные исключения (excludeDirs, excludeFiles и т.д.).

Если я никогда не нажимаю Choose items…, скрипт ведёт себя как в самой первой версии — сканирует всё дерево, кроме общих исключений.

Куда складывается результат

Всё, что связано со сканом, кладётся в папку scan_tmp под DOCUMENT_ROOT:

  • туда пишет временный список файлов,

  • туда же складывает JSONL, JSON, части и ZIP-архивы.

На странице видно, есть ли готовые данные, и есть отдельная кнопка Delete data — она удаляет содержимое scan_tmp и даёт начать с чистого листа.

Форматы вывода: JSONL, JSON и части

На выходе меня интересовало сразу несколько сценариев:

  • работать с JSONL в своих пайплайнах;

  • загружать полный JSON/части в GPT-ассистентов (там нужен один валидный JSON);

  • не думать о лимитах ассистентов по размеру файла.

JSONL

Это основной формат для «машинной» обработки.

Каждая строка — отдельный файл:

{"file":"local/components/app/orders/class.php","content":"..."}

Плюсы:

  • можно читать построчно, не держа всё в памяти;

  • удобно обрабатывать большими порциями;

  • хорошо ложится на любые стриминговые сценарии и индексы.

Я использую JSONL, если хочу:

  • написать свой скрипт/агента, который анализирует проект;

  • делать векторизацию кода и складывать всё это в базу;

  • комбинировать это с RAG-подходами.

JSON (цельный)

Многим внешним инструментам нужен один JSON-документ.
Например, GPT-ассистентам при загрузке файлов.

Поэтому я делаю ещё один вариант — массив:

[
  {"file":"...","content":"..."},
  {"file":"...","content":"..."}
]

Этот формат удобно:

  • грузить прямо в ассистента как файл;

  • хранить снепшоты релиза;

  • использовать в тех местах, где JSONL не поддерживается.

JSON по частям

Для больших проектов один JSON может оказаться слишком объёмным.
У ассистентов есть лимиты по размеру файла и по токенам.

Поэтому я добавил автоматическую нарезку:

  • при достижении примерно TOKEN_MAX слов текущий JSON закрывается;

  • создаётся следующая часть;

  • итогом получается несколько файлов:

    • project_1.json, project_2.json, project_3.json и т.п.

Важный момент: режется по строкам/записям, а не по байтам.
То есть каждый объект {file, content} всегда целиком внутри одного файла.

Плюс сразу собирается ZIP со всеми частями — так удобно его скачать и загрузить в ассистента по одному файлу.

ZIP-архивы и подсчёт «токенов»

Дополнительно скрипт делает:

  • ZIP с JSONL;

  • ZIP с JSON;

  • ZIP с JSON-частями.

И выводит примерное количество «токенов» (по факту — слов) в данных.
Это не точный счётчик конкретной модели, но хороший ориентир по масштабу проекта.

Обратная сторона: restore.php и восстановление из JSONL

После того как я какое-то время пользовался только сканером, стало не хватать обратной операции.

Возникли сценарии:

  • «хочу у себя локально поднять минимальную копию проекта из снапшота»;

  • «хочу отдать подрядчику JSONL + скрипт, чтобы он сам развернул структуру у себя»;

  • «нужно быстро собрать песочницу только из тех файлов, которые попали в скан».

Так появился второй скрипт — restore.php.

Что он делает

В двух словах:

  • берёт JSONL, который сделал scan.php;

  • для каждой строки с {file, content}:

    • нормализует путь;

    • создаёт нужную папку;

    • записывает файл в выбранную директорию.

Важно:

  • restore.php не пытается быть полноценным бэкапом;

  • он честно восстанавливает только то, что было в JSONL:

    • если вы исключили vendor, в восстановленный проект он не попадёт;

    • если вы выкинули часть модулей через Choose items…, их тоже не будет.

То есть это именно восстановление фильтрованного среза, а не «верни мне прод ровно как был».

Как им пользоваться

Процесс простой:

  1. Кладу restore.php туда же, где scan.php.

  2. Открываю в браузере.

  3. Прохожу авторизацию:

    • либо через Bitrix-админа (если так настроено),

    • либо по паролю.

  4. Выбираю исходный JSONL:

    • либо из списка файлов в scan_tmp,

    • либо указываю путь вручную.

  5. Задаю целевую папку:

    • это директория под DOCUMENT_ROOT, например restored_project;

    • если её нет — скрипт создаст.

  6. Включаю или выключаю опции:

    • Dry run — просто посчитать, что было бы создано/перезаписано, без реальных записей;

    • Overwrite existing files — разрешить перезапись уже существующих файлов в целевой папке.

  7. Запускаю.

Скрипт идёт по JSONL построчно, аккуратно проверяет:

  • что путь относительный;

  • что нет попыток выйти через ..;

  • что конечный путь остаётся внутри целевой директории.

По итогу выдаёт статистику:

  • сколько строк обработано;

  • сколько валидных записей;

  • сколько файлов создано;

  • сколько перезаписано;

  • сколько пропущено;

  • сколько записей были некорректны.

Для чего это удобно

На практике restore.php оказался полезен в таких ситуациях:

  • Лёгкая копия проекта для ИИ-экспериментов

    Не хочется тянуть продовый проект со всеми артефактами.
    Я делаю скан с аккуратными исключениями и разворачиваю этот срез отдельно.
    Там можно спокойно переписывать код, давать доступ ассистентам, ставить инструменты.

  • Передача проекта внешнему разработчику

    Вместо живого доступа к репо/серверу я могу:

    • сделать JSONL только из нужных частей,

    • отправить подрядчику JSONL + restore.php,

    • он у себя разворачивает структуру и работает.

  • Минимальный репро-проект

    Если баг живёт в каком-то модуле, можно:

    • отсканировать только этот модуль,

    • развернуть его в отдельную папку,

    • добавить нужные зависимости,

    • и воспроизводить проблему уже в уменьшенном окружении.

Как это всё используется на практике (мой рабочий цикл)

Пример того, как я живу с этим инструментом день-в-день.

Сценарий 1. Обсудить с ИИ доработку в существующем проекте

  1. Выбираю проект и добавляю scan.php (если его там ещё нет).

  2. Настраиваю:

    • исключения vendor, upload и то, что точно не нужно;

    • ограничиваю корень до нужного модуля;

    • при необходимости через Choose items… выкидываю тяжёлые или неинтересные папки.

  3. Запускаю скан. Получаю JSONL.

  4. Пишу небольшой скрипт/агента, который:

    • читает JSONL,

    • ищет нужные файлы по маскам,

    • собирает контекст и формирует запрос к модели.

  5. Дальше мы с ИИ обсуждаем уже не абстрактный PHP, а конкретный проект:
    модель реально видит файлы, о которых я говорю.

Сценарий 2. Ассистент «по проекту» в GPT

  1. Беру JSON-части (если проект большой) или один JSON-файл.

  2. Создаю GPT-ассистента.

  3. Загружаю эти файлы как «знания» ассистента.

  4. В инструкциях пишу, что это конкретный проект, и объясняю, как с ним работать.

  5. Разработчик приходит к ассистенту и спрашивает:

    • «Где у нас реализована регистрация?»

    • «Какие классы отвечают за оплату?»

    • «Помоги добавить ещё один статус заказа и не забыть про все места».

Ассистент отвечает уже на основе реального кода, а не по памяти модели.

Сценарий 3. Аудит legacy или чужого проекта

  1. Кидаю scan.php на сервер (лучше в стейджинг/копию).

  2. Сканирую нужную часть проекта.

  3. Получаю JSONL, вытаскиваю его к себе.

  4. Пользуюсь либо:

    • ассистентом, либо

    • своим скриптом, который бегает по JSONL.

Можно попросить ИИ:

  • описать архитектуру по директориям;

  • найти «подозрительные» место в коде;

  • составить список технического долга по модулям.

Сценарий 4. Восстановить срез как отдельный проект

  1. Беру уже готовый JSONL (например, старый снапшот релиза).

  2. Через restore.php разворачиваю это в restored_project/.

  3. Получаю отдельную структуру:

    • с теми же путями;

    • но без того, что я выкидывал при скане.

Дальше можно:

  • подключить туда свои инструменты;

  • дать доступ ассистентам;

  • использовать как «песочницу» для экспериментов.

Как попробовать у себя

Минимальный путь такой.

  1. Клонировать репозиторий

    git clone https://github.com/simai/scan2json.git
    
  2. Положить scan.php и restore.php в проект

    Например, в корень сайта под DOCUMENT_ROOT.
    Я обычно не коммичу их в основной репозиторий проекта, а держу только на сервере/копии.

  3. Настроить доступ

    В начале файлов есть константы:

    • ACCESS_BITRIX — использовать ли проверку через Bitrix-админа;

    • ACCESS_PASSWORD — пароль для простого режима.

    Я обязательно меняю пароль с дефолтного и не выкладываю скрипты наружу без защиты.

  4. Открыть scan.php в браузере

    • пройти авторизацию;

    • выбрать папку;

    • при необходимости нажать Choose items…;

    • запустить скан.

  5. Скачать JSONL/JSON/части

    Посмотреть, где появился scan_tmp, и забрать оттуда файлы.

  6. При желании — протестировать restore.php

    На отдельной тестовой директории:

    • указать JSONL;

    • задать новую папку;

    • сначала сделать Dry run, потом настоящий запуск.

Не обязательно серьёзно интегрировать инструмент: достаточно одной тестовой сессии, чтобы почувствовать, «заходит» тебе такой подход или нет.

Финал

Для меня scan2json — живой эксперимент, который родился из реальной боли:
я устал объяснять ИИ проекты по кусочкам.

Сейчас это уже не просто скрипт «на коленке», а небольшой набор инструментов:

  • scan.php — чтобы снять структурированный снимок проекта в JSON/JSONL;

  • restore.php — чтобы по этому снимку собрать отдельную копию.

Я сам активно пользуюсь им в своей работе и периодически его дорабатываю.
Если тебе эта идея откликается — можно:

  • взять репозиторий,

  • попробовать на своём проекте,

  • и, если будут мысли, завести issue или PR.

Буду рад любым комментариям, предложениям по улучшению и даже просто фидбеку в духе «попробовал — вот где больно, а вот где зашло».

Комментарии (1)


  1. ktibr0
    06.12.2025 11:56

    Хм...спасибо, весьма интересная идея.

    Натолкнуло на размышления....

    Читал, что модели лучше понимают запрос, контекст, если им давать в json виде.

    Если развить идею, то сам кодинг/вайбкодинг можно начинать с просьбы сформировать структуру проекта в виде json, в котором описать не только файловую структуру, но функции, параметры вызовов, переменные и т.д.

    Ни разу не программист, может это так и делается во взрослых проектах, но описанную выше логику еще не встречал, попробую применить, ибо даже в курсоре млдель часто задваивает функции, или вызывает их с отсутствующими переменными, ну и, соответственно, просить модель перед самим кодированием сверять мысли со структурой, описать изменения сначала в json и после одобрения, уже непосредственно кодить, дополняя json.