Общее dev-окружение для проектов foxford: образ девконтейнера, AI-обвязка (скиллы, MCP-серверы, Hermes-роли) и скелет нового проекта — всё ставится на машину разработчика одной командой, без registry и ручной настройки.
Архитектура и внутреннее устройство платформы — в SPEC.md (нужно только если ты её поддерживаешь или расширяешь, не для повседневной работы).
Dev Container — открытая спецификация (за ней стоит VS Code, GitHub Codespaces, JetBrains): описание окружения разработки как Docker-образа, который редактор поднимает и подключается к нему, а код редактируется как обычно. Инструментарий, версии языков и зависимостей живут внутри контейнера, а не на хосте.
Мы взяли его за основу по трём причинам:
- Изоляция. Тулчейн проекта (ноды, компиляторы, глобальные CLI) не течёт в систему разработчика и не конфликтует с другими проектами на той же машине — у каждого свой контейнер.
- Воспроизводимость. Один и тот же образ у всех разработчиков.
- Одна команда, любая ОС. macOS, Linux, WSL —
install.shсам определяет ОС и доводит хост до состояния «докер есть, дальше редактор всё поднимет сам», без ручной настройки под каждую платформу отдельно.
# 1. Поставить платформу (клон + CLI в ~/.local/bin)
curl -fsSL https://raw.githubusercontent.com/foxford/ai-devcontainer/main/install.sh | bash
# 2. Проверить машину (docker, персист-каталоги, образ)
adc doctorТребования на машине: git, VS Code | Cursor (или совместимый редактор с
devcontainers). Всё остальное — docker, rsync, jq — install.sh проверит и
поставит сам под ОС хоста (macOS/Linux, включая WSL).
adc new my-service ~/Development/@foxford/my-service
cd ~/Development/@foxford/my-service && code .
# → Reopen in Container: образ dev-base:local соберётся сам (~5-10 мин в первый
# раз, дальше docker-кеш), postCreate засидит .hermes/.agents/skills,
# поставит AI-тулзы, построит graphify-граф, разведёт скиллы по агентам.cd ~/Development/@foxford/existing-repo
adc sync --adopt # заводит .devcontainer/, код проекта не трогает
code .
# → Reopen in Container: дальше всё как у нового проекта.Команда — adc (ставит install.sh).
| Команда | Где | Что делает |
|---|---|---|
adc new <name> [dir] [--type <t>] |
хост | новый проект из skeleton (git init, имя подставлено); без --type спросит тип, если их несколько |
adc sync [--adopt] [--type <t>] |
везде | применить платформу к проекту: скиллы, доки, MCP, .gitignore; печатает, что именно приехало с прошлого раза. --adopt заводит .devcontainer/ в репозитории, который раньше платформой не управлялся (код проекта не трогает) |
adc update |
везде | на хосте: git pull платформы + пересборка образа. В контейнере (клон read-only) — то же, что sync |
adc doctor |
везде | проверить: docker, образ, персист, PATH, доступные типы скаффолда; из каталога проекта — ещё и его devcontainer.json |
adc skill <cmd> |
везде | скиллы проекта: list / status / fork / unfork / migrate / sync |
adc mcp <cmd> |
контейнер | MCP-серверы проекта: list (все слои и что активно), add / rm, enable / disable, sync |
adc plans [--all] |
везде | задачи проекта: что в работе, что дольше всех не двигалось, сколько закрыто |
adc ensure-image |
хост | собрать/дособрать dev-base:local вручную (обычно не нужно — сама встаёт при первом контейнере) |
adc prepare |
хост | хостовая подготовка проекта; её зовёт initializeCommand — руками не нужна |
Граница «хост / контейнер» жёсткая: команда, набранная не на той стороне,
отказывается с объяснением, а не работает наполовину. Причина не в аккуратности —
снаружи контейнера ~/.codex, ~/.hermes и ~/.dsh принадлежат самому
разработчику и общие на все его проекты, а тома /opt/ai-tools там нет вовсе:
adc mcp sync на хосте подмешал бы серверы одного проекта в личный конфиг.
По той же причине adc sync на хосте раскладывает скиллы и доки, но раздачу
MCP пропускает и говорит об этом.
Обновление платформы не трогает существующие проекты само по себе: окружение подтянется при следующем Rebuild Container, конфиг-пакеты — руками (подробности — в SPEC.md).
adc mcp list зелёный.
Почти всегда — нет маунта ~/.claude.json. Это файл рядом с ~/.claude, а не
внутри; в нём лежит одобрение проектных серверов из .mcp.json, и без персиста
оно теряется на каждом rebuild. Проверить — adc doctor из каталога проекта:
он назовёт недостающий маунт и даст готовую строку. Дальше — adc prepare на
хосте и Rebuild Container.
--pull, который тянет базовый образ из registry, а
dev-base:local существует только локально → pull access denied. Обычные
«Reopen in Container» / «Rebuild Container» работают. Нужна пересборка с нуля:
docker rmi dev-base:local && adc ensure-imageСтоковые скиллы живут в платформе (skills/) и в проект не копируются —
подмешиваются из /opt/ai-devcontainer/skills. Проект хранит только то, чем
отличается: <repo>/.agents/skills/. Собранное дерево, которое видят агенты, —
<repo>/.claude/skills/ (в .gitignore, пересобирается на каждом postCreate).
adc skill list # что откуда приезжает
adc skill fork senior-qa # взять SKILL.md под правку проектом
adc skill fork impeccable scripts/live.mjs # перекрыть можно ЛЮБОЙ файл
adc skill status # разошлась ли платформа под форками
adc skill unfork senior-qa # вернуться на платформенную версию
adc skill sync # пересобрать после adc updateНа хосте то же самое — adc skill <cmd> из каталога проекта.
Проект, у которого скиллы вендорены по старой схеме, переводится разово:
adc skill migrate — файлы, совпадающие с платформой, выкидываются (поедут
централизованно), отличающиеся остаются форками. Без миграции проект не
получит обновлений: его копии перекрывают платформенный слой целиком.
Четыре слоя. Правило приоритета одно и запоминается фразой: проектное бьёт глобальное, моё бьёт общее.
| Слой | Где лежит | Чей и где виден |
|---|---|---|
--global |
mcp/servers.json платформы |
общий, во всех проектах · только чтение |
--user |
/opt/ai-tools/share/mcp/servers.json |
мой, во всех моих проектах |
--project |
<repo>/.agents/mcp.json |
команды, уезжает в гит |
--local |
<repo>/.agents/mcp.local.json |
мой, только в этом проекте |
Имена слоёв — те же, что у claude mcp add --scope, чтобы не заводить свой
словарь. Пользовательский слой живёт в томе platform-ai-tools, общем на все
проекты: токен вроде OBSIDIAN_API_KEY вписывается один раз на машину, а
не заново в каждом новом проекте.
adc mcp list # все слои: что есть, что активно, почему нет
adc mcp add --json '<сниппет из README>' # вставить как есть с сайта сервера
adc mcp add linear -- npx -y linear-mcp-server
adc mcp add linear --url https://… --header 'Authorization: Bearer ${TOKEN}'
adc mcp enable figma # спросит недостающий токен и запишет
adc mcp disable playwright --local # перекрыть нижний слой
adc mcp sync # пересобрать (после adc update)Слой не спрашивается молча: без флага и без TTY команда берёт локальный — самый узкий. Тихо положить личный сервер в гит команды нельзя.
Секрет в проектный слой не попадёт. .agents/mcp.json лежит в гите, поэтому
adc mcp add --project вынимает литеральный токен в .agents/mcp.secrets.env
(он в .gitignore) и оставляет в слое ${ИМЯ}. Вписанный руками секрет ловит
adc mcp sync и предлагает adc mcp fix-secrets. В --local и --user токен
не трогаем: те слои в гит не едут.
Раскладывать приходится в четыре места: проектный скоуп для MCP есть только у
Claude Code (<repo>/.mcp.json), а Codex, Hermes и DSH держат серверы в home
(~/.codex/config.toml, ~/.hermes/config.yaml, ~/.dsh/cordis.patch.yml).
Персист в платформе пер-проектный, так что проекты за эти конфиги не дерутся.
.mcp.json — артефакт сборки, а не конфиг: он пересобирается на каждом
sync и несёт раскрытые токены. Править его руками бесполезно (правка теряется),
поэтому в скелете он скрыт из проводника VS Code через files.exclude.
| Сервер | Что даёт | Когда появляется |
|---|---|---|
playwright |
браузер агента: клики, снапшоты, консоль | всегда |
chrome-devtools |
профилирование, трейсы, троттлинг, Core Web Vitals | как встанет браузер |
figma |
фреймы, токены дизайна, код по выделению | явный опт-ин FIGMA_MCP_ENABLED |
Сервер, которому не хватает токена или браузера, не раздаётся вовсе (поле
x-requires) — иначе агент получал бы инструменты, падающие на первом вызове.
Такой сервер виден в adc mcp list как ○ с причиной и командой починки;
включить — adc mcp enable <имя>, она сама спросит недостающую переменную.
Выключить лишний — adc mcp disable <имя> (это null поверх нижнего слоя).
Секреты — два файла, той же цепочкой: /opt/ai-tools/share/mcp/secrets.env
(один на машину) и <repo>/.agents/mcp.secrets.env (перекрывает его, в
.gitignore, права 600). ${ИМЯ} раскрывает wire-mcp — сам, а не агент: ${VAR}
умеют Claude и Hermes, на Codex не проверено, и схема «у двоих работает, у
третьего молча пусто» хуже честной.
figma по умолчанию выключен. И в Codex url-серверы не добавляются
автоматически: codex mcp add --url коннектится прямо при добавлении и
поднимает интерактивный OAuth-промпт — в postCreate это висяк.
Не путать с e2e-раннером самого проекта, если он у скаффолда есть и тоже на Playwright: это отдельный процесс, его гоняет CI. MCP — инструмент для агента, чтобы исследовать живое приложение вручную. Если оба используют Playwright, браузеры ставятся в общий named volume и переживают rebuild.
Два независимых канала. Когда агенту использовать какой — скилл obsidian.
Vault-каталог — работает сразу, без настройки. В каждом проекте есть
маунт obsidian-vault (~/.ai-devcontainer-dev/<проект>/obsidian
на хосте):
graphify update . --obsidian --obsidian-dir /home/node/obsidian-vaultОткрой ~/.ai-devcontainer-dev/<проект>/obsidian как vault в хостовом
Obsidian — файлы появляются сразу, персист переживает rebuild.
MCP-сервер obsidian — живой доступ агента к тому vault'у, что реально
открыт в Obsidian на хосте (не обязательно к каталогу выше). Настройка,
один раз:
- Установи плагин Local REST API через Community Plugins в Obsidian.
- В его настройках включи Enable Non-encrypted (HTTP) Server (порт
27123) — так проще, чем доверять самоподписанный сертификат HTTPS-порта, а трафик и так не выходит за пределы хоста. - Скопируй API Key из тех же настроек.
- В
.agents/mcp.secrets.envпроекта:OBSIDIAN_API_KEY=<ключ>. adc mcp sync, перезапусти агента.
Без ключа сервер молча не раздаётся (та же схема, что у figma). Полный
список инструментов (vault_read/vault_write/search_simple/tag_list/...)
— в скилле obsidian.