/** English translations — mirrors the keys defined in `zh.ts`. */
export const messages = {
    // ─── Card buttons ────────────────────────────────────────────────────────
    'card.btn.open_terminal': '🖥️ Open Terminal',
    'card.btn.open_writable_terminal': '🖥️ Open Writable Terminal',
    'card.btn.get_write_link': '🔑 Get Write Link',
    'card.writable_terminal_link': '🔑 Writable terminal (visible to everyone here — anyone can drive it): [{url}]({url})',
    'card.btn.restart_cli': '🔄 Restart {cliName}',
    'card.btn.disconnect': '⏏ Disconnect',
    'card.btn.close_session': '❌ Close Session',
    'card.btn.resume_session': '▶️ Resume Session',
    'card.btn.show_output': '📖 Show Output',
    'card.btn.hide_output': '📕 Hide Output',
    'card.btn.export_text': '📝 Export Text',
    'card.btn.refresh': '🔃 Refresh',
    'card.btn.takeover': '🔄 Take Over',
    'card.btn.skip_repo': '▶️ Start Session Directly',
    'card.btn.manual_repo': 'Use This Directory',
    'card.btn.half_page_up': '⇞ Page ½ Up',
    'card.btn.half_page_down': '⇟ Page ½ Down',
    'card.btn.send_custom': '📝 Send Custom Reply',
    'card.btn.retry_last_task': '🔁 Retry Last Task',
    // ─── Card status ─────────────────────────────────────────────────────────
    'card.status.starting': 'Starting…',
    'card.status.working': 'Working',
    'card.status.idle': 'Awaiting input',
    'card.status.analyzing': 'Analyzing…',
    'card.status.limited': 'Limit reached',
    'card.status.retry_ready': 'Ready to retry',
    'card.status.executing': 'Executing…',
    'card.status.session_closed': '🛑 Session Closed',
    'card.status.relay_frozen': '🔄 Relayed away',
    'card.status.selected': 'Selected',
    'card.status.waiting_screenshot': '_(Waiting for first screenshot…)_',
    'card.status.truncated_prefix': '… (truncated)',
    'card.pending.title': 'Processing',
    'card.pending.body': '🔄 Processing your request...',
    'card.pending.detoured_title': 'Sent',
    'card.pending.detoured_body': 'The final reply was sent to another target.',
    'card.pending.mentioned_body': 'The final reply was sent as a new message.',
    // ─── Card body text ──────────────────────────────────────────────────────
    'card.body.cli_terminated': '{cliName} process has exited.',
    'card.body.relay_frozen': 'This session was relayed elsewhere via /relay; this card is frozen as a historical snapshot. Action buttons removed to prevent accidentally affecting the now-live session in the other chat.',
    'card.body.click_resume_or_run': 'Click "Resume Session" to continue, or run in your terminal:',
    'card.body.click_resume_only': 'Click "Resume Session" to continue.',
    'card.body.cli_no_cli_resume': '_{cliName} cannot resume a specific session from the CLI; you can resume here in Lark._',
    'card.body.working_dir': '📁 Working dir:',
    'card.body.choose_label': 'Choice:',
    'card.usage_limit.retry_at': '⚠️ {cliName} usage limit has been reached. Try again after {retryLabel}.',
    'card.usage_limit.retry_ready': '✅ {cliName} usage limit should have reset. Retry the last task, or send a new message.',
    'card.private.snapshot_note': '🔒 Private static snapshot (visible only to you, not live-updating). Tap Open Terminal for the live view.',
    // ─── Repo select card ────────────────────────────────────────────────────
    'card.repo.title': '📁 Project Repository',
    'card.repo.placeholder_switch': 'Pick a repo to switch to',
    'card.repo.manual_placeholder': 'Enter any working directory, e.g. /path/to/project or ~/projects/foo',
    'card.repo.manual_empty': 'Please enter a working directory path',
    'card.repo.current_active': 'Current working directory:',
    'card.repo.current_marker': ' ← current',
    'card.repo.note': 'You can also reply `/repo <N>` (e.g. `/repo 1`), or `/repo <path|name>` directly (e.g. `/repo botmux`, `/repo ~/projects/foo`) to skip this card. `/repo wt <N|name> [branch]` opens a fresh worktree off the remote default branch.',
    'card.repo.placeholder_worktree': '🌿 Open a repo as a new worktree',
    'card.repo.toast_worktree_creating': 'Creating worktree — will post in the thread when done…',
    // In-group authorization card
    'card.grant.title': '🔑 Access Request',
    'card.grant.body_request': 'User **{name}** is requesting to use me in this chat. <at id={owner}></at> allow them to talk to me here?',
    'card.grant.body_owner': 'Grant **{name}** permission to talk to me in this chat? (<at id={owner}></at>)',
    'card.grant.body_owner_multi': 'Grant {names} permission to talk to me in this chat? (<at id={owner}></at>)',
    'card.grant.btn_chat': 'Grant talk in this chat',
    'card.grant.btn_global': 'Grant talk globally',
    'card.grant.btn_deny': 'Deny',
    'card.grant.note': 'Grants talk access only (this chat or global); /restart, /close, terminal write and other sensitive ops remain limited to allowedUsers (owner).',
    'card.grant.toast_owner_only': 'Only the owner can do this',
    'card.grant.toast_expired': 'This request has expired',
    'card.grant.toast_no_repo_perm': 'No permission',
    'card.grant.toast_failed': 'Grant failed: {reason}',
    'card.land.title': '🧳 Land sandbox changes',
    'card.land.body': 'The sandbox session produced **{files}** changed file(s) (**+{ins} / -{del}**).\nTarget: `{dir}`\n\nReview the diff below; "Apply to disk" will `git apply` these changes back to the real repo.',
    'card.land.files_header': 'Changed files',
    'card.land.preview_header': 'Diff preview',
    'card.land.truncated': 'Preview truncated — full diff in the attached .patch file',
    'card.land.patch_note': 'Full .patch attached (git apply-able)',
    'card.land.btn_apply': 'Apply to disk',
    'card.land.btn_discard': 'Discard',
    'card.land.note': 'Owner-only; changes come from an isolated clone — the real repo is untouched until you apply.',
    'card.land.applied_title': '✅ Landed',
    'card.land.discarded_title': '🗑 Discarded sandbox changes',
    'card.land.failed_title': '❌ Land failed',
    'card.land.applied_body': 'Applied {files} changed file(s) (+{ins}/-{del}) to `{dir}`.',
    'card.land.discarded_body': 'Changes not applied; the sandbox clone is unchanged.',
    'card.land.toast_owner_only': 'Only the owner can land changes',
    'card.land.stale': 'This card is missing session info (likely an old card); send /land again in the session.',
    'cmd.land.no_workingdir': 'The session has no workingDir; cannot determine the land target.',
    'cmd.land.cannot': 'Cannot land: {error}',
    'cmd.land.empty': 'No changes in the sandbox clone — nothing to land.',
    'card.grant.result_chat': '✅ Granted for this chat',
    'card.grant.result_global': '✅ Granted globally',
    'card.grant.result_deny': '🚫 Request denied',
    'card.grant.partial_failed': '⚠️ The following targets failed to authorize (re-run /grant to retry): {names}',
    'card.grant.notify_chat': '✅ {at} can now use me in this chat — just @-mention me.',
    'card.grant.notify_global': '✅ {at} now has global access — @-mention me in any chat.',
    'card.grant.notify_quota_suffix': ' (message quota: {n}; access is revoked once used up)',
    // Message quota exhausted
    'quota.exhausted_notify': '⚠️ {at} has used up their message quota ({limit}/{limit}); talk access has been revoked. Ask the owner to /grant again to continue.',
    // Granted-user command restriction (when restrictGrantCommands is on)
    'cmd.grant_restricted': '⚠️ This grant only allows plain conversation — commands like {cmd} are not permitted. Ask the owner to lift the restriction.',
    // /grant, /revoke command replies
    'cmd.grant.owner_only': 'Only the owner can use /grant.',
    'cmd.grant.usage': 'Usage: @bot /grant @someone [N] (let them talk to me in this chat — you can @ several people/bots at once; with a number, grants an N-message quota) | @bot /grant (let every member of this chat talk). Talk-only; sensitive ops stay governed by allowedUsers.',
    'cmd.grant.bad_quota': '⚠️ Bad quota. Usage: @bot /grant @someone 5 (grant a 5-message quota; must be a positive integer). Omit the number to use the default quota / unlimited.',
    'cmd.grant.chat_done': '✅ Everyone in this chat can now talk to me (just @-mention me). Sensitive ops still limited to allowedUsers.',
    'cmd.grant.chat_already': 'ℹ️ This chat is already open to talk; nothing to do.',
    'cmd.grant.chat_failed': '⚠️ Grant failed: {reason}',
    'cmd.revoke.owner_only': 'Only the owner can use /revoke.',
    'cmd.revoke.usage': 'Usage: @bot /revoke @someone (revoke their talk access in this chat — you can @ several people/bots at once) | @bot /revoke (revoke the whole-chat talk grant).',
    'cmd.revoke.chat_done': '✅ Revoked the whole-chat talk grant for this chat.',
    'cmd.revoke.chat_none': 'ℹ️ This chat has no whole-chat talk grant.',
    'cmd.revoke.chat_failed': '⚠️ Revoke failed: {reason}',
    'cmd.revoke.done': "Revoked {name}'s access (scope: {scope}).",
    'cmd.revoke.would_open': '⚠️ Revoke failed: that user is the last global allowed user / owner; revoking would open the bot to everyone. Blocked.',
    'cmd.revoke.failed': '⚠️ Revoke failed: {reason}',
    'cmd.revoke.scope_chat': 'this-chat ',
    'cmd.revoke.scope_global': 'global',
    'cmd.revoke.scope_global_talk': 'global-talk ',
    'cmd.revoke.scope_none': 'none',
    'cmd.revoke.multi_header': 'Revoke results:',
    'cmd.revoke.multi_ok': '✅ {name} — revoked (scope: {scope})',
    'cmd.revoke.multi_would_open': '⚠️ {name} — skipped: last global allowed user / owner, revoking would open the bot to everyone',
    'cmd.revoke.multi_failed': '⚠️ {name} — revoke failed: {reason}',
    // ─── Adopt card ──────────────────────────────────────────────────────────
    'card.adopt.title': '📡 Choose a CLI session to adopt',
    'card.adopt.placeholder_select': 'Pick a CLI session',
    'card.adopt.uptime_unknown': 'unknown',
    'card.adopt.section_live': '**Take over a running session** (observe a live tmux / zellij process)',
    'card.adopt.section_resume': '**Resume a past session** (import from disk via --resume; no live process needed)',
    'card.adopt.placeholder_resume': 'Pick a past session to resume',
    'card.codex_app_thread.title': '📱 Choose a Codex App conversation',
    'card.codex_app_thread.subtitle': 'Resume an existing local Codex App thread. botmux will continue it through the app-server protocol.',
    'card.codex_app_thread.placeholder_select': 'Pick a Codex App conversation',
    'card.codex_app_thread.updated_unknown': 'unknown time',
    // ─── /relay picker card (pull mode) ─────────────────────────────────────
    'card.relay.title': '📋 Pick a session to relay into this chat',
    'card.relay.title_p2p': '📋 Pick a session to relay into this DM',
    'card.relay.empty': 'No relayable sessions.\n(Picker only lists sessions you own, on this bot, in other chats.)',
    'card.relay.btn_pull': 'Pull into this chat',
    'card.relay.btn_confirm': 'Confirm relay into this chat',
    'card.relay.btn_confirm_p2p': 'Confirm relay into this DM',
    'card.relay.btn_confirm_running': 'Session is running — cannot relay yet',
    'card.relay.field_status': 'Status',
    'card.relay.status_running': '🟢 Running',
    'card.relay.status_idle': '⚪ Idle',
    'card.relay.selected_tag': 'selected',
    'card.relay.hint_pick_first': 'Tap any session above to select it, then press Confirm to relay.',
    'card.relay.search_placeholder': '🔍 Search sessions (title / chat / path)',
    'card.relay.btn_search': 'Search',
    'card.relay.btn_prev_page': '← Prev',
    'card.relay.btn_next_page': 'Next →',
    'card.relay.page_indicator': 'Page {current} / {total}',
    'card.relay.empty_filtered': 'No sessions match "{query}". Try a different keyword.',
    'card.relay.placeholder_select': 'Pick a session to relay',
    'card.relay.type_group': 'group chat',
    'card.relay.type_topic': 'topic chat',
    'card.relay.type_p2p': 'direct message',
    'card.relay.field_type': 'Type',
    'card.relay.field_location': 'Where',
    'card.relay.field_time': 'Active',
    'card.relay.toast_adopt_not_relayable': 'That session was attached via /adopt to an external tmux — it cannot be relayed.',
    'card.relay.toast_not_found': 'Session not found or already closed.',
    'card.relay.toast_not_owner': 'Only the session owner can relay it.',
    'card.relay.toast_not_invoker': 'This menu was summoned by someone else. Run /relay yourself to get your own.',
    'card.relay.toast_worker_busy': '⚠️ The session is mid-turn. Wait for it to go idle, then relay.',
    'card.relay.toast_not_started_yet': '⚠️ The session has not picked a repo yet, so the CLI never started. Pick a repo in the source chat first.',
    'card.relay.toast_same_chat': 'That session is already at the target location (same topic/chat) — nothing to relay.',
    'card.relay.toast_failed': 'Relay failed: {error}',
    'card.relay.toast_target_has_session': 'This chat already has an active session for the bot. Please /close it first, then relay.',
    'cmd.relay.target_has_session_msg': '⚠️ This bot already has an active session "{title}" in this chat. Please /close it first, then relay a new one in.',
    'card.relay.toast_success': '✅ Relayed into this chat',
    // ─── TUI prompt card ─────────────────────────────────────────────────────
    'card.tui.input_placeholder': 'Type a custom reply…',
    // ─── Command responses ───────────────────────────────────────────────────
    'cmd.no_active_session': 'No active session in this topic.',
    'cmd.card.operator_only': '⚠️ Only authorized users (allowedUsers) can use /card.',
    'cmd.term.operator_only': '⚠️ Only authorized users (allowedUsers) can use /term to get the operable terminal link.',
    'cmd.term.no_session': 'No active session in this topic — /term needs a running session.',
    'cmd.term.not_ready': '🔒 Terminal isn’t ready yet; send /term again once the session is up.',
    'cmd.term.failed': '⚠️ Failed to send the operable link (both the private card and DM failed); check the daemon logs.',
    'cmd.term.sent_dm': '🔑 Sent the operable terminal link to your DM (kept out of the group).',
    'cmd.card.off_ok': '🔕 Streaming cards turned off for this chat; status now shows via the in-place pending card (no withdrawal). /card on to restore; /card summons a live card.',
    'cmd.card.on_ok': '🔔 Streaming cards restored for this chat. The next status update posts a live card again.',
    'cmd.card.fail': '⚠️ Operation failed: {reason}',
    'cmd.card.usage': 'Usage: /card (summon) | /card off (suppress for this chat) | /card on (restore)',
    'cmd.card.not_ready': 'Terminal not ready yet — the streaming card will appear once the session is up.',
    'cmd.card.private_not_ready': '🔒 Terminal not ready yet — send /card again once the session is up.',
    'cmd.card.private_not_group': '🔒 Private cards only work in regular group chats — topic groups / DMs are unsupported (Feishu limitation).',
    'cmd.card.private_no_audience': '🔒 No audience: this bot has no owner configured (allowedUsers is empty).',
    'cmd.card.private_failed': '🔒 Failed to send the private card — likely missing send permission, the bot is not in this chat, or this is a topic group.',
    'cmd.card.private_partial': '🔒 Private card sent to {sent}/{total}; some recipients failed.',
    'cmd.reply_mode.status': 'Current group reply mode: {mode}\nCommands: /reply-mode chat | topic | new-topic (shared remains a compatibility alias for topic)',
    'cmd.reply_mode.updated': '✅ Group reply mode updated to: {mode}',
    'cmd.reply_mode.unsupported': '⚠️ /reply-mode only works in regular groups and DMs; topic groups need no setting (they are already threaded).',
    'cmd.reply_mode.owner_only': '⚠️ Only owner/allowedUsers can change the session mode.',
    'cmd.reply_mode.usage': 'Usage: /reply-mode chat | topic | new-topic\nchat = flat replies while reusing the group session; topic = topic display while reusing the same group session (shared is a compatibility alias); new-topic = each @ opens a fresh topic + its own session.',
    'cmd.reply_mode.failed': '⚠️ Failed to update reply mode: {reason}',
    'cmd.reply_mode.dm_status': 'Current DM session mode: {mode}\nCommands: /reply-mode chat | topic',
    'cmd.reply_mode.dm_updated': '✅ DM session mode updated to: {mode}',
    'cmd.reply_mode.dm_usage': 'Usage (DM): /reply-mode chat | topic\nchat = the whole DM shares one continuous session; topic = each message starts its own session.',
    'cmd.reply_mode.dm_shared_unsupported': '⚠️ shared only makes sense in regular groups; DMs support chat | topic only.',
    'cmd.restart.in_progress': '🔄 Restarting {cliName}…',
    'cmd.restart.terminated': '{cliName} has been terminated; it will auto-resume on your next message.',
    'cmd.cd.usage': 'Usage: /cd <path>\nExample: /cd ~/projects/my-app',
    'cmd.cd.switched': 'Working directory switched to {path}. It will resume there on your next message.',
    'cmd.cd.dir_not_exist': 'Directory does not exist: {path}',
    'cmd.cd.cannot_read': 'Cannot read path: {path} ({msg})',
    'cmd.cd.not_a_directory': 'Path is not a directory: {path}',
    'cmd.repo.no_prior_scan': 'Run `/repo` first to see the project list.',
    'cmd.repo.index_out_of_range': 'Index out of range. Valid: 1-{max}',
    'cmd.repo.path_not_found': '❌ No such directory or project: `{arg}`\nPass an absolute path, a relative path, or a first-level project name under workingDir.',
    'cmd.repo.selected_in_pending': '✅ Selected {name}',
    'cmd.repo.switched_to': '🔄 Switched to {name}',
    'cmd.repo.warning_running': '⚠️ A session is already running. Switching repos will close it and start a new one.\nIf that\'s what you want, pick the new repo from the card below.',
    'cmd.repo.scan_dir_not_exist': 'Scan dirs do not exist: {dirs}\nCheck that workingDir in bots.json points to a valid directory.',
    'cmd.repo.working_dir_not_exist': '❌ Configured working directory does not exist or is not a directory: {dirs}\nCheck workingDir / workingDirs in ~/.botmux/bots.json, or run `botmux setup` and choose an existing directory.',
    'cmd.repo.no_git_repos': 'No git repositories found under {dirs}.',
    'cmd.repo.worktree_usage': 'Usage: `/repo wt <N|name|path> [new-branch]` — create a worktree off the repo\'s remote default branch and open it.',
    'cmd.repo.worktree_creating': '🌿 Creating a worktree for `{repo}` (includes a git fetch, may take a few seconds)…',
    'cmd.repo.worktree_created': '🌿 Worktree created: `{path}`\nBranch `{branch}`, based on `{base}`',
    'cmd.repo.worktree_failed': '❌ Worktree creation failed: {error}',
    'cmd.repo.worktree_in_progress': '⏳ A worktree is already being created — please wait…',
    'cmd.repo.worktree_created_not_switched': '🌿 Worktree created: `{path}` (branch `{branch}`), but the session changed meanwhile — not switched automatically. Use `/repo {path}` to open it.',
    'cmd.repo.worktree_switch_failed': '⚠️ Worktree created at `{path}`, but switching to it failed: {error}\nUse `/repo {path}` to open it manually.',
    'cmd.skip.opened': '▶️ Session started (working dir: {cwd})',
    'cmd.status.running': 'running',
    'cmd.status.waiting': 'idle',
    'cmd.status.fallback_no_session': 'No active session in this topic.\nDaemon active sessions: {count}\n{cliName}: v{version}',
    'cmd.login.no_credentials': '❌ Cannot read app credentials.',
    'cmd.login.title': '🔐 Lark User OAuth',
    'cmd.login.step1': '1. Click the link below to authorize:',
    'cmd.login.step2': '2. After authorizing, the browser redirects to a page that fails to load ("This site can\'t be reached / ERR_CONNECTION_REFUSED") — this is expected (the callback points at 127.0.0.1, but the daemon runs remotely).',
    'cmd.login.step3': '3. Copy the full http://127.0.0.1:9768/callback?code=... URL from the address bar and send it back to this topic.\n   ⚠️ If the address bar doesn\'t show it (intercepted / no redirect): press F12 → Network tab → enable All → click Authorize → find the request to 127.0.0.1:9768 → right-click → Copy → Copy URL → send it here.',
    'cmd.login.footer': 'With auth you can download images and resources from third-party cards.',
    'cmd.login.status_hint': 'Check status: /login status',
    'cmd.subdoc.no_session': '⚠️ /subscribe-lark-doc needs a live session: start a session in this topic first, then subscribe to a doc.',
    'cmd.subdoc.usage': 'Usage:\n• /subscribe-lark-doc <Feishu doc link> — subscribe; the doc\'s comments feed into this session\n• /subscribe-lark-doc list — list docs subscribed by this session\n• /subscribe-lark-doc off — unsubscribe all docs of this session',
    'cmd.subdoc.need_login': '🔐 Subscribing to doc comments needs user authorization with DOC permissions (read/write comments + event subscription — different from the generic /login). Open the link below to authorize, then resend /subscribe-lark-doc <doc link>.\n⚠️ You must also enable these permissions for the app in the developer console and subscribe to the "doc comment" event, otherwise comments won\'t arrive.',
    'cmd.subdoc.subscribed': '✅ Subscribed to doc {title}.\nIts comments ({mode}) will feed into this session as messages, and my replies will be posted as doc comments.\nUnsubscribe: /subscribe-lark-doc off',
    'cmd.subdoc.subscribed_moved': '✅ Subscribed to doc {title} ({mode}).\n⚠️ This doc was bound to another session before; it is now rebound to this session.',
    'cmd.subdoc.unsubscribed': '✅ Unsubscribed {count} doc(s) from this session.',
    'cmd.subdoc.none': 'This session has no subscribed docs yet. Use /subscribe-lark-doc <doc link> to subscribe.',
    'cmd.subdoc.list_title': '📄 Docs subscribed by this session:',
    'cmd.subdoc.failed': '❌ Subscribe failed: {err}',
    'cmd.subdoc.mode_mention': 'only comments @ me',
    'cmd.subdoc.mode_all': 'all new comments',
    'daemon.doc_comment_prefix': '[From a Feishu doc comment · {author}]',
    'daemon.doc_comment_reply_prefix': '↪ Reply to {author}: ',
    'daemon.doc_comment_replied_card': '✅ Replied in the Feishu doc comments.',
    'cmd.adopt.already_adopted': 'This topic is already adopting {label} ({pane}).\nClick "Disconnect" on the card first, then run /adopt again (the original CLI is untouched).',
    'cmd.adopt.no_sessions': 'No adoptable CLI session found.',
    'cmd.adopt.pane_not_found': 'tmux pane not found: {pane}',
    'cmd.adopt.target_exited': '⚠️ Target CLI session has exited.',
    'cmd.adopt.success': '📡 Adopted {cliName} · {project} ({pane})',
    'cmd.adopt.resume_success': '↩️ Resumed {cliName} session · {project} — “{title}”',
    'cmd.adopt.resume_not_found': '⚠️ That past session no longer exists or is already in use ({id})',
    'cmd.detach.success': '⏏ Disconnected. The original CLI session is untouched.',
    'cmd.detach.not_adopted': 'This topic is not an /adopt session; /detach does not apply. Use /close to end the botmux session (which also kills the CLI).',
    'cmd.codex_app_adopt.no_threads': 'No resumable Codex App conversation found.',
    'cmd.codex_app_adopt.thread_not_found': 'Codex App conversation not found: {threadId}',
    'cmd.codex_app_adopt.list_failed': 'Failed to read Codex App conversations: {error}',
    'cmd.codex_app_adopt.success': '📱 Resumed Codex App conversation: {title}',
    'cmd.oncall.need_group': '/oncall must be used in a group chat (as a new topic).',
    'cmd.oncall.not_bound': 'This group is not bound to an oncall project yet.\n\nUsage:\n/oncall bind <path>     — bind this group to a project directory, skip the repo-selection card\n/oncall unbind          — unbind this group from oncall\n/oncall status          — show current binding\n\nOnce bound: anyone in the group can @ the bot to ask; only allowedUsers can click card buttons or run /cd /restart /close.',
    'cmd.oncall.bound': '🟢 Bound to oncall\nWorking dir: {dir}\n\nUse /oncall unbind to release; /cd <path> changes the working directory (still in oncall mode).',
    'cmd.oncall.bind_usage': 'Usage: /oncall bind <path>\nExample: /oncall bind ~/projects/payments-service',
    'cmd.oncall.bind_failed_no_bot': '⚠️ Cannot find this bot in the config file. Bind failed.',
    'cmd.oncall.bind_failed': '⚠️ Bind failed: {reason}',
    'cmd.oncall.bind_success': '✅ {verb} oncall\nChat: {chatId}\nWorking dir: {target} → {resolved}\n\nNext time a new topic opens in this group it will spawn the CLI here directly, skipping the repo card.',
    'cmd.oncall.verb_bound': 'Bound',
    'cmd.oncall.verb_updated': 'Updated',
    'cmd.oncall.unbind_failed': '⚠️ Unbind failed: {reason}',
    'cmd.oncall.unbind_not_bound': 'This group is not bound to oncall. (Unbind intent recorded — default-oncall won\'t auto-bind this group again.)',
    'cmd.oncall.unbind_success': '✅ Oncall binding released. The next new topic will fall back to the default repo-selection flow.',
    'cmd.oncall.unknown_sub': 'Unknown subcommand: {sub}\nSupported: /oncall bind <path> | /oncall unbind | /oncall status',
    'cmd.config.no_bot': '⚠️ Cannot identify the responding bot — /botconfig aborted.',
    'cmd.config.no_owner': '⚠️ This bot has no allowedUsers (no owner) configured, so config cannot be edited from chat. Run `botmux setup` on the host.',
    'cmd.config.not_admin': '⛔ Only owner / allowedUsers may edit config.',
    'cmd.config.card_dm_failed': '⚠️ The config card is sent only via private message, but I couldn\'t reach you this time (it is not shown in the group, to avoid exposing your config there). Make sure you have a 1:1 chat with me, then send /botconfig again.',
    'cmd.config.help': '⚙️ /botconfig — edit this bot\'s operational config remotely (owner/allowedUsers only; written + hot-applied, no restart)\n\nUsage:\n/botconfig get               — show current config\n/botconfig set <field> <value>  — change a field\n/botconfig unset <field>     — clear back to default (some fields)\n\nEditable fields:\n{fields}\n\nThe sensitive field allowedUsers needs a trailing `confirm`; secrets (appSecret/credentials) cannot be changed here.',
    'cmd.config.snapshot': '⚙️ Current config\nCLI: {cli}　Brand: {brand}　Admins: {admins}\nWorking dirs: {dirs}\n\n{fields}\n\n(edit: /botconfig set <field> <value>; help: /botconfig help)',
    'cmd.config.set_usage': 'Usage: /botconfig set <field> <value>　or　/botconfig unset <field>\nList editable fields: /botconfig help',
    'cmd.config.unknown_field': 'Unknown field: {field}\nEditable fields: {fields}',
    'cmd.config.not_clearable': 'Field {field} cannot be unset (use /botconfig set {field} on|off, or set a new value).',
    'cmd.config.value_required': 'Please provide a value for {field}: /botconfig set {field} <value>',
    'cmd.config.invalid_bool': '{field} expects a boolean (on/off), got: {value}',
    'cmd.config.invalid_enum': '{field} must be one of {values}.',
    'cmd.config.invalid_cli': '⚠️ {msg}',
    'cmd.config.set_ok': '✅ Updated {field}: {old} → {new}\n{effect}',
    'cmd.config.unset_ok': '✅ Cleared {field} (was {old}), reverted to default.\n{effect}',
    'cmd.config.effect_immediate': '↳ Takes effect immediately (next message/event reads the new value).',
    'cmd.config.effect_next_session': '↳ Applies to new sessions; running sessions pick it up after /restart.',
    'cmd.config.write_failed': '⚠️ Write failed: {reason}',
    'cmd.config.unknown_sub': 'Unknown subcommand: {sub}\nSupported: /botconfig get | set | unset | help',
    'cmd.config.card_dmd': '⚙️ Config card sent to your DM (only you can see it) → open our 1:1 chat to view / edit.',
    'card.config.title': '⚙️ {name} · config',
    'card.config.summary': '🤖 CLI **{cli}**　·　🧠 model **{model}**　·　🌐 lang **{lang}**　·　👤 admins **{admins}**',
    'card.config.default': 'default',
    'card.config.sec.runtime': '🧠 Runtime',
    'card.config.sec.card': '🎴 Card behavior',
    'card.config.sec.autostart': '🚀 Auto-start',
    'card.config.sec.security': '🔒 Security & grants',
    'card.config.quota_label': 'Msg quota',
    'card.config.quota_off': 'Unlimited',
    'card.config.p2p.placeholder': 'DM mode',
    'card.config.p2p.thread': '🧵 thread (separate session/DM)',
    'card.config.p2p.chat': '💬 chat (continuous session)',
    'config.label.disableStreamingCard': 'Disable live card',
    'config.label.writableTerminalLinkInCard': 'Writable terminal in card',
    'config.label.privateCard': 'Private snapshot card',
    'config.label.autoStartOnGroupJoin': 'Auto-start on join',
    'config.label.autoStartOnNewTopic': 'Auto-start on new topic',
    'config.label.disableCliBypass': 'Disable bypass · safer',
    'config.label.restrictGrantCommands': 'Grantees chat-only',
    'card.config.note': '🟢=on ⚪=off, tap to flip & refresh (model/cli take effect next session). Language: `/botconfig zh` | `/botconfig en`.\nMore fields via "✏️ Text fields"; allowedUsers / oncall use `/botconfig help`.',
    'card.config.text_btn': '✏️ Text fields',
    'card.config.text_title': '✏️ {name} · text fields',
    'card.config.text_note': 'Edit then tap Save (one write); leaving a field blank clears it. For long/multi-line roles use `/role team set`.',
    'card.config.lbl_brand': 'Card signature (brandLabel)',
    'card.config.lbl_prompt': 'On-join first prompt',
    'card.config.lbl_role': 'Default role (team, cross-chat)',
    'card.config.save': '💾 Save',
    'card.config.back': '⬅ Back to config',
    'card.config.text_saved': '✓ Saved',
    'card.config.text_sent': '✏️ Text-fields card sent to your DM ↓',
    'card.config.text_send_fail': '⚠️ Send failed (you may not have a 1:1 with this bot yet)',
    'config.label.autoStartPrompt': 'On-join first prompt',
    'cmd.config.allow_usage': 'Usage: /botconfig set allowedUsers <email/on_/ou_, comma or space separated> confirm\ne.g.: /botconfig set allowedUsers alice@corp.com, ou_xxx confirm',
    'cmd.config.allow_invalid': '⚠️ Invalid entries (need a full email or on_/ou_ prefix): {items}',
    'cmd.config.allow_confirm': '⚠️ About to set the admin list to:\n{list}\n\nThis changes who can manage this bot. To confirm, resend with a trailing `confirm`:\n/botconfig set allowedUsers {list} confirm',
    'cmd.config.allow_ok': '✅ Admin list updated ({total} entries, {count} open_ids resolved). Effective immediately.',
    'cmd.config.allow_lockout': '⛔ The new list does not include you — that would lock you out (self-lockout guard). Keep yourself in the list and retry.',
    'cmd.config.allow_empty': '⚠️ The new list resolved to empty (no email/ID could be resolved). Rejected.',
    'cmd.group.empty_fallback': 'New session {ts}',
    'cmd.group.no_bot': '⚠️ Cannot identify the responding bot — /group aborted.',
    'cmd.group.no_sender': '⚠️ Cannot get sender open_id — /group aborted.',
    'cmd.group.created': '✅ Created group "{name}"\n👉 {link}\n\nJust start chatting in the new group — the whole group is one CLI session.{hints}',
    'cmd.group.failed': '⚠️ Failed to create group: {error}',
    'cmd.group.warn_invite_rejected': '⚠️ Feishu rejected the auto-invite (your open_id may not be in this bot\'s app scope). Please join the group manually.',
    'cmd.group.warn_transfer_failed': '⚠️ Owner transfer failed ({reason}); the bot is still the group owner. You can transfer it manually inside the new group.',
    'cmd.group.bots_invited': '🤖 Bots in the group: {bots}',
    'cmd.group.warn_bots_rejected': '⚠️ Some bots could not be added ({bots}) — they may not be in this bot\'s app scope. Please add them manually.',
    'cmd.group.warn_share_link_failed': '⚠️ Couldn\'t fetch a group share link; the link above only opens for members already in the group — the owner should invite non-members manually.',
    'cmd.group.resolve_failed': '⚠️ Could not resolve the @-mentioned bots (chat member info may be temporarily unavailable); group not created. Please retry shortly.',
    // ─── /relay --create ────────────────────────────────────────────────────
    'cmd.relay.empty_group_name': 'Session Relay {ts}',
    'cmd.relay.no_session': '⚠️ /relay must be invoked from a thread with an active session.',
    'cmd.relay.target_has_session': '⚠️ This bot already has an active session "{title}" in this chat. Please /close it first, then /relay to bring a new one in.',
    'cmd.relay.no_sender': '⚠️ Could not resolve sender open_id; /relay cancelled.',
    'cmd.relay.no_mentions': '⚠️ /relay --create requires at least one @-mentioned bot as a collaborator in the new group.',
    'cmd.relay.not_owner': '⚠️ Only the original session owner can /relay --create this session to a new group.',
    'cmd.relay.adopt_not_relayable': '⚠️ This session was attached via /adopt to an external tmux. The CLI runs on your machine and botmux does not control its tmux lifecycle, so it cannot be relayed.',
    'cmd.relay.not_started_yet': '⚠️ The session has not picked a repo and the CLI has never started — nothing to relay yet. Pick a repo in this session first.',
    'cmd.relay.worker_busy': '⚠️ The session is mid-turn (working). Wait for the current turn to finish (idle), then try /relay again.',
    'cmd.relay.resolve_failed': '⚠️ Could not resolve the @-mentioned bots; /relay cancelled. Please retry shortly.',
    'cmd.relay.m1_announce': '📋 Relayed from "{sourceChat}".\n⚠️ Lark message history stays in the source chat; AI memory has been carried over.\n@ the corresponding bot to continue.',
    'cmd.relay.m1_announce_dm': '📋 Relayed from "{sourceChat}".\n⚠️ Lark message history stays in the source chat; AI memory has been carried over.\nJust send a message to continue.',
    'cmd.relay.m1_final_all_ok': '🚀 Sessions relayed into this chat\n📋 Source: {sourceChat}\n✅ Ready: {successBots}\n⚠️ Lark message history stays in the source chat; AI memory has been carried over. @ the corresponding bot to continue.',
    'cmd.relay.m1_final_partial': '🚀 Sessions relayed into this chat\n📋 Source: {sourceChat}\n✅ Ready: {successBots}\n⚠️ Failed to migrate: {failedBots}\n→ Run /relay in this chat to pull those bots\' sessions from the source manually.\n⚠️ Lark message history stays in the source chat; AI memory has been carried over.',
    'cmd.relay.m1_final_all_fresh': '🚀 New group created and bots invited.\n📋 Source: {sourceChat}\nNo source session to migrate — @ any bot to start a fresh conversation.',
    'cmd.relay.created': '✅ Created group "{name}" and relayed sessions\n👉 {link}\n\n{report}',
    'cmd.relay.report_leader_ok': '• {bot} (current session) relayed',
    'cmd.relay.report_leader_no_session': '• {bot} (current session) — skipped (no active session)',
    'cmd.relay.report_leader_failed': '• {bot} (current session) — relay failed: {error}',
    'cmd.relay.report_peer_ok': '• {bot} relayed',
    'cmd.relay.report_peer_no_session': '• {bot} — skipped (no session in source thread)',
    'cmd.relay.report_peer_not_owner': '• {bot} — skipped (session not yours)',
    'cmd.relay.report_peer_offline': '• {bot} — skipped (daemon offline)',
    'cmd.relay.report_peer_busy': '• {bot} — skipped (session busy, timed out)',
    'cmd.relay.report_peer_failed': '• {bot} — relay failed: {error}',
    'cmd.relay.failed': '⚠️ /relay --create failed: {error}',
    // ─── /schedule ───────────────────────────────────────────────────────────
    'schedule.empty_with_examples': 'No scheduled tasks yet.\n\nExamples:\n/schedule daily 17:50 summarize today\'s AI news\n/schedule weekdays 9:00 check service status\n/schedule mondays 10:00 generate weekly report',
    'schedule.list_header': 'Scheduled tasks ({count}):',
    'schedule.next_label': ' → next: {time}',
    'schedule.last_label': ' | last: {time}',
    'schedule.removed': 'Removed scheduled task {id}',
    'schedule.not_found': 'Task {id} not found',
    'schedule.enabled': 'Enabled scheduled task {id}',
    'schedule.disabled': 'Disabled scheduled task {id}',
    'schedule.triggered_now': 'Triggered scheduled task {id} now',
    'schedule.created': '✅ Scheduled task created!\n\nID: {id}\nName: {name}\nRule: {rule}\nPrompt: {prompt}\nWorking dir: {dir}\nNext run: {next}',
    'schedule.parse_failed': 'Cannot parse schedule. Use a natural-language form:\n\n/schedule daily 17:50 summarize today\'s AI news\n/schedule weekdays 9:00 check service status\n/schedule mondays 10:00 generate weekly report\n/schedule hourly check service health\n/schedule every 30 minutes ping the service\n/schedule 1st of each month 9:00 generate monthly report\n\nManagement:\n/schedule list — list all tasks\n/schedule remove <id> — delete a task\n/schedule enable <id> — enable a task\n/schedule disable <id> — disable a task\n/schedule run <id> — run once now',
    // ─── /help ───────────────────────────────────────────────────────────────
    'help.heading_session': '📌 Session management:',
    'help.close': '/close      - Close current session, kill {cliName}',
    'help.restart': '/restart    - Restart {cliName} (keep session)',
    'help.cd': '/cd <path>  - Change working dir and restart {cliName}',
    'help.repo_list': '/repo       - Pending selection: start in default dir; mid-session: show project picker',
    'help.repo_n': '/repo <N>   - Switch to project #N',
    'help.repo_path': '/repo <path|name> - Use a path or a project name under workingDir, skipping the card',
    'help.repo_wt': '/repo wt <N|name> [branch] - Create a worktree off the remote default branch and open it',
    'help.status': '/status     - Show current session status (incl. terminal URL)',
    'help.card': '/card       - Manually post the streaming card for this session (summons it even when streaming is off, and resumes live updates; with private-card mode on, sends a static snapshot visible only to authorized users instead)',
    'help.term': '/term       - Get the operable (write-enabled) terminal link for this session, delivered privately to the owner (visible-to-you in-chat, falling back to DM in topic/p2p — never exposed in the group)',
    'help.subscribe_doc': '/subscribe-lark-doc <doc link|list|off> - Subscribe a Feishu doc: its comments feed into this session, my replies go back into the comment (needs /login first)',
    'help.heading_passthrough': '🔀 Passthrough to {cliName} (forwarded verbatim to its built-in slash commands):',
    'help.heading_schedule': '⏰ Scheduled tasks:',
    'help.schedule_create': '/schedule daily 17:50 summarize AI news  - create a scheduled task (natural language)',
    'help.schedule_list': '/schedule list                            - list all tasks',
    'help.schedule_remove': '/schedule remove <id>                     - delete a task',
    'help.schedule_toggle': '/schedule enable/disable <id>             - enable/disable a task',
    'help.schedule_run': '/schedule run <id>                        - run once now',
    'help.schedule_formats': 'Time formats: daily, weekly on <day>, monthly on <N>, weekdays, every N hours, every N minutes',
    'help.heading_adopt': '📡 Session adopt:',
    'help.adopt': '/adopt              - Take over a running CLI session, or resume a past one from disk',
    'help.adopt_pane': '/adopt <tmux_pane>  - Adopt a specific tmux pane (or a past session id)',
    'help.detach': '/detach             - Disconnect this topic from the adopted session (the original CLI is untouched; /disconnect is an alias)',
    'help.heading_login': '🔐 User OAuth:',
    'help.login': '/login              - Lark user OAuth (lets you download images etc. from third-party cards)',
    'help.login_status': '/login status       - Show auth status',
    'help.heading_oncall': '🛎️ Oncall mode (group chats):',
    'help.oncall_bind': '/oncall bind <path>  - Bind this group to a project, skipping the repo-selection card',
    'help.oncall_unbind': '/oncall unbind       - Unbind this group',
    'help.oncall_status': '/oncall status       - Show oncall binding for this group',
    'help.heading_grant': '🔑 Access grants (owner only):',
    'help.grant': '@bot /grant @someone   - Let them talk in this chat (you can @ several at once); /grant (no target) opens the whole chat to talk',
    'help.revoke': '@bot /revoke @someone  - Revoke their talk access (you can @ several at once); /revoke (no target) revokes the whole-chat grant',
    'help.heading_config': '⚙️ Edit config remotely (owner only; written + hot-applied, no restart):',
    'help.config_get': '/botconfig get  - Show this bot\'s current operational config',
    'help.config_set': '/botconfig set <field> <value>  - Change model/cli/lang/toggles; /botconfig help for all fields',
    'help.heading_group': '🆕 One-shot session group:',
    'help.group': '/group <name>  (alias /g)  - Auto-create a group and invite you (the whole group = one CLI session)',
    'help.list_slash': '/list-slash-command  (alias /slash)  - List the slash commands available now (fixed allowlist / CLI defaults / custom config / auto-discovered)',
    'help.help': '/help       - Show this help',
    // ─── /list-slash-command ─────────────────────────────────────────────────
    'slashlist.heading': '🧩 Slash commands available now ({cliName})',
    'slashlist.part_builtin': '① Fixed allowlist (builtin passthrough):',
    'slashlist.part_adapter': '② Current CLI defaults:',
    'slashlist.part_custom': '③ User-configured (bots.json · customPassthroughCommands):',
    'slashlist.part_custom_empty': '(none configured — add customPassthroughCommands: ["/xxx"] to this bot in bots.json to allow more)',
    'slashlist.part_discovered': '④ Auto-discovered (CLI filesystem commands / skills / plugins):',
    'slashlist.part_discovered_empty': '(none found — no commands/skills/plugins in this CLI directory; working dir: {dir})',
    'slashlist.part_discovered_unsupported': '({cliName} has not declared an enumerable filesystem command directory)',
    'slashlist.more': '… {n} more not shown',
    'slashlist.mcp_note': 'ℹ️ MCP /mcp__<server>__<prompt> commands need a live handshake to enumerate and are not listed here. Detected MCP servers: {servers}',
    'slashlist.col_cmd': 'Command',
    'slashlist.col_desc': 'Description',
    // ─── AI system prompt (Claude Code: --append-system-prompt) ──────────────
    'ai.routing.intro': 'You are connected to a Lark (Feishu) topic group. The user is reading on Lark and CANNOT see your terminal output.',
    'ai.routing.must_use_botmux': 'To make the user see something, you MUST send it via the `botmux send` command. Terminal output does NOT reach the chat.',
    'ai.routing.usage_heading': 'How to use it:',
    'ai.routing.usage_send_when': '- Use `botmux send` for: key conclusions, plans (wait for user approval before acting), final results, progress updates.',
    'ai.routing.usage_send_text': '- Plain text is fine: `botmux send "your message"`. Formatting is auto-handled.',
    'ai.routing.usage_heredoc': '- Multi-line messages MUST use a heredoc. Never write `botmux send "line1\\nline2"` — the `\\n` may appear literally in Lark.',
    'ai.routing.heredoc_example': "  Correct multi-line example:\n```bash\nbotmux send <<'EOF'\nline 1\nline 2\nEOF\n```",
    'ai.routing.usage_images': '- Attach images: `botmux send --images /path/to/img.png "caption"`',
    'ai.routing.usage_files': '- Attach files: `botmux send --files /path/to/file.pdf "FYI"`',
    'ai.routing.usage_history': '- Need prior context? Use `botmux history` to read this session\'s history.',
    'ai.routing.usage_bots_list': '- List collaborator bots in the group: `botmux bots list`',
    // ─── AI identity (multi-bot routing rules) ───────────────────────────────
    'ai.identity.unknown': '(unknown)',
    'ai.identity.routing_intro': 'There may be multiple bots in the group. Messages use `@name` and `open_id` to route. Check name/open_id above to decide if a message is for you:',
    'ai.identity.rule_own_part': '- Do ONLY the part addressed to you; do not pick up work assigned to other bots',
    'ai.identity.rule_silent_when_other': '- If the whole message is for another bot, stay silent — do not reply',
    'ai.identity.rule_no_proactive_pull': '- **Do not proactively pull other bots in by default.** Unless the user explicitly asks, or a segment can only be done by another bot, finish your part and stop.',
    'ai.identity.mention_intro': '**Hard fact about cross-bot collaboration**: in a Lark topic group, other bots **do NOT receive** the messages you send via `botmux send` by default —',
    'ai.identity.mention_must': 'To hand work off to another bot, you **MUST** explicitly pass `--mention <other-bot-open-id>`. Without `--mention`, the other bot is not triggered at all.',
    'ai.identity.mention_partners': '- Collaborator `open_id`s are listed in the `<available_bots>` block attached to every user message. You can also run `botmux bots list`.',
    'ai.identity.mention_usage': '- Usage: `botmux send --mention ou_xxx "message"` (repeat `--mention` for multiple bots). `--mention-back` @s back whoever (person or bot) triggered this turn — open_id is pulled from the session, no need to type it.',
    'ai.identity.mention_gate': '- **@ hard-gate**: every `botmux send` MUST explicitly pick one or it errors out — `--mention` (name someone) / `--mention-back` (@ the triggerer) / `--no-mention` (none). Choose by message VALUE: a substantive conclusion the other party should read/confirm/decide on → --mention-back (or --mention); pure record / low-priority progress / short ack → --no-mention; a contentless "got it" is better not sent at all. Do not let --no-mention become the default, and do not @ people for nothing.',
    'ai.identity.mention_when_to': '- When to `--mention`: the user explicitly asks for the handoff, you\'re passing a sub-task to the other bot, or you need them to give the final result / take an independent action.',
    'ai.identity.mention_when_not': '- When NOT to `--mention`: pure status updates / acknowledgements / thanks — fold them into the next message that has real content, to avoid mutual ping-spam.',
    // ─── AI hints (non-Claude CLIs: BOTMUX_SHELL_HINTS) ──────────────────────
    'ai.shell.intro': 'You are running inside a Lark (Feishu) topic group. The user reads on Lark and cannot see your terminal output.',
    'ai.shell.commands_are_shell': 'IMPORTANT: `botmux send` / `botmux history` / `botmux quoted` / `botmux bots` are SHELL commands (CLI programs installed in $PATH), NOT MCP tools. Run them via the Bash tool — don\'t look for them in the MCP tool list.',
    'ai.shell.how_to_send': 'To send a message to the user (the only way): run `botmux send "your message"` via Bash. Attach images with `--images /path`, files with `--files /path`.',
    'ai.shell.multiline_heredoc': 'Multi-line messages MUST use a heredoc — never `botmux send "line1\\nline2"`, since `\\n` may appear literally in Lark.',
    'ai.shell.heredoc_example': "Correct multi-line example:\n```bash\nbotmux send <<'EOF'\nline 1\nline 2\nEOF\n```",
    'ai.shell.helpers': 'Helpers: `botmux history` (read this session\'s history — thread/topic sessions are topic-scoped; regular-group chat-scope sessions are group-wide), `botmux quoted <message_id>` (fetch a quoted message — only use it when the prompt header shows `[user quoted message ...]`), `botmux bots list` (list other bots in the group).',
    'ai.shell.when_to_send': 'When to send: key conclusions, plans (wait for user approval before acting), final results, progress updates. A bare `print`/`echo` does NOT count as a reply.',
    'ai.shell.mention_gate': '@ decision (mandatory): every `botmux send` MUST explicitly pick one or it errors — `--mention <open_id:name>` (name a person/bot; REQUIRED to hand work to another bot) / `--mention-back` (@ the sender of the message you are replying to) / `--no-mention` (none). Choose by VALUE: substantive conclusion the other party should read/confirm/decide → --mention-back; pure record / low-priority / short ack → --no-mention; a contentless "got it" is better not sent. Do not default to --no-mention, and do not @ people for nothing.',
    // ─── AI prompt blocks (session-manager) ──────────────────────────────────
    'ai.attach.hint': 'Read these with the Read tool. The index matches the [image N] / [file N] placeholders in the body.',
    'ai.identity.short_routing': 'Reminder: handing work off to another bot REQUIRES `botmux send --mention <their open_id>` — without it, the other bot is not triggered.',
    'ai.available_bots.hint': 'To hand work off to a bot listed here you MUST --mention its open_id (botmux send --mention ou_xxx ...). Without --mention the other bot receives nothing.',
    'ai.followup.reminder': 'Replies must go via `botmux send` — terminal output does not reach the user.',
    'ai.cursor.sender_note': 'The sender tag is metadata identifying the current speaker — never copy its open_id or name (e.g. ou_xxx:Alice) into your botmux send body or opening line; to @ the triggerer use botmux send --mention-back.',
    'ai.bridge.attachments_label': '[Attachments]',
    'ai.bridge.mentions_label': '[@Mentions]',
    'schedule.title_prefix': '[Scheduled]',
    // ─── Role command ─────────────────────────────────────────────────────────
    'role.current': '📋 Effective role:',
    'role.byte_count': '{bytes} / {max} bytes',
    'role.empty': 'No effective role (neither team-level nor chat-level set).\n\nUsage:\n• /role set <Markdown> — Set chat role (overrides team)\n• /role team set <Markdown> — Set team-level role (this bot, cross-chat default)\n• /role cap set <one-liner> — Set capability label (shown in roster)',
    'role.saved_via_cmd': '✅ Chat role updated ({bytes} / {max} bytes). Takes effect on next session.',
    'role.deleted_via_cmd': '✅ Chat role deleted.',
    'role.nothing_to_delete': 'No role configured for this chat.',
    'role.set_empty': 'Role content cannot be empty. Usage: /role set <Markdown content>',
    'role.no_chat': 'Cannot determine chat info. Please retry.',
    'role.src_chat': '(source: chat override)',
    'role.src_team': '(source: team default)',
    'role.team_current': '📋 Team-level role (this bot, cross-chat default):',
    'role.team_empty': 'No team-level role set.\nUsage: /role team set <Markdown> — set; /role team delete — delete',
    'role.team_saved': '✅ Team-level role updated ({bytes} / {max} bytes). Chats without an override pick it up on next session.',
    'role.team_deleted': '✅ Team-level role deleted.',
    'role.team_nothing': 'No team-level role to delete.',
    'role.cap_current': '🏷️ Capability label: {cap}',
    'role.cap_empty': 'No capability label set.\nUsage: /role cap set <one-line capability>',
    'role.cap_saved': '✅ Capability label updated: {cap}',
    'role.cap_cleared': '✅ Capability label cleared.',
    'role.cap_set_empty': 'Capability label cannot be empty. Usage: /role cap set <description>',
    'role.help': '/role usage:\n• /role — View effective role\n• /role set <Markdown> — Set chat role (overrides team)\n• /role delete — Delete chat role\n• /role team [set <Markdown>|delete] — Team-level role (cross-chat default)\n• /role cap [set <one-liner>|clear] — Capability label (shown in roster)',
    'pair.usage': 'Usage: /pair <code> (the code is shown when you click "Log in" on the team platform web UI).',
    'pair.ok': '✅ Login paired. Return to the web page to continue.',
    'pair.expired': 'Pairing code expired. Start login again on the web page.',
    'pair.already': 'That pairing code was already used. Start login again on the web page.',
    'pair.not_found': 'Invalid pairing code. Check it and retry (expired codes need a new login).',
    // ─── Card handler responses ──────────────────────────────────────────────
    'card.action.adopt_no_restart': '⚠️ Restart is disabled in adopt mode (it would not touch your tmux pane / CLI process anyway).',
    'card.action.restarted': '🔄 Restarted {cliName}',
    'card.action.restarted_fresh': '🔄 Re-launched {cliName}',
    'card.action.resume_missing_session_id': '⚠️ Missing session_id, cannot resume.',
    'card.action.resume_success': '✅ Session resumed. Send a message to continue with {cliName}.',
    'card.action.resume_not_found': '⚠️ Session {short} not found — it may have been cleaned up.',
    'card.action.resume_not_closed': 'Session is already active, no need to resume.',
    'card.action.resume_anchor_occupied': '⚠️ This topic already has a newer session{detail}; cannot resume the older one.',
    'card.action.resume_anchor_holder': ' (holder: {short})',
    'card.action.resume_adopt_unsupported': '⚠️ Adopted sessions cannot be resumed.',
    'card.action.disconnected': '⏏ Disconnected. The original CLI is untouched.',
    'card.voice.toast_wait': '🔊 Generating a voice summary, hang tight…',
    'card.voice.toast_already': '🔊 A voice summary for this reply is already on the way.',
    'card.voice.toast_session_gone': '⚠️ Session is offline; cannot generate a voice summary.',
    'card.voice.toast_need_auth': '🔒 You are not authorized to use this bot, so you cannot generate a voice summary. Ask an admin for access.',
    'card.action.takeover_retired': '⚠️ The old "Take Over" button is retired. In bridge mode, botmux bridges the original CLI so replies still come back to Lark — no takeover needed. Full takeover (`/adopt --takeover`) is on the roadmap.',
    'card.action.terminal_not_ready': '⚠️ Terminal is not ready yet, please try again shortly.',
    'card.action.no_output': '(no output yet)',
    'card.action.tui_select_title': 'Select options',
    'card.action.tui_custom_input': 'Custom input',
    'card.action.tui_done': 'Done',
    'card.action.continue_using_current_repo': 'Keeping current repo: {cwd}',
    'card.action.retry_last_task_missing': '⚠️ Cannot find the last task to retry. Send a new message instead.',
    'card.action.retry_last_task_unavailable': '⚠️ This retry state is no longer active. Send a new message instead.',
    'card.action.retry_last_task_not_ready': '⚠️ The limit is still active. Try again after {retryLabel}.',
    // ─── Worker → daemon notices ─────────────────────────────────────────────
    'worker.adopted_session_exited': '⏏ Adopted CLI session has exited.',
    'worker.crash_loop_stopped': '⚠️ {cliName} crashed {count} times in 1 minute. Auto-restart disabled. Send a message to retry.',
    // ─── CLI setup wizard / pm2 lifecycle (no per-bot context) ───────────────
    'setup.lark_create_app': 'First create a Lark app at: https://open.feishu.cn/app',
    'setup.lark_permissions_required': 'Permissions to grant:',
    'setup.lark_perm_im_message': '  - im:message (send/receive messages)',
    'setup.lark_perm_group_at_msg': '  - im:message.group_at_msg (group messages)',
    'setup.lark_perm_resource': '  - im:resource (file download)',
    'setup.lark_perm_chat': '  - im:chat (group info)',
    'setup.lark_perm_user_base': '  - contact:user.base:readonly (user info)',
    'setup.lark_enable_events': 'Enable Event Subscription (WebSocket mode):',
    'setup.supported_clis': 'Supported CLIs: 1) claude-code  2) aiden  3) coco  4) codex  5) cursor  6) gemini  7) opencode  8) antigravity  9) mtr  10) hermes  11) codex-app  12) mira',
    'setup.prompt_cli_choice': 'CLI adapter [1]: ',
    'setup.prompt_working_dir': 'Default working directory [~]: ',
    'setup.prompt_allowed_users': 'Allowed users (emails or open_ids, comma-separated; empty = no restriction): ',
    'setup.prompt_lang': 'UI language [zh/en, default en]: ',
    'setup.section_lark_app_config': '── Lark app config ──',
    'setup.section_reconfigure': '── Reconfigure ──',
    'setup.section_add_new_bot': '── Add new bot ──',
    'setup.config_written': '✅ Config written: {path}',
    'setup.next_step': 'Next:',
    'setup.next_step_short': 'Next: botmux restart',
    'setup.next_start_daemon': '  1. botmux start              start the daemon (required before Lark long-poll connects)',
    'setup.next_autostart': '  2. botmux autostart enable   enable on-boot autostart (recommended: {platformName}; no sudo)',
    'setup.platform_mac': 'macOS launchd',
    'setup.platform_linux': 'Linux user systemd',
    'setup.platform_other': 'current platform not supported',
    'setup.wizard_title': '🤖 botmux setup wizard',
    'setup.config_dir': 'Config dir: {path}',
    'setup.data_dir': 'Data dir: {path}',
    'setup.bots_already_configured': '{count} bot(s) already configured:',
    'setup.prompt_action_existing_bots': 'Action: 1) Add new bot  2) Reconfigure  (1/2) [1]: ',
    'setup.old_config_backed_up': 'Old config backed up: {path}',
    'setup.added_bot': '✅ Added bot {appId}; {count} total',
    'setup.config_file': '   Config file: {path}',
    'setup.using_single_bot_config': 'Currently using single-bot config: {path}',
    'setup.prompt_action_env_exists': 'Action: 1) Add new bot  2) Overwrite current config  (1/2): ',
    'setup.old_env_backed_up': '   Old .env backed up: {path}',
    'setup.env_missing_keys': '⚠️  Current .env is missing LARK_APP_ID or LARK_APP_SECRET — please complete the basics first',
    'setup.current_bot': 'Current bot: {appId} ({cliId})',
    'setup.migrated_to_multibot': '✅ Migrated to multi-bot config',
    'start.no_config': '❌ No config file found',
    'start.run_setup': '   First run: botmux setup',
    'start.daemon_started': '✅ Daemon started{suffix}',
    'start.daemon_started_suffix': ' ({count} bots, each in its own process)',
    'start.logs_hint': '   Logs: botmux logs',
    'start.status_hint': '   Status: botmux status',
    'start.autostart_synced': '   autostart unit synced to current Node/cli.js paths',
    // ─── Daemon runtime notices ──────────────────────────────────────────────
    'daemon.auto_start_join_title': 'Proactive start (joined chat)',
    'daemon.auto_start_join_seed': '🚀 Joined this chat — getting to work…',
    'daemon.cmd_requires_session': '{cmd} requires an existing session (send a normal message first to start the CLI).',
    'daemon.cmd_allowed_users_only': '⚠️ {cmd} is restricted to `allowedUsers`.',
    'daemon.download_failed_need_login': '⚠️ Some images/files failed to download (missing User Token). Send `/login` in this topic to authorize, then resend.',
    'daemon.foreign_bot_mention_prefix': '[@mention from {botName}]',
    'daemon.cmd_needs_active_cli': '{cmd} needs an active CLI process; no running session in this topic.',
    'daemon.enriched_mentions_label': '@mentions in this message:',
    'daemon.choose_repo_first': 'Pick a repo from the card above first — your message is queued and will be sent once a repo is chosen.',
    // ─── i18n coverage v2: shared fragments + cards + chat messages ───────────
    'common.operator': 'By: {by}',
    'common.empty_paren': '(empty)',
    'common.truncated_short': '…(truncated)',
    // Workflow approval card
    'card.wf.none': 'None',
    'card.wf.title_pending': 'Approval needed: {node}',
    'card.wf.title_resolved': '{prefix}: {node}',
    'card.wf.review_content': 'Review content',
    'card.wf.review_preview_suffix': ' (preview; full text in Web details below)',
    'card.wf.comment_placeholder': 'Optional: add a review comment',
    'card.wf.btn_approve': '✅ Approve',
    'card.wf.btn_reject': '❌ Reject',
    'card.wf.btn_cancel_run': 'Cancel run',
    'card.wf.btn_web_detail': 'Web details',
    'card.wf.resolved.approved': 'Approved',
    'card.wf.resolved.rejected': 'Rejected',
    'card.wf.resolved.cancelled': 'Cancelled',
    'card.wf.banner.approved': '✅ Approved',
    'card.wf.banner.rejected': '❌ Rejected',
    'card.wf.banner.cancelled': '🛑 Cancelled',
    'card.wf.comment_label': 'Note: {comment}',
    'card.wf.truncated': '…(truncated; view the full text on the Web)',
    // Workflow progress card
    'card.wf.field.status': 'Status',
    'card.wf.field.progress': 'Progress',
    'card.wf.field.failed': 'Failed',
    'card.wf.progress_value': '{done} / {total} nodes done',
    'card.wf.section.running': 'Running',
    'card.wf.section.waiting': 'Awaiting approval',
    'card.wf.section.loop': 'Loop nodes',
    'card.wf.failure_summary': 'Failure summary',
    'card.wf.terminal.live': 'View live terminal',
    'card.wf.terminal.log': 'View execution log',
    'card.wf.usage': 'Usage: /workflow run <id> [key=value ...]\nor: /workflow cancel <runId>',
    'wf.err.missing_run_id': 'Missing runId',
    'wf.err.cancel_only_run_id': '/workflow cancel only accepts a runId',
    'wf.err.run_id_charset': 'runId may only contain letters, digits, underscore, dot and hyphen',
    'wf.err.unknown_subcommand': 'Only /workflow run / cancel subcommands are supported',
    'wf.err.missing_workflow_id': 'Missing workflow id',
    'wf.err.workflow_id_charset': 'workflow id may only contain letters, digits, underscore, dot and hyphen',
    'wf.err.param_format': 'Parameters must be key=value: {token}',
    'wf.err.param_name_charset': 'Parameter names may only contain letters, digits, underscore, dot and hyphen: {key}',
    'wf.err.duplicate_param': 'Duplicate parameter: {key}',
    'wf.cmd_failed': 'Workflow command failed: {error}',
    // Ask card
    'card.ask.field.deadline': 'Deadline',
    'card.ask.field.answerable': 'Can answer',
    'card.ask.question_n': 'Question {n}',
    'card.ask.submit': 'Submit',
    'card.ask.custom_reply_hint': '💬 None of the options fit? Just reply with your answer in this topic.',
    'card.ask.title': 'botmux ask',
    'card.ask.title_done': 'botmux ask · done',
    'card.ask.toast.unauthorized': "You're not allowed to answer this ask",
    'card.ask.toast.already_settled': 'This ask was already answered or closed',
    'card.ask.toast.stale': '⚠️ This ask is no longer valid',
    'card.ask.custom_reply': 'Custom reply',
    'card.ask.q_summary_line': 'Q{n}: {labels}',
    'card.ask.q_unparseable': 'Q{n}: (unparseable)',
    'card.ask.selected': 'Selected',
    'card.ask.supplement': 'Note: {comment}',
    'card.ask.timed_out': 'Timed out, no answer',
    'card.ask.invalidated': 'No longer valid',
    'card.ask.no_approver': 'No eligible responders',
    'card.ask.answerable_talk_members': 'anyone who can talk to me in this chat',
    // Voice summary instruction (injected into the model session)
    'card.voice.summary_instruction': '🔊 [Voice summary request] Condense your last reply to the user into spoken prose of at most 5 sentences suitable for reading aloud: drop code, commands, file paths, URLs, English abbreviations and markdown; state only the conclusions, and get to the point in the first sentence. Then call `botmux send --voice "<the condensed spoken text>"` to send it as voice. Send only this one voice message — no extra text.',
    // Card action toasts (dedupe / in-flight / approver gate)
    'toast.action_received_no_repeat': 'Action received — please don’t click again',
    'toast.action_in_progress': 'Action in progress — please wait',
    'toast.action_received_bg': 'Action received — processing in the background',
    'toast.not_in_approver_list': 'You’re not on the approver list — no action taken',
    // Quote hint (injected into the CLI prompt)
    'prompt.quote_hint': '[User quoted a message — run `botmux quoted {id}` to view it]',
    // Markdown / contextual reply card chrome
    'card.you': 'You',
    'card.sent_to': 'Sent to: ',
    // Adopt preamble card title
    'card.adopt_last_round': '📜 Last exchange before /adopt',
    'card.local_turn_resumed': '🖥️ Local terminal turn resumed (model was mid-output during the daemon restart)',
    'card.local_turn': '🖥️ Local terminal turn (typed directly in the adopted pane, synced to Lark)',
    // Scheduler announcements
    'scheduler.task_started': '🕐 Scheduled task “{name}” started',
    'scheduler.task_triggered_target_chat': '🕐 Scheduled task “{name}” triggered in the target chat',
    'scheduler.task_triggered_target_thread': '🕐 Scheduled task “{name}” triggered in the target topic',
    // External event trigger seed
    'trigger.external_event': 'External event: {source}',
    // Worker-side submit / notify messages
    'worker.submit_impossible': '⚠️ Your last message was NOT delivered to {cliName}: the current keybinding config can’t auto-submit from the terminal.\nReason: {reason}\nAdjust the Claude Code Chat keybinding, then resend.\nStart: {preview}',
    'worker.submit_unconfirmed': '⚠️ Your last message was sent to {cliName} but submission couldn’t be confirmed (after retrying Enter and waiting {secs}s, no new entry showed up in {transcriptLabel}). It may be stuck in the input box — check the Web terminal and press Enter manually or resend.\nStart: {preview}',
    'worker.coco_session_dir_gone': '⚠️ The current CoCo session directory was deleted (e2e cleanup or a manual rm). Content written to events.jsonl lands on a stale inode the bridge can’t read. Restart CoCo and run /adopt again.',
    // Restart / maintenance report (bot-0 DM to owner)
    'restart.updated_restarted': '🔄 **botmux updated & restarted**',
    'restart.restarted': '🔄 **botmux restarted**',
    'restart.version_delta': 'Version: {old} → {new}',
    'restart.version': 'Version: {version}',
    'restart.unfinished_sessions': 'Unfinished sessions: {count}',
    'restart.dashboard': 'Dashboard: {url}',
    'restart.changelog_label': 'What’s new:',
    'restart.changelog_link_fallback': 'Details: {url}',
    'restart.card_title': 'botmux maintenance notice',
    // Auto-start (joined chat) member-read failure admin DM
    'daemon.auto_start_member_read_failed': '⚠️ botmux “auto-start when added to a new chat” is on, but reading the chat members failed, so it can’t tell whether any authorized user is present — auto-start was skipped.\n\nMost likely cause: missing permission to read chat members (im:chat / chat info), or the “bot added to chat” event `im.chat.member.bot.added_v1` isn’t subscribed.\n\nGo to the Lark Open Platform → your app → Permissions / Event subscriptions to add them, then `botmux restart`.\n\nDetails: {detail}',
    // Sandbox landing (/land) errors
    'sandbox.no_clone': 'This session has no sandbox change layer (sandbox off, or the layer was cleaned up).',
    'sandbox.clone_not_git': 'The sandbox change layer is unavailable; landing isn’t supported.',
    'sandbox.nothing_to_land': 'No changes to land.',
    'sandbox.target_not_git': 'Landing target directory does not exist: {dir}',
    'sandbox.apply_failed': 'Landing failed: {detail}',
    'sandbox.diff_failed': 'Reading sandbox changes failed: {detail}',
    'sandbox.workingdir_not_found': 'Session workingDir not found',
    'sandbox.no_changes_left': 'The sandbox change layer has no changes left',
};
//# sourceMappingURL=en.js.map