""" Kin — SQLite database schema and connection management. All tables from DESIGN.md section 3.5 State Management. """ import sqlite3 from pathlib import Path DB_PATH = Path(__file__).parent.parent / "kin.db" SCHEMA = """ -- Проекты (центральный реестр) CREATE TABLE IF NOT EXISTS projects ( id TEXT PRIMARY KEY, name TEXT NOT NULL, path TEXT NOT NULL, tech_stack JSON, status TEXT DEFAULT 'active', priority INTEGER DEFAULT 5, pm_prompt TEXT, claude_md_path TEXT, forgejo_repo TEXT, language TEXT DEFAULT 'ru', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- Задачи (привязаны к проекту) CREATE TABLE IF NOT EXISTS tasks ( id TEXT PRIMARY KEY, project_id TEXT NOT NULL REFERENCES projects(id), title TEXT NOT NULL, status TEXT DEFAULT 'pending', priority INTEGER DEFAULT 5, assigned_role TEXT, parent_task_id TEXT REFERENCES tasks(id), brief JSON, spec JSON, review JSON, test_result JSON, security_result JSON, forgejo_issue_id INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- Решения и грабли (внешняя память PM-агента) CREATE TABLE IF NOT EXISTS decisions ( id INTEGER PRIMARY KEY AUTOINCREMENT, project_id TEXT NOT NULL REFERENCES projects(id), task_id TEXT REFERENCES tasks(id), type TEXT NOT NULL, category TEXT, title TEXT NOT NULL, description TEXT NOT NULL, tags JSON, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- Логи агентов (дебаг, обучение, cost tracking) CREATE TABLE IF NOT EXISTS agent_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, project_id TEXT NOT NULL REFERENCES projects(id), task_id TEXT REFERENCES tasks(id), agent_role TEXT NOT NULL, session_id TEXT, action TEXT NOT NULL, input_summary TEXT, output_summary TEXT, tokens_used INTEGER, model TEXT, cost_usd REAL, success BOOLEAN, error_message TEXT, duration_seconds INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- Модули проекта (карта для PM) CREATE TABLE IF NOT EXISTS modules ( id INTEGER PRIMARY KEY AUTOINCREMENT, project_id TEXT NOT NULL REFERENCES projects(id), name TEXT NOT NULL, type TEXT NOT NULL, path TEXT NOT NULL, description TEXT, owner_role TEXT, dependencies JSON, UNIQUE(project_id, name) ); -- Pipelines (история запусков) CREATE TABLE IF NOT EXISTS pipelines ( id INTEGER PRIMARY KEY AUTOINCREMENT, task_id TEXT NOT NULL REFERENCES tasks(id), project_id TEXT NOT NULL REFERENCES projects(id), route_type TEXT NOT NULL, steps JSON NOT NULL, status TEXT DEFAULT 'running', total_cost_usd REAL, total_tokens INTEGER, total_duration_seconds INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, completed_at DATETIME ); -- Кросс-проектные зависимости CREATE TABLE IF NOT EXISTS project_links ( id INTEGER PRIMARY KEY AUTOINCREMENT, from_project TEXT NOT NULL REFERENCES projects(id), to_project TEXT NOT NULL REFERENCES projects(id), type TEXT NOT NULL, description TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- Тикеты от пользователей CREATE TABLE IF NOT EXISTS support_tickets ( id INTEGER PRIMARY KEY AUTOINCREMENT, project_id TEXT NOT NULL REFERENCES projects(id), source TEXT NOT NULL, client_id TEXT, client_message TEXT NOT NULL, classification TEXT, guard_result TEXT, guard_reason TEXT, anamnesis JSON, task_id TEXT REFERENCES tasks(id), response TEXT, response_approved BOOLEAN DEFAULT FALSE, status TEXT DEFAULT 'new', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, resolved_at DATETIME ); -- Настройки бота для каждого проекта CREATE TABLE IF NOT EXISTS support_bot_config ( project_id TEXT PRIMARY KEY REFERENCES projects(id), telegram_bot_token TEXT, welcome_message TEXT, faq JSON, auto_reply BOOLEAN DEFAULT FALSE, require_approval BOOLEAN DEFAULT TRUE, brand_voice TEXT, forbidden_topics JSON, escalation_keywords JSON ); -- Индексы CREATE INDEX IF NOT EXISTS idx_tasks_project_status ON tasks(project_id, status); CREATE INDEX IF NOT EXISTS idx_decisions_project ON decisions(project_id); CREATE INDEX IF NOT EXISTS idx_decisions_tags ON decisions(tags); CREATE INDEX IF NOT EXISTS idx_agent_logs_project ON agent_logs(project_id, created_at); CREATE INDEX IF NOT EXISTS idx_agent_logs_cost ON agent_logs(project_id, cost_usd); CREATE INDEX IF NOT EXISTS idx_tickets_project ON support_tickets(project_id, status); CREATE INDEX IF NOT EXISTS idx_tickets_client ON support_tickets(client_id); """ def get_connection(db_path: Path = DB_PATH) -> sqlite3.Connection: conn = sqlite3.connect(str(db_path)) conn.execute("PRAGMA journal_mode=WAL") conn.execute("PRAGMA foreign_keys=ON") conn.row_factory = sqlite3.Row return conn def _migrate(conn: sqlite3.Connection): """Run migrations for existing databases.""" # Check if language column exists on projects cols = {r[1] for r in conn.execute("PRAGMA table_info(projects)").fetchall()} if "language" not in cols: conn.execute("ALTER TABLE projects ADD COLUMN language TEXT DEFAULT 'ru'") conn.commit() def init_db(db_path: Path = DB_PATH) -> sqlite3.Connection: conn = get_connection(db_path) conn.executescript(SCHEMA) conn.commit() _migrate(conn) return conn if __name__ == "__main__": conn = init_db() tables = conn.execute( "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name" ).fetchall() print(f"Initialized {len(tables)} tables:") for t in tables: print(f" - {t['name']}") conn.close()