@grinev/opencode-telegram-bot 0.21.0 → 0.21.2

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.
Files changed (182) hide show
  1. package/.env.example +18 -2
  2. package/README.md +21 -8
  3. package/dist/app/{start-bot-app.js → bootstrap/start-bot-app.js} +16 -15
  4. package/dist/{bot/utils/assistant-run-footer.js → app/formatters/assistant-run-footer-formatter.js} +1 -1
  5. package/dist/{summary → app/formatters}/subagent-formatter.js +8 -3
  6. package/dist/{summary/formatter.js → app/formatters/summary-formatter.js} +4 -132
  7. package/dist/{summary → app/formatters}/tool-message-batcher.js +1 -1
  8. package/dist/{bot/assistant-run-state.js → app/managers/assistant-run-state-manager.js} +1 -1
  9. package/dist/{attach/manager.js → app/managers/attach-manager.js} +1 -1
  10. package/dist/{background-session/tracker.js → app/managers/background-session-manager.js} +2 -2
  11. package/dist/{scheduled-task/foreground-state.js → app/managers/foreground-session-state-manager.js} +1 -1
  12. package/dist/{interaction/manager.js → app/managers/interaction-manager.js} +31 -1
  13. package/dist/{permission/manager.js → app/managers/permission-manager.js} +1 -1
  14. package/dist/{question/manager.js → app/managers/question-manager.js} +1 -1
  15. package/dist/{rename/manager.js → app/managers/rename-manager.js} +1 -1
  16. package/dist/{scheduled-task/creation-manager.js → app/managers/scheduled-task-creation-manager.js} +2 -2
  17. package/dist/{summary/aggregator.js → app/managers/summary-aggregation-manager.js} +3 -3
  18. package/dist/{agent/manager.js → app/services/agent-selection-service.js} +4 -5
  19. package/dist/{attach/service.js → app/services/attach-service.js} +23 -44
  20. package/dist/{bot/utils/busy-reconciliation.js → app/services/busy-reconciliation-service.js} +12 -7
  21. package/dist/app/services/command-catalog-service.js +21 -0
  22. package/dist/{bot/utils/external-user-input.js → app/services/external-user-input-service.js} +4 -19
  23. package/dist/app/services/file-browser-service.js +265 -0
  24. package/dist/{bot/utils/file-download.js → app/services/file-download-service.js} +14 -28
  25. package/dist/app/services/mcp-catalog-service.js +79 -0
  26. package/dist/app/services/message-history-service.js +81 -0
  27. package/dist/{model/capabilities.js → app/services/model-capabilities-service.js} +2 -2
  28. package/dist/{model/context-limit.js → app/services/model-context-limit-service.js} +4 -4
  29. package/dist/{model/manager.js → app/services/model-selection-service.js} +4 -4
  30. package/dist/{project/manager.js → app/services/project-service.js} +3 -3
  31. package/dist/app/services/project-switch-service.js +39 -0
  32. package/dist/{bot/utils/busy-guard.js → app/services/run-control-service.js} +3 -14
  33. package/dist/{scheduled-task/executor.js → app/services/scheduled-task-executor-service.js} +5 -5
  34. package/dist/{scheduled-task/runtime.js → app/services/scheduled-task-runtime-service.js} +17 -56
  35. package/dist/{scheduled-task/schedule-parser.js → app/services/scheduled-task-schedule-parser-service.js} +3 -3
  36. package/dist/{scheduled-task/session-ignore.js → app/services/scheduled-task-session-ignore-service.js} +1 -1
  37. package/dist/{session/cache-manager.js → app/services/session-cache-service.js} +3 -3
  38. package/dist/{session/manager.js → app/services/session-service.js} +1 -1
  39. package/dist/app/services/skills-catalog-service.js +21 -0
  40. package/dist/{stt/client.js → app/services/stt-service.js} +2 -2
  41. package/dist/{tts/client.js → app/services/tts-service.js} +68 -5
  42. package/dist/{variant/manager.js → app/services/variant-selection-service.js} +3 -3
  43. package/dist/{scheduled-task/store.js → app/stores/scheduled-task-store.js} +3 -3
  44. package/dist/{settings/manager.js → app/stores/settings-store.js} +3 -3
  45. package/dist/app/types/worktree.js +1 -0
  46. package/dist/bot/{handlers/agent.js → callbacks/agent-selection-callback-handler.js} +8 -57
  47. package/dist/bot/callbacks/callback-router.js +110 -0
  48. package/dist/bot/{commands/commands.js → callbacks/command-catalog-callback-handler.js} +17 -197
  49. package/dist/bot/{handlers/context.js → callbacks/context-control-callback-handler.js} +3 -32
  50. package/dist/bot/callbacks/file-browser-callback-handler.js +219 -0
  51. package/dist/bot/callbacks/inline-menu-cancel-callback-handler.js +32 -0
  52. package/dist/bot/{commands/mcps.js → callbacks/mcp-catalog-callback-handler.js} +7 -172
  53. package/dist/bot/{commands/messages.js → callbacks/message-history-callback-handler.js} +17 -248
  54. package/dist/bot/{handlers/model.js → callbacks/model-selection-callback-handler.js} +10 -71
  55. package/dist/bot/callbacks/permission-callback-handler.js +114 -0
  56. package/dist/bot/callbacks/project-callback-handler.js +79 -0
  57. package/dist/bot/callbacks/question-callback-handler.js +159 -0
  58. package/dist/bot/{commands/rename.js → callbacks/rename-callback-handler.js} +6 -34
  59. package/dist/bot/{commands/tasklist.js → callbacks/scheduled-task-callback-handler.js} +83 -68
  60. package/dist/bot/{commands/sessions.js → callbacks/session-callback-handler.js} +19 -158
  61. package/dist/bot/{commands/skills.js → callbacks/skills-catalog-callback-handler.js} +8 -188
  62. package/dist/bot/{handlers/variant.js → callbacks/variant-selection-callback-handler.js} +7 -69
  63. package/dist/bot/callbacks/worktree-callback-handler.js +97 -0
  64. package/dist/bot/commands/{abort.js → abort-command.js} +6 -6
  65. package/dist/bot/commands/command-catalog-command.js +42 -0
  66. package/dist/bot/commands/{detach.js → detach-command.js} +8 -8
  67. package/dist/bot/commands/ls-command.js +48 -0
  68. package/dist/bot/commands/mcp-catalog-command.js +39 -0
  69. package/dist/bot/commands/messages-command.js +59 -0
  70. package/dist/bot/commands/{new.js → new-command.js} +12 -11
  71. package/dist/bot/commands/open-command.js +45 -0
  72. package/dist/bot/commands/{opencode-start.js → opencode-start-command.js} +1 -1
  73. package/dist/bot/commands/{opencode-stop.js → opencode-stop-command.js} +1 -1
  74. package/dist/bot/commands/projects-command.js +32 -0
  75. package/dist/bot/commands/rename-command.js +33 -0
  76. package/dist/bot/commands/sessions-command.js +42 -0
  77. package/dist/bot/commands/skills-catalog-command.js +42 -0
  78. package/dist/bot/commands/{start.js → start-command.js} +12 -12
  79. package/dist/bot/commands/{status.js → status-command.js} +9 -9
  80. package/dist/bot/commands/{task.js → task-command.js} +9 -81
  81. package/dist/bot/commands/tasklist-command.js +40 -0
  82. package/dist/bot/commands/{tts.js → tts-command.js} +2 -2
  83. package/dist/bot/commands/worktree-command.js +47 -0
  84. package/dist/bot/handlers/{document.js → document-handler.js} +3 -3
  85. package/dist/bot/handlers/{media-group.js → media-group-handler.js} +3 -3
  86. package/dist/bot/handlers/photo-handler.js +44 -0
  87. package/dist/bot/handlers/prompt.js +16 -16
  88. package/dist/bot/handlers/text-message-handler.js +61 -0
  89. package/dist/bot/handlers/tts-response-handler.js +29 -0
  90. package/dist/bot/handlers/{voice.js → voice-handler.js} +2 -2
  91. package/dist/bot/index.js +27 -1130
  92. package/dist/{keyboard/manager.js → bot/keyboards/keyboard-manager.js} +6 -6
  93. package/dist/bot/keyboards/keyboard-types.js +1 -0
  94. package/dist/bot/{utils/keyboard.js → keyboards/main-reply-keyboard.js} +2 -2
  95. package/dist/bot/menus/agent-selection-menu.js +54 -0
  96. package/dist/bot/menus/command-catalog-menu.js +101 -0
  97. package/dist/bot/menus/context-control-menu.js +33 -0
  98. package/dist/bot/menus/file-browser-menu.js +265 -0
  99. package/dist/bot/{handlers → menus}/inline-menu.js +4 -33
  100. package/dist/bot/menus/mcp-catalog-menu.js +97 -0
  101. package/dist/bot/menus/message-history-menu.js +107 -0
  102. package/dist/bot/menus/model-selection-menu.js +66 -0
  103. package/dist/bot/menus/permission-menu.js +116 -0
  104. package/dist/bot/{commands/projects.js → menus/project-selection-menu.js} +5 -105
  105. package/dist/bot/{handlers/question.js → menus/question-menu.js} +10 -166
  106. package/dist/bot/menus/rename-menu.js +6 -0
  107. package/dist/bot/menus/scheduled-task-menu.js +42 -0
  108. package/dist/bot/menus/session-selection-menu.js +115 -0
  109. package/dist/bot/menus/skills-catalog-menu.js +99 -0
  110. package/dist/bot/menus/variant-selection-menu.js +67 -0
  111. package/dist/bot/menus/worktree-selection-menu.js +76 -0
  112. package/dist/bot/{utils → messages}/assistant-rendering.js +2 -2
  113. package/dist/bot/messages/busy-blocked-renderer.js +11 -0
  114. package/dist/bot/messages/external-user-input-notification.js +22 -0
  115. package/dist/bot/messages/scheduled-task-delivery.js +63 -0
  116. package/dist/bot/{utils → messages}/send-downloaded-file.js +1 -1
  117. package/dist/bot/messages/summary-message-formatter.js +130 -0
  118. package/dist/bot/{utils → messages}/telegram-text.js +60 -0
  119. package/dist/{interaction/guard.js → bot/middleware/interaction-guard-decision.js} +11 -4
  120. package/dist/bot/middleware/interaction-guard.js +2 -2
  121. package/dist/bot/middleware/unknown-command.js +1 -1
  122. package/dist/{pinned/format.js → bot/pinned/pinned-message-format.js} +1 -2
  123. package/dist/{pinned/manager.js → bot/pinned/pinned-message-manager.js} +10 -10
  124. package/dist/bot/pinned/pinned-message-types.js +1 -0
  125. package/dist/{summary → bot/render}/markdown-to-telegram-v2.js +2 -2
  126. package/dist/bot/render/types.js +1 -0
  127. package/dist/bot/routers/command-router.js +73 -0
  128. package/dist/bot/routers/message-router.js +150 -0
  129. package/dist/bot/services/attach-presentation.js +40 -0
  130. package/dist/bot/services/event-subscription-service.js +842 -0
  131. package/dist/bot/services/project-switch-presentation.js +26 -0
  132. package/dist/bot/streaming/response-streamer.js +18 -2
  133. package/dist/cli.js +4 -4
  134. package/dist/config.js +21 -3
  135. package/dist/i18n/ar.js +491 -0
  136. package/dist/i18n/index.js +7 -0
  137. package/dist/index.js +1 -1
  138. package/dist/opencode/ready-refresh.js +2 -2
  139. package/dist/{service → runtime/service}/manager.js +2 -2
  140. package/dist/runtime/service/types.js +1 -0
  141. package/package.json +1 -1
  142. package/dist/bot/commands/ls.js +0 -445
  143. package/dist/bot/commands/open.js +0 -319
  144. package/dist/bot/commands/worktree.js +0 -201
  145. package/dist/bot/handlers/permission.js +0 -235
  146. package/dist/bot/utils/browser-roots.js +0 -140
  147. package/dist/bot/utils/file-tree.js +0 -92
  148. package/dist/bot/utils/send-tts-response.js +0 -37
  149. package/dist/bot/utils/switch-project.js +0 -45
  150. package/dist/bot/utils/telegram-file-url.js +0 -11
  151. package/dist/interaction/busy.js +0 -8
  152. package/dist/interaction/cleanup.js +0 -32
  153. /package/dist/{scheduled-task/display.js → app/formatters/scheduled-task-display-formatter.js} +0 -0
  154. /package/dist/{bot/utils/abort-error-suppression.js → app/managers/abort-suppression-manager.js} +0 -0
  155. /package/dist/{external-input/suppression.js → app/managers/external-input-suppression-manager.js} +0 -0
  156. /package/dist/{scheduled-task/next-run.js → app/services/scheduled-task-next-run-service.js} +0 -0
  157. /package/dist/{git/worktree.js → app/services/worktree-service.js} +0 -0
  158. /package/dist/{agent/types.js → app/types/agent.js} +0 -0
  159. /package/dist/{interaction/types.js → app/types/interaction.js} +0 -0
  160. /package/dist/{model/types.js → app/types/model.js} +0 -0
  161. /package/dist/{keyboard/types.js → app/types/permission.js} +0 -0
  162. /package/dist/{permission/types.js → app/types/project.js} +0 -0
  163. /package/dist/{pinned/types.js → app/types/question.js} +0 -0
  164. /package/dist/{scheduled-task/types.js → app/types/scheduled-task.js} +0 -0
  165. /package/dist/{question/types.js → app/types/session.js} +0 -0
  166. /package/dist/{service/types.js → app/types/settings.js} +0 -0
  167. /package/dist/{telegram/render/types.js → app/types/summary.js} +0 -0
  168. /package/dist/{variant/types.js → app/types/variant.js} +0 -0
  169. /package/dist/bot/commands/{help.js → help-command.js} +0 -0
  170. /package/dist/bot/{utils → messages}/send-with-markdown-fallback.js +0 -0
  171. /package/dist/bot/{utils → messages}/thinking-message.js +0 -0
  172. /package/dist/{telegram → bot}/render/block-fallback.js +0 -0
  173. /package/dist/{telegram → bot}/render/block-parser.js +0 -0
  174. /package/dist/{telegram → bot}/render/block-renderer.js +0 -0
  175. /package/dist/{telegram → bot}/render/chunker.js +0 -0
  176. /package/dist/{telegram → bot}/render/inline-renderer.js +0 -0
  177. /package/dist/{telegram → bot}/render/markdown-normalizer.js +0 -0
  178. /package/dist/{telegram → bot}/render/pipeline.js +0 -0
  179. /package/dist/{telegram → bot}/render/validator.js +0 -0
  180. /package/dist/bot/{utils/commands.js → routers/command-utils.js} +0 -0
  181. /package/dist/bot/{utils → streaming}/finalize-assistant-response.js +0 -0
  182. /package/dist/{service/runtime.js → runtime/service/env.js} +0 -0
@@ -0,0 +1,491 @@
1
+ /**
2
+ * Arabic localization for OpenCode Telegram Bot.
3
+ *
4
+ * Keep upstream emoji and technical terms where they help recognition.
5
+ * Prefer natural Arabic wording over literal translation.
6
+ * Keep this dictionary complete to avoid falling back to another locale.
7
+ */
8
+ export const ar = {
9
+ "cmd.description.status": "عرض حالة الخادم والجلسة",
10
+ "cmd.description.new": "بدء جلسة جديدة",
11
+ "cmd.description.stop": "إيقاف المهمة الحالية",
12
+ "cmd.description.detach": "الخروج من الجلسة دون إيقافها",
13
+ "cmd.description.sessions": "عرض الجلسات السابقة",
14
+ "cmd.description.messages": "استعراض رسائل الجلسة",
15
+ "cmd.description.tts": "تشغيل أو إيقاف الردود الصوتية",
16
+ "cmd.description.projects": "عرض المشاريع",
17
+ "cmd.description.worktree": "التبديل بين نسخ العمل في Git",
18
+ "cmd.description.task": "إنشاء مهمة مجدولة",
19
+ "cmd.description.tasklist": "عرض المهام المجدولة",
20
+ "cmd.description.commands": "الأوامر المخصصة",
21
+ "cmd.description.skills": "قائمة المهارات",
22
+ "cmd.description.mcps": "خوادم MCP",
23
+ "cmd.description.opencode_start": "تشغيل خادم OpenCode",
24
+ "cmd.description.opencode_stop": "إيقاف خادم OpenCode",
25
+ "cmd.description.ls": "استعراض ملفات المجلد",
26
+ "cmd.description.help": "المساعدة",
27
+ "cmd.description.rename": "تغيير اسم الجلسة الحالية",
28
+ "cmd.description.open": "إضافة مشروع عبر استعراض المجلدات",
29
+ "callback.unknown_command": "الأمر غير معروف",
30
+ "callback.processing_error": "تعذر تنفيذ الطلب",
31
+ "error.load_agents": "❌ تعذر تحميل قائمة الوكلاء",
32
+ "error.load_models": "❌ تعذر تحميل قائمة النماذج",
33
+ "error.load_variants": "❌ تعذر تحميل خيارات النموذج",
34
+ "error.context_button": "❌ تعذر تنفيذ إجراء السياق",
35
+ "error.generic": "🔴 حدث خطأ غير متوقع.",
36
+ "interaction.blocked.expired": "⚠️ انتهت صلاحية هذا الإجراء. ابدأه من جديد.",
37
+ "interaction.blocked.expected_callback": "⚠️ استخدم الأزرار الظاهرة في الرسالة لهذه الخطوة، أو اضغط إلغاء.",
38
+ "interaction.blocked.expected_text": "⚠️ أرسل رسالة نصية لإكمال هذه الخطوة.",
39
+ "interaction.blocked.expected_command": "⚠️ أرسل أمرًا لإكمال هذه الخطوة.",
40
+ "interaction.blocked.command_not_allowed": "⚠️ لا يمكن استخدام هذا الأمر في الخطوة الحالية.",
41
+ "interaction.blocked.finish_current": "⚠️ أكمل الإجراء الحالي أولًا، ثم افتح قائمة أخرى.",
42
+ "inline.blocked.expected_choice": "⚠️ اختر أحد الخيارات من الأزرار الظاهرة في الرسالة، أو اضغط إلغاء.",
43
+ "inline.blocked.command_not_allowed": "⚠️ لا يمكن استخدام هذا الأمر أثناء فتح القائمة الحالية.",
44
+ "question.blocked.expected_answer": "⚠️ أجب عن السؤال الحالي باستخدام الأزرار، أو اختر إجابة مخصصة، أو اضغط إلغاء.",
45
+ "question.blocked.command_not_allowed": "⚠️ لا يمكن استخدام هذا الأمر قبل إنهاء السؤال الحالي.",
46
+ "inline.button.cancel": "❌ إلغاء",
47
+ "inline.inactive_callback": "انتهت صلاحية هذه القائمة",
48
+ "inline.cancelled_callback": "تم الإلغاء",
49
+ "common.unknown": "غير معروف",
50
+ "common.unknown_error": "خطأ غير معروف",
51
+ "start.welcome": "👋 أهلًا بك في OpenCode Telegram Bot!\n\nالأوامر الأساسية:\n/projects — اختيار مشروع\n/sessions — عرض الجلسات\n/new — بدء جلسة جديدة\n/commands — الأوامر المخصصة\n/skills — قائمة المهارات\n/task — إنشاء مهمة مجدولة\n/tasklist — عرض المهام المجدولة\n/status — حالة الخادم والجلسة\n/help — المساعدة\n\nاستخدم الأزرار السفلية للتبديل بين الوكيل والنموذج وخيارات التشغيل.",
52
+ "help.keyboard_hint": "💡 استخدم الأزرار السفلية للتبديل بين الوكيل والنموذج وخيارات التشغيل وإدارة السياق.",
53
+ "help.text": "📖 **المساعدة**\n\n/status - عرض حالة الخادم والجلسة\n/sessions - عرض الجلسات\n/new - بدء جلسة جديدة\n/help - المساعدة",
54
+ "bot.thinking": "💭 جارٍ التفكير...",
55
+ "bot.project_not_selected": "🏗 لم تحدد مشروعًا بعد.\n\nاختر مشروعًا أولًا باستخدام /projects.",
56
+ "bot.creating_session": "🔄 جارٍ بدء جلسة جديدة...",
57
+ "bot.create_session_error": "🔴 تعذر بدء جلسة جديدة. جرّب /new أو افحص حالة الخادم باستخدام /status.",
58
+ "bot.session_created": "✅ تم إنشاء الجلسة: {title}",
59
+ "bot.session_busy": "⏳ الوكيل مشغول بتنفيذ مهمة الآن. انتظر حتى ينتهي، أو استخدم /abort لإيقاف المهمة الحالية.",
60
+ "bot.session_reset_project_mismatch": "⚠️ الجلسة النشطة مرتبطة بمشروع مختلف، لذلك تمت إعادة ضبطها. استخدم /sessions لاختيار جلسة أو /new لبدء جلسة جديدة.",
61
+ "bot.prompt_send_error": "تعذر إرسال الطلب إلى OpenCode.",
62
+ "bot.session_error": "🔴 أعاد OpenCode الخطأ التالي: {message}",
63
+ "bot.session_retry": "🔁 {message}\n\nاستمر مزوّد الخدمة في إرجاع الخطأ نفسه بعد عدة محاولات. استخدم /abort لإيقاف المهمة.",
64
+ "bot.external_user_input": "رسالة واردة من واجهة أخرى",
65
+ "background.session_fallback": "الجلسة {id}",
66
+ "background.assistant_response": "🔔 وصل رد جديد من جلسة تعمل في الخلفية: {session}",
67
+ "background.question_asked": "❓ توجد جلسة في الخلفية تنتظر إجابتك: {session}",
68
+ "background.permission_asked": "🔐 توجد جلسة في الخلفية تطلب صلاحية: {session}",
69
+ "background.open_session_button": "فتح الجلسة",
70
+ "bot.unknown_command": "⚠️ الأمر غير معروف: {command}. استخدم /help لعرض الأوامر المتاحة.",
71
+ "bot.photo_downloading": "⏳ جارٍ تنزيل الصورة...",
72
+ "bot.photo_too_large": "⚠️ حجم الصورة أكبر من الحد المسموح ({maxSizeMb}MB)",
73
+ "bot.photo_model_no_image": "⚠️ النموذج الحالي لا يدعم الصور. سيتم إرسال النص فقط.",
74
+ "bot.photo_download_error": "🔴 تعذر تنزيل الصورة",
75
+ "bot.photo_no_caption": "💡 نصيحة: أضف وصفًا للصورة حتى يفهم الوكيل المطلوب منها.",
76
+ "bot.file_downloading": "⏳ جارٍ تنزيل الملف...",
77
+ "bot.files_downloading": "⏳ جارٍ تنزيل الملفات...",
78
+ "bot.file_too_large": "⚠️ حجم الملف أكبر من الحد المسموح ({maxSizeMb}MB)",
79
+ "bot.file_download_error": "🔴 تعذر تنزيل الملف",
80
+ "bot.file_type_unsupported": "⚠️ نوع الملف غير مدعوم. أرسل صورة أو ملف PDF أو ملفًا نصيًا أو برمجيًا.",
81
+ "bot.media_group_not_processed": "⚠️ تعذر معالجة ملف أو أكثر في هذه المجموعة. لم يتم إرسال أي ملف إلى OpenCode.",
82
+ "bot.media_group_download_error": "🔴 تعذر تنزيل أحد الملفات. لم يتم إرسال أي ملف إلى OpenCode.",
83
+ "bot.model_no_pdf": "⚠️ النموذج الحالي لا يدعم ملفات PDF. سيتم إرسال النص فقط.",
84
+ "bot.text_file_too_large": "⚠️ حجم الملف النصي أكبر من الحد المسموح ({maxSizeKb}KB)",
85
+ "status.header_running": "🟢 خادم OpenCode يعمل",
86
+ "status.health.healthy": "يعمل بشكل طبيعي",
87
+ "status.health.unhealthy": "توجد مشكلة",
88
+ "status.line.health": "الحالة: {health}",
89
+ "status.line.version": "الإصدار: {version}",
90
+ "status.line.managed_yes": "تم تشغيله بواسطة البوت: نعم",
91
+ "status.line.managed_no": "تم تشغيله بواسطة البوت: لا",
92
+ "status.line.pid": "PID: {pid}",
93
+ "status.line.uptime_sec": "مدة التشغيل: {seconds} ثانية",
94
+ "status.line.mode": "الوكيل: {mode}",
95
+ "status.line.model": "النموذج: {model}",
96
+ "status.line.tts": "الردود الصوتية: {tts}",
97
+ "status.tts.on": "مفعّلة",
98
+ "status.tts.off": "معطّلة",
99
+ "status.agent_not_set": "غير محدد",
100
+ "status.project_selected": "المشروع: {project}",
101
+ "status.worktree_selected": "نسخة العمل: {worktree}",
102
+ "status.project_not_selected": "المشروع: غير محدد",
103
+ "status.project_hint": "استخدم /projects لاختيار مشروع",
104
+ "status.session_selected": "الجلسة الحالية: {title}",
105
+ "status.session_not_selected": "الجلسة الحالية: غير محددة",
106
+ "status.session_hint": "استخدم /sessions لاختيار جلسة أو /new لبدء جلسة جديدة",
107
+ "status.server_unavailable": "🔴 خادم OpenCode غير متاح\n\nاستخدم /opencode_start لتشغيل الخادم.",
108
+ "tts.enabled": "🔊 تم تفعيل الردود الصوتية.",
109
+ "tts.not_configured": "⚠️ الردود الصوتية غير متاحة حاليًا. اضبط `TTS_API_URL` و`TTS_API_KEY` أولًا.",
110
+ "tts.disabled": "🔇 تم تعطيل الردود الصوتية.",
111
+ "tts.failed": "⚠️ تعذر إنشاء الرد الصوتي.",
112
+ "projects.empty": "📭 لم يتم العثور على مشاريع.\n\nافتح مجلدًا في OpenCode وأنشئ جلسة واحدة على الأقل، ثم سيظهر المشروع هنا.",
113
+ "projects.select": "اختر مشروعًا:",
114
+ "projects.select_with_current": "اختر مشروعًا:\n\nالمشروع الحالي: 🏗 {project}",
115
+ "projects.page_indicator": "الصفحة {current}/{total}",
116
+ "projects.prev_page": "⬅️ السابق",
117
+ "projects.next_page": "التالي ➡️",
118
+ "projects.fetch_error": "🔴 تعذر تحميل المشاريع. تأكد من أن خادم OpenCode يعمل ثم حاول مرة أخرى.",
119
+ "projects.page_load_error": "تعذر تحميل هذه الصفحة. حاول مرة أخرى.",
120
+ "projects.selected": "✅ تم اختيار المشروع: {project}\n\n📋 تمت إعادة ضبط الجلسة الحالية. استخدم /sessions لاختيار جلسة أو /new لبدء جلسة جديدة.",
121
+ "projects.select_error": "🔴 تعذر اختيار المشروع.",
122
+ "sessions.project_not_selected": "🏗 لم تحدد مشروعًا بعد.\n\nاختر مشروعًا أولًا باستخدام /projects.",
123
+ "sessions.empty": "📭 لا توجد جلسات لهذا المشروع.\n\nابدأ جلسة جديدة باستخدام /new.",
124
+ "sessions.select": "اختر جلسة:",
125
+ "sessions.select_page": "اختر جلسة (الصفحة {page}):",
126
+ "sessions.fetch_error": "🔴 تعذر تحميل الجلسات. تأكد من أن خادم OpenCode يعمل ثم حاول مرة أخرى.",
127
+ "sessions.select_project_first": "🔴 حدد مشروعًا أولًا باستخدام /projects.",
128
+ "sessions.page_empty_callback": "لا توجد جلسات في هذه الصفحة",
129
+ "sessions.page_load_error_callback": "تعذر تحميل هذه الصفحة. حاول مرة أخرى.",
130
+ "sessions.button.prev_page": "⬅️ السابق",
131
+ "sessions.button.next_page": "التالي ➡️",
132
+ "sessions.loading_context": "⏳ جارٍ تحميل السياق وأحدث الرسائل...",
133
+ "sessions.selected": "✅ تم اختيار الجلسة: {title}",
134
+ "sessions.select_error": "🔴 تعذر اختيار الجلسة.",
135
+ "sessions.preview.empty": "لا توجد رسائل حديثة.",
136
+ "sessions.preview.title": "أحدث الرسائل:",
137
+ "sessions.preview.you": "أنت:",
138
+ "sessions.preview.agent": "الوكيل:",
139
+ "messages.project_not_selected": "🏗 لم تحدد مشروعًا بعد.\n\nاختر مشروعًا أولًا باستخدام /projects.",
140
+ "messages.session_not_selected": "💬 لم تحدد جلسة بعد.\n\nاختر جلسة باستخدام /sessions أو ابدأ جلسة جديدة باستخدام /new.",
141
+ "messages.session_project_mismatch": "⚠️ الجلسة المحددة مرتبطة بمشروع مختلف. اختر الجلسة مجددًا باستخدام /sessions.",
142
+ "messages.empty": "📭 لا توجد رسائل منك في الجلسة الحالية.",
143
+ "messages.select": "اختر رسالة:",
144
+ "messages.select_page": "اختر رسالة (الصفحة {page}):",
145
+ "messages.fetch_error": "🔴 تعذر تحميل الرسائل. تأكد من أن خادم OpenCode يعمل ثم حاول مرة أخرى.",
146
+ "messages.inactive_callback": "انتهت صلاحية قائمة الرسائل",
147
+ "messages.cancelled_callback": "تم الإلغاء",
148
+ "messages.page_empty_callback": "لا توجد رسائل في هذه الصفحة",
149
+ "messages.button.prev_page": "⬅️ السابق",
150
+ "messages.button.next_page": "التالي ➡️",
151
+ "messages.button.revert": "↩️ الرجوع لهذه النقطة",
152
+ "messages.button.fork": "🔀 إنشاء نسخة متفرعة",
153
+ "messages.button.back": "⬅️ رجوع",
154
+ "messages.button.cancel": "❌ إلغاء",
155
+ "messages.revert_success": "✅ تمت إعادة الجلسة إلى هذه الرسالة:\n\n{text}",
156
+ "messages.revert_error": "❌ تعذر الرجوع إلى هذه الرسالة. حاول مرة أخرى.",
157
+ "messages.fork_success": "🔀 تم إنشاء جلسة متفرعة بدءًا من الرسالة التالية:\n\n{text}",
158
+ "messages.fork_error": "❌ تعذر إنشاء جلسة متفرعة. حاول مرة أخرى.",
159
+ "attach.project_not_selected": "🏗 لم تحدد مشروعًا بعد.\n\nاختر مشروعًا أولًا باستخدام /projects.",
160
+ "attach.session_not_selected": "💬 لم تحدد جلسة بعد.\n\nاختر جلسة أولًا باستخدام /sessions.",
161
+ "attach.session_project_mismatch": "⚠️ الجلسة المحددة لا تطابق المشروع الحالي. اختر الجلسة مجددًا باستخدام /sessions.",
162
+ "attach.connected": "✅ تم الاتصال بالجلسة: {title}",
163
+ "attach.already_connected": "ℹ️ أنت متصل بالفعل بالجلسة: {title}",
164
+ "attach.status.idle_message": "الحالة: خاملة. بانتظار أحداث جديدة.",
165
+ "attach.status.busy_message": "الحالة: مشغولة. الطلبات الجديدة محظورة مؤقتًا.",
166
+ "attach.restored_question": "تمت استعادة سؤال معلق لهذه الجلسة.",
167
+ "attach.restored_permissions": "تمت استعادة طلبات الصلاحيات المعلقة: {count}.",
168
+ "attach.disconnect_hint": "لقطع الاتصال، انتقل إلى جلسة أو مشروع آخر.",
169
+ "attach.error": "🔴 تعذر الاتصال بالجلسة الحالية.",
170
+ "detach.project_not_selected": "🏗 لم تحدد مشروعًا بعد.\n\nاختر مشروعًا أولًا باستخدام /projects.",
171
+ "detach.no_active_session": "ℹ️ البوت غير متصل بأي جلسة بالفعل.",
172
+ "detach.success": "✅ تم قطع الاتصال بالجلسة: {title}\n\nلم يتم إيقاف جلسة OpenCode. إذا كانت لا تزال تعمل، فستستمر بشكل منفصل. للتحقق منها لاحقًا، اخترها مجددًا باستخدام /sessions.",
173
+ "detach.error": "🔴 تعذر قطع الاتصال بالجلسة الحالية.",
174
+ "new.project_not_selected": "🏗 لم تحدد مشروعًا بعد.\n\nاختر مشروعًا أولًا باستخدام /projects.",
175
+ "new.created": "✅ تم إنشاء جلسة جديدة: {title}",
176
+ "new.create_error": "🔴 خادم OpenCode غير متاح أو حدث خطأ أثناء إنشاء الجلسة.",
177
+ "stop.no_active_session": "🛑 لم يتم تشغيل الوكيل\n\nأنشئ جلسة باستخدام /new أو اختر جلسة باستخدام /sessions.",
178
+ "stop.in_progress": "🛑 تم إيقاف تدفق الأحداث، جارٍ إرسال إشارة الإيقاف...\n\nبانتظار توقف الوكيل.",
179
+ "stop.warn_unconfirmed": "⚠️ تم إيقاف تدفق الأحداث، لكن الخادم لم يؤكد الإيقاف.\n\nتحقق من /status ثم أعد محاولة /abort بعد بضع ثوانٍ.",
180
+ "stop.warn_maybe_finished": "⚠️ تم إيقاف تدفق الأحداث، لكن الوكيل ربما كان قد انتهى بالفعل.",
181
+ "stop.success": "✅ تم إيقاف إجراء الوكيل. لن تُرسل أي رسائل أخرى من هذا التشغيل.",
182
+ "stop.warn_still_busy": "⚠️ تم إرسال الإشارة، لكن الوكيل لا يزال مشغولًا.\n\nتدفق الأحداث معطّل بالفعل، لذلك لن تُرسل رسائل وسيطة.",
183
+ "stop.warn_timeout": "⚠️ انتهت مهلة طلب الإيقاف.\n\nتدفق الأحداث معطّل بالفعل، أعد محاولة /abort بعد بضع ثوانٍ.",
184
+ "stop.warn_local_only": "⚠️ تم إيقاف تدفق الأحداث محليًا، لكن فشل الإيقاف على الخادم.",
185
+ "stop.error": "🔴 تعذر إيقاف الإجراء.\n\nتم إيقاف تدفق الأحداث، حاول /abort مرة أخرى.",
186
+ "opencode_start.already_running_managed": "⚠️ خادم OpenCode يعمل بالفعل\n\nPID: {pid}\nمدة التشغيل: {seconds} ثانية",
187
+ "opencode_start.already_running_external": "✅ خادم OpenCode يعمل بالفعل كعملية خارجية\n\nالإصدار: {version}\n\nلم يتم تشغيل هذا الخادم بواسطة البوت، لذلك لا يمكن إيقافه باستخدام /opencode-stop.",
188
+ "opencode_start.already_running": "✅ خادم OpenCode يعمل بالفعل\n\nالإصدار: {version}",
189
+ "opencode_start.remote_configured": "⚠️ يعمل /opencode_start فقط مع خادم OpenCode محلي.",
190
+ "opencode_start.starting": "🔄 جارٍ تشغيل خادم OpenCode...",
191
+ "opencode_start.start_error": "🔴 تعذر تشغيل خادم OpenCode\n\nالخطأ: {error}\n\nتأكد من تثبيت OpenCode CLI وأنه متاح في PATH:\nopencode --version\nnpm install -g @opencode-ai/cli",
192
+ "opencode_start.started_not_ready": "⚠️ تم تشغيل خادم OpenCode، لكنه لا يستجيب\n\nPID: {pid}\n\nقد لا يزال الخادم قيد التشغيل. جرّب /status بعد بضع ثوانٍ.",
193
+ "opencode_start.success": "✅ تم تشغيل خادم OpenCode بنجاح\n\nPID: {pid}\nالإصدار: {version}",
194
+ "opencode_start.error": "🔴 حدث خطأ أثناء تشغيل الخادم.\n\nراجع سجلات التطبيق للتفاصيل.",
195
+ "opencode_stop.external_running": "⚠️ خادم OpenCode يعمل كعملية خارجية\n\nلم يتم تشغيل هذا الخادم عبر /opencode-start.\nأوقفه يدويًا أو استخدم /status للتحقق من الحالة.",
196
+ "opencode_stop.remote_configured": "⚠️ يعمل /opencode_stop فقط مع خادم OpenCode محلي.",
197
+ "opencode_stop.not_running": "⚠️ خادم OpenCode لا يعمل",
198
+ "opencode_stop.pid_not_found": "⚠️ يستجيب خادم OpenCode على المنفذ {port}، لكن لم يتم العثور على عملية محلية لإيقافها.",
199
+ "opencode_stop.stopping": "🛑 جارٍ إيقاف خادم OpenCode...\n\nPID: {pid}",
200
+ "opencode_stop.stop_error": "🔴 تعذر إيقاف خادم OpenCode\n\nالخطأ: {error}",
201
+ "opencode_stop.still_running": "لا يزال الخادم يستجيب بعد طلب الإيقاف.",
202
+ "opencode_stop.success": "✅ تم إيقاف خادم OpenCode بنجاح",
203
+ "opencode_stop.error": "🔴 حدث خطأ أثناء إيقاف الخادم.\n\nراجع سجلات التطبيق للتفاصيل.",
204
+ "agent.changed_callback": "تم تغيير الوكيل إلى: {name}",
205
+ "agent.changed_message": "✅ تم تغيير الوكيل إلى: {name}",
206
+ "agent.change_error_callback": "تعذر تغيير الوكيل",
207
+ "agent.menu.current": "الوكيل الحالي: {name}\n\nاختر وكيلًا:",
208
+ "agent.menu.select": "اختر وكيلًا:",
209
+ "agent.menu.empty": "⚠️ لا يوجد وكلاء متاحون",
210
+ "agent.menu.error": "🔴 تعذر تحميل قائمة الوكلاء",
211
+ "model.changed_callback": "تم تغيير النموذج إلى: {name}",
212
+ "model.changed_message": "✅ تم تغيير النموذج إلى: {name}",
213
+ "model.change_error_callback": "تعذر تغيير النموذج",
214
+ "model.menu.empty": "⚠️ لا توجد نماذج متاحة",
215
+ "model.menu.select": "اختر نموذجًا:",
216
+ "model.menu.current": "النموذج الحالي: {name}\n\nاختر نموذجًا:",
217
+ "model.menu.favorites_title": "⭐ المفضلة (أضف النماذج إلى المفضلة من OpenCode CLI)",
218
+ "model.menu.favorites_empty": "— لا توجد نماذج في المفضلة.",
219
+ "model.menu.recent_title": "🕘 المستخدمة مؤخرًا",
220
+ "model.menu.recent_empty": "— لا توجد نماذج مستخدمة مؤخرًا.",
221
+ "model.menu.favorites_hint": "ℹ️ أضف النماذج إلى المفضلة في OpenCode CLI لإبقائها أعلى القائمة.",
222
+ "model.menu.error": "🔴 تعذر تحميل قائمة النماذج",
223
+ "model.search.button": "🔍 بحث",
224
+ "model.search.prompt": "🔍 اكتب اسم النموذج للبحث عنه:",
225
+ "model.search.results_title": "نتائج البحث عن \"{query}\":",
226
+ "model.search.no_results": "لم يتم العثور على نماذج مطابقة لـ \"{query}\"",
227
+ "model.search.search_again": "↩ البحث مرة أخرى",
228
+ "model.search.error": "تعذر البحث عن النماذج",
229
+ "variant.model_not_selected_callback": "خطأ: لم يتم تحديد النموذج",
230
+ "variant.changed_callback": "تم تغيير الخيار إلى: {name}",
231
+ "variant.changed_message": "✅ تم تغيير الخيار إلى: {name}",
232
+ "variant.change_error_callback": "تعذر تغيير الخيار",
233
+ "variant.select_model_first": "⚠️ اختر نموذجًا أولًا",
234
+ "variant.menu.empty": "⚠️ لا توجد خيارات متاحة",
235
+ "variant.menu.current": "الخيار الحالي: {name}\n\nاختر خيارًا:",
236
+ "variant.menu.error": "🔴 تعذر تحميل قائمة الخيارات",
237
+ "context.button.confirm": "✅ نعم، اضغط السياق",
238
+ "context.no_active_session": "⚠️ لا توجد جلسة نشطة. أنشئ جلسة باستخدام /new",
239
+ "context.confirm_text": '📊 ضغط السياق للجلسة "{title}"\n\nسيؤدي ذلك إلى تقليل استخدام السياق عبر إزالة الرسائل القديمة من السجل. لن تتم مقاطعة المهمة الحالية.\n\nهل تريد المتابعة؟',
240
+ "context.callback_session_not_found": "لم يتم العثور على الجلسة",
241
+ "context.callback_compacting": "جارٍ ضغط السياق...",
242
+ "context.progress": "⏳ جارٍ ضغط السياق...",
243
+ "context.error": "❌ فشل ضغط السياق",
244
+ "context.success": "✅ تم ضغط السياق بنجاح",
245
+ "permission.inactive_callback": "انتهت صلاحية طلب الصلاحية",
246
+ "permission.processing_error_callback": "تعذر تنفيذ الطلب",
247
+ "permission.no_active_request_callback": "لا يوجد طلب صلاحية نشط",
248
+ "permission.reply.once": "تم السماح لمرة واحدة",
249
+ "permission.reply.always": "تم السماح دائمًا",
250
+ "permission.reply.reject": "تم الرفض",
251
+ "permission.send_reply_error": "❌ تعذر إرسال رد الصلاحية",
252
+ "permission.blocked.expected_reply": "⚠️ أجب عن طلب الصلاحية أولًا باستخدام الأزرار أعلاه.",
253
+ "permission.blocked.command_not_allowed": "⚠️ لا يمكن استخدام هذا الأمر قبل الرد على طلب الصلاحية.",
254
+ "permission.header": "{emoji} طلب صلاحية: {name}\n\n",
255
+ "permission.button.allow": "✅ سماح لمرة واحدة",
256
+ "permission.button.always": "🔓 سماح دائم",
257
+ "permission.button.reject": "❌ رفض",
258
+ "permission.name.bash": "تشغيل أمر Bash",
259
+ "permission.name.edit": "تعديل ملف",
260
+ "permission.name.write": "إنشاء أو كتابة ملف",
261
+ "permission.name.read": "قراءة ملف",
262
+ "permission.name.webfetch": "فتح محتوى من الويب",
263
+ "permission.name.websearch": "البحث في الويب",
264
+ "permission.name.glob": "البحث عن ملفات",
265
+ "permission.name.grep": "البحث داخل الملفات",
266
+ "permission.name.list": "عرض محتويات مجلد",
267
+ "permission.name.task": "تشغيل مهمة فرعية",
268
+ "permission.name.lsp": "LSP",
269
+ "permission.name.external_directory": "الوصول إلى مجلد خارجي",
270
+ "question.inactive_callback": "انتهت صلاحية الاستبيان",
271
+ "question.processing_error_callback": "تعذر تنفيذ الطلب",
272
+ "question.select_one_required_callback": "اختر خيارًا واحدًا على الأقل",
273
+ "question.enter_custom_callback": "أرسل إجابتك المخصصة كرسالة",
274
+ "question.cancelled": "❌ تم إلغاء الاستبيان",
275
+ "question.answer_already_received": "تم استلام الإجابة بالفعل، يرجى الانتظار...",
276
+ "question.completed_no_answers": "✅ اكتمل الاستبيان (بدون إجابات)",
277
+ "question.no_active_project": "❌ لا يوجد مشروع نشط",
278
+ "question.no_active_request": "❌ لا يوجد طلب نشط",
279
+ "question.send_answers_error": "❌ تعذر إرسال الإجابات إلى الوكيل",
280
+ "question.multi_hint": "\n(يمكنك اختيار عدة خيارات)",
281
+ "question.button.submit": "✅ تم",
282
+ "question.button.custom": "🔤 إجابة مخصصة",
283
+ "question.button.cancel": "❌ إلغاء",
284
+ "question.use_custom_button_first": '⚠️ لإرسال نص، اضغط أولًا على زر "إجابة مخصصة" للسؤال الحالي.',
285
+ "question.summary.title": "✅ اكتمل الاستبيان!\n\n",
286
+ "question.summary.question": "السؤال {index}:\n{question}\n\n",
287
+ "question.summary.answer": "الإجابة:\n{answer}\n\n",
288
+ "keyboard.agent_mode": "{emoji} الوكيل: {name}",
289
+ "keyboard.context": "📊 {used} / {limit} ({percent}%)",
290
+ "keyboard.context_empty": "📊 0",
291
+ "keyboard.variant": "💭 {name}",
292
+ "keyboard.variant_default": "💡 الإعداد الافتراضي",
293
+ "keyboard.updated": "⌨️ تم تحديث لوحة التحكم",
294
+ "pinned.default_session_title": "جلسة جديدة",
295
+ "pinned.unknown": "غير معروف",
296
+ "pinned.line.project": "المشروع: {project}",
297
+ "pinned.line.worktree": "نسخة العمل: {worktree}",
298
+ "pinned.line.model": "النموذج: {model}",
299
+ "pinned.line.attach": "التتبع: {status}",
300
+ "pinned.attach.status.idle": "نشط، خامل",
301
+ "pinned.attach.status.busy": "نشط، مشغول",
302
+ "pinned.line.context": "السياق: {used} / {limit} ({percent}%)",
303
+ "pinned.line.cost": "التكلفة: تم إنفاق {cost}",
304
+ "subagent.header": "وكيل فرعي {agent}: {description}",
305
+ "subagent.line.status": "الحالة: {status}",
306
+ "subagent.line.task": "المهمة: {task}",
307
+ "subagent.line.agent": "الوكيل: {agent}",
308
+ "subagent.working": "جارٍ العمل...",
309
+ "subagent.working_with_details": "جارٍ العمل: {details}",
310
+ "subagent.completed": "مكتملة",
311
+ "subagent.failed": "فشلت المهمة",
312
+ "subagent.status.pending": "قيد الانتظار",
313
+ "subagent.status.running": "قيد التشغيل",
314
+ "subagent.status.completed": "مكتملة",
315
+ "subagent.status.error": "خطأ",
316
+ "pinned.files.title": "الملفات ({count}):",
317
+ "pinned.files.item": " {path}{diff}",
318
+ "pinned.files.more": " ... و{count} أخرى",
319
+ "tool.todo.overflow": "*({count} مهام أخرى)*",
320
+ "tool.file_header.write": "كتابة ملف/مسار: {path}\n============================================================\n\n",
321
+ "tool.file_header.edit": "تعديل ملف/مسار: {path}\n============================================================\n\n",
322
+ "runtime.wizard.ask_token": "أدخل رمز بوت Telegram الذي حصلت عليه من @BotFather.\n> ",
323
+ "runtime.wizard.ask_language": "اختر لغة الواجهة.\nأدخل رقم اللغة من القائمة أو رمز اللغة.\nاضغط Enter لاستخدام اللغة الافتراضية: {defaultLocale}\n{options}\n> ",
324
+ "runtime.wizard.language_invalid": "أدخل رقمًا من القائمة أو رمز لغة مدعومًا.\n",
325
+ "runtime.wizard.language_selected": "تم اختيار اللغة: {language}\n",
326
+ "runtime.wizard.token_required": "رمز البوت مطلوب. حاول مرة أخرى.\n",
327
+ "runtime.wizard.token_invalid": "صيغة رمز البوت غير صحيحة. الصيغة المتوقعة: <id>:<secret>. حاول مرة أخرى.\n",
328
+ "runtime.wizard.ask_user_id": "أدخل معرّف حسابك في Telegram. يمكنك الحصول عليه من @userinfobot.\n> ",
329
+ "runtime.wizard.user_id_invalid": "أدخل رقمًا صحيحًا موجبًا أكبر من صفر.\n",
330
+ "runtime.wizard.ask_api_url": "أدخل رابط OpenCode API، أو اضغط Enter لاستخدام الرابط الافتراضي: {defaultUrl}\n> ",
331
+ "runtime.wizard.ask_server_username": "أدخل اسم مستخدم خادم OpenCode (اختياري).\nاضغط Enter لاستخدام القيمة الافتراضية: {defaultUsername}\n> ",
332
+ "runtime.wizard.ask_server_password": "أدخل كلمة مرور خادم OpenCode (اختياري).\nاضغط Enter لتركها فارغة.\n> ",
333
+ "runtime.wizard.api_url_invalid": "أدخل رابطًا صالحًا (http/https) أو اضغط Enter لاستخدام الافتراضي.\n",
334
+ "runtime.wizard.start": "إعداد OpenCode Telegram Bot.\n",
335
+ "runtime.wizard.saved": "تم حفظ الإعدادات في:\n- {envPath}\n- {settingsPath}\n",
336
+ "runtime.wizard.not_configured_starting": "لم يتم إعداد التطبيق بعد. جارٍ تشغيل معالج الإعداد...\n",
337
+ "runtime.wizard.tty_required": "يتطلب معالج الإعداد التفاعلي طرفية TTY. شغّل `opencode-telegram config` في shell تفاعلية.",
338
+ "rename.no_session": "⚠️ لا توجد جلسة نشطة. أنشئ جلسة أو اختر واحدة أولًا.",
339
+ "rename.prompt": "📝 أدخل عنوانًا جديدًا للجلسة:\n\nالحالي: {title}",
340
+ "rename.empty_title": "⚠️ لا يمكن أن يكون العنوان فارغًا.",
341
+ "rename.success": "✅ تمت إعادة تسمية الجلسة إلى: {title}",
342
+ "rename.error": "🔴 تعذر تغيير اسم الجلسة.",
343
+ "rename.cancelled": "❌ تم إلغاء تغيير الاسم.",
344
+ "rename.inactive_callback": "انتهت صلاحية طلب تغيير الاسم",
345
+ "rename.inactive": "⚠️ طلب تغيير الاسم غير نشط. شغّل /rename مرة أخرى.",
346
+ "rename.blocked.expected_name": "⚠️ أدخل اسم الجلسة الجديد كنص أو اضغط إلغاء في رسالة تغيير الاسم.",
347
+ "rename.blocked.command_not_allowed": "⚠️ لا يمكن استخدام هذا الأمر أثناء انتظار اسم جديد للجلسة.",
348
+ "rename.button.cancel": "❌ إلغاء",
349
+ "task.prompt.schedule": "⏰ أرسل موعد المهمة بلغة طبيعية.\n\nأمثلة:\n- كل 5 دقائق\n- كل يوم الساعة 17:00\n- غدًا الساعة 12:00",
350
+ "task.schedule_empty": "⚠️ لا يمكن أن يكون الموعد فارغًا.",
351
+ "task.parse.in_progress": "⏳ جارٍ فهم الموعد...",
352
+ "task.parse_error": "🔴 تعذر فهم الموعد.\n\n{message}\n\nأرسله مرة أخرى بصيغة أوضح.",
353
+ "task.schedule_preview": "✅ تم فهم الموعد\n\nكما فهمته: {summary}\n{cronLine}المنطقة الزمنية: {timezone}\nالنوع: {kind}\nالتشغيل التالي: {nextRunAt}",
354
+ "task.schedule_preview.cron": "Cron: {cron}",
355
+ "task.prompt.body": "📝 أرسل الآن وصف المهمة التي تريد تشغيلها تلقائيًا.",
356
+ "task.prompt_empty": "⚠️ لا يمكن أن يكون وصف المهمة فارغًا.",
357
+ "task.created": "✅ تم إنشاء المهمة المجدولة\n\nالمهمة: {description}\nالمشروع: {project}\nالنموذج: {model}\nالموعد: {schedule}\n{cronLine}التشغيل التالي: {nextRunAt}",
358
+ "task.created.cron": "Cron: {cron}",
359
+ "task.button.retry_schedule": "🔁 تعديل الموعد",
360
+ "task.button.cancel": "❌ إلغاء",
361
+ "task.retry_schedule_callback": "جارٍ إعادة إدخال الموعد...",
362
+ "task.cancel_callback": "جارٍ الإلغاء...",
363
+ "task.cancelled": "❌ تم إلغاء إنشاء المهمة المجدولة.",
364
+ "task.inactive_callback": "انتهت صلاحية مسار إنشاء المهمة المجدولة",
365
+ "task.inactive": "⚠️ إنشاء المهمة المجدولة غير نشط. شغّل /task مرة أخرى.",
366
+ "task.blocked.expected_input": "⚠️ أكمل إعداد المهمة المجدولة الحالية أولًا بإرسال نص أو استخدام الزر في رسالة الموعد.",
367
+ "task.blocked.command_not_allowed": "⚠️ لا يمكن استخدام هذا الأمر أثناء إنشاء مهمة مجدولة.",
368
+ "task.limit_reached": "⚠️ وصلت إلى الحد الأقصى للمهام ({limit}). احذف مهمة مجدولة أولًا.",
369
+ "task.schedule_too_frequent": "الموعد المتكرر متقارب جدًا. أقل فترة مسموحة هي مرة واحدة كل 5 دقائق.",
370
+ "task.kind.cron": "متكررة",
371
+ "task.kind.once": "لمرة واحدة",
372
+ "task.run.success": "⏰ اكتملت المهمة المجدولة: {description}",
373
+ "task.run.error": "🔴 فشلت المهمة المجدولة: {description}\n\nالخطأ: {error}",
374
+ "task.run.error.interactive_question": "طلبت المهمة المجدولة سؤالًا تفاعليًا ولا يمكنها المتابعة دون تدخل المستخدم.",
375
+ "task.run.error.interactive_permission": "طلبت المهمة المجدولة صلاحية تفاعلية ولا يمكنها المتابعة دون تدخل المستخدم.",
376
+ "tasklist.empty": "📭 لا توجد مهام مجدولة حتى الآن.",
377
+ "tasklist.select": "اختر مهمة مجدولة:",
378
+ "tasklist.details": "⏰ مهمة مجدولة\n\nالمهمة: {prompt}\nالمشروع: {project}\nالموعد: {schedule}\n{cronLine}المنطقة الزمنية: {timezone}\nالتشغيل التالي: {nextRunAt}\nآخر تشغيل: {lastRunAt}\nعدد مرات التشغيل: {runCount}",
379
+ "tasklist.details.cron": "Cron: {cron}",
380
+ "tasklist.button.delete": "🗑 حذف",
381
+ "tasklist.button.cancel": "❌ إلغاء",
382
+ "tasklist.deleted_callback": "تم الحذف",
383
+ "tasklist.cancelled_callback": "تم الإلغاء",
384
+ "tasklist.inactive_callback": "انتهت صلاحية قائمة المهام المجدولة",
385
+ "tasklist.load_error": "🔴 تعذر تحميل المهام المجدولة.",
386
+ "commands.select": "اختر أمرًا من OpenCode:",
387
+ "commands.empty": "📭 لا توجد أوامر مخصصة لهذا المشروع.",
388
+ "commands.fetch_error": "🔴 تعذر تحميل أوامر OpenCode.",
389
+ "commands.no_description": "لا يوجد وصف",
390
+ "commands.button.execute": "✅ تنفيذ",
391
+ "commands.button.cancel": "❌ إلغاء",
392
+ "commands.confirm": "أكد تنفيذ الأمر {command}. لتنفيذه مع وسيطات، أرسل الوسيطات كرسالة.",
393
+ "commands.inactive_callback": "انتهت صلاحية قائمة الأوامر",
394
+ "commands.cancelled_callback": "تم الإلغاء",
395
+ "commands.execute_callback": "جارٍ تنفيذ الأمر...",
396
+ "commands.executing_prefix": "⚡ جارٍ تنفيذ الأمر:",
397
+ "commands.arguments_empty": "⚠️ لا يمكن أن تكون الوسيطات فارغة. أرسل نصًا أو اضغط تنفيذ.",
398
+ "commands.execute_error": "🔴 تعذر تنفيذ أمر OpenCode.",
399
+ "commands.select_page": "اختر أمرًا من OpenCode (الصفحة {page}):",
400
+ "commands.button.prev_page": "⬅️ السابق",
401
+ "commands.button.next_page": "التالي ➡️",
402
+ "commands.page_empty_callback": "لا توجد أوامر في هذه الصفحة",
403
+ "commands.page_load_error_callback": "تعذر تحميل هذه الصفحة. حاول مرة أخرى.",
404
+ "commands.download.no_roots": "لم يتم إعداد أي مسارات رئيسية مسموح بها للاستعراض.",
405
+ "commands.download.downloading": "جارٍ تنزيل الملف...",
406
+ "commands.download.not_found": "لم يتم العثور على الملف",
407
+ "commands.download.not_file": "المسار ليس ملفًا",
408
+ "commands.download.file_too_large": "الملف كبير جدًا",
409
+ "commands.download.size": "الحجم",
410
+ "commands.download.modified": "آخر تعديل",
411
+ "commands.download.error": "تعذر تنزيل الملف.",
412
+ "skills.select": "اختر مهارة من OpenCode:",
413
+ "skills.empty": "📭 لا توجد مهارات متاحة لهذا المشروع.",
414
+ "skills.fetch_error": "🔴 تعذر تحميل مهارات OpenCode.",
415
+ "skills.no_description": "لا يوجد وصف",
416
+ "skills.button.execute": "✅ تشغيل",
417
+ "skills.button.cancel": "❌ إلغاء",
418
+ "skills.confirm": "أكد تشغيل المهارة {skill}. لتشغيلها مع وسيطات، أرسل الوسيطات كرسالة.",
419
+ "skills.inactive_callback": "انتهت صلاحية قائمة المهارات",
420
+ "skills.cancelled_callback": "تم الإلغاء",
421
+ "skills.execute_callback": "جارٍ تشغيل المهارة...",
422
+ "skills.executing_prefix": "⚡ جارٍ تشغيل المهارة:",
423
+ "skills.arguments_empty": "⚠️ لا يمكن أن تكون الوسيطات فارغة. أرسل نصًا أو اضغط تشغيل.",
424
+ "skills.select_page": "اختر مهارة من OpenCode (الصفحة {page}):",
425
+ "skills.button.prev_page": "⬅️ السابق",
426
+ "skills.button.next_page": "التالي ➡️",
427
+ "skills.page_empty_callback": "لا توجد مهارات في هذه الصفحة",
428
+ "skills.page_load_error_callback": "تعذر تحميل هذه الصفحة. حاول مرة أخرى.",
429
+ "mcps.select": "خوادم MCP:",
430
+ "mcps.empty": "📭 لم تتم إضافة أي خوادم MCP.",
431
+ "mcps.fetch_error": "🔴 تعذر تحميل خوادم MCP.",
432
+ "mcps.toggle_error": "🔴 تعذر تغيير حالة خادم MCP.",
433
+ "mcps.enabling": "جارٍ التفعيل...",
434
+ "mcps.disabling": "جارٍ التعطيل...",
435
+ "mcps.status.connected": "🟢 متصل",
436
+ "mcps.status.disabled": "🔴 معطّل",
437
+ "mcps.status.failed": "⚠️ تعذر الاتصال",
438
+ "mcps.status.needs_auth": "🔒 يحتاج إلى تسجيل دخول",
439
+ "mcps.status.needs_client_registration": "🔒 يحتاج إلى تسجيل عميل",
440
+ "mcps.detail.title": "الخادم: {name}",
441
+ "mcps.detail.status": "الحالة: {status}",
442
+ "mcps.detail.error": "الخطأ: {error}",
443
+ "mcps.button.enable": "🟢 تفعيل",
444
+ "mcps.button.disable": "🔴 تعطيل",
445
+ "mcps.button.back": "⬅️ رجوع",
446
+ "mcps.auth_required": "هذا الخادم يحتاج إلى تسجيل دخول، ولا يمكن تفعيله من داخل البوت.",
447
+ "legacy.models.fetch_error": "🔴 تعذر تحميل قائمة النماذج. تحقق من حالة الخادم باستخدام /status.",
448
+ "legacy.models.empty": "📋 لا توجد نماذج متاحة. اضبط المزوّدين في OpenCode.",
449
+ "legacy.models.header": "📋 النماذج المتاحة:\n\n",
450
+ "legacy.models.no_provider_models": " ⚠️ لا توجد نماذج متاحة\n",
451
+ "legacy.models.env_hint": "💡 لاستخدام النموذج في .env:\n",
452
+ "legacy.models.error": "🔴 حدث خطأ أثناء تحميل قائمة النماذج.",
453
+ "stt.recognizing": "🎤 جارٍ تحويل الصوت إلى نص...",
454
+ "stt.recognized": "🎤 النص المستخرج من الرسالة الصوتية:\n{text}",
455
+ "stt.not_configured": "🎤 ميزة تحويل الصوت إلى نص غير مهيأة بعد.\n\nاضبط STT_API_URL وSTT_API_KEY داخل ملف .env لتفعيلها.",
456
+ "stt.error": "🔴 تعذر تحويل الصوت إلى نص: {error}",
457
+ "stt.empty_result": "🎤 لم يتم التقاط كلام واضح في الرسالة الصوتية.",
458
+ "worktree.branch_detached": "HEAD مفصول",
459
+ "worktree.select_with_current": "اختر نسخة عمل (Git worktree):",
460
+ "worktree.project_not_selected": "🏗 لم تحدد مشروعًا بعد.\n\nاختر مشروعًا أولًا باستخدام /projects.",
461
+ "worktree.not_git_repo": "🌿 لا يمكن استخدام Git worktrees مع المشروع الحالي لأنه ليس مستودع Git.",
462
+ "worktree.empty": "📭 لا توجد نسخ عمل (Git worktrees) لهذا المستودع.",
463
+ "worktree.fetch_error": "🔴 تعذر تحميل نسخ العمل من Git.",
464
+ "worktree.not_git_repo_callback": "المشروع الحالي ليس مستودع Git",
465
+ "worktree.page_empty_callback": "لا توجد نسخ عمل في هذه الصفحة",
466
+ "worktree.selection_missing_callback": "نسخة العمل المحددة لم تعد متاحة",
467
+ "worktree.already_selected_callback": "نسخة العمل هذه محددة بالفعل",
468
+ "worktree.selected": "✅ تم اختيار نسخة العمل: {worktree}\n\n📋 تمت إعادة ضبط الجلسة الحالية. استخدم /sessions لاختيار جلسة أو /new للمتابعة.",
469
+ "worktree.select_error": "🔴 تعذر اختيار نسخة العمل.",
470
+ "open.back": "⬆️ مجلد سابق",
471
+ "open.roots": "📋 العودة إلى المسارات الرئيسية",
472
+ "open.prev_page": "⬅️ السابق",
473
+ "open.next_page": "التالي ➡️",
474
+ "open.select_current": "✅ اختيار هذا المجلد",
475
+ "open.select_root": "📂 اختر أحد المسارات الرئيسية للاستعراض:",
476
+ "open.access_denied": "⛔ لا يمكن الوصول إلى هذا المسار لأنه خارج المجلدات المسموح بها",
477
+ "open.scan_error": "🔴 تعذر استعراض المجلد: {error}",
478
+ "open.open_error": "🔴 تعذر فتح مستعرض المجلدات.",
479
+ "open.selected": "✅ تمت إضافة المشروع: {project}\n\n📋 استخدم /sessions لاختيار جلسة أو /new لبدء العمل.",
480
+ "open.select_error": "🔴 تعذر إضافة المشروع.",
481
+ "open.no_subfolders": "📭 لا توجد مجلدات فرعية",
482
+ "open.subfolder_count": "{count} مجلد فرعي",
483
+ "open.subfolders_count": "{count} مجلدات فرعية",
484
+ "ls.access_denied": "⛔ لا يمكن الوصول إلى هذا المسار لأنه خارج المشروع الحالي",
485
+ "ls.scan_error": "🔴 تعذر استعراض محتويات المجلد",
486
+ "ls.header": "محتويات المجلد",
487
+ "ls.total": "الإجمالي: {count} عنصر",
488
+ "ls.file.header": "تفاصيل الملف",
489
+ "ls.file.download": "📥 تنزيل الملف",
490
+ "ls.file.back": "⬅️ رجوع",
491
+ };
@@ -1,4 +1,5 @@
1
1
  import { en } from "./en.js";
2
+ import { ar } from "./ar.js";
2
3
  import { de } from "./de.js";
3
4
  import { es } from "./es.js";
4
5
  import { fr } from "./fr.js";
@@ -11,6 +12,12 @@ const LOCALE_DEFINITIONS = [
11
12
  dateLocale: "en-US",
12
13
  dictionary: en,
13
14
  },
15
+ {
16
+ code: "ar",
17
+ label: "العربية",
18
+ dateLocale: "ar-SA",
19
+ dictionary: ar,
20
+ },
14
21
  {
15
22
  code: "de",
16
23
  label: "Deutsch",
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ async function main() {
14
14
  setRuntimeMode(modeResult.mode);
15
15
  const { initializeLogger } = await import("./utils/logger.js");
16
16
  await initializeLogger();
17
- const { startBotApp } = await import("./app/start-bot-app.js");
17
+ const { startBotApp } = await import("./app/bootstrap/start-bot-app.js");
18
18
  await startBotApp();
19
19
  }
20
20
  void main().catch((error) => {
@@ -1,5 +1,5 @@
1
- import { reconcileStoredModelSelection } from "../model/manager.js";
2
- import { warmupSessionDirectoryCache } from "../session/cache-manager.js";
1
+ import { reconcileStoredModelSelection } from "../app/services/model-selection-service.js";
2
+ import { warmupSessionDirectoryCache } from "../app/services/session-cache-service.js";
3
3
  import { logger } from "../utils/logger.js";
4
4
  import { opencodeClient } from "./client.js";
5
5
  import { opencodeReadyLifecycle } from "./ready-lifecycle.js";
@@ -3,8 +3,8 @@ import fsPromises from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { exec, spawn } from "node:child_process";
5
5
  import { promisify } from "node:util";
6
- import { getRuntimePaths } from "../runtime/paths.js";
7
- import { buildServiceChildEnv } from "./runtime.js";
6
+ import { getRuntimePaths } from "../paths.js";
7
+ import { buildServiceChildEnv } from "./env.js";
8
8
  const execAsync = promisify(exec);
9
9
  const SERVICE_STATE_FILE_NAME = "bot-service.json";
10
10
  const PROCESS_EXIT_POLL_MS = 100;
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grinev/opencode-telegram-bot",
3
- "version": "0.21.0",
3
+ "version": "0.21.2",
4
4
  "description": "Telegram bot client for OpenCode to run and monitor coding tasks from chat.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",