Версия: 1.0.0
Дата: 2026-02-17
Статус: concept
Тип: infrastructure-concept
FileStorage — базовая абстракция платформы для работы с файлами.
Проблема: Код напрямую работает с файловой системой через open(), пути hardcoded, нет единого подхода.
Решение: Единый Storage Layer для всех операций с файлами.
┌─────────────────────────────────────────┐
│ FileStorage - это ГРУЗОВИК │
│ │
│ НЕ генерирует содержимое │
│ ТОЛЬКО перевозит готовое │
└─────────────────────────────────────────┘
Аналогия:
Грузовик ≠ Создатель груза
Грузовик = Перевозчик груза
FileStorage ≠ Генератор текста
FileStorage = Записыватель текста
Принцип:
- FileStorage получает готовый текст
- FileStorage записывает как дали
- FileStorage не думает о содержимом
# Проект А
with open("/opt/claude-workspace/projects/foo/CLAUDE.md", "w") as f:
f.write(content)
# Проект Б
path = "/opt/claude-workspace/projects/bar/index.yaml"
with open(path, "r") as f:
data = f.read()
# Проект В
import os
os.makedirs("/opt/claude-workspace/projects/baz/design")
Проблемы:
| Проблема | Последствие |
|---|---|
| Hardcode путей | /opt/claude-workspace/... вшито в код |
| Дублирование | Каждый проект пишет свой код работы с файлами |
| Нет правил | Где хранить документы? Где данные? Каждый решает сам |
| Сложно тестировать | Тесты работают с реальными файлами |
| Нет абстракции | Невозможно изменить backend (S3, FTP) |
КОД → STORAGE LAYER → ФАЙЛОВАЯ СИСТЕМА
↓ ↓
FileStorage WORKSPACE/DATASPACE
GitStorage Git Repository
Как работает:
- Весь код работает через Storage Layer
- Storage решает где хранить (WORKSPACE vs DATASPACE)
- Storage решает как хранить (Git или нет)
- Один интерфейс для всех проектов
Пример:
from library.storages.filesystem import FileStorage
fs = FileStorage()
# Все проекты используют одинаково
fs.write("projects/foo/CLAUDE.md", content)
fs.write("projects/bar/index.yaml", data)
# FileStorage сам определяет абсолютный путь
# FileStorage сам создаёт папки
# FileStorage сам выбирает WORKSPACE vs DATASPACE
┌────────────────────────────────────────────────┐
│ L6: ИНТЕГРАЦИИ │
│ (DM, PM используют FileStorage) │
└───────────────────┬────────────────────────────┘
↓
┌────────────────────────────────────────────────┐
│ L5: СЕРВИСЫ │
│ (business logic) │
└───────────────────┬────────────────────────────┘
↓
┌────────────────────────────────────────────────┐
│ L4: КОМПОНЕНТЫ (← FileStorage здесь) │
│ library/storages/filesystem.py │
└───────────────────┬────────────────────────────┘
↓
┌────────────────────────────────────────────────┐
│ L3: ФУНКЦИИ │
│ (атомарные операции) │
└───────────────────┬────────────────────────────┘
↓
┌────────────────────────────────────────────────┐
│ L2: ПРИМИТИВЫ │
│ (Path, File) │
└────────────────────────────────────────────────┘
library/storages/
├── base.py ← BaseStorage (абстракция)
├── filesystem.py ← FileStorage (локальная ФС)
├── git.py ← GitStorage (версионирование)
├── s3.py ← S3Storage (облако) [будущее]
├── postgres/ ← PostgresStorage (БД)
└── cache/ ← CacheStorage (Redis)
BaseStorage:
class BaseStorage(ABC):
"""Базовый класс всех storage."""
@abstractmethod
def read(self, key: str) -> Any:
pass
@abstractmethod
def write(self, key: str, value: Any) -> None:
pass
@abstractmethod
def exists(self, key: str) -> bool:
pass
class FileStorage(BaseStorage):
"""
Абстракция работы с файлами.
Автоматически выбирает:
- WORKSPACE (/opt/claude-workspace) — код, документы
- DATASPACE (/mnt/beget-s3) — данные, изображения
"""
def read(self, path: str) -> str:
"""
Прочитать файл.
Args:
path: Относительный путь (projects/foo/file.md)
Returns:
Содержимое файла
"""
def write(self, path: str, content: str) -> None:
"""
Записать файл.
Args:
path: Относительный путь
content: ГОТОВЫЙ текст для записи
Note:
FileStorage НЕ генерирует content.
FileStorage ТОЛЬКО записывает что дали.
"""
def exists(self, path: str) -> bool:
"""Проверить существование файла."""
def list(self, pattern: str) -> List[str]:
"""
Список файлов по glob-паттерну.
Args:
pattern: Glob паттерн (projects/**/*.md)
Returns:
Список путей
"""
def delete(self, path: str) -> None:
"""Удалить файл."""
def copy(self, src: str, dst: str) -> None:
"""Копировать файл."""
def move(self, src: str, dst: str) -> None:
"""Переместить файл."""
def mkdir(self, path: str) -> None:
"""Создать папку (и родительские)."""
class FileStorage:
WORKSPACE = Path("/opt/claude-workspace")
DATASPACE = Path("/mnt/beget-s3")
# Расширения для DATASPACE
BINARY_EXTENSIONS = {
'.xlsx', '.xls', # Excel
'.jpg', '.jpeg', # Изображения
'.png', '.gif',
'.pdf', '.docx', # Документы
'.zip', '.tar.gz', # Архивы
'.mp4', '.mp3', # Медиа
}
def _resolve_path(self, path: str) -> Path:
"""
Определить где хранить файл.
Правило:
- Бинарные данные → DATASPACE
- Текст, код → WORKSPACE
"""
ext = Path(path).suffix.lower()
if ext in self.BINARY_EXTENSIONS:
return self.DATASPACE / path
else:
return self.WORKSPACE / path
Примеры:
| Файл | Где хранится | Почему |
|---|---|---|
projects/foo/CLAUDE.md |
WORKSPACE | Текст, версионируется |
projects/foo/index.yaml |
WORKSPACE | Код, версионируется |
projects/foo/data.xlsx |
DATASPACE | Бинарные данные |
projects/foo/logo.png |
DATASPACE | Изображение |
library/storages/fs.py |
WORKSPACE | Код |
# ═══════════════════════════════════════════
# FileStorage = ТРАНСПОРТ (не генератор)
# ═══════════════════════════════════════════
fs = FileStorage()
fs.write(path, content)
# ↑
# ОТКУДА content?
Текст создают:
# Claude генерирует В ПАМЯТИ:
content = """# foo
**Тип:** it-project
## Что это
Проект foo - система управления задачами для команды.
Включает трекинг задач, sprint planning, интеграцию с Git.
Поддерживает Kanban и Scrum методологии.
## Технологии
- Python 3.11
- FastAPI
- PostgreSQL
- React
"""
# ← ТЕКСТ СОЗДАН Claude (AI думал, анализировал)
# Передаёт готовый в FileStorage:
fs.write("projects/foo/CLAUDE.md", content)
# ↑
# УЖЕ ГОТОВЫЙ ТЕКСТ
# DM подставляет переменные:
template = """id: {name}
type: {type}
status: active
created: {date}
"""
content = template.format(
name="foo",
type="it-project",
date="2026-02-17"
)
# ← ТЕКСТ СОЗДАН DM (примитивная подстановка)
# Передаёт готовый в FileStorage:
fs.write("projects/foo/index.yaml", content)
# ↑
# УЖЕ ГОТОВЫЙ ТЕКСТ
# Шаг 1: DM создаёт skeleton
skeleton = """# {name}
## Что это
<!-- AI: Опиши проект {name} -->
## Технологии
<!-- AI: Перечисли технологии -->
"""
content = skeleton.format(name="foo")
fs.write("projects/foo/CLAUDE.md", content)
# Шаг 2: Claude заполняет
new_content = """# foo
## Что это
Проект foo - система управления...
## Технологии
- Python 3.11
- FastAPI
"""
fs.write("projects/foo/CLAUDE.md", new_content)
# ↑
# ПЕРЕЗАПИСЫВАЕТ готовым текстом
# system/dm/assembly.py
from library.storages.filesystem import FileStorage
from library.storages.git import GitStorage
class DocumentAssembler:
def __init__(self):
self.fs = FileStorage() # ← Используем абстракцию
self.git = GitStorage()
def create_project(self, name: str, type: str):
"""
Создать проект из шаблона.
Returns:
Инструкции для Claude (что заполнить)
"""
# ═══════════════════════════════════
# 1. Создаём простые файлы целиком
# ═══════════════════════════════════
index = f"""id: {name}
type: {type}
status: active
"""
self.fs.write(f"projects/{name}/index.yaml", index)
# ↑
# ПЕРЕДАЁМ ГОТОВЫЙ
# ═══════════════════════════════════
# 2. Создаём skeleton для Claude
# ═══════════════════════════════════
skeleton = f"""# {name}
**Тип:** {type}
## Что это
<!-- AI: Опиши проект {name} -->
## Технологии
<!-- AI: Перечисли технологии для {type} -->
"""
self.fs.write(f"projects/{name}/CLAUDE.md", skeleton)
# ↑
# ПЕРЕДАЁМ ГОТОВЫЙ
# ═══════════════════════════════════
# 3. Возвращаем инструкции Claude
# ═══════════════════════════════════
return {
"skeleton_created": True,
"files_to_fill": [
{
"path": f"projects/{name}/CLAUDE.md",
"sections": [
{
"marker": "<!-- AI: Опиши проект {name} -->",
"prompt": f"Опиши проект {name} типа {type}"
},
{
"marker": "<!-- AI: Перечисли технологии -->",
"prompt": f"Технологии для {type}"
}
]
}
]
}
FileStorage в DM:
- Получает готовый текст (из шаблона)
- Записывает в файл
- Создаёт папки автоматически
- Выбирает WORKSPACE/DATASPACE
# ❌ Hardcode путей
with open("/opt/claude-workspace/projects/foo/file.md", "w") as f:
f.write(content)
# ❌ Ручное создание папок
os.makedirs("/opt/claude-workspace/projects/foo/design", exist_ok=True)
# ❌ Нет правил где хранить
# Документы в WORKSPACE? Данные в DATASPACE? Каждый решает сам
# ❌ Сложно тестировать
# Тесты работают с реальными файлами
# ❌ Код дублируется
# Каждый проект пишет свою работу с файлами
# ✅ Относительные пути
fs.write("projects/foo/file.md", content)
# ✅ Папки автоматически
# FileStorage создаёт сам
# ✅ Правила встроены
# .md → WORKSPACE
# .xlsx → DATASPACE
# ✅ Легко тестировать
fs = MockFileStorage() # Mock для тестов
# ✅ Переиспользование
# Один FileStorage для всех проектов
| Аспект | БЕЗ FileStorage | С FileStorage |
|---|---|---|
| Пути | Абсолютные hardcoded | Относительные |
| Создание папок | Вручную makedirs |
Автоматически |
| WORKSPACE vs DATASPACE | Каждый решает сам | Автоматически по расширению |
| Переиспользование | Код дублируется | Один раз в library/ |
| Тестирование | Сложно (реальные файлы) | Легко (моки) |
| Миграция | Править во всех проектах | Изменить 1 место |
| Backend | Только локальная ФС | Можно добавить S3, FTP |
┌────────────────────────────────────────────┐
│ ПОЛЬЗОВАТЕЛЬ │
│ "создай проект foo" │
└───────────────┬────────────────────────────┘
↓
┌────────────────────────────────────────────┐
│ CLAUDE │
│ Bash("python system/dm/cli.py create") │
└───────────────┬────────────────────────────┘
↓
┌────────────────────────────────────────────┐
│ DM │
│ 1. Генерирует текст из шаблона │
│ 2. Вызывает FileStorage │
│ fs.write(path, content) │
└───────────────┬────────────────────────────┘
↓
┌────────────────────────────────────────────┐
│ FileStorage │
│ 1. Определяет WORKSPACE vs DATASPACE │
│ 2. Создаёт папки │
│ 3. Записывает файл │
└───────────────┬────────────────────────────┘
↓
┌────────────────────────────────────────────┐
│ ФАЙЛОВАЯ СИСТЕМА │
│ /opt/claude-workspace/projects/foo/... │
└────────────────────────────────────────────┘
┌──────────────────────────────────────┐
│ CLAUDE (AI) │
│ • Генерирует СОДЕРЖАНИЕ │
│ • Думает, анализирует │
│ • Создаёт уникальный текст │
└──────────────┬───────────────────────┘
↓ управляет
┌──────────────────────────────────────┐
│ DM (Python) │
│ • Создаёт СТРУКТУРУ │
│ • Шаблоны с подстановкой │
│ • Определяет что нужно сгенерировать│
└──────────────┬───────────────────────┘
↓ использует
┌──────────────────────────────────────┐
│ FileStorage (Python) │
│ • Записывает ГОТОВЫЙ текст │
│ • WORKSPACE vs DATASPACE │
│ • НЕ генерирует содержание │
└──────────────┬───────────────────────┘
↓ работает с
┌──────────────────────────────────────┐
│ ФАЙЛОВАЯ СИСТЕМА │
│ /opt/claude-workspace/ │
│ /mnt/beget-s3/ │
└──────────────────────────────────────┘
Каждый делает СВОЁ:
- Claude → содержание (AI)
- DM → структура (шаблоны)
- FileStorage → хранение (техника)
library/storages/
├── filesystem.py ← Локальная ФС (есть)
├── git.py ← Git версионирование (есть)
│
├── s3.py ← AWS S3 (будущее)
├── ftp.py ← FTP storage (будущее)
├── webdav.py ← WebDAV (будущее)
└── dropbox.py ← Dropbox API (будущее)
Преимущество абстракции:
- Код проектов не меняется
- Меняется только backend в FileStorage
- Можно комбинировать (локально + S3)
FileStorage НЕ генерирует текст
FileStorage ТОЛЬКО записывает готовый текст
Генерация содержания → Claude / DM
Хранение файлов → FileStorage
Все проекты используют FileStorage одинаково
Переиспользование вместо дублирования
WORKSPACE vs DATASPACE → автоматически
Создание папок → автоматически
Права доступа → автоматически
Сегодня: локальная ФС
Завтра: S3, FTP, WebDAV
Код не меняется
Версия: 1.0.0
Дата: 2026-02-17
Статус: concept