opencode-db 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
i18n.py ADDED
@@ -0,0 +1,532 @@
1
+ """Интернационализация: поддержка русского и английского языка.
2
+
3
+ Использование:
4
+ from i18n import _, set_lang
5
+
6
+ set_lang("en")
7
+ print(_("list.header.id")) # → "ID"
8
+ print(_("export.saved", path="f")) # → "✅ Диалог сохранён: f"
9
+
10
+ Структура словаря:
11
+ L[ключ] = {"ru": "...", "en": "..."}
12
+
13
+ Если ключа нет — возвращается сам ключ.
14
+ Если язык не указан — русский (по умолчанию).
15
+ """
16
+
17
+ import os
18
+
19
+ current_lang = os.environ.get("OPENCODE_DB_LANG", "ru")
20
+
21
+
22
+ L: dict[str, dict[str, str]] = {
23
+ # ==================================================================
24
+ # ГЛОБАЛЬНЫЕ
25
+ # ==================================================================
26
+ "db.not_found": {
27
+ "ru": "❌ База OpenCode не найдена: {path}",
28
+ "en": "❌ OpenCode database not found: {path}",
29
+ },
30
+ "db.not_found_hint": {
31
+ "ru": " Убедись, что opencode установлен и хотя бы раз запускался.",
32
+ "en": " Make sure OpenCode is installed and has been run at least once.",
33
+ },
34
+ "session.not_found": {
35
+ "ru": "❌ Сессия {session_id} не найдена",
36
+ "en": "❌ Session {session_id} not found",
37
+ },
38
+ "session.no_sessions": {
39
+ "ru": "❌ Нет сессий в базе",
40
+ "en": "❌ No sessions in the database",
41
+ },
42
+ "session.multiple_match": {
43
+ "ru": "❌ Найдено несколько сессий с префиксом {session_id}:",
44
+ "en": "❌ Multiple sessions match prefix {session_id}:",
45
+ },
46
+ "session.specify_id": {
47
+ "ru": " Уточни ID",
48
+ "en": " Use a longer prefix or the full ID",
49
+ },
50
+ "session.latest": {
51
+ "ru": " 🕐 Последняя сессия: {title}",
52
+ "en": " 🕐 Latest session: {title}",
53
+ },
54
+ "cmd.unknown": {
55
+ "ru": "❌ Неизвестная команда: {cmd}",
56
+ "en": "❌ Unknown command: {cmd}",
57
+ },
58
+ "empty": {
59
+ "ru": " (пусто)",
60
+ "en": " (empty)",
61
+ },
62
+ "total_count": {
63
+ "ru": " Всего: {n}",
64
+ "en": " Total: {n}",
65
+ },
66
+ "canceled": {
67
+ "ru": " Отменено.",
68
+ "en": " Canceled.",
69
+ },
70
+ "yes_no_prompt": {
71
+ "ru": "Подтвердить операцию?",
72
+ "en": "Proceed with the operation?",
73
+ },
74
+ "yes_no_true": {
75
+ "ru": " [Y/n]: ",
76
+ "en": " [Y/n]: ",
77
+ },
78
+ "yes_no_false": {
79
+ "ru": " [y/N]: ",
80
+ "en": " [y/N]: ",
81
+ },
82
+ "not_obsidian": {
83
+ "ru": "ℹ️ log.md пропущен (не Obsidian-проект)",
84
+ "en": "ℹ️ log.md skipped (not an Obsidian project)",
85
+ },
86
+ "log_updated": {
87
+ "ru": "✅ log.md обновлён: {path}",
88
+ "en": "✅ log.md updated: {path}",
89
+ },
90
+ # ==================================================================
91
+ # LIST
92
+ # ==================================================================
93
+ "list.header.id": {"ru": "ID", "en": "ID"},
94
+ "list.header.title": {"ru": "Название", "en": "Title"},
95
+ "list.header.project": {"ru": "Проект", "en": "Project"},
96
+ "list.header.model": {"ru": "Модель", "en": "Model"},
97
+ "list.header.activity": {"ru": "Активность", "en": "Activity"},
98
+ "list.header.messages": {"ru": "Сообщ.", "en": "Msgs"},
99
+ "list.header.cost": {"ru": "Стоимость", "en": "Cost"},
100
+ # ==================================================================
101
+ # INFO
102
+ # ==================================================================
103
+ "info.id": {"ru": "ID", "en": "ID"},
104
+ "info.project": {"ru": "Проект", "en": "Project"},
105
+ "info.model": {"ru": "Модель", "en": "Model"},
106
+ "info.agent": {"ru": "Агент", "en": "Agent"},
107
+ "info.version": {"ru": "Версия", "en": "Version"},
108
+ "info.created": {"ru": "Создана", "en": "Created"},
109
+ "info.updated": {"ru": "Активна", "en": "Last active"},
110
+ "info.duration": {"ru": "Длит-сть", "en": "Duration"},
111
+ "info.parent": {"ru": "Родитель", "en": "Parent"},
112
+ "info.branches": {"ru": "Веток", "en": "Children"},
113
+ "info.messages": {"ru": "Сообщений", "en": "Messages"},
114
+ "info.parts": {"ru": "Частей", "en": "Parts"},
115
+ "info.tokens_input": {"ru": "Токены ввода", "en": "Input tokens"},
116
+ "info.tokens_output": {"ru": "Токены вывода", "en": "Output tokens"},
117
+ "info.tokens_reasoning": {"ru": "Токены reasoning", "en": "Reasoning tokens"},
118
+ "info.cache_read": {"ru": "Кэш (чтение)", "en": "Cache (read)"},
119
+ "info.cache_write": {"ru": "Кэш (запись)", "en": "Cache (write)"},
120
+ "info.tokens_total": {"ru": "Всего токенов", "en": "Total tokens"},
121
+ "info.cost": {"ru": "Стоимость", "en": "Cost"},
122
+ "info.code_changes": {"ru": "Изменения кода", "en": "Code changes"},
123
+ "info.files_changed": {"ru": "Файлов", "en": "Files"},
124
+ "info.lines_added": {"ru": "Добавлено", "en": "Added"},
125
+ "info.lines_deleted": {"ru": "Удалено", "en": "Deleted"},
126
+ "info.todos": {"ru": "Задачи (todo)", "en": "Tasks (todo)"},
127
+ "info.todos_status": {
128
+ "ru": "Статус: {status}, создано: {created}",
129
+ "en": "Status: {status}, created: {created}",
130
+ },
131
+ "info.unnamed": {"ru": "Безымянная сессия", "en": "Unnamed session"},
132
+ "info.children_prefix": {"ru": " └─ {id} — {title}", "en": " └─ {id} — {title}"},
133
+ "info.parent_fmt": {"ru": " {id} — {title}", "en": " {id} — {title}"},
134
+ "info.not_found": {
135
+ "ru": "❌ Сессия {session_id} не найдена",
136
+ "en": "❌ Session {session_id} not found",
137
+ },
138
+ # ==================================================================
139
+ # EXPORT
140
+ # ==================================================================
141
+ "export.saved": {"ru": "✅ Диалог сохранён: {path}", "en": "✅ Dialog saved: {path}"},
142
+ "export.messages": {"ru": " Сообщений: {n}", "en": " Messages: {n}"},
143
+ "export.exists": {
144
+ "ru": "❌ Файл уже существует: {path}",
145
+ "en": "❌ File already exists: {path}",
146
+ },
147
+ "export.exists_hint": {
148
+ "ru": " Используй --force для перезаписи",
149
+ "en": " Use --force to overwrite",
150
+ },
151
+ "export.interactive_header": {"ru": "Последние сессии:", "en": "Recent sessions:"},
152
+ "export.interactive_prompt": {
153
+ "ru": "Выбери номер (или Enter для отмены): ",
154
+ "en": "Pick a number (or Enter to cancel): ",
155
+ },
156
+ "export.interactive_error": {
157
+ "ru": "Введи число от 1 до {n}",
158
+ "en": "Enter a number from 1 to {n}",
159
+ },
160
+ "export.interactive_exporting": {"ru": " Экспорт: {title}", "en": " Exporting: {title}"},
161
+ "export.no_sessions": {"ru": "❌ Нет сессий для экспорта.", "en": "❌ No sessions to export."},
162
+ "export.interactive_abort": {"ru": " Отменено.", "en": " Canceled."},
163
+ # ==================================================================
164
+ # DELETE
165
+ # ==================================================================
166
+ "delete.header": {"ru": "УДАЛЕНИЕ СЕССИИ", "en": "DELETE SESSION"},
167
+ "delete.id": {"ru": "ID", "en": "ID"},
168
+ "delete.title": {"ru": "Название", "en": "Title"},
169
+ "delete.model": {"ru": "Модель", "en": "Model"},
170
+ "delete.created": {"ru": "Создана", "en": "Created"},
171
+ "delete.messages": {"ru": "Сообщений", "en": "Messages"},
172
+ "delete.parts": {"ru": "Частей", "en": "Parts"},
173
+ "delete.cost": {"ru": "Стоимость", "en": "Cost"},
174
+ "delete.dry_run": {
175
+ "ru": " 🔍 Сухой запуск — ничего не удалено.",
176
+ "en": " 🔍 Dry run — nothing deleted.",
177
+ },
178
+ "delete.dry_run_hint": {
179
+ "ru": " Для удаления запусти без --dry-run",
180
+ "en": " Run without --dry-run to actually delete",
181
+ },
182
+ "delete.confirm": {"ru": "Удалить сессию {id}?", "en": "Delete session {id}?"},
183
+ "delete.done": {"ru": " ✅ Сессия удалена: {id}…", "en": " ✅ Session deleted: {id}…"},
184
+ "delete.done_detail": {
185
+ "ru": " Сообщений: {msg}, частей: {parts}",
186
+ "en": " Messages: {msg}, parts: {parts}",
187
+ },
188
+ # ==================================================================
189
+ # COSTS
190
+ # ==================================================================
191
+ "costs.session": {"ru": "Сессия", "en": "Session"},
192
+ "costs.model": {"ru": "Модель", "en": "Model"},
193
+ "costs.input": {"ru": "Ввод", "en": "Input"},
194
+ "costs.output": {"ru": "Вывод", "en": "Output"},
195
+ "costs.reasoning": {"ru": "Reasoning", "en": "Reasoning"},
196
+ "costs.total": {"ru": "Всего", "en": "Total"},
197
+ "costs.cost": {"ru": "Стоимость", "en": "Cost"},
198
+ "costs.total_header": {"ru": "ИТОГО ПО {scope}", "en": "TOTAL BY {scope}"},
199
+ "costs.scope_all": {"ru": "ВСЕЙ БД", "en": "DATABASE"},
200
+ "costs.scope_project": {"ru": "ПРОЕКТУ", "en": "PROJECT"},
201
+ "costs.sessions": {"ru": "Сессий", "en": "Sessions"},
202
+ "costs.tokens_input": {"ru": "Токенов ввода", "en": "Input tokens"},
203
+ "costs.tokens_output": {"ru": "Токенов вывода", "en": "Output tokens"},
204
+ "costs.tokens_reasoning": {"ru": "Токенов reasoning", "en": "Reasoning tokens"},
205
+ "costs.tokens_total": {"ru": "Всего токенов", "en": "Total tokens"},
206
+ "costs.cache_read": {"ru": "Кэш (чтение)", "en": "Cache (read)"},
207
+ "costs.cache_write": {"ru": "Кэш (запись)", "en": "Cache (write)"},
208
+ # ==================================================================
209
+ # PRUNE
210
+ # ==================================================================
211
+ "prune.header": {"ru": "Будет удалено сессий: {n}", "en": "Sessions to delete: {n}"},
212
+ "prune.none": {"ru": " ✅ Нет сессий для удаления.", "en": " ✅ No sessions to delete."},
213
+ "prune.filter_required": {
214
+ "ru": "❌ Укажи хотя бы один фильтр: --older-than, --keep-last или --project",
215
+ "en": "❌ Specify at least one filter: --older-than, --keep-last, or --project",
216
+ },
217
+ "prune.filter_example": {
218
+ "ru": " Пример: opencode-db prune --older-than 90d --dry-run",
219
+ "en": " Example: opencode-db prune --older-than 90d --dry-run",
220
+ },
221
+ "prune.bad_spec": {
222
+ "ru": "❌ Не могу разобрать: --older-than {spec}",
223
+ "en": "❌ Cannot parse: --older-than {spec}",
224
+ },
225
+ "prune.bad_spec_hint": {
226
+ "ru": " Используй формат: 30d, 90d, 6m, 1y",
227
+ "en": " Use format: 30d, 90d, 6m, 1y",
228
+ },
229
+ "prune.dry_run": {
230
+ "ru": " 🔍 Сухой запуск — ничего не удалено.",
231
+ "en": " 🔍 Dry run — nothing deleted.",
232
+ },
233
+ "prune.confirm": {"ru": "Удалить {n} сессий?", "en": "Delete {n} sessions?"},
234
+ "prune.done": {"ru": " ✅ Удалено {n} сессий.", "en": " ✅ Deleted {n} sessions."},
235
+ "prune.cost_total": {
236
+ "ru": "Стоимость удаляемых: {cost}",
237
+ "en": "Cost of deleted sessions: {cost}",
238
+ },
239
+ "prune.tokens_total": {"ru": "Освободится токенов: ~{n}", "en": "Tokens freed: ~{n}"},
240
+ # ==================================================================
241
+ # SEARCH
242
+ # ==================================================================
243
+ "search.header": {
244
+ "ru": "🔍 Результаты поиска: «{query}»",
245
+ "en": "🔍 Search results: «{query}»",
246
+ },
247
+ "search.none": {
248
+ "ru": "\n 🔍 Ничего не найдено по запросу: {query}",
249
+ "en": "\n 🔍 Nothing found for: {query}",
250
+ },
251
+ "search.found": {"ru": "Найдено: {n}", "en": "Found: {n}"},
252
+ # ==================================================================
253
+ # TREE
254
+ # ==================================================================
255
+ "tree.header": {
256
+ "ru": "🌳 Дерево сессий (от: {title})",
257
+ "en": "🌳 Session tree (from: {title})",
258
+ },
259
+ "tree.header_all": {
260
+ "ru": "🌳 Все корневые сессии (ветвление)",
261
+ "en": "🌳 All root sessions (branching)",
262
+ },
263
+ "tree.header_project": {
264
+ "ru": "🌳 Дерево сессий проекта {project}",
265
+ "en": "🌳 Session tree for project {project}",
266
+ },
267
+ # ==================================================================
268
+ # PROJECTS
269
+ # ==================================================================
270
+ "projects.header.id": {"ru": "ID", "en": "ID"},
271
+ "projects.header.name": {"ru": "Название", "en": "Name"},
272
+ "projects.header.activity": {"ru": "Активность", "en": "Activity"},
273
+ "projects.header.sessions": {"ru": "Сессии", "en": "Sessions"},
274
+ "projects.header.tokens": {"ru": "Токены", "en": "Tokens"},
275
+ "projects.header.cost": {"ru": "Стоимость", "en": "Cost"},
276
+ # ==================================================================
277
+ # STATS
278
+ # ==================================================================
279
+ "stats.header": {"ru": "📊 СТАТИСТИКА БАЗЫ OPENCODE", "en": "📊 OPENCODE DATABASE STATISTICS"},
280
+ "stats.projects": {"ru": "Проектов", "en": "Projects"},
281
+ "stats.sessions": {"ru": "Сессий", "en": "Sessions"},
282
+ "stats.messages": {"ru": "Сообщений", "en": "Messages"},
283
+ "stats.parts": {"ru": "Частей", "en": "Parts"},
284
+ "stats.todos": {"ru": "Задач (todo)", "en": "Tasks (todo)"},
285
+ "stats.first_session": {"ru": "Первая сессия", "en": "First session"},
286
+ "stats.last_session": {"ru": "Последняя", "en": "Last session"},
287
+ "stats.tokens_input": {"ru": "Токенов ввода", "en": "Input tokens"},
288
+ "stats.tokens_output": {"ru": "Токенов вывода", "en": "Output tokens"},
289
+ "stats.tokens_reasoning": {"ru": "Токенов reasoning", "en": "Reasoning tokens"},
290
+ "stats.tokens_total": {"ru": "Всего токенов", "en": "Total tokens"},
291
+ "stats.cost": {"ru": "Стоимость", "en": "Cost"},
292
+ "stats.top_models": {"ru": "Топ моделей", "en": "Top models"},
293
+ "stats.agents": {"ru": "Агенты", "en": "Agents"},
294
+ # ==================================================================
295
+ # TODOS
296
+ # ==================================================================
297
+ "todos.header": {"ru": "📝 Задачи ({n})", "en": "📝 Tasks ({n})"},
298
+ "todos.none": {"ru": "\n 📝 Нет задач.", "en": "\n 📝 No tasks."},
299
+ "todos.session": {"ru": "Сессия", "en": "Session"},
300
+ "todos.status": {"ru": "Статус", "en": "Status"},
301
+ "todos.created": {"ru": "создано", "en": "created"},
302
+ # ==================================================================
303
+ # VACUUM
304
+ # ==================================================================
305
+ "vacuum.header": {"ru": "🗄️ Оптимизация базы данных", "en": "🗄️ Database optimization"},
306
+ "vacuum.size_before": {"ru": "Размер до", "en": "Size before"},
307
+ "vacuum.size_after": {"ru": "Размер после", "en": "Size after"},
308
+ "vacuum.path": {"ru": "Путь", "en": "Path"},
309
+ "vacuum.freed": {"ru": "Освобождено", "en": "Freed"},
310
+ "vacuum.vacuum": {"ru": "Выполняется VACUUM...", "en": "Running VACUUM..."},
311
+ "vacuum.reindex": {"ru": "Выполняется REINDEX...", "en": "Running REINDEX..."},
312
+ "vacuum.analyze": {"ru": "Выполняется ANALYZE...", "en": "Running ANALYZE..."},
313
+ "vacuum.confirm": {"ru": "Запустить VACUUM?", "en": "Run VACUUM?"},
314
+ "vacuum.error": {"ru": "❌ Ошибка: {error}", "en": "❌ Error: {error}"},
315
+ "vacuum.done": {"ru": "✅", "en": "✅"},
316
+ "vacuum.canceled": {"ru": " Отменено.", "en": " Canceled."},
317
+ # ==================================================================
318
+ # HELP
319
+ # ==================================================================
320
+ "help.header_box_top": {
321
+ "ru": "╔══════════════════════════════════════════════════════════╗",
322
+ "en": "╔══════════════════════════════════════════════════════════╗",
323
+ },
324
+ "help.header_title": {
325
+ "ru": "║ opencode-db — справка ║",
326
+ "en": "║ opencode-db — help ║",
327
+ },
328
+ "help.header_box_bottom": {
329
+ "ru": "╚══════════════════════════════════════════════════════════╝",
330
+ "en": "╚══════════════════════════════════════════════════════════╝",
331
+ },
332
+ "help.intro": {
333
+ "ru": "opencode-db — управление базой данных OpenCode:",
334
+ "en": "opencode-db — OpenCode database manager:",
335
+ },
336
+ "help.intro2": {
337
+ "ru": "просмотр, экспорт, аналитика, очистка сессий.",
338
+ "en": "browse, export, analyze, clean up sessions.",
339
+ },
340
+ "help.usage": {
341
+ "ru": "Использование: opencode-db <команда> [флаги]",
342
+ "en": "Usage: opencode-db <command> [flags]",
343
+ },
344
+ "help.footer": {
345
+ "ru": "opencode-db help <команда> — справка по конкретной команде",
346
+ "en": "opencode-db help <command> — help for a specific command",
347
+ },
348
+ "help.footer2": {
349
+ "ru": "opencode-db <команда> --help — флаги команды",
350
+ "en": "opencode-db <command> --help — command flags",
351
+ },
352
+ "help.footer3": {
353
+ "ru": "opencode-db --help — краткий список команд",
354
+ "en": "opencode-db --help — brief command list",
355
+ },
356
+ "help.unknown": {"ru": "❌ Неизвестная команда: {cmd}", "en": "❌ Unknown command: {cmd}"},
357
+ "help.available": {"ru": " Доступные: {cmds}", "en": " Available: {cmds}"},
358
+ # ==================================================================
359
+ # HELP — команды (краткие описания в справке)
360
+ # ==================================================================
361
+ "help.cmd.list": {"ru": "Список сессий", "en": "List sessions"},
362
+ "help.cmd.info": {"ru": "Детальная информация о сессии", "en": "Session details"},
363
+ "help.cmd.export": {"ru": "Экспорт диалога в Markdown", "en": "Export dialog to Markdown"},
364
+ "help.cmd.delete": {"ru": "Удаление сессии", "en": "Delete a session"},
365
+ "help.cmd.costs": {"ru": "Анализ расходов на токены", "en": "Token cost analysis"},
366
+ "help.cmd.prune": {"ru": "Массовая очистка сессий", "en": "Bulk session cleanup"},
367
+ "help.cmd.search": {"ru": "Поиск по сообщениям", "en": "Search messages"},
368
+ "help.cmd.tree": {"ru": "Дерево ветвления сессий", "en": "Session branching tree"},
369
+ "help.cmd.projects": {"ru": "Список проектов", "en": "Project list"},
370
+ "help.cmd.stats": {"ru": "Статистика БД", "en": "Database statistics"},
371
+ "help.cmd.todos": {"ru": "Задачи из сессий", "en": "Tasks from sessions"},
372
+ "help.cmd.vacuum": {"ru": "Оптимизация БД", "en": "Database optimization"},
373
+ "help.cmd.help": {"ru": "Подробная справка", "en": "Detailed help"},
374
+ # ==================================================================
375
+ # HELP — примеры
376
+ # ==================================================================
377
+ "help.list.e0": {"ru": "Сессии за последнее время", "en": "Recent sessions"},
378
+ "help.list.e1": {"ru": "Показать 10 последних", "en": "Show last 10"},
379
+ "help.list.e2": {"ru": "Сортировка по стоимости", "en": "Sort by cost"},
380
+ "help.list.e3": {"ru": "Фильтр по проекту", "en": "Filter by project"},
381
+ "help.list.e4": {"ru": "Вывод в JSON", "en": "JSON output"},
382
+ "help.info.e0": {
383
+ "ru": "Все метаданные, токены, todo, ветвление",
384
+ "en": "Metadata, tokens, todos, branching",
385
+ },
386
+ "help.info.e1": {"ru": "То же в JSON", "en": "Same in JSON"},
387
+ "help.export.e0": {"ru": "Экспорт по ID", "en": "Export by ID"},
388
+ "help.export.e1": {"ru": "Самая свежая сессия", "en": "Most recent session"},
389
+ "help.export.e2": {"ru": "Интерактивный выбор из списка", "en": "Interactive picker"},
390
+ "help.export.e3": {
391
+ "ru": "Полный вывод без обрезания output'ов",
392
+ "en": "Full output, no truncation",
393
+ },
394
+ "help.export.e4": {"ru": "Перезаписать существующий файл", "en": "Overwrite existing file"},
395
+ "help.export.e5": {"ru": "Добавить заметку в log.md", "en": "Add a note to log.md"},
396
+ "help.delete.e0": {"ru": "Удалить с подтверждением", "en": "Delete with confirmation"},
397
+ "help.delete.e1": {"ru": "Показать что будет удалено", "en": "Preview what will be deleted"},
398
+ "help.delete.e2": {"ru": "Без подтверждения", "en": "Skip confirmation"},
399
+ "help.costs.e0": {"ru": "Топ сессий по расходам", "en": "Top sessions by cost"},
400
+ "help.costs.e1": {"ru": "Детально по сессии", "en": "Single session breakdown"},
401
+ "help.costs.e2": {"ru": "Сумма по всей БД", "en": "Total across all sessions"},
402
+ "help.costs.e3": {"ru": "Сумма по проекту", "en": "Total by project"},
403
+ "help.costs.e4": {"ru": "Вывод в JSON", "en": "JSON output"},
404
+ "help.prune.e0": {"ru": "Удалить старше 90 дней", "en": "Delete older than 90 days"},
405
+ "help.prune.e1": {"ru": "Оставить 20 последних", "en": "Keep 20 most recent"},
406
+ "help.prune.e2": {"ru": "Показать что будет удалено", "en": "Preview deletions"},
407
+ "help.prune.e3": {"ru": "Без подтверждения", "en": "Skip confirmation"},
408
+ "help.search.e0": {"ru": "Поиск по всем сессиям", "en": "Search all sessions"},
409
+ "help.search.e1": {"ru": "В конкретной сессии", "en": "Within a specific session"},
410
+ "help.search.e2": {"ru": "Вывод в JSON", "en": "JSON output"},
411
+ "help.tree.e0": {"ru": "Корневые сессии с детьми", "en": "Root sessions with children"},
412
+ "help.tree.e1": {"ru": "Ветка от указанной сессии", "en": "Branch from a session"},
413
+ "help.tree.e2": {"ru": "Глубина 3 уровня", "en": "Max depth of 3"},
414
+ "help.projects.e0": {"ru": "Проекты со статистикой", "en": "Projects with statistics"},
415
+ "help.projects.e1": {"ru": "Вывод в JSON", "en": "JSON output"},
416
+ "help.stats.e0": {
417
+ "ru": "Сессии, сообщения, токены, стоимость, топ моделей",
418
+ "en": "Sessions, messages, tokens, costs, top models",
419
+ },
420
+ "help.stats.e1": {"ru": "Вывод в JSON", "en": "JSON output"},
421
+ "help.todos.e0": {"ru": "Все задачи", "en": "All tasks"},
422
+ "help.todos.e1": {"ru": "Задачи конкретной сессии", "en": "Tasks for a session"},
423
+ "help.todos.e2": {"ru": "Только ожидающие", "en": "Pending only"},
424
+ "help.todos.e3": {"ru": "Вывод в JSON", "en": "JSON output"},
425
+ "help.vacuum.e0": {
426
+ "ru": "VACUUM + REINDEX + ANALYZE с подтверждением",
427
+ "en": "VACUUM + REINDEX + ANALYZE with confirmation",
428
+ },
429
+ "help.vacuum.e1": {"ru": "Без подтверждения", "en": "Skip confirmation"},
430
+ # ==================================================================
431
+ # MARKDOWN EXPORT (formatters.py, config.py)
432
+ # ==================================================================
433
+ "md.reasoning": {"ru": "Рассуждение", "en": "Reasoning"},
434
+ "md.sound": {"ru": "Аудиосообщение", "en": "Voice message"},
435
+ "md.result": {"ru": "Результат", "en": "Result"},
436
+ "md.header.title": {"ru": "Диалог", "en": "Conversation"},
437
+ "md.header.date": {"ru": "Дата", "en": "Date"},
438
+ "md.header.model": {"ru": "Модель", "en": "Model"},
439
+ "md.header.session_id": {"ru": "ID сессии", "en": "Session ID"},
440
+ "md.header.messages": {"ru": "Сообщений", "en": "Messages"},
441
+ "md.log_agents": {"ru": "AGENTS.md: {v}", "en": "AGENTS.md: {v}"},
442
+ "md.log_files": {
443
+ "ru": "Файлов: {files}, директорий: {dirs}",
444
+ "en": "Files: {files}, directories: {dirs}",
445
+ },
446
+ "md.log_not_found": {"ru": "не найден", "en": "not found"},
447
+ "md.log_lines": {
448
+ "ru": "{lines} строк, {sections} секций",
449
+ "en": "{lines} lines, {sections} sections",
450
+ },
451
+ # ==================================================================
452
+ # SCHEMA GUARD
453
+ # ==================================================================
454
+ "schema.table_missing": {
455
+ "ru": " ⚠️ Таблица '{table}' не найдена в БД. Возможно, OpenCode обновился.",
456
+ "en": " ⚠️ Table '{table}' not found in the database. OpenCode may have been updated.",
457
+ },
458
+ "schema.column_missing": {
459
+ "ru": " ⚠️ В таблице '{table}' отсутствуют колонки: {columns}",
460
+ "en": " ⚠️ Table '{table}' is missing columns: {columns}",
461
+ },
462
+ "schema.check_upgrade": {
463
+ "ru": " Проверь совместимость версий opencode-db и OpenCode.",
464
+ "en": " Check opencode-db and OpenCode version compatibility.",
465
+ },
466
+ # ==================================================================
467
+ # ФОРМАТТЕРЫ
468
+ # ==================================================================
469
+ "fmt.rows_count": {"ru": "\n Всего: {n}", "en": "\n Total: {n}"},
470
+ # ==================================================================
471
+ # HELP — лейблы для _print_command_help
472
+ # ==================================================================
473
+ "help.cmd_header": {"ru": " ── {cmd} ── {desc}", "en": " ── {cmd} ── {desc}"},
474
+ # ==================================================================
475
+ # INFO — duration helpers
476
+ # ==================================================================
477
+ "info.dur_sec": {"ru": "{n} сек", "en": "{n} sec"},
478
+ "info.dur_min": {"ru": "{n} мин", "en": "{n} min"},
479
+ "info.dur_hours": {"ru": "{h}ч {m}мин", "en": "{h}h {m}m"},
480
+ # ==================================================================
481
+ # CLI — da
482
+ # ==================================================================
483
+ "cli.yn_yes": {"ru": "да", "en": "yes"},
484
+ # ==================================================================
485
+ # MARKDOWN ROLES (export headers)
486
+ # ==================================================================
487
+ "export.role_user": {"ru": "👤 Вы ({ts})", "en": "👤 You ({ts})"},
488
+ "export.role_assistant": {
489
+ "ru": "🤖 Ассистент{agent} ({ts})",
490
+ "en": "🤖 Assistant{agent} ({ts})",
491
+ },
492
+ "export.agent_tag": {"ru": " ({agent})", "en": " ({agent})"},
493
+ # ==================================================================
494
+ # COSTS — scope labels
495
+ # ==================================================================
496
+ "costs.by_project": {"ru": "ПРОЕКТУ", "en": "PROJECT"},
497
+ "costs.by_all": {"ru": "ВСЕЙ БД", "en": "DATABASE"},
498
+ # ==================================================================
499
+ # TABLE — header separators
500
+ # ==================================================================
501
+ # (эмодзи и символы не переводим)
502
+ }
503
+
504
+
505
+ def set_lang(lang: str):
506
+ """Устанавливает язык вывода.
507
+
508
+ Args:
509
+ lang: "ru" или "en"
510
+ """
511
+ global current_lang
512
+ if lang in ("ru", "en"):
513
+ current_lang = lang
514
+
515
+
516
+ def _(key: str, **kwargs) -> str:
517
+ """Возвращает локализованную строку по ключу.
518
+
519
+ Args:
520
+ key: ключ в словаре L (например "list.header.id")
521
+ **kwargs: значения для str.format()
522
+
523
+ Returns:
524
+ str на текущем языке, или сам key если ключ не найден
525
+ """
526
+ entry = L.get(key)
527
+ if entry is None:
528
+ return key
529
+ text = entry.get(current_lang, entry.get("ru", key))
530
+ if kwargs:
531
+ return text.format(**kwargs)
532
+ return text
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: opencode-db
3
+ Version: 0.3.0
4
+ Summary: OpenCode database CLI manager — browse, export, analyze, clean up sessions
5
+ Requires-Python: >=3.12
6
+ License-File: LICENSE
7
+ Dynamic: license-file
@@ -0,0 +1,26 @@
1
+ cli.py,sha256=4KDlBmuoYzeFy79sbExPTcPswGCacASw44Q0xwUMdXQ,2040
2
+ cmd_costs.py,sha256=XAs7wj9tZe3QnrUoJ54DTL6NFccXPSeWrjrQCrZERHY,5970
3
+ cmd_delete.py,sha256=dRgm7QML290wrGscMCz3818l6eZ69flH5YneI1WccVQ,1879
4
+ cmd_export.py,sha256=DtHYLrEJxQtjYsBCtS3xDmdIsTnb1DlIhXBgoMZJWIU,6057
5
+ cmd_help.py,sha256=jHJ6RRSt_uvOWrsfVFlJXWVnB4qop-2_eB3N_V16_6k,5596
6
+ cmd_info.py,sha256=dumw4vSZcjdFapLB5vH2XVk8lGIazBpTjl9t9WUkxNo,5826
7
+ cmd_list.py,sha256=NLvcGIna56gj683L2BunUyVEJRVxbT-W4-AyhCETEwQ,3998
8
+ cmd_projects.py,sha256=oSX8ngjK5GWbObLIUJGm96WkCTZV1I3GNkRz9qCJ9ks,2701
9
+ cmd_prune.py,sha256=RUyNi-iu47aQATBSfiLuHKvC4QxGYiP3cOybxjSyKuU,4297
10
+ cmd_search.py,sha256=FU420LHUGjoI86-FChZSvWe98tcBJzxjLWKYJuRFWYY,3390
11
+ cmd_stats.py,sha256=zcArLn4MskoYJtZ2zNQ9eBx43y3GVt1HWU9LQ5P-HR0,4555
12
+ cmd_todos.py,sha256=TQtCVtvcLNc3Rtn7WBRbV0Y6Trs_aIRetaGoUZ4OJL8,2756
13
+ cmd_tree.py,sha256=Js9Rl7ZudWA2PO_IPTbZVVOENtRkGxZLBcYsI8_Uo10,2894
14
+ cmd_vacuum.py,sha256=bf1CWVzif0ufmAv_iUOd6zCtnWc23ttw_qLEg7OzAno,1911
15
+ config.py,sha256=Nrre_pS2JI5UGOqdLBJFuG5NUXAk2FImapxgyahyEAU,655
16
+ db.py,sha256=OX35DUVGLgcoHIhaFOcgMuJHHVHz-M9Cunkz-EAgAzY,11640
17
+ formatters.py,sha256=cMRV88fTwu1H53qyvxrsi0SBjgLMiL7lslbC62kORuY,6609
18
+ i18n.py,sha256=Rj2UAOZvSnDpJQ3E31KVCWX3K5gO3oDr-U_HgbObPzo,30006
19
+ utils.py,sha256=YI73dNbRnM2mLl-0jGJukUmWLiIvdUqM3uIX6dnHOdA,3658
20
+ commands/__init__.py,sha256=YETQJBFAkefRCNHqXTa67blS0g8766ApVcg6OhG81oE,1252
21
+ opencode_db-0.3.0.dist-info/licenses/LICENSE,sha256=NhUr8ufTYb5u9F08J5bmDQ0xTVmCOexoq1ki_RdCqys,1210
22
+ opencode_db-0.3.0.dist-info/METADATA,sha256=2xT2S7eTV30z1wAvQB-PUg1EOblFZc4expdXh1dbzlQ,209
23
+ opencode_db-0.3.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
24
+ opencode_db-0.3.0.dist-info/entry_points.txt,sha256=dMHc-RWkGEu0mTNvHHThSEKQdGbCdwdKUCO-cLF7TCw,41
25
+ opencode_db-0.3.0.dist-info/top_level.txt,sha256=lKPHa0NndvZjjoZ5mIatAdvtFlPA3i-Fm_tEl3JvgTg,178
26
+ opencode_db-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ opencode-db = cli:main
@@ -0,0 +1,25 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vasilev Stas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ © 2026 Vasilev Stas — код можно свободно использовать, изменять и распространять
@@ -0,0 +1,20 @@
1
+ cli
2
+ cmd_costs
3
+ cmd_delete
4
+ cmd_export
5
+ cmd_help
6
+ cmd_info
7
+ cmd_list
8
+ cmd_projects
9
+ cmd_prune
10
+ cmd_search
11
+ cmd_stats
12
+ cmd_todos
13
+ cmd_tree
14
+ cmd_vacuum
15
+ commands
16
+ config
17
+ db
18
+ formatters
19
+ i18n
20
+ utils