micro-models-agent 1.6.0 → 2.0.0

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.
package/dist/main.js CHANGED
@@ -2312,6 +2312,7 @@ var init_defaults = __esm(() => {
2312
2312
  description: "browser + file reading for visual tasks"
2313
2313
  }
2314
2314
  },
2315
+ modelCapabilities: {},
2315
2316
  contextWindow: 32768,
2316
2317
  contextBudget: {
2317
2318
  systemPrompt: 0.1,
@@ -2803,6 +2804,10 @@ Fix the error and re-edit the file (a clean write clears the failure), or mark t
2803
2804
  "cli.list_sessions": "List all sessions",
2804
2805
  "cli.show_details": "Show session details",
2805
2806
  "cli.delete_session": "Delete a session",
2807
+ "cli.export_session": "Export a session to a file (md, json, or jsonl)",
2808
+ "cli.export_format": "Output format: md|json|jsonl (default md)",
2809
+ "cli.export_out": "Output path ('-' for stdout)",
2810
+ "cli.export_no_redact": "Do not redact secrets",
2806
2811
  "cli.first_run": "First run detected. Running setup wizard...",
2807
2812
  "cli.first_run_oneshot": "No configuration found — running with defaults. Run `mma` (no arguments) once to set up the wizard.",
2808
2813
  "cli.setup_saved_restart": "Settings saved. Restarting with the new configuration...",
@@ -2871,20 +2876,21 @@ Available commands:`,
2871
2876
  "repl.exit_usage": "Usage: /exit",
2872
2877
  "repl.clear": "Clear the screen",
2873
2878
  "repl.clear_usage": "Usage: /clear",
2879
+ "repl.clear_hint": "Screen cleared. To start a fresh session and context: /new",
2874
2880
  "repl.run": "Run a prompt through the agent",
2875
2881
  "repl.run_usage": "Usage: /run <prompt>",
2876
- "repl.config": "Show current configuration",
2877
- "repl.config_usage": "Usage: /config",
2878
- "repl.reasoning": "Toggle showing model reasoning/thinking",
2879
- "repl.reasoning_usage": "Usage: /reasoning [auto|none|low|medium|high|max]",
2882
+ "repl.config": "Show or change settings (get / set / reset)",
2883
+ "repl.config_usage": "Usage: /config [<key> [value]] | /config reset <key> | /config reset --all [--yes] | /config migrate",
2884
+ "repl.reasoning": "Show reasoning status, toggle display, or set effort level",
2885
+ "repl.reasoning_usage": "Usage: /reasoning [show|hide|level <auto|default|none|low|medium|high|max>]",
2880
2886
  "repl.reasoning_level_set": "Reasoning level set to: {level}",
2881
2887
  "repl.reasoning_invalid_level": "Invalid level '{level}'. Valid: {valid}",
2882
- "repl.verbose": "Chat verbosity: quiet, normal, or verbose",
2888
+ "repl.verbose": "Chat verbosity (quiet, normal, verbose)",
2883
2889
  "repl.verbose_usage": "Usage: /verbose [quiet|normal|verbose]",
2884
2890
  "repl.verbose_set": "Chat verbosity: {level}",
2885
2891
  "repl.verbose_invalid_level": "Invalid level '{level}'. Valid: {valid}",
2886
2892
  "repl.last_action_none": "No recent action to show.",
2887
- "repl.status": "Show agent status",
2893
+ "repl.status": "Show a compact status line",
2888
2894
  "repl.status_usage": "Usage: /status",
2889
2895
  "repl.plugins": "List loaded plugins",
2890
2896
  "repl.plugins_usage": "Usage: /plugins [--all]",
@@ -2924,11 +2930,13 @@ Available commands:`,
2924
2930
  "repl.context": "Context:",
2925
2931
  "repl.sysprompt_label": "System prompt:",
2926
2932
  "repl.sysprompt_size": "{used} / {budget} tokens",
2927
- "repl.sysprompt_desc": "Show system prompt",
2933
+ "repl.sysprompt_desc": "System prompt size and blocks (use --full for the text)",
2934
+ "repl.sysprompt_usage": "Usage: /sysprompt [--full]",
2935
+ "repl.sysprompt_full_hint": "Full prompt text hidden — run /sysprompt --full to print it.",
2928
2936
  "repl.sysprompt_tokens": "System prompt ({count} tokens):",
2929
2937
  "repl.excluded_blocks": `
2930
2938
  Excluded blocks: {count}`,
2931
- "repl.context_usage": "Usage: /context [<size> | system <fraction> | reserve <fraction>] (min 1024; fractions 0.05–0.9)",
2939
+ "repl.context_usage": "Usage: /context [<size, min 1024> | system <share> | reserve <share>] (share 0.05–0.9 or e.g. 10%; system+reserve ≤ 0.95)",
2932
2940
  "repl.msgs": "msgs",
2933
2941
  "repl.max_iters": "Max iters:",
2934
2942
  "repl.stuck_thresh": "Stuck thresh:",
@@ -2972,13 +2980,105 @@ Excluded blocks: {count}`,
2972
2980
  "repl.provider_list": "List configured providers or hot-swap the active one",
2973
2981
  "repl.provider_current": "Current provider",
2974
2982
  "repl.provider_set": "Provider set to: {name}",
2975
- "repl.provider_usage": "/provider list - show entries (* = active, prio = fallback order) | /provider use <name> - switch without restart | /provider add <name> [--url <url>] [--key <key>] [--priority <n>] [--context-window <n>] [--rpm <n>] [--parallel <n>] - add to config",
2983
+ "repl.provider_usage": `Usage: /provider list | /provider use <name> | /provider add <name> [flags]
2984
+ --url <url> base API URL (fallback: known presets)
2985
+ --key <key> API key
2986
+ --priority <n> fallback order; lower = tried first
2987
+ --context-window <n> per-provider context window (tokens)
2988
+ --rpm <n> max requests/minute (default 60)
2989
+ --parallel <n> max parallel tasks (default 1)`,
2976
2990
  "repl.provider_add_missing_value": "Missing value for {flag}",
2991
+ "repl.provider_add_invalid_number": "Invalid number for {flag}: '{value}'",
2977
2992
  "repl.provider_switch_hint": "Switch to it with: /provider use {name}",
2993
+ "repl.provider_flag_url": "Base API URL (fallback: known presets)",
2994
+ "repl.provider_flag_key": "API key",
2995
+ "repl.provider_flag_priority": "Fallback order; lower = tried first",
2996
+ "repl.provider_flag_context_window": "Per-provider context window (tokens)",
2997
+ "repl.provider_flag_rpm": "Max requests per minute (default 60)",
2998
+ "repl.provider_flag_parallel": "Max parallel tasks (default 1)",
2978
2999
  "repl.model_list": "List models of the active provider or switch the model",
2979
3000
  "repl.model_current": "Current model",
2980
3001
  "repl.model_set": "Model set to: {name}",
2981
- "repl.model_usage": "/model list - fetch models from the active provider | /model use <name> - switch model",
3002
+ "repl.model_usage": "Usage: /model list | /model use <name>",
3003
+ "repl.unknown_sub": "Unknown subcommand '{sub}' for /{cmd}. See usage below.",
3004
+ "repl.help_unknown": "Unknown command '/{name}'",
3005
+ "repl.help_aliases": "Aliases: {names}",
3006
+ "repl.help_examples": "Examples:",
3007
+ "repl.export": "Export a session to a file",
3008
+ "repl.export_usage": "Usage: /export [<id|name>] [--format md|json|jsonl] [--out <path>] [--no-redact]",
3009
+ "config.header": "Managed settings",
3010
+ "config.col_setting": "Setting",
3011
+ "config.col_value": "Value",
3012
+ "config.col_domain": "Domain",
3013
+ "config.col_change": "Change with",
3014
+ "config.session_line": "Session: {name} ({id}) — {count} messages",
3015
+ "config.usage_hint": "Get: /config <key> · Set: /config <key> <value> · Reset: /config reset <key>",
3016
+ "config.saved": "saved · reset: /config reset {key}",
3017
+ "config.read_only_hint": "read-only",
3018
+ "config.read_only": "'{key}' is read-only",
3019
+ "config.unknown_key": "Unknown key '{key}'. {suggestion}",
3020
+ "config.suggest": "Did you mean '{name}'?",
3021
+ "config.valid_keys": "Valid keys: {keys}",
3022
+ "config.invalid_value": "Invalid value for {key}. {hint}",
3023
+ "config.range": "Value out of range for {key}",
3024
+ "config.combined": "system + reserve must stay below 0.95",
3025
+ "config.hint_string": "Expected a non-empty string",
3026
+ "config.hint_enum": "Allowed: {values}",
3027
+ "config.hint_bool": "Expected on/off",
3028
+ "config.hint_share": "Expected 0.05–0.9 or a percentage (e.g. 10%)",
3029
+ "config.hint_int": "Expected an integer ≥ {min}",
3030
+ "config.reset_ok": "{key} restored to session-start value: {value} · saved",
3031
+ "config.reset_unchanged": "{key} already at session value",
3032
+ "config.reset_none": "No settings changed this session",
3033
+ "config.reset_all_confirm": "Reset {count} changed setting(s) to session-start values? [y/N] ",
3034
+ "config.reset_all_done": "Restored {count} setting(s) · saved",
3035
+ "config.reset_all_needs_yes": "Refusing to reset without confirmation in non-interactive mode; pass --yes",
3036
+ "config.reset_cancelled": "Reset cancelled",
3037
+ "config.reset_usage": "Usage: /config reset <key> | /config reset --all [--yes]",
3038
+ "config.alias_label": "alias of",
3039
+ "config.group_label": "group",
3040
+ "reasoning.status": "Reasoning: display {display} · effort {level} {auto}",
3041
+ "reasoning.on": "on",
3042
+ "reasoning.off": "off",
3043
+ "reasoning.auto_on": "(auto policy)",
3044
+ "reasoning.auto_off": "(auto policy off)",
3045
+ "reasoning.auto_off_hint": "Auto policy off. Restore with: /config reset reasoning.mode",
3046
+ "reasoning.display_set": "Reasoning display: {state}",
3047
+ "reasoning.unknown_sub": "Unknown argument '{sub}'.",
3048
+ "reasoning.sub_show": "Show reasoning content",
3049
+ "reasoning.sub_hide": "Hide reasoning content",
3050
+ "reasoning.sub_level": "Set reasoning effort level",
3051
+ "verbose.status": "Verbosity: {level}",
3052
+ "verbose.sub_quiet": "One line per action",
3053
+ "verbose.sub_normal": "Add short result summaries",
3054
+ "verbose.sub_verbose": "Full per-action detail",
3055
+ "context.unknown_arg": "Unknown argument '{arg}'.",
3056
+ "context.sub_system": "System-prompt share",
3057
+ "context.sub_reserve": "Response-reserve share",
3058
+ "model.not_in_list": "Note: '{name}' is not in the known model list",
3059
+ "model.sub_list": "List models of the active provider",
3060
+ "model.sub_use": "Switch model",
3061
+ "provider.sub_list": "List configured providers",
3062
+ "provider.sub_use": "Switch active provider",
3063
+ "provider.sub_add": "Add a provider",
3064
+ "map.sub_summary": "Show the project map summary",
3065
+ "map.sub_refresh": "Rebuild the project index",
3066
+ "map.sub_find": "Search the project map",
3067
+ "lsp.sub_status": "Show LSP server status",
3068
+ "lsp.sub_restart": "Reset disabled servers",
3069
+ "lsp.sub_check": "Run an LSP check on a path",
3070
+ "skill.sub_list": "List available skills",
3071
+ "skill.sub_loaded": "List loaded skills",
3072
+ "skill.sub_load": "Load a skill",
3073
+ "skill.sub_unload": "Unload a skill",
3074
+ "skill.sub_search": "Search skills",
3075
+ "export.written": "Exported to {path}",
3076
+ "export.redacted": "secrets redacted (--no-redact to disable)",
3077
+ "export.raw": "secrets NOT redacted",
3078
+ "export.empty": "session has no messages — nothing exported",
3079
+ "export.invalid_format": "Unknown format '{format}'. Use md|json|jsonl",
3080
+ "export.missing_value": "Missing value for {flag}",
3081
+ "export.write_failed": "Failed to write export: {error}",
2982
3082
  "repl.group.general": "General",
2983
3083
  "repl.group.agent": "Agent",
2984
3084
  "repl.group.session": "Sessions",
@@ -3676,6 +3776,10 @@ var init_ru = __esm(() => {
3676
3776
  "cli.list_sessions": "Список всех сессий",
3677
3777
  "cli.show_details": "Показать детали сессии",
3678
3778
  "cli.delete_session": "Удалить сессию",
3779
+ "cli.export_session": "Экспортировать сессию в файл (md, json или jsonl)",
3780
+ "cli.export_format": "Формат вывода: md|json|jsonl (по умолчанию md)",
3781
+ "cli.export_out": "Путь вывода ('-' для stdout)",
3782
+ "cli.export_no_redact": "Не скрывать секреты",
3679
3783
  "cli.first_run": "Первый запуск. Запускаем мастер настройки...",
3680
3784
  "cli.first_run_oneshot": "Конфигурация не найдена — запуск с настройками по умолчанию. Запустите `mma` без аргументов один раз для настройки.",
3681
3785
  "cli.setup_saved_restart": "Настройки сохранены. Перезапуск с новой конфигурацией...",
@@ -3744,20 +3848,21 @@ var init_ru = __esm(() => {
3744
3848
  "repl.exit_usage": "Использование: /exit",
3745
3849
  "repl.clear": "Очистить экран",
3746
3850
  "repl.clear_usage": "Использование: /clear",
3851
+ "repl.clear_hint": "Экран очищен. Новая сессия и контекст: /new",
3747
3852
  "repl.run": "Выполнить запрос через агента",
3748
3853
  "repl.run_usage": "Использование: /run <запрос>",
3749
- "repl.config": "Показать текущую конфигурацию",
3750
- "repl.config_usage": "Использование: /config",
3751
- "repl.reasoning": "Переключить показ рассуждений модели",
3752
- "repl.reasoning_usage": "Использование: /reasoning [auto|none|low|medium|high|max]",
3854
+ "repl.config": "Показать или изменить настройки (get / set / reset)",
3855
+ "repl.config_usage": "Использование: /config [<ключ> [значение]] | /config reset <ключ> | /config reset --all [--yes] | /config migrate",
3856
+ "repl.reasoning": "Показать статус рассуждений, переключить показ или уровень",
3857
+ "repl.reasoning_usage": "Использование: /reasoning [show|hide|level <auto|default|none|low|medium|high|max>]",
3753
3858
  "repl.reasoning_level_set": "Уровень рассуждений установлен: {level}",
3754
3859
  "repl.reasoning_invalid_level": "Неверный уровень '{level}'. Допустимые: {valid}",
3755
- "repl.verbose": "Подробность вывода: quiet, normal или verbose",
3860
+ "repl.verbose": "Подробность вывода (quiet, normal, verbose)",
3756
3861
  "repl.verbose_usage": "Использование: /verbose [quiet|normal|verbose]",
3757
3862
  "repl.verbose_set": "Подробность вывода: {level}",
3758
3863
  "repl.verbose_invalid_level": "Неверный уровень '{level}'. Допустимые: {valid}",
3759
3864
  "repl.last_action_none": "Нет недавнего действия для показа.",
3760
- "repl.status": "Показать статус агента",
3865
+ "repl.status": "Показать компактную строку статуса",
3761
3866
  "repl.status_usage": "Использование: /status",
3762
3867
  "repl.plugins": "Список загруженных плагинов",
3763
3868
  "repl.plugins_usage": "Использование: /plugins [--all]",
@@ -3797,11 +3902,13 @@ var init_ru = __esm(() => {
3797
3902
  "repl.context": "Контекст:",
3798
3903
  "repl.sysprompt_label": "Системный промпт:",
3799
3904
  "repl.sysprompt_size": "{used} / {budget} токенов",
3800
- "repl.sysprompt_desc": "Показать системный промпт",
3905
+ "repl.sysprompt_desc": "Размер системного промпта и блоки (текст — через --full)",
3906
+ "repl.sysprompt_usage": "Использование: /sysprompt [--full]",
3907
+ "repl.sysprompt_full_hint": "Полный текст промпта скрыт — выполните /sysprompt --full.",
3801
3908
  "repl.sysprompt_tokens": "Системный промпт ({count} токенов):",
3802
3909
  "repl.excluded_blocks": `
3803
3910
  Исключённые блоки: {count}`,
3804
- "repl.context_usage": "Использование: /context [<размер> | system <доля> | reserve <доля>] (мин 1024; доли 0.05–0.9)",
3911
+ "repl.context_usage": "Использование: /context [<размер, мин 1024> | system <доля> | reserve <доля>] (доля 0.05–0.9 или напр. 10%; system+reserve ≤ 0.95)",
3805
3912
  "repl.msgs": "сообщ.",
3806
3913
  "repl.max_iters": "Макс итераций:",
3807
3914
  "repl.stuck_thresh": "Порог зависания:",
@@ -3845,13 +3952,105 @@ var init_ru = __esm(() => {
3845
3952
  "repl.provider_list": "Список настроенных провайдеров или горячая смена активного",
3846
3953
  "repl.provider_current": "Текущий провайдер",
3847
3954
  "repl.provider_set": "Провайдер установлен: {name}",
3848
- "repl.provider_usage": "/provider list — записи (* = активный, prio = порядок fallback) | /provider use <имя> — смена без перезапуска | /provider add <имя> [--url <url>] [--key <ключ>] [--priority <n>] [--context-window <n>] [--rpm <n>] [--parallel <n>] — добавить в конфиг",
3955
+ "repl.provider_usage": `Использование: /provider list | /provider use <имя> | /provider add <имя> [флаги]
3956
+ --url <url> базовый URL API (иначе — из пресетов)
3957
+ --key <ключ> API-ключ
3958
+ --priority <n> порядок fallback; меньше = раньше
3959
+ --context-window <n> окно контекста провайдера (токены)
3960
+ --rpm <n> запросов/мин (по умолчанию 60)
3961
+ --parallel <n> параллельных задач (по умолчанию 1)`,
3849
3962
  "repl.provider_add_missing_value": "Нет значения для {flag}",
3963
+ "repl.provider_add_invalid_number": "Неверное число для {flag}: '{value}'",
3850
3964
  "repl.provider_switch_hint": "Переключитесь на него: /provider use {name}",
3965
+ "repl.provider_flag_url": "Базовый URL API (иначе — из пресетов)",
3966
+ "repl.provider_flag_key": "API-ключ",
3967
+ "repl.provider_flag_priority": "Порядок fallback; меньше = раньше",
3968
+ "repl.provider_flag_context_window": "Окно контекста провайдера (токены)",
3969
+ "repl.provider_flag_rpm": "Запросов в минуту (по умолчанию 60)",
3970
+ "repl.provider_flag_parallel": "Параллельных задач (по умолчанию 1)",
3851
3971
  "repl.model_list": "Список моделей активного провайдера или смена модели",
3852
3972
  "repl.model_current": "Текущая модель",
3853
3973
  "repl.model_set": "Модель установлена: {name}",
3854
- "repl.model_usage": "/model list — получить модели активного провайдера | /model use <имя> — сменить модель",
3974
+ "repl.model_usage": "Использование: /model list | /model use <имя>",
3975
+ "repl.unknown_sub": "Неизвестная подкоманда '{sub}' для /{cmd}. См. использование ниже.",
3976
+ "repl.help_unknown": "Неизвестная команда '/{name}'",
3977
+ "repl.help_aliases": "Алиасы: {names}",
3978
+ "repl.help_examples": "Примеры:",
3979
+ "repl.export": "Экспортировать сессию в файл",
3980
+ "repl.export_usage": "Использование: /export [<id|имя>] [--format md|json|jsonl] [--out <путь>] [--no-redact]",
3981
+ "config.header": "Управляемые настройки",
3982
+ "config.col_setting": "Настройка",
3983
+ "config.col_value": "Значение",
3984
+ "config.col_domain": "Домен",
3985
+ "config.col_change": "Как менять",
3986
+ "config.session_line": "Сессия: {name} ({id}) — {count} сообщений",
3987
+ "config.usage_hint": "Чтение: /config <ключ> · Запись: /config <ключ> <значение> · Сброс: /config reset <ключ>",
3988
+ "config.saved": "сохранено · сброс: /config reset {key}",
3989
+ "config.read_only_hint": "только чтение",
3990
+ "config.read_only": "'{key}' — только чтение",
3991
+ "config.unknown_key": "Неизвестный ключ '{key}'. {suggestion}",
3992
+ "config.suggest": "Возможно, вы имели в виду '{name}'?",
3993
+ "config.valid_keys": "Допустимые ключи: {keys}",
3994
+ "config.invalid_value": "Неверное значение для {key}. {hint}",
3995
+ "config.range": "Значение вне диапазона для {key}",
3996
+ "config.combined": "system + reserve должны оставаться ниже 0.95",
3997
+ "config.hint_string": "Ожидается непустая строка",
3998
+ "config.hint_enum": "Допустимые: {values}",
3999
+ "config.hint_bool": "Ожидается on/off",
4000
+ "config.hint_share": "Ожидается 0.05–0.9 или процент (напр. 10%)",
4001
+ "config.hint_int": "Ожидается целое ≥ {min}",
4002
+ "config.reset_ok": "{key} восстановлен к значению на старте сессии: {value} · сохранено",
4003
+ "config.reset_unchanged": "{key} уже соответствует значению сессии",
4004
+ "config.reset_none": "В этой сессии настройки не менялись",
4005
+ "config.reset_all_confirm": "Сбросить {count} изменённых настроек к значениям на старте сессии? [y/N] ",
4006
+ "config.reset_all_done": "Восстановлено настроек: {count} · сохранено",
4007
+ "config.reset_all_needs_yes": "Отказ от сброса без подтверждения в неинтерактивном режиме; укажите --yes",
4008
+ "config.reset_cancelled": "Сброс отменён",
4009
+ "config.reset_usage": "Использование: /config reset <ключ> | /config reset --all [--yes]",
4010
+ "config.alias_label": "алиас",
4011
+ "config.group_label": "группа",
4012
+ "reasoning.status": "Рассуждения: показ {display} · усилие {level} {auto}",
4013
+ "reasoning.on": "вкл",
4014
+ "reasoning.off": "выкл",
4015
+ "reasoning.auto_on": "(авто-политика)",
4016
+ "reasoning.auto_off": "(авто-политика выключена)",
4017
+ "reasoning.auto_off_hint": "Авто-политика выключена. Вернуть: /config reset reasoning.mode",
4018
+ "reasoning.display_set": "Показ рассуждений: {state}",
4019
+ "reasoning.unknown_sub": "Неизвестный аргумент '{sub}'.",
4020
+ "reasoning.sub_show": "Показывать рассуждения",
4021
+ "reasoning.sub_hide": "Скрыть рассуждения",
4022
+ "reasoning.sub_level": "Установить уровень рассуждений",
4023
+ "verbose.status": "Подробность: {level}",
4024
+ "verbose.sub_quiet": "Одна строка на действие",
4025
+ "verbose.sub_normal": "Плюс короткие сводки результатов",
4026
+ "verbose.sub_verbose": "Полная деталь по действиям",
4027
+ "context.unknown_arg": "Неизвестный аргумент '{arg}'.",
4028
+ "context.sub_system": "Доля системного промпта",
4029
+ "context.sub_reserve": "Доля резерва ответа",
4030
+ "model.not_in_list": "Примечание: '{name}' нет в списке известных моделей",
4031
+ "model.sub_list": "Список моделей активного провайдера",
4032
+ "model.sub_use": "Сменить модель",
4033
+ "provider.sub_list": "Список настроенных провайдеров",
4034
+ "provider.sub_use": "Сменить активного провайдера",
4035
+ "provider.sub_add": "Добавить провайдера",
4036
+ "map.sub_summary": "Показать сводку карты проекта",
4037
+ "map.sub_refresh": "Перестроить индекс проекта",
4038
+ "map.sub_find": "Поиск по карте проекта",
4039
+ "lsp.sub_status": "Статус LSP-серверов",
4040
+ "lsp.sub_restart": "Сбросить отключённые серверы",
4041
+ "lsp.sub_check": "Запустить LSP-проверку пути",
4042
+ "skill.sub_list": "Список доступных скиллов",
4043
+ "skill.sub_loaded": "Список загруженных скиллов",
4044
+ "skill.sub_load": "Загрузить скилл",
4045
+ "skill.sub_unload": "Выгрузить скилл",
4046
+ "skill.sub_search": "Поиск скиллов",
4047
+ "export.written": "Экспортировано в {path}",
4048
+ "export.redacted": "секреты скрыты (--no-redact чтобы отключить)",
4049
+ "export.raw": "секреты НЕ скрыты",
4050
+ "export.empty": "в сессии нет сообщений — экспорт не создан",
4051
+ "export.invalid_format": "Неизвестный формат '{format}'. Используйте md|json|jsonl",
4052
+ "export.missing_value": "Нет значения для {flag}",
4053
+ "export.write_failed": "Не удалось записать экспорт: {error}",
3855
4054
  "repl.reload": "Перезагрузить агента с текущим конфигом",
3856
4055
  "repl.reload_usage": "Использование: /reload",
3857
4056
  "repl.reloading": "Перезагрузка агента...",
@@ -5118,7 +5317,7 @@ var init_domains = __esm(() => {
5118
5317
  "reasoning",
5119
5318
  "hallucination"
5120
5319
  ],
5121
- provider: ["provider", "orchestrator", "retry", "pricing"],
5320
+ provider: ["provider", "orchestrator", "retry", "pricing", "modelCapabilities"],
5122
5321
  moe: ["moe", "experts"],
5123
5322
  security: ["security"],
5124
5323
  browser: ["browser"],
@@ -6605,7 +6804,7 @@ class OpenAICompatProvider {
6605
6804
  }
6606
6805
  }
6607
6806
  async* streamOnce(messages, tools, signal, options, onEmit, idleTimeoutMs) {
6608
- const maxTokens = options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096;
6807
+ const maxTokens = options?.maxTokens ?? this.config.maxCompletionTokens ?? DEFAULT_MAX_COMPLETION_TOKENS;
6609
6808
  const body = buildRequestBody({
6610
6809
  model: this.model,
6611
6810
  messages,
@@ -6871,7 +7070,7 @@ class OpenAICompatProvider {
6871
7070
  };
6872
7071
  }
6873
7072
  async doNonStreaming(messages, tools, signal, options) {
6874
- const maxTokens = options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096;
7073
+ const maxTokens = options?.maxTokens ?? this.config.maxCompletionTokens ?? DEFAULT_MAX_COMPLETION_TOKENS;
6875
7074
  const body = buildRequestBody({
6876
7075
  model: this.model,
6877
7076
  messages,
@@ -7047,7 +7246,7 @@ class OpenAICompatProvider {
7047
7246
  });
7048
7247
  }
7049
7248
  }
7050
- var REQUEST_TIMEOUT_MS = 120000, MAX_RATE_WAIT_MS = 30000, cachedUserAgent = null;
7249
+ var REQUEST_TIMEOUT_MS = 120000, MAX_RATE_WAIT_MS = 30000, DEFAULT_MAX_COMPLETION_TOKENS = 8192, cachedUserAgent = null;
7051
7250
  var init_openai_compat = __esm(() => {
7052
7251
  init_token_counter();
7053
7252
  init_i18n();
@@ -7547,7 +7746,7 @@ var init_factory = __esm(() => {
7547
7746
  });
7548
7747
 
7549
7748
  // src/llm/model-loader.ts
7550
- async function getLoadedContextLength(baseUrl, model, timeoutMs = 3000) {
7749
+ async function probeModel(baseUrl, model, timeoutMs = 3000) {
7551
7750
  const base = baseUrl.replace(/\/$/, "").replace(/\/v1\/?$/, "");
7552
7751
  try {
7553
7752
  const response = await fetch(`${base}/api/v1/models`, {
@@ -7561,10 +7760,18 @@ async function getLoadedContextLength(baseUrl, model, timeoutMs = 3000) {
7561
7760
  if (!found)
7562
7761
  return null;
7563
7762
  const loadedInstance = (found.loaded_instances ?? [])[0];
7564
- const actual = loadedInstance?.config?.context_length ?? found.max_context_length;
7565
- if (typeof actual !== "number" || actual <= 0)
7763
+ const contextLength = loadedInstance?.config?.context_length ?? found.max_context_length;
7764
+ if (typeof contextLength !== "number" || contextLength <= 0)
7566
7765
  return null;
7567
- return { model: found.key ?? found.name ?? model, actual };
7766
+ const raw = found.capabilities;
7767
+ let capabilities;
7768
+ if (raw && typeof raw.vision === "boolean") {
7769
+ capabilities = { vision: raw.vision };
7770
+ if (typeof raw.trained_for_tool_use === "boolean") {
7771
+ capabilities.tools = raw.trained_for_tool_use;
7772
+ }
7773
+ }
7774
+ return { model: found.key ?? found.name ?? model, contextLength, capabilities };
7568
7775
  } catch {
7569
7776
  return null;
7570
7777
  }
@@ -15436,6 +15643,15 @@ var init_tool_output = __esm(() => {
15436
15643
  init_i18n();
15437
15644
  });
15438
15645
 
15646
+ // src/core/agent/tool-images.ts
15647
+ function queueToolImages(sink, images) {
15648
+ if (!sink || !images || images.length === 0)
15649
+ return;
15650
+ for (const image of images) {
15651
+ sink.addPendingImage(image);
15652
+ }
15653
+ }
15654
+
15439
15655
  // src/core/agent/tool-batch.ts
15440
15656
  class ToolBatchExecutor {
15441
15657
  deps;
@@ -15565,6 +15781,7 @@ class ToolBatchExecutor {
15565
15781
  arguments: call.arguments
15566
15782
  });
15567
15783
  answeredToolCallIds.add(call.id);
15784
+ queueToolImages(contextManager, result.images);
15568
15785
  const tokensAfterTool = contextManager.getEstimatedTokens();
15569
15786
  const costAfterTool = this.deps.costTracker.total;
15570
15787
  onTool?.({
@@ -21290,6 +21507,119 @@ var init_session = __esm(() => {
21290
21507
  init_i18n();
21291
21508
  });
21292
21509
 
21510
+ // src/llm/image-utils.ts
21511
+ var exports_image_utils = {};
21512
+ __export(exports_image_utils, {
21513
+ readClipboardImage: () => readClipboardImage,
21514
+ loadUrlAsDataUrl: () => loadUrlAsDataUrl,
21515
+ loadFileAsDataUrl: () => loadFileAsDataUrl,
21516
+ detectMime: () => detectMime,
21517
+ bufferToDataUrl: () => bufferToDataUrl
21518
+ });
21519
+ import { readFileSync as readFileSync20 } from "fs";
21520
+ import { extname as extname4 } from "path";
21521
+ function detectMime(filePath) {
21522
+ const ext = extname4(filePath).toLowerCase();
21523
+ return MIME_MAP[ext] ?? "image/png";
21524
+ }
21525
+ async function readClipboardImage() {
21526
+ if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21527
+ try {
21528
+ const img = Bun.Image.fromClipboard();
21529
+ if (img) {
21530
+ const buf = await img.resize(800, 800, { fit: "inside" }).jpeg({ quality: 60 }).buffer();
21531
+ return Buffer.from(buf);
21532
+ }
21533
+ } catch {}
21534
+ }
21535
+ return readClipboardFallback();
21536
+ }
21537
+ async function readClipboardFallback() {
21538
+ const { platform: platform7 } = await import("os");
21539
+ const { execSync } = await import("child_process");
21540
+ const { readFileSync: readFileSync21, unlinkSync: unlinkSync5 } = await import("fs");
21541
+ const { join: join29 } = await import("path");
21542
+ if (platform7() !== "linux")
21543
+ return null;
21544
+ const tmpPath = join29(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
21545
+ const commands = [
21546
+ `wl-paste --type image/png > "${tmpPath}" 2>/dev/null`,
21547
+ `xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`
21548
+ ];
21549
+ for (const cmd of commands) {
21550
+ try {
21551
+ execSync(cmd, { timeout: 5000 });
21552
+ const buf = readFileSync21(tmpPath);
21553
+ unlinkSync5(tmpPath);
21554
+ if (buf.length > 0)
21555
+ return buf;
21556
+ } catch {}
21557
+ }
21558
+ try {
21559
+ unlinkSync5(tmpPath);
21560
+ } catch {}
21561
+ return null;
21562
+ }
21563
+ async function loadFileAsDataUrl(filePath) {
21564
+ const buf = readFileSync20(filePath);
21565
+ if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21566
+ try {
21567
+ const img = new Bun.Image(buf);
21568
+ const { width } = await img.metadata();
21569
+ const targetWidth = Math.min(800, width || 800);
21570
+ const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
21571
+ return { dataUrl };
21572
+ } catch {}
21573
+ }
21574
+ const b64 = buf.toString("base64");
21575
+ const ext = filePath.split(".").pop()?.toLowerCase();
21576
+ const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
21577
+ return { dataUrl: `data:${mime};base64,${b64}` };
21578
+ }
21579
+ async function loadUrlAsDataUrl(url) {
21580
+ const resp = await fetch(url);
21581
+ if (!resp.ok) {
21582
+ throw new Error(`Failed to fetch image: ${resp.status} ${resp.statusText}`);
21583
+ }
21584
+ const buf = Buffer.from(await resp.arrayBuffer());
21585
+ if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21586
+ try {
21587
+ const img = new Bun.Image(buf);
21588
+ const { width } = await img.metadata();
21589
+ const targetWidth = Math.min(800, width || 800);
21590
+ const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
21591
+ return { dataUrl };
21592
+ } catch {}
21593
+ }
21594
+ const b64 = buf.toString("base64");
21595
+ return { dataUrl: `data:image/png;base64,${b64}` };
21596
+ }
21597
+ async function bufferToDataUrl(buf) {
21598
+ if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21599
+ try {
21600
+ const img = new Bun.Image(buf);
21601
+ const { width } = await img.metadata();
21602
+ const targetWidth = Math.min(800, width || 800);
21603
+ const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
21604
+ return { dataUrl };
21605
+ } catch {}
21606
+ }
21607
+ const b64 = buf.toString("base64");
21608
+ return { dataUrl: `data:image/png;base64,${b64}` };
21609
+ }
21610
+ var MIME_MAP;
21611
+ var init_image_utils = __esm(() => {
21612
+ MIME_MAP = {
21613
+ ".png": "image/png",
21614
+ ".jpg": "image/jpeg",
21615
+ ".jpeg": "image/jpeg",
21616
+ ".gif": "image/gif",
21617
+ ".webp": "image/webp",
21618
+ ".bmp": "image/bmp",
21619
+ ".svg": "image/svg+xml"
21620
+ };
21621
+ });
21622
+
21293
21623
  // src/tools/browser.ts
21294
21624
  import { join as join29 } from "path";
21295
21625
  import { mkdirSync as mkdirSync16 } from "fs";
@@ -21326,18 +21656,9 @@ function formatScreenshotForTextModel(snapshot) {
21326
21656
 
21327
21657
  ${snapshot}`;
21328
21658
  }
21329
- function modelSupportsVision(model) {
21330
- const m = model.toLowerCase();
21331
- return /vision|\bvl\b|qwen.*vl|gpt-4|claude|gemini|llava|pixtral/.test(m);
21332
- }
21333
- function formatScreenshotResult(snapshot, screenshot, supportsVision) {
21334
- if (!supportsVision) {
21335
- return formatScreenshotForTextModel(snapshot);
21336
- }
21337
- const base642 = screenshot.toString("base64");
21338
- return `${snapshot}
21339
-
21340
- [Screenshot: data:image/png;base64,${base642}]`;
21659
+ async function screenshotToImagePart(screenshot) {
21660
+ const { dataUrl } = await bufferToDataUrl(screenshot);
21661
+ return { type: "image_url", image_url: { url: dataUrl } };
21341
21662
  }
21342
21663
  function createBrowserTool() {
21343
21664
  return {
@@ -21402,12 +21723,15 @@ function createBrowserTool() {
21402
21723
  }
21403
21724
  const s = getSession(ctx);
21404
21725
  const result = await serialize(() => s.execute(action, args));
21405
- const supportsVision = modelSupportsVision(ctx.config.model);
21406
21726
  if (result.screenshot) {
21407
- return {
21408
- success: result.success,
21409
- output: formatScreenshotResult(result.output, result.screenshot, supportsVision)
21410
- };
21727
+ if (ctx.capabilities?.vision) {
21728
+ return {
21729
+ success: result.success,
21730
+ output: result.output,
21731
+ images: [await screenshotToImagePart(result.screenshot)]
21732
+ };
21733
+ }
21734
+ return { success: result.success, output: formatScreenshotForTextModel(result.output) };
21411
21735
  }
21412
21736
  return { success: result.success, output: result.output };
21413
21737
  }
@@ -21426,125 +21750,13 @@ var session = null, sessionFactory, actionChain;
21426
21750
  var init_browser = __esm(() => {
21427
21751
  init_session();
21428
21752
  init_types();
21753
+ init_image_utils();
21429
21754
  init_i18n();
21430
21755
  init_output();
21431
21756
  init_utils();
21432
21757
  actionChain = Promise.resolve();
21433
21758
  });
21434
21759
 
21435
- // src/llm/image-utils.ts
21436
- var exports_image_utils = {};
21437
- __export(exports_image_utils, {
21438
- readClipboardImage: () => readClipboardImage,
21439
- loadUrlAsDataUrl: () => loadUrlAsDataUrl,
21440
- loadFileAsDataUrl: () => loadFileAsDataUrl,
21441
- detectMime: () => detectMime,
21442
- bufferToDataUrl: () => bufferToDataUrl
21443
- });
21444
- import { readFileSync as readFileSync20 } from "fs";
21445
- import { extname as extname4 } from "path";
21446
- function detectMime(filePath) {
21447
- const ext = extname4(filePath).toLowerCase();
21448
- return MIME_MAP[ext] ?? "image/png";
21449
- }
21450
- async function readClipboardImage() {
21451
- if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21452
- try {
21453
- const img = Bun.Image.fromClipboard();
21454
- if (img) {
21455
- const buf = await img.resize(800, 800, { fit: "inside" }).jpeg({ quality: 60 }).buffer();
21456
- return Buffer.from(buf);
21457
- }
21458
- } catch {}
21459
- }
21460
- return readClipboardFallback();
21461
- }
21462
- async function readClipboardFallback() {
21463
- const { platform: platform7 } = await import("os");
21464
- const { execSync } = await import("child_process");
21465
- const { readFileSync: readFileSync21, unlinkSync: unlinkSync5 } = await import("fs");
21466
- const { join: join30 } = await import("path");
21467
- if (platform7() !== "linux")
21468
- return null;
21469
- const tmpPath = join30(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
21470
- const commands = [
21471
- `wl-paste --type image/png > "${tmpPath}" 2>/dev/null`,
21472
- `xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`
21473
- ];
21474
- for (const cmd of commands) {
21475
- try {
21476
- execSync(cmd, { timeout: 5000 });
21477
- const buf = readFileSync21(tmpPath);
21478
- unlinkSync5(tmpPath);
21479
- if (buf.length > 0)
21480
- return buf;
21481
- } catch {}
21482
- }
21483
- try {
21484
- unlinkSync5(tmpPath);
21485
- } catch {}
21486
- return null;
21487
- }
21488
- async function loadFileAsDataUrl(filePath) {
21489
- const buf = readFileSync20(filePath);
21490
- if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21491
- try {
21492
- const img = new Bun.Image(buf);
21493
- const { width } = await img.metadata();
21494
- const targetWidth = Math.min(800, width || 800);
21495
- const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
21496
- return { dataUrl };
21497
- } catch {}
21498
- }
21499
- const b64 = buf.toString("base64");
21500
- const ext = filePath.split(".").pop()?.toLowerCase();
21501
- const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
21502
- return { dataUrl: `data:${mime};base64,${b64}` };
21503
- }
21504
- async function loadUrlAsDataUrl(url) {
21505
- const resp = await fetch(url);
21506
- if (!resp.ok) {
21507
- throw new Error(`Failed to fetch image: ${resp.status} ${resp.statusText}`);
21508
- }
21509
- const buf = Buffer.from(await resp.arrayBuffer());
21510
- if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21511
- try {
21512
- const img = new Bun.Image(buf);
21513
- const { width } = await img.metadata();
21514
- const targetWidth = Math.min(800, width || 800);
21515
- const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
21516
- return { dataUrl };
21517
- } catch {}
21518
- }
21519
- const b64 = buf.toString("base64");
21520
- return { dataUrl: `data:image/png;base64,${b64}` };
21521
- }
21522
- async function bufferToDataUrl(buf) {
21523
- if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21524
- try {
21525
- const img = new Bun.Image(buf);
21526
- const { width } = await img.metadata();
21527
- const targetWidth = Math.min(800, width || 800);
21528
- const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
21529
- return { dataUrl };
21530
- } catch {}
21531
- }
21532
- const b64 = buf.toString("base64");
21533
- return { dataUrl: `data:image/png;base64,${b64}` };
21534
- }
21535
- var MIME_MAP;
21536
- var init_image_utils = __esm(() => {
21537
- MIME_MAP = {
21538
- ".png": "image/png",
21539
- ".jpg": "image/jpeg",
21540
- ".jpeg": "image/jpeg",
21541
- ".gif": "image/gif",
21542
- ".webp": "image/webp",
21543
- ".bmp": "image/bmp",
21544
- ".svg": "image/svg+xml"
21545
- };
21546
- });
21547
-
21548
21760
  // src/tools/attach-image.ts
21549
21761
  import { existsSync as existsSync35 } from "fs";
21550
21762
  import { resolve as resolve17 } from "path";
@@ -24550,7 +24762,7 @@ import {
24550
24762
  appendFileSync as appendFileSync6
24551
24763
  } from "fs";
24552
24764
  import { join as join34 } from "path";
24553
- import { gzipSync } from "zlib";
24765
+ import { gzipSync, gunzipSync } from "zlib";
24554
24766
 
24555
24767
  class SessionStore {
24556
24768
  baseDir;
@@ -24686,6 +24898,24 @@ class SessionStore {
24686
24898
  return [];
24687
24899
  }
24688
24900
  }
24901
+ loadRotatedHistory(id) {
24902
+ const gzPath = join34(this.baseDir, `${id}.jsonl.gz`);
24903
+ if (!existsSync41(gzPath))
24904
+ return [];
24905
+ try {
24906
+ const content = gunzipSync(readFileSync25(gzPath)).toString("utf-8");
24907
+ return content.split(`
24908
+ `).filter(Boolean).map((line) => {
24909
+ try {
24910
+ return JSON.parse(line);
24911
+ } catch {
24912
+ return null;
24913
+ }
24914
+ }).filter((m) => m !== null);
24915
+ } catch {
24916
+ return [];
24917
+ }
24918
+ }
24689
24919
  appendSessionLog(id, entry) {
24690
24920
  const dir = this.sessionDir(id);
24691
24921
  mkdirSync18(dir, { recursive: true, mode: 448 });
@@ -24882,6 +25112,12 @@ class SessionManager {
24882
25112
  return [];
24883
25113
  return this.store.loadHistory(this.activeId);
24884
25114
  }
25115
+ getHistory(id) {
25116
+ const live = this.store.loadHistory(id);
25117
+ if (live.length > 0)
25118
+ return live;
25119
+ return this.store.loadRotatedHistory(id);
25120
+ }
24885
25121
  getSessionContext() {
24886
25122
  if (!this.activeId)
24887
25123
  return;
@@ -25291,7 +25527,7 @@ class BrowserModule {
25291
25527
  "Workflow: open(url) → read snapshot → click/type on element numbers → read updated snapshot.",
25292
25528
  'Each snapshot includes a "Content" section with the visible page text, a "Console" section with browser messages, and network errors. Use these to understand the page, detect JS errors, and verify the result of your actions.',
25293
25529
  'Always start with "open" action. Use element numbers from snapshot for click/type targets.',
25294
- 'Use "screenshot" action only if the model supports vision. Otherwise rely on the text snapshot.'
25530
+ 'The "screenshot" action is delivered automatically: a vision model receives the image, a text-only model receives the text snapshot instead. You do not need to decide — just call it and read what you get.'
25295
25531
  ].join(`
25296
25532
  `),
25297
25533
  priority: "normal",
@@ -27704,6 +27940,19 @@ var init_context_log = __esm(() => {
27704
27940
  SEP2 = "─".repeat(78);
27705
27941
  });
27706
27942
 
27943
+ // src/modules/providers/capabilities.ts
27944
+ function resolveModelCapabilities(preset, probe, override) {
27945
+ const base = preset ? { vision: preset.vision, tools: preset.tools } : { ...DEFAULT_MODEL_CAPABILITIES };
27946
+ return { ...base, ...probe ?? {}, ...override ?? {} };
27947
+ }
27948
+ var DEFAULT_MODEL_CAPABILITIES;
27949
+ var init_capabilities = __esm(() => {
27950
+ DEFAULT_MODEL_CAPABILITIES = {
27951
+ vision: false,
27952
+ tools: false
27953
+ };
27954
+ });
27955
+
27707
27956
  // src/core/environment.ts
27708
27957
  import { existsSync as existsSync50, readFileSync as readFileSync32, readdirSync as readdirSync16 } from "fs";
27709
27958
  import { spawnSync as spawnSync3 } from "child_process";
@@ -28335,7 +28584,9 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
28335
28584
  lines.push(`Runtime verification: when a bug is described in terms of runtime behavior (what happens when the app runs), reproduce it — run the app / build / browser tool — and confirm the fix against actual runtime output before calling "plan update step=N status=done". Check existing servers first with process_list/process_log: a stale dev server started BEFORE your change still runs the old pipeline and keeps failing even after the files on disk are fixed. Restart or kill it (process_kill) and verify the exact endpoint/port the user reported — a fresh server on a new port does NOT prove the user's error is gone.`);
28336
28585
  const sec = config.security;
28337
28586
  if (sec?.enabled) {
28338
- const secLines = [`SECURITY RESTRICTIONS (enforced — blocked calls return "[SECURITY BLOCKED]", do not retry them):`];
28587
+ const secLines = [
28588
+ `SECURITY RESTRICTIONS (enforced — blocked calls return "[SECURITY BLOCKED]", do not retry them):`
28589
+ ];
28339
28590
  if (sec.bash?.enabled && sec.bash.blacklist.length > 0) {
28340
28591
  const cmds = sec.bash.blacklist.slice(0, 15).join(", ");
28341
28592
  secLines.push(`Forbidden bash commands: ${cmds}${sec.bash.blacklist.length > 15 ? ", …" : ""}.`);
@@ -28417,19 +28668,23 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
28417
28668
  logger4.info(t("env.model_loaded", { model: config.model, seconds: String(loadResult.loadTime) }));
28418
28669
  }
28419
28670
  }
28671
+ const capabilitiesRef = {};
28420
28672
  const contextProbePromise = (async () => {
28421
28673
  try {
28422
- const probe = await getLoadedContextLength(config.provider.baseUrl, config.model);
28674
+ const probe = await probeModel(config.provider.baseUrl, config.model);
28423
28675
  if (!probe)
28424
28676
  return null;
28425
- if (probe.actual < config.contextWindow) {
28426
- logger4.logSilent("warn", `Context probe: model "${probe.model}" is loaded with ${probe.actual} tokens, but contextWindow is configured as ${config.contextWindow} — overflow/429 risk. Lower contextWindow or reload the model with a larger context.`);
28427
- } else if (probe.actual > config.contextWindow) {
28428
- logger4.logSilent("info", `Context probe: model "${probe.model}" supports ${probe.actual} tokens — consider "mma context ${probe.actual}" to use the full window.`);
28677
+ if (probe.capabilities)
28678
+ capabilitiesRef.probe = probe.capabilities;
28679
+ const contextProbe = { model: probe.model, actual: probe.contextLength };
28680
+ if (probe.contextLength < config.contextWindow) {
28681
+ logger4.logSilent("warn", `Context probe: model "${probe.model}" is loaded with ${probe.contextLength} tokens, but contextWindow is configured as ${config.contextWindow} — overflow/429 risk. Lower contextWindow or reload the model with a larger context.`);
28682
+ } else if (probe.contextLength > config.contextWindow) {
28683
+ logger4.logSilent("info", `Context probe: model "${probe.model}" supports ${probe.contextLength} tokens — consider "mma context ${probe.contextLength}" to use the full window.`);
28429
28684
  } else {
28430
- logger4.logSilent("debug", `Context probe: configured contextWindow matches loaded context (${probe.actual})`);
28685
+ logger4.logSilent("debug", `Context probe: configured contextWindow matches loaded context (${probe.contextLength})`);
28431
28686
  }
28432
- return probe;
28687
+ return contextProbe;
28433
28688
  } catch {
28434
28689
  return null;
28435
28690
  }
@@ -28608,6 +28863,14 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
28608
28863
  get: () => sessionManager,
28609
28864
  configurable: true
28610
28865
  });
28866
+ Object.defineProperty(toolCtx, "capabilities", {
28867
+ get: () => {
28868
+ const cfg = configRef.current;
28869
+ const spec = BUILTIN_PROVIDERS.find((p) => p.type === cfg.provider.type);
28870
+ return resolveModelCapabilities(spec?.capabilities, capabilitiesRef.probe, cfg.modelCapabilities?.[cfg.model]);
28871
+ },
28872
+ configurable: true
28873
+ });
28611
28874
  const toolExecutor = new ToolExecutor(toolRegistry, toolCtx, pluginManager);
28612
28875
  toolCtx.llmProvider = llmProvider;
28613
28876
  toolCtx.toolExecutor = toolExecutor;
@@ -28831,6 +29094,7 @@ var init_bootstrap = __esm(() => {
28831
29094
  init_i18n();
28832
29095
  init_agent();
28833
29096
  init_context_log();
29097
+ init_capabilities();
28834
29098
  init_version();
28835
29099
  init_environment();
28836
29100
  init_utils();
@@ -29719,6 +29983,87 @@ var init_setup_order = __esm(() => {
29719
29983
  ];
29720
29984
  });
29721
29985
 
29986
+ // src/modules/session/export.ts
29987
+ function roleHeading(role) {
29988
+ switch (role) {
29989
+ case "user":
29990
+ return "User";
29991
+ case "assistant":
29992
+ return "Assistant";
29993
+ case "tool":
29994
+ return "Tool";
29995
+ case "system":
29996
+ return "System";
29997
+ default:
29998
+ return role;
29999
+ }
30000
+ }
30001
+ function renderSessionMarkdown(meta, messages) {
30002
+ const lines = [];
30003
+ const name = meta?.name ?? "session";
30004
+ const id = meta?.id ?? "unknown";
30005
+ lines.push(`# ${name} (${id})`);
30006
+ lines.push("");
30007
+ if (meta) {
30008
+ lines.push(`- Model: ${meta.model}`);
30009
+ lines.push(`- Created: ${meta.createdAt}`);
30010
+ lines.push(`- Updated: ${meta.updatedAt}`);
30011
+ lines.push(`- Messages: ${meta.messageCount}`);
30012
+ if (meta.projectDir)
30013
+ lines.push(`- Project: ${meta.projectDir}`);
30014
+ }
30015
+ lines.push("");
30016
+ lines.push("---");
30017
+ lines.push("");
30018
+ for (const msg of messages) {
30019
+ const heading = roleHeading(msg.role);
30020
+ const nameSuffix = msg.name ? ` (${msg.name})` : "";
30021
+ lines.push(`## ${heading}${nameSuffix}`);
30022
+ lines.push("");
30023
+ const text = getMessageText(msg.content);
30024
+ lines.push(text.length > 0 ? text : "_(empty)_");
30025
+ lines.push("");
30026
+ if (Array.isArray(msg.tool_calls)) {
30027
+ for (const tc of msg.tool_calls) {
30028
+ lines.push(`- tool_call: \`${tc.function.name}\` ${tc.function.arguments}`);
30029
+ }
30030
+ lines.push("");
30031
+ }
30032
+ }
30033
+ return lines.join(`
30034
+ `);
30035
+ }
30036
+ function buildSessionJson(meta, messages) {
30037
+ return JSON.stringify({ meta, messages }, null, 2);
30038
+ }
30039
+ function buildSessionJsonl(log2) {
30040
+ return log2.map((e) => JSON.stringify(e)).join(`
30041
+ `);
30042
+ }
30043
+ function formatExport(bundle, format) {
30044
+ switch (format) {
30045
+ case "md":
30046
+ return renderSessionMarkdown(bundle.meta, bundle.messages);
30047
+ case "json":
30048
+ return buildSessionJson(bundle.meta, bundle.messages);
30049
+ case "jsonl":
30050
+ return buildSessionJsonl(bundle.log);
30051
+ }
30052
+ }
30053
+ function redactExport(text, redact) {
30054
+ return redact ? redactContextSecrets(text) : text;
30055
+ }
30056
+ function sanitizeFileName(name) {
30057
+ const cleaned = name.replace(/[<>:"/\\|?*\u0000-\u001f]/g, "-").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^[-.]+|[-.]+$/g, "");
30058
+ return cleaned.length > 0 ? cleaned.slice(0, 60) : "session";
30059
+ }
30060
+ function extensionFor(format) {
30061
+ return format === "jsonl" ? "jsonl" : format;
30062
+ }
30063
+ var init_export = __esm(() => {
30064
+ init_context_log();
30065
+ });
30066
+
29722
30067
  // src/modules/indexer/map-command.ts
29723
30068
  async function runMapAction(module, action, query) {
29724
30069
  const summary = async () => {
@@ -38008,6 +38353,404 @@ var init_cli = __esm(() => {
38008
38353
  MMA_ROOT = findMmaRoot(HERE);
38009
38354
  });
38010
38355
 
38356
+ // src/config/registry.ts
38357
+ function shareField(key, canonical) {
38358
+ const field = key === "system" ? "systemPrompt" : "responseReserve";
38359
+ const other = key === "system" ? "responseReserve" : "systemPrompt";
38360
+ return {
38361
+ key: canonical,
38362
+ aliases: [`context.${key}`],
38363
+ domain: "budget",
38364
+ valueType: "share",
38365
+ reconfigure: true,
38366
+ get: (c) => c.contextBudget[field],
38367
+ set: (c, v) => setBudgetShare(c, key, v),
38368
+ extraValidate: (c, v) => {
38369
+ const n = v;
38370
+ const total = n + c.contextBudget[other];
38371
+ return total > MAX_COMBINED2 ? "combined" : null;
38372
+ }
38373
+ };
38374
+ }
38375
+ function resolveConfigKey(name) {
38376
+ return BY_NAME.get(name) ?? null;
38377
+ }
38378
+ function configDefs() {
38379
+ return DEFINITIONS;
38380
+ }
38381
+ function canonicalKeys() {
38382
+ return DEFINITIONS.map((d) => d.key);
38383
+ }
38384
+ function keysForGroup(prefix) {
38385
+ return DEFINITIONS.filter((d) => d.key.startsWith(prefix + "."));
38386
+ }
38387
+ function allConfigNames() {
38388
+ const out = [];
38389
+ for (const def of DEFINITIONS) {
38390
+ out.push({ name: def.key, def, alias: false });
38391
+ for (const a of def.aliases)
38392
+ out.push({ name: a, def, alias: true });
38393
+ }
38394
+ return out;
38395
+ }
38396
+ function formatConfigValue(value) {
38397
+ if (typeof value === "boolean")
38398
+ return value ? "on" : "off";
38399
+ return String(value);
38400
+ }
38401
+ function parseBool(raw) {
38402
+ const v = raw.trim().toLowerCase();
38403
+ if (["on", "true", "1", "yes", "y", "enable", "enabled"].includes(v))
38404
+ return { ok: true, value: true };
38405
+ if (["off", "false", "0", "no", "n", "disable", "disabled"].includes(v))
38406
+ return { ok: true, value: false };
38407
+ return { ok: false, reason: "invalid" };
38408
+ }
38409
+ function parseConfigValue(def, raw) {
38410
+ const trimmed = raw.trim();
38411
+ switch (def.valueType) {
38412
+ case "string":
38413
+ return trimmed.length > 0 ? { ok: true, value: trimmed } : { ok: false, reason: "invalid" };
38414
+ case "bool":
38415
+ return parseBool(trimmed);
38416
+ case "enum": {
38417
+ const v = trimmed.toLowerCase();
38418
+ if (def.enumValues && !def.enumValues.includes(v))
38419
+ return { ok: false, reason: "invalid" };
38420
+ return { ok: true, value: v };
38421
+ }
38422
+ case "int": {
38423
+ if (!/^-?\d+$/.test(trimmed))
38424
+ return { ok: false, reason: "invalid" };
38425
+ const n = Number(trimmed);
38426
+ if (!Number.isFinite(n))
38427
+ return { ok: false, reason: "invalid" };
38428
+ if (def.min !== undefined && n < def.min)
38429
+ return { ok: false, reason: "invalid" };
38430
+ if (def.max !== undefined && n > def.max)
38431
+ return { ok: false, reason: "invalid" };
38432
+ return { ok: true, value: n };
38433
+ }
38434
+ case "share": {
38435
+ const isPct = trimmed.endsWith("%");
38436
+ const body = isPct ? trimmed.slice(0, -1).trim() : trimmed;
38437
+ if (!/^-?\d+(\.\d+)?$/.test(body))
38438
+ return { ok: false, reason: "invalid" };
38439
+ let n = Number(body);
38440
+ if (isPct)
38441
+ n = n / 100;
38442
+ if (!Number.isFinite(n) || n < MIN_SHARE2 || n > MAX_SHARE2)
38443
+ return { ok: false, reason: "invalid" };
38444
+ return { ok: true, value: n };
38445
+ }
38446
+ }
38447
+ }
38448
+ function getConfigValue(config, name) {
38449
+ const def = resolveConfigKey(name);
38450
+ if (!def)
38451
+ return { ok: false, reason: "unknown-key", input: name };
38452
+ const value = def.get(config);
38453
+ return { ok: true, key: def.key, def, value, display: formatConfigValue(value) };
38454
+ }
38455
+ function setConfigValue(config, name, raw) {
38456
+ const def = resolveConfigKey(name);
38457
+ if (!def)
38458
+ return { ok: false, reason: "unknown-key", input: name };
38459
+ if (def.readOnly)
38460
+ return { ok: false, reason: "read-only", input: name, key: def.key, def };
38461
+ const parsed = parseConfigValue(def, raw);
38462
+ if (!parsed.ok)
38463
+ return { ok: false, reason: "invalid-value", input: name, key: def.key, def };
38464
+ const extra = def.extraValidate?.(config, parsed.value);
38465
+ if (extra)
38466
+ return { ok: false, reason: extra, input: name, key: def.key, def };
38467
+ def.set(config, parsed.value);
38468
+ return {
38469
+ ok: true,
38470
+ key: def.key,
38471
+ def,
38472
+ value: parsed.value,
38473
+ display: formatConfigValue(parsed.value),
38474
+ reconfigure: def.reconfigure,
38475
+ reasoningChanged: def.key === "reasoning.mode"
38476
+ };
38477
+ }
38478
+ function captureConfigSnapshot(config) {
38479
+ const snapshot = new Map;
38480
+ for (const def of DEFINITIONS) {
38481
+ try {
38482
+ snapshot.set(def.key, structuredClone(def.get(config)));
38483
+ } catch {}
38484
+ }
38485
+ return snapshot;
38486
+ }
38487
+ var REASONING_LEVELS, LOG_LEVELS, VERBOSITY_LEVELS, LOCALES, MIN_SHARE2 = 0.05, MAX_SHARE2 = 0.9, MAX_COMBINED2 = 0.95, DEFINITIONS, BY_NAME;
38488
+ var init_registry2 = __esm(() => {
38489
+ init_defaults();
38490
+ init_budget();
38491
+ REASONING_LEVELS = [
38492
+ "auto",
38493
+ "default",
38494
+ "none",
38495
+ "low",
38496
+ "medium",
38497
+ "high",
38498
+ "max"
38499
+ ];
38500
+ LOG_LEVELS = ["silent", "error", "warn", "info", "debug"];
38501
+ VERBOSITY_LEVELS = ["quiet", "normal", "verbose"];
38502
+ LOCALES = ["en", "ru"];
38503
+ DEFINITIONS = [
38504
+ {
38505
+ key: "model",
38506
+ aliases: [],
38507
+ domain: "agent",
38508
+ valueType: "string",
38509
+ reconfigure: true,
38510
+ get: (c) => c.model,
38511
+ set: (c, v) => {
38512
+ c.model = String(v);
38513
+ }
38514
+ },
38515
+ {
38516
+ key: "provider.active",
38517
+ aliases: [],
38518
+ domain: "provider",
38519
+ valueType: "string",
38520
+ reconfigure: true,
38521
+ get: (c) => c.provider.active ?? c.provider.type,
38522
+ set: (c, v) => {
38523
+ c.provider.active = String(v);
38524
+ },
38525
+ extraValidate: (c, v) => {
38526
+ const entries = c.provider.entries ?? [];
38527
+ if (entries.length === 0)
38528
+ return null;
38529
+ const name = String(v);
38530
+ const found = entries.some((e) => e.label === name || e.type === name);
38531
+ return found ? null : "invalid";
38532
+ }
38533
+ },
38534
+ {
38535
+ key: "contextWindow",
38536
+ aliases: ["context.window"],
38537
+ domain: "budget",
38538
+ valueType: "int",
38539
+ min: 1024,
38540
+ reconfigure: true,
38541
+ get: (c) => c.contextWindow,
38542
+ set: (c, v) => {
38543
+ c.contextWindow = v;
38544
+ }
38545
+ },
38546
+ shareField("system", "contextBudget.systemPrompt"),
38547
+ shareField("reserve", "contextBudget.responseReserve"),
38548
+ {
38549
+ key: "showReasoning",
38550
+ aliases: ["reasoning.show"],
38551
+ domain: "reasoning",
38552
+ valueType: "bool",
38553
+ reconfigure: false,
38554
+ get: (c) => c.showReasoning,
38555
+ set: (c, v) => {
38556
+ c.showReasoning = v;
38557
+ }
38558
+ },
38559
+ {
38560
+ key: "reasoning.mode",
38561
+ aliases: ["reasoning.level"],
38562
+ domain: "reasoning",
38563
+ valueType: "enum",
38564
+ enumValues: REASONING_LEVELS,
38565
+ reconfigure: true,
38566
+ get: (c) => c.reasoning?.mode ?? "auto",
38567
+ set: (c, v) => {
38568
+ const base = c.reasoning ?? structuredClone(DEFAULTS.reasoning);
38569
+ c.reasoning = { ...base, mode: v };
38570
+ }
38571
+ },
38572
+ {
38573
+ key: "ui.verbosity",
38574
+ aliases: ["verbosity"],
38575
+ domain: "ui",
38576
+ valueType: "enum",
38577
+ enumValues: VERBOSITY_LEVELS,
38578
+ reconfigure: false,
38579
+ get: (c) => c.ui.verbosity ?? (c.ui.verbose ? "verbose" : "quiet"),
38580
+ set: (c, v) => {
38581
+ c.ui.verbosity = v;
38582
+ }
38583
+ },
38584
+ {
38585
+ key: "maxToolIterations",
38586
+ aliases: [],
38587
+ domain: "agent",
38588
+ valueType: "int",
38589
+ min: 1,
38590
+ reconfigure: false,
38591
+ get: (c) => c.maxToolIterations,
38592
+ set: (c, v) => {
38593
+ c.maxToolIterations = v;
38594
+ }
38595
+ },
38596
+ {
38597
+ key: "stuckThreshold",
38598
+ aliases: [],
38599
+ domain: "agent",
38600
+ valueType: "int",
38601
+ min: 1,
38602
+ reconfigure: false,
38603
+ get: (c) => c.stuckThreshold,
38604
+ set: (c, v) => {
38605
+ c.stuckThreshold = v;
38606
+ }
38607
+ },
38608
+ {
38609
+ key: "logLevel",
38610
+ aliases: [],
38611
+ domain: "agent",
38612
+ valueType: "enum",
38613
+ enumValues: LOG_LEVELS,
38614
+ reconfigure: false,
38615
+ get: (c) => c.logLevel,
38616
+ set: (c, v) => {
38617
+ c.logLevel = String(v);
38618
+ }
38619
+ },
38620
+ {
38621
+ key: "locale",
38622
+ aliases: [],
38623
+ domain: "i18n",
38624
+ valueType: "enum",
38625
+ enumValues: LOCALES,
38626
+ reconfigure: false,
38627
+ get: (c) => c.locale,
38628
+ set: (c, v) => {
38629
+ c.locale = String(v);
38630
+ }
38631
+ },
38632
+ {
38633
+ key: "version",
38634
+ aliases: [],
38635
+ domain: "agent",
38636
+ valueType: "string",
38637
+ reconfigure: false,
38638
+ readOnly: true,
38639
+ get: (c) => c.version
38640
+ }
38641
+ ];
38642
+ BY_NAME = new Map;
38643
+ for (const def of DEFINITIONS) {
38644
+ BY_NAME.set(def.key, def);
38645
+ for (const alias of def.aliases)
38646
+ BY_NAME.set(alias, def);
38647
+ }
38648
+ });
38649
+
38650
+ // src/ui/theme.ts
38651
+ function resolveVerbosity(ui) {
38652
+ if (ui?.verbosity)
38653
+ return ui.verbosity;
38654
+ return ui?.verbose ? "verbose" : "quiet";
38655
+ }
38656
+ function toolCategory(tool) {
38657
+ for (const [category, tools] of Object.entries(CATEGORY_TOOLS)) {
38658
+ if (tools.has(tool))
38659
+ return category;
38660
+ }
38661
+ return "meta";
38662
+ }
38663
+ function toolAccent(tool) {
38664
+ switch (toolCategory(tool)) {
38665
+ case "recon":
38666
+ return pc.cyan;
38667
+ case "mutate":
38668
+ return pc.magenta;
38669
+ case "execute":
38670
+ return pc.blue;
38671
+ case "meta":
38672
+ default:
38673
+ return pc.dim;
38674
+ }
38675
+ }
38676
+ function actionMarkFor(tool) {
38677
+ return toolAccent(tool)(glyph.action);
38678
+ }
38679
+ function successMark() {
38680
+ return pc.green(glyph.success);
38681
+ }
38682
+ function failureMark() {
38683
+ return pc.red(glyph.failure);
38684
+ }
38685
+ function formatDuration(ms) {
38686
+ if (ms < 1000)
38687
+ return `${ms}ms`;
38688
+ return `${(ms / 1000).toFixed(1)}s`;
38689
+ }
38690
+ var glyph, CATEGORY_TOOLS;
38691
+ var init_theme = __esm(() => {
38692
+ init_colors();
38693
+ glyph = {
38694
+ action: "◇",
38695
+ success: "✓",
38696
+ failure: "✗",
38697
+ warning: "⚠",
38698
+ thinking: "✦",
38699
+ question: "?",
38700
+ user: "↵",
38701
+ detail: "↳",
38702
+ group: "│",
38703
+ metric: "⟡",
38704
+ compact: "⟳"
38705
+ };
38706
+ CATEGORY_TOOLS = {
38707
+ recon: new Set([
38708
+ "read_file",
38709
+ "list_dir",
38710
+ "glob",
38711
+ "grep",
38712
+ "file_info",
38713
+ "project_map",
38714
+ "chunk_query",
38715
+ "web_search",
38716
+ "web_fetch",
38717
+ "web_browse",
38718
+ "search_history",
38719
+ "recall",
38720
+ "session_info",
38721
+ "process_list",
38722
+ "process_log"
38723
+ ]),
38724
+ mutate: new Set([
38725
+ "write_file",
38726
+ "edit_file",
38727
+ "create_dir",
38728
+ "delete_file",
38729
+ "move_file",
38730
+ "attach_image"
38731
+ ]),
38732
+ execute: new Set([
38733
+ "bash",
38734
+ "process_kill",
38735
+ "pipeline_run",
38736
+ "browser",
38737
+ "download_file",
38738
+ "subagent",
38739
+ "mcp_call"
38740
+ ]),
38741
+ meta: new Set([
38742
+ "plan",
38743
+ "todo",
38744
+ "verify",
38745
+ "lsp_check",
38746
+ "enable_tools",
38747
+ "set_thinking",
38748
+ "load_skill",
38749
+ "remember"
38750
+ ])
38751
+ };
38752
+ });
38753
+
38011
38754
  // src/cli/repl-commands.ts
38012
38755
  var exports_repl_commands = {};
38013
38756
  __export(exports_repl_commands, {
@@ -38015,7 +38758,8 @@ __export(exports_repl_commands, {
38015
38758
  COMMAND_GROUPS: () => COMMAND_GROUPS
38016
38759
  });
38017
38760
  import { join as join55, dirname as dirname25 } from "path";
38018
- import { existsSync as existsSync60 } from "fs";
38761
+ import { existsSync as existsSync60, writeFileSync as writeFileSync25, mkdirSync as mkdirSync26 } from "fs";
38762
+ import { homedir as homedir18 } from "os";
38019
38763
  function registerAllCommands(ctx) {
38020
38764
  registerBuiltinCommands(ctx);
38021
38765
  registerMmaCommands(ctx);
@@ -38027,7 +38771,8 @@ function registerBuiltinCommands(ctx) {
38027
38771
  name: "help",
38028
38772
  description: t("repl.help"),
38029
38773
  usage: t("repl.help_usage"),
38030
- action: () => ctx.showHelp()
38774
+ examples: ["/help", "/help config"],
38775
+ action: (args) => ctx.showHelp(args[0])
38031
38776
  });
38032
38777
  ctx.registerCommand({
38033
38778
  name: "exit",
@@ -38042,6 +38787,7 @@ function registerBuiltinCommands(ctx) {
38042
38787
  usage: t("repl.clear_usage"),
38043
38788
  action: () => {
38044
38789
  ctx.output.clearScreen();
38790
+ ctx.output.writeLine(pc.dim(t("repl.clear_hint")));
38045
38791
  }
38046
38792
  });
38047
38793
  }
@@ -38117,36 +38863,267 @@ function registerRunCommands(ctx) {
38117
38863
  }
38118
38864
  });
38119
38865
  }
38866
+ function changeHint(def) {
38867
+ if (def.readOnly)
38868
+ return t("config.read_only_hint");
38869
+ return SUGAR_HINTS[def.key] ?? `/config ${def.key} <value>`;
38870
+ }
38871
+ function configValueHint(def) {
38872
+ if (!def)
38873
+ return "";
38874
+ switch (def.valueType) {
38875
+ case "enum":
38876
+ return t("config.hint_enum", { values: (def.enumValues ?? []).join(", ") });
38877
+ case "bool":
38878
+ return t("config.hint_bool");
38879
+ case "share":
38880
+ return t("config.hint_share");
38881
+ case "int":
38882
+ return t("config.hint_int", { min: String(def.min ?? 1) });
38883
+ default:
38884
+ return t("config.hint_string");
38885
+ }
38886
+ }
38887
+ function editDistance(a, b) {
38888
+ const dp = Array.from({ length: b.length + 1 }, (_, i) => i);
38889
+ for (let i = 1;i <= a.length; i++) {
38890
+ let prev = dp[0];
38891
+ dp[0] = i;
38892
+ for (let j = 1;j <= b.length; j++) {
38893
+ const tmp = dp[j];
38894
+ dp[j] = Math.min(dp[j] + 1, dp[j - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1));
38895
+ prev = tmp;
38896
+ }
38897
+ }
38898
+ return dp[b.length];
38899
+ }
38900
+ function nearestConfigKey(input) {
38901
+ const names = canonicalKeys();
38902
+ let best = null;
38903
+ for (const name of names) {
38904
+ const d = editDistance(input.toLowerCase(), name.toLowerCase());
38905
+ if (best === null || d < best.dist)
38906
+ best = { name, dist: d };
38907
+ }
38908
+ if (!best)
38909
+ return null;
38910
+ const threshold = Math.max(2, Math.floor(best.name.length / 3));
38911
+ return best.dist <= threshold ? best.name : null;
38912
+ }
38913
+ function renderConfigError(ctx, result) {
38914
+ const key = result.key ?? result.input;
38915
+ switch (result.reason) {
38916
+ case "unknown-key": {
38917
+ const suggestion = nearestConfigKey(result.input);
38918
+ const suffix = suggestion ? t("config.suggest", { name: suggestion }) : t("config.valid_keys", { keys: canonicalKeys().join(", ") });
38919
+ ctx.output.writeLine(pc.yellow(t("config.unknown_key", { key: result.input, suggestion: suffix })), "stderr");
38920
+ return;
38921
+ }
38922
+ case "read-only":
38923
+ ctx.output.writeLine(pc.yellow(t("config.read_only", { key })), "stderr");
38924
+ return;
38925
+ case "range":
38926
+ ctx.output.writeLine(pc.yellow(t("config.range", { key })), "stderr");
38927
+ return;
38928
+ case "combined":
38929
+ ctx.output.writeLine(pc.yellow(t("config.combined")), "stderr");
38930
+ return;
38931
+ default:
38932
+ ctx.output.writeLine(pc.yellow(t("config.invalid_value", { key, hint: configValueHint(result.def) })), "stderr");
38933
+ }
38934
+ }
38935
+ function applyConfigSideEffects(ctx, result) {
38936
+ if (result.key === "ui.verbosity") {
38937
+ ctx.output.setSuppressInfoLogs(result.value === "quiet");
38938
+ } else if (result.key === "reasoning.mode") {
38939
+ ctx.agent.setReasoningLevel(result.value);
38940
+ } else if (result.key === "locale") {
38941
+ setLocale(result.value);
38942
+ }
38943
+ }
38944
+ function persistConfig(ctx) {
38945
+ const configPath = join55(ctx.configDir, "config.json");
38946
+ saveConfig(ctx.config, configPath, dirname25(configPath));
38947
+ }
38948
+ async function mutateConfig(ctx, key, raw) {
38949
+ const result = setConfigValue(ctx.config, key, raw);
38950
+ if (!result.ok)
38951
+ return result;
38952
+ applyConfigSideEffects(ctx, result);
38953
+ persistConfig(ctx);
38954
+ if (result.reconfigure)
38955
+ await ctx.agent.reconfigure(ctx.config);
38956
+ if (result.key === "locale")
38957
+ ctx.reregisterCommands();
38958
+ return result;
38959
+ }
38960
+ function reportSaved(ctx, result) {
38961
+ ctx.output.writeLine(pc.green(t("config.saved", { key: result.key })));
38962
+ }
38963
+ function rawFromValue(value) {
38964
+ if (typeof value === "boolean")
38965
+ return value ? "on" : "off";
38966
+ return String(value);
38967
+ }
38968
+ function printConfigTable(ctx, defs) {
38969
+ const list = defs ?? configDefs();
38970
+ const rows = list.map((def) => [
38971
+ def.key,
38972
+ formatConfigValue(def.get(ctx.config)),
38973
+ def.domain,
38974
+ changeHint(def)
38975
+ ]);
38976
+ ctx.output.writeLine(pc.bold(t("config.header")));
38977
+ for (const line of renderTable([
38978
+ t("config.col_setting"),
38979
+ t("config.col_value"),
38980
+ t("config.col_domain"),
38981
+ t("config.col_change")
38982
+ ], rows)) {
38983
+ ctx.output.writeLine(line);
38984
+ }
38985
+ const meta = ctx.sessionManager?.getActiveMeta();
38986
+ if (meta) {
38987
+ ctx.output.writeLine(pc.dim(t("config.session_line", {
38988
+ name: meta.name,
38989
+ id: meta.id,
38990
+ count: String(meta.messageCount)
38991
+ })));
38992
+ }
38993
+ ctx.output.writeLine(pc.dim(t("config.usage_hint")));
38994
+ }
38995
+ function confirm(ctx, prompt) {
38996
+ return new Promise((resolve25) => {
38997
+ ctx.withExclusiveInput(async () => {
38998
+ await new Promise((done) => {
38999
+ ctx.rl.question(prompt, (answer) => {
39000
+ resolve25(/^y(es)?$/i.test(answer.trim()));
39001
+ done();
39002
+ });
39003
+ });
39004
+ });
39005
+ });
39006
+ }
39007
+ async function runConfigReset(ctx, rest) {
39008
+ const snapshot = ctx.getConfigSnapshot();
39009
+ const yes = rest.includes("--yes");
39010
+ const all = rest.includes("--all");
39011
+ const target = rest.find((a) => !a.startsWith("--"));
39012
+ if (all) {
39013
+ const changed = canonicalKeys().filter((k) => {
39014
+ const def2 = resolveConfigKey(k);
39015
+ if (!def2 || !snapshot.has(k))
39016
+ return false;
39017
+ return rawFromValue(def2.get(ctx.config)) !== rawFromValue(snapshot.get(k));
39018
+ });
39019
+ if (changed.length === 0) {
39020
+ ctx.output.writeLine(t("config.reset_none"));
39021
+ return;
39022
+ }
39023
+ if (!yes) {
39024
+ if (!process.stdin.isTTY) {
39025
+ ctx.output.writeLine(pc.yellow(t("config.reset_all_needs_yes")), "stderr");
39026
+ return;
39027
+ }
39028
+ const ok = await confirm(ctx, t("config.reset_all_confirm", { count: String(changed.length) }));
39029
+ if (!ok) {
39030
+ ctx.output.writeLine(t("config.reset_cancelled"));
39031
+ return;
39032
+ }
39033
+ }
39034
+ let anyReconfig = false;
39035
+ let localeChanged = false;
39036
+ for (const k of changed) {
39037
+ const result2 = setConfigValue(ctx.config, k, rawFromValue(snapshot.get(k)));
39038
+ if (result2.ok) {
39039
+ applyConfigSideEffects(ctx, result2);
39040
+ anyReconfig = anyReconfig || result2.reconfigure;
39041
+ localeChanged = localeChanged || result2.key === "locale";
39042
+ }
39043
+ }
39044
+ persistConfig(ctx);
39045
+ if (anyReconfig)
39046
+ await ctx.agent.reconfigure(ctx.config);
39047
+ if (localeChanged)
39048
+ ctx.reregisterCommands();
39049
+ ctx.output.writeLine(pc.green(t("config.reset_all_done", { count: String(changed.length) })));
39050
+ return;
39051
+ }
39052
+ if (!target) {
39053
+ ctx.output.writeLine(t("config.reset_usage"));
39054
+ return;
39055
+ }
39056
+ const def = resolveConfigKey(target);
39057
+ if (!def) {
39058
+ renderConfigError(ctx, { reason: "unknown-key", input: target });
39059
+ return;
39060
+ }
39061
+ if (def.readOnly || !snapshot.has(def.key)) {
39062
+ ctx.output.writeLine(t("config.reset_unchanged", { key: def.key }));
39063
+ return;
39064
+ }
39065
+ if (rawFromValue(def.get(ctx.config)) === rawFromValue(snapshot.get(def.key))) {
39066
+ ctx.output.writeLine(t("config.reset_unchanged", { key: def.key }));
39067
+ return;
39068
+ }
39069
+ const result = await mutateConfig(ctx, def.key, rawFromValue(snapshot.get(def.key)));
39070
+ if (!result.ok) {
39071
+ renderConfigError(ctx, result);
39072
+ return;
39073
+ }
39074
+ ctx.output.writeLine(pc.green(t("config.reset_ok", { key: result.key, value: result.display })));
39075
+ }
39076
+ function printReasoningStatus(ctx) {
39077
+ const display = ctx.config.showReasoning ? t("reasoning.on") : t("reasoning.off");
39078
+ const mode = ctx.config.reasoning?.mode ?? "auto";
39079
+ const auto = mode === "auto" ? t("reasoning.auto_on") : t("reasoning.auto_off");
39080
+ ctx.output.writeLine(t("reasoning.status", { display, level: mode, auto }));
39081
+ }
38120
39082
  function registerConfigCommands(ctx) {
38121
39083
  ctx.registerCommand({
38122
39084
  name: "config",
38123
39085
  description: t("repl.config"),
38124
39086
  usage: t("repl.config_usage"),
38125
- action: (args) => {
39087
+ examples: ["/config", "/config model", "/config reasoning.mode high", "/config reset ui.verbosity"],
39088
+ action: async (args) => {
38126
39089
  if ((args[0] ?? "").trim().toLowerCase() === "migrate") {
38127
39090
  return runConfigMigrate(ctx);
38128
39091
  }
38129
- const cfg = ctx.config;
38130
- const ctxW = cfg.contextWindow;
38131
- const sys = Math.floor(ctxW * cfg.contextBudget.systemPrompt);
38132
- const res = Math.floor(ctxW * cfg.contextBudget.responseReserve);
38133
- ctx.output.writeLine(`${t("repl.model")} ${cfg.model}`);
38134
- const providers = ctx.agent.listProviders();
38135
- const active = providers.find((p) => p.active);
38136
- if (active) {
38137
- ctx.output.writeLine(`${t("repl.provider")} ${active.label} (${pc.dim(active.type)}) → ${pc.dim(active.baseUrl)}`);
38138
- } else {
38139
- ctx.output.writeLine(`${t("repl.provider")} ${cfg.provider.type} → ${cfg.provider.baseUrl}`);
38140
- }
38141
- ctx.output.writeLine(`${t("repl.context")} ${ctxW} (sys:${sys} res:${res} hist:${ctxW - sys - res})`);
38142
- ctx.output.writeLine(`${t("repl.max_iters")} ${cfg.maxToolIterations}`);
38143
- ctx.output.writeLine(`${t("repl.stuck_thresh")} ${cfg.stuckThreshold}`);
38144
- ctx.output.writeLine(`${t("repl.reasoning_label")} ${cfg.showReasoning ? pc.green(t("repl.show")) : pc.dim(t("repl.hide"))}`);
38145
- ctx.output.writeLine(`${t("repl.log_level")} ${cfg.logLevel}`);
38146
- ctx.output.writeLine(`${t("repl.locale")} ${cfg.locale}`);
38147
- const meta = ctx.sessionManager?.getActiveMeta();
38148
- if (meta) {
38149
- ctx.output.writeLine(`${t("repl.session_label")} ${meta.name} (${meta.id}) — ${meta.messageCount} msgs`);
39092
+ if (args.length === 0) {
39093
+ printConfigTable(ctx);
39094
+ return;
39095
+ }
39096
+ if (args[0] === "reset") {
39097
+ await runConfigReset(ctx, args.slice(1));
39098
+ return;
39099
+ }
39100
+ const name = args[0];
39101
+ if (args.length === 1) {
39102
+ const group = keysForGroup(name);
39103
+ if (group.length > 0) {
39104
+ printConfigTable(ctx, group);
39105
+ return;
39106
+ }
39107
+ const got = getConfigValue(ctx.config, name);
39108
+ if (!got.ok) {
39109
+ renderConfigError(ctx, got);
39110
+ return;
39111
+ }
39112
+ ctx.output.writeLine(`${got.key} = ${got.display}`);
39113
+ return;
39114
+ }
39115
+ const raw = args.slice(1).join(" ");
39116
+ const result = await mutateConfig(ctx, name, raw);
39117
+ if (!result.ok) {
39118
+ renderConfigError(ctx, result);
39119
+ return;
39120
+ }
39121
+ reportSaved(ctx, result);
39122
+ if (result.key === "model") {
39123
+ const known = ctx.knownModels();
39124
+ if (known.length > 0 && !known.includes(String(result.value))) {
39125
+ ctx.output.writeLine(pc.yellow(t("model.not_in_list", { name: String(result.value) })));
39126
+ }
38150
39127
  }
38151
39128
  }
38152
39129
  });
@@ -38154,37 +39131,60 @@ function registerConfigCommands(ctx) {
38154
39131
  name: "reasoning",
38155
39132
  description: t("repl.reasoning"),
38156
39133
  usage: t("repl.reasoning_usage"),
38157
- action: (args) => {
38158
- const level = args[0]?.trim().toLowerCase();
38159
- const VALID_LEVELS2 = ["auto", "none", "low", "medium", "high", "max"];
38160
- if (!level) {
38161
- ctx.config.showReasoning = !ctx.config.showReasoning;
38162
- const status = ctx.config.showReasoning ? pc.green(t("repl.show")) : pc.dim(t("repl.hide"));
38163
- ctx.output.writeLine(t("repl.reasoning_status", { status }));
38164
- } else if (VALID_LEVELS2.includes(level)) {
38165
- ctx.agent.setReasoningLevel(level);
38166
- ctx.output.writeLine(pc.green(t("repl.reasoning_level_set", { level })));
38167
- } else {
38168
- ctx.output.writeLine(pc.yellow(t("repl.reasoning_invalid_level", { level, valid: VALID_LEVELS2.join(", ") })));
39134
+ examples: ["/reasoning", "/reasoning show", "/reasoning level high"],
39135
+ action: async (args) => {
39136
+ const sub = args[0]?.trim().toLowerCase();
39137
+ if (!sub) {
39138
+ printReasoningStatus(ctx);
39139
+ return;
39140
+ }
39141
+ if (sub === "show" || sub === "on" || sub === "hide" || sub === "off") {
39142
+ const on = sub === "show" || sub === "on";
39143
+ const result = await mutateConfig(ctx, "showReasoning", on ? "on" : "off");
39144
+ if (!result.ok) {
39145
+ renderConfigError(ctx, result);
39146
+ return;
39147
+ }
39148
+ ctx.output.writeLine(pc.green(t("reasoning.display_set", { state: on ? t("reasoning.on") : t("reasoning.off") })));
39149
+ return;
39150
+ }
39151
+ if (sub === "level") {
39152
+ const lvl = args[1]?.trim().toLowerCase();
39153
+ if (!lvl) {
39154
+ ctx.output.writeLine(t("repl.reasoning_usage"));
39155
+ return;
39156
+ }
39157
+ const result = await mutateConfig(ctx, "reasoning.mode", lvl);
39158
+ if (!result.ok) {
39159
+ renderConfigError(ctx, result);
39160
+ return;
39161
+ }
39162
+ ctx.output.writeLine(pc.green(t("repl.reasoning_level_set", { level: lvl })));
39163
+ if (lvl !== "auto")
39164
+ ctx.output.writeLine(pc.dim(t("reasoning.auto_off_hint")));
39165
+ return;
38169
39166
  }
39167
+ ctx.output.writeLine(pc.yellow(t("reasoning.unknown_sub", { sub })));
39168
+ ctx.output.writeLine(t("repl.reasoning_usage"));
38170
39169
  }
38171
39170
  });
38172
39171
  ctx.registerCommand({
38173
39172
  name: "verbose",
38174
39173
  description: t("repl.verbose"),
38175
39174
  usage: t("repl.verbose_usage"),
38176
- action: (args) => {
38177
- const VALID = ["quiet", "normal", "verbose"];
39175
+ examples: ["/verbose", "/verbose normal"],
39176
+ action: async (args) => {
38178
39177
  const level = args[0]?.trim().toLowerCase();
38179
- if (level && !VALID.includes(level)) {
38180
- ctx.output.writeLine(pc.yellow(t("repl.verbose_invalid_level", { level, valid: VALID.join(", ") })));
39178
+ if (!level) {
39179
+ ctx.output.writeLine(t("verbose.status", { level: resolveVerbosity(ctx.config.ui) }));
38181
39180
  return;
38182
39181
  }
38183
- const current = ctx.config.ui.verbosity ?? (ctx.config.ui.verbose ? "verbose" : "quiet");
38184
- const next = level ?? (current === "verbose" ? "quiet" : "verbose");
38185
- ctx.config.ui.verbosity = next;
38186
- ctx.output.setSuppressInfoLogs(next === "quiet");
38187
- ctx.output.writeLine(pc.green(t("repl.verbose_set", { level: next })));
39182
+ const result = await mutateConfig(ctx, "ui.verbosity", level);
39183
+ if (!result.ok) {
39184
+ renderConfigError(ctx, result);
39185
+ return;
39186
+ }
39187
+ ctx.output.writeLine(pc.green(t("repl.verbose_set", { level: result.display })));
38188
39188
  }
38189
39189
  });
38190
39190
  ctx.registerCommand({
@@ -38194,16 +39194,13 @@ function registerConfigCommands(ctx) {
38194
39194
  action: () => {
38195
39195
  const providers = ctx.agent.listProviders();
38196
39196
  const active = providers.find((p) => p.active);
38197
- ctx.output.writeLine(`${t("repl.model")} ${ctx.config.model}`);
38198
- if (active) {
38199
- ctx.output.writeLine(`${t("repl.provider")} ${active.label} (${pc.dim(active.type)}) @ ${pc.dim(active.baseUrl)}`);
38200
- } else {
38201
- ctx.output.writeLine(`${t("repl.provider")} ${ctx.config.provider.type} @ ${pc.dim(ctx.config.provider.baseUrl)}`);
38202
- }
39197
+ const providerLabel = active ? active.label : ctx.config.provider.type;
39198
+ const parts = [ctx.config.model, providerLabel];
38203
39199
  const meta = ctx.sessionManager?.getActiveMeta();
38204
- if (meta) {
38205
- ctx.output.writeLine(`${t("repl.session_label")} ${meta.name} (${meta.id}) — ${meta.messageCount} messages`);
38206
- }
39200
+ if (meta)
39201
+ parts.push(`${meta.name} (${meta.messageCount} ${t("repl.msgs")})`);
39202
+ parts.push(`${ctx.config.contextWindow} tok`);
39203
+ ctx.output.writeLine(parts.join(pc.dim(" · ")));
38207
39204
  }
38208
39205
  });
38209
39206
  ctx.registerCommand({
@@ -38226,22 +39223,28 @@ function registerConfigCommands(ctx) {
38226
39223
  ctx.registerCommand({
38227
39224
  name: "sysprompt",
38228
39225
  description: t("repl.sysprompt_desc"),
38229
- usage: t("repl.sysprompt_desc"),
38230
- action: () => {
39226
+ usage: t("repl.sysprompt_usage"),
39227
+ examples: ["/sysprompt", "/sysprompt --full"],
39228
+ action: (args) => {
38231
39229
  const info = ctx.agent.getSystemPromptInfo();
39230
+ const full = args.includes("--full");
38232
39231
  ctx.output.writeLine(pc.bold(t("repl.sysprompt_tokens", { count: String(info.tokenCount) })));
38233
- ctx.output.writeLine(pc.dim("─".repeat(60)));
38234
- for (const line of info.text.split(`
38235
- `)) {
38236
- ctx.output.writeLine(line);
38237
- }
38238
- ctx.output.writeLine(pc.dim("─".repeat(60)));
38239
39232
  if (info.excluded.length > 0) {
38240
39233
  ctx.output.writeLine(pc.yellow(t("repl.excluded_blocks", { count: String(info.excluded.length) })));
38241
39234
  for (const e of info.excluded) {
38242
39235
  ctx.output.writeLine(pc.dim(` - ${e.slice(0, 80)}${e.length > 80 ? "..." : ""}`));
38243
39236
  }
38244
39237
  }
39238
+ if (full) {
39239
+ ctx.output.writeLine(pc.dim("─".repeat(60)));
39240
+ for (const line of info.text.split(`
39241
+ `)) {
39242
+ ctx.output.writeLine(line);
39243
+ }
39244
+ ctx.output.writeLine(pc.dim("─".repeat(60)));
39245
+ } else {
39246
+ ctx.output.writeLine(pc.dim(t("repl.sysprompt_full_hint")));
39247
+ }
38245
39248
  }
38246
39249
  });
38247
39250
  ctx.registerCommand({
@@ -38281,13 +39284,14 @@ function registerProviderCommands(ctx) {
38281
39284
  ctx.output.writeLine(t("repl.provider_usage"));
38282
39285
  return;
38283
39286
  }
38284
- try {
38285
- await ctx.agent.setProvider(name);
38286
- ctx.refreshModelCache();
38287
- ctx.output.writeLine(pc.green(t("repl.provider_set", { name })));
38288
- } catch (e) {
38289
- ctx.output.writeLine(pc.red(e.message), "stderr");
39287
+ const result = await mutateConfig(ctx, "provider.active", name);
39288
+ if (!result.ok) {
39289
+ renderConfigError(ctx, result);
39290
+ return;
38290
39291
  }
39292
+ ctx.refreshModelCache();
39293
+ ctx.output.writeLine(pc.green(t("repl.provider_set", { name })));
39294
+ reportSaved(ctx, result);
38291
39295
  };
38292
39296
  const add = async (args) => {
38293
39297
  const flags = {};
@@ -38310,6 +39314,13 @@ function registerProviderCommands(ctx) {
38310
39314
  ctx.output.writeLine(t("repl.provider_usage"));
38311
39315
  return;
38312
39316
  }
39317
+ for (const flag of ["priority", "context-window", "rpm", "parallel"]) {
39318
+ const v = flags[flag];
39319
+ if (v !== undefined && !Number.isFinite(Number(v))) {
39320
+ ctx.output.writeLine(pc.red(t("repl.provider_add_invalid_number", { flag: `--${flag}`, value: v })), "stderr");
39321
+ return;
39322
+ }
39323
+ }
38313
39324
  const entries = Array.isArray(ctx.config.provider.entries) ? ctx.config.provider.entries : [];
38314
39325
  if (entries.length === 0) {
38315
39326
  entries.push({
@@ -38340,8 +39351,7 @@ function registerProviderCommands(ctx) {
38340
39351
  });
38341
39352
  ctx.config.provider.entries = entries;
38342
39353
  ctx.config.provider.active = ctx.config.provider.active || ctx.config.provider.type;
38343
- const configPath = join55(ctx.configDir, "config.json");
38344
- saveConfig(ctx.config, configPath, dirname25(configPath));
39354
+ persistConfig(ctx);
38345
39355
  await ctx.agent.reconfigure(ctx.config);
38346
39356
  ctx.output.writeLine(pc.green(t("cli.provider_added", { name })));
38347
39357
  ctx.output.writeLine(t("repl.provider_switch_hint", { name }));
@@ -38355,10 +39365,12 @@ function registerProviderCommands(ctx) {
38355
39365
  name: "provider",
38356
39366
  description: t("repl.provider_list"),
38357
39367
  usage: t("repl.provider_usage"),
39368
+ examples: ["/provider list", "/provider use openrouter", "/provider add openai --key sk-..."],
38358
39369
  action: async (args) => {
38359
39370
  const subcmd = args[0];
38360
39371
  const handler = subcmd ? handlers[subcmd] : handlers.list;
38361
39372
  if (!handler) {
39373
+ ctx.output.writeLine(pc.yellow(t("repl.unknown_sub", { cmd: "provider", sub: subcmd })));
38362
39374
  ctx.output.writeLine(t("repl.provider_usage"));
38363
39375
  return;
38364
39376
  }
@@ -38406,11 +39418,17 @@ function registerProviderCommands(ctx) {
38406
39418
  ctx.output.writeLine(t("repl.model_usage"));
38407
39419
  return;
38408
39420
  }
38409
- ctx.config.model = name;
38410
- const configPath = join55(ctx.configDir, "config.json");
38411
- saveConfig(ctx.config, configPath, dirname25(configPath));
38412
- await ctx.agent.reconfigure(ctx.config);
39421
+ const result = await mutateConfig(ctx, "model", name);
39422
+ if (!result.ok) {
39423
+ renderConfigError(ctx, result);
39424
+ return;
39425
+ }
38413
39426
  ctx.output.writeLine(pc.green(t("repl.model_set", { name })));
39427
+ const known = ctx.knownModels();
39428
+ if (known.length > 0 && !known.includes(name)) {
39429
+ ctx.output.writeLine(pc.yellow(t("model.not_in_list", { name })));
39430
+ }
39431
+ reportSaved(ctx, result);
38414
39432
  };
38415
39433
  const modelHandlers = {
38416
39434
  list: listModels,
@@ -38420,10 +39438,12 @@ function registerProviderCommands(ctx) {
38420
39438
  name: "model",
38421
39439
  description: t("repl.model_list"),
38422
39440
  usage: t("repl.model_usage"),
39441
+ examples: ["/model list", "/model use qwen/qwen3.5-9b"],
38423
39442
  action: async (args) => {
38424
39443
  const subcmd = args[0];
38425
39444
  const handler = subcmd ? modelHandlers[subcmd] : modelHandlers.list;
38426
39445
  if (!handler) {
39446
+ ctx.output.writeLine(pc.yellow(t("repl.unknown_sub", { cmd: "model", sub: subcmd })));
38427
39447
  ctx.output.writeLine(t("repl.model_usage"));
38428
39448
  return;
38429
39449
  }
@@ -38434,12 +39454,8 @@ function registerProviderCommands(ctx) {
38434
39454
  name: "context",
38435
39455
  description: t("cli.manage_context"),
38436
39456
  usage: t("repl.context_usage"),
39457
+ examples: ["/context", "/context 64000", "/context system 10%", "/context reserve 0.2"],
38437
39458
  action: async (args) => {
38438
- const save = async () => {
38439
- const configPath = join55(ctx.configDir, "config.json");
38440
- saveConfig(ctx.config, configPath, dirname25(configPath));
38441
- await ctx.agent.reconfigure(ctx.config);
38442
- };
38443
39459
  if (args.length === 0) {
38444
39460
  for (const line of budgetBreakdownLines(ctx.config))
38445
39461
  ctx.output.writeLine(line);
@@ -38447,24 +39463,32 @@ function registerProviderCommands(ctx) {
38447
39463
  return;
38448
39464
  }
38449
39465
  if (args[0] === "system" || args[0] === "reserve") {
38450
- const key = args[0];
38451
- const value = Number(args[1]);
38452
- if (setBudgetShare(ctx.config, key, value) !== null) {
38453
- ctx.output.writeLine(pc.yellow(t("cli.context_invalid_fraction")));
39466
+ const raw = args[1];
39467
+ if (!raw) {
39468
+ ctx.output.writeLine(t("repl.context_usage"));
38454
39469
  return;
38455
39470
  }
38456
- await save();
38457
- ctx.output.writeLine(pc.green(t("cli.context_fraction_set", { key, value })));
39471
+ const key = args[0] === "system" ? "contextBudget.systemPrompt" : "contextBudget.responseReserve";
39472
+ const result2 = await mutateConfig(ctx, key, raw);
39473
+ if (!result2.ok) {
39474
+ renderConfigError(ctx, result2);
39475
+ return;
39476
+ }
39477
+ ctx.output.writeLine(pc.green(t("cli.context_fraction_set", { key: args[0], value: result2.display })));
39478
+ return;
39479
+ }
39480
+ if (!/^\d+$/.test(args[0])) {
39481
+ ctx.output.writeLine(pc.yellow(t("context.unknown_arg", { arg: args[0] })));
39482
+ ctx.output.writeLine(t("repl.context_usage"));
38458
39483
  return;
38459
39484
  }
38460
- const size = parseInt(args[0], 10);
38461
- if (isNaN(size) || size < 1024) {
38462
- ctx.output.writeLine(t("cli.invalid_context_size"));
39485
+ const result = await mutateConfig(ctx, "contextWindow", args[0]);
39486
+ if (!result.ok) {
39487
+ renderConfigError(ctx, result);
38463
39488
  return;
38464
39489
  }
38465
- ctx.config.contextWindow = size;
38466
- await save();
38467
- ctx.output.writeLine(pc.green(t("cli.context_set", { size })));
39490
+ ctx.output.writeLine(pc.green(t("cli.context_set", { size: String(result.value) })));
39491
+ reportSaved(ctx, result);
38468
39492
  }
38469
39493
  });
38470
39494
  }
@@ -38523,15 +39547,15 @@ function registerLspCommands(ctx) {
38523
39547
  name: "lsp",
38524
39548
  description: t("repl.lsp"),
38525
39549
  usage: t("repl.lsp_usage"),
39550
+ examples: ["/lsp status", "/lsp restart", "/lsp check src/index.ts"],
38526
39551
  action: async (args) => {
38527
- const subcommand = args[0] || "status";
38528
39552
  const lspModule = ctx.agent.getModule("lsp");
38529
39553
  if (!lspModule) {
38530
39554
  ctx.output.writeLine(pc.yellow(t("repl.lsp_not_available")));
38531
39555
  return;
38532
39556
  }
38533
- switch (subcommand) {
38534
- case "status": {
39557
+ const handlers = {
39558
+ status: () => {
38535
39559
  const disabled = lspModule.getDisabledServers();
38536
39560
  const failures = lspModule.getFailureCounts();
38537
39561
  const config = ctx.config.lsp;
@@ -38549,16 +39573,14 @@ function registerLspCommands(ctx) {
38549
39573
  if (disabled.length === 0 && failures.size === 0) {
38550
39574
  ctx.output.writeLine(pc.green(t("repl.lsp_all_ok")));
38551
39575
  }
38552
- break;
38553
- }
38554
- case "restart": {
39576
+ },
39577
+ restart: () => {
38555
39578
  ctx.output.writeLine(pc.yellow(t("repl.lsp_restarting")));
38556
39579
  lspModule.resetDisabledServers();
38557
39580
  ctx.output.writeLine(pc.green(t("repl.lsp_restarted")));
38558
- break;
38559
- }
38560
- case "check": {
38561
- const path = args[1];
39581
+ },
39582
+ check: async (rest) => {
39583
+ const path = rest[0];
38562
39584
  if (!path) {
38563
39585
  ctx.output.writeLine(pc.yellow(t("repl.lsp_check_usage")));
38564
39586
  return;
@@ -38572,13 +39594,16 @@ function registerLspCommands(ctx) {
38572
39594
  } catch (e) {
38573
39595
  ctx.output.writeLine(pc.red(t("repl.lsp_check_error", { error: errMsg(e) })), "stderr");
38574
39596
  }
38575
- break;
38576
- }
38577
- default: {
38578
- ctx.output.writeLine(pc.dim(t("repl.lsp_usage")));
38579
- break;
38580
39597
  }
39598
+ };
39599
+ const subcommand = args[0] || "status";
39600
+ const handler = handlers[subcommand];
39601
+ if (!handler) {
39602
+ ctx.output.writeLine(pc.yellow(t("repl.unknown_sub", { cmd: "lsp", sub: subcommand })));
39603
+ ctx.output.writeLine(pc.dim(t("repl.lsp_usage")));
39604
+ return;
38581
39605
  }
39606
+ await handler(args.slice(1));
38582
39607
  }
38583
39608
  });
38584
39609
  }
@@ -38642,8 +39667,9 @@ function registerSessionCommands(ctx) {
38642
39667
  ctx.registerCommand({
38643
39668
  name: "resume",
38644
39669
  description: t("repl.resume"),
38645
- aliases: ["switch", "use"],
39670
+ aliases: ["switch"],
38646
39671
  usage: t("repl.resume_usage"),
39672
+ examples: ["/resume", "/resume my-session"],
38647
39673
  action: (args) => {
38648
39674
  const query = args.join(" ");
38649
39675
  const sessions = ctx.sessionManager.list();
@@ -38730,6 +39756,77 @@ function registerSessionCommands(ctx) {
38730
39756
  ctx.output.writeLine(`${t("session.deleted", { id: match.id })}: ${match.name}`);
38731
39757
  }
38732
39758
  });
39759
+ ctx.registerCommand({
39760
+ name: "export",
39761
+ description: t("repl.export"),
39762
+ usage: t("repl.export_usage"),
39763
+ examples: [
39764
+ "/export",
39765
+ "/export my-session --format md",
39766
+ "/export --format json --out -"
39767
+ ],
39768
+ action: async (args) => {
39769
+ let format = "md";
39770
+ let out;
39771
+ let redact = true;
39772
+ const positional = [];
39773
+ for (let i = 0;i < args.length; i++) {
39774
+ const a = args[i];
39775
+ if (a === "--format") {
39776
+ const v = args[++i];
39777
+ if (!v) {
39778
+ ctx.output.writeLine(t("export.missing_value", { flag: a }));
39779
+ return;
39780
+ }
39781
+ format = v;
39782
+ } else if (a === "--out") {
39783
+ const v = args[++i];
39784
+ if (v === undefined) {
39785
+ ctx.output.writeLine(t("export.missing_value", { flag: a }));
39786
+ return;
39787
+ }
39788
+ out = v;
39789
+ } else if (a === "--no-redact") {
39790
+ redact = false;
39791
+ } else {
39792
+ positional.push(a);
39793
+ }
39794
+ }
39795
+ if (format !== "md" && format !== "json" && format !== "jsonl") {
39796
+ ctx.output.writeLine(pc.yellow(t("export.invalid_format", { format })), "stderr");
39797
+ return;
39798
+ }
39799
+ const query = positional.join(" ");
39800
+ const sessions = ctx.sessionManager.list();
39801
+ const match = query ? sessions.find((s) => s.id === query || s.id.startsWith(query) || s.name.toLowerCase().includes(query.toLowerCase())) : sessions.find((s) => s.id === ctx.sessionManager.getActive());
39802
+ if (!match) {
39803
+ ctx.output.writeLine(query ? t("session.no_match", { query }) : t("session.no_active"));
39804
+ return;
39805
+ }
39806
+ const messages = ctx.sessionManager.getHistory(match.id);
39807
+ const log2 = ctx.sessionManager.loadSessionLog(match.id);
39808
+ if (format === "jsonl" ? log2.length === 0 : messages.length === 0) {
39809
+ ctx.output.writeLine(pc.yellow(t("export.empty")));
39810
+ return;
39811
+ }
39812
+ const text = redactExport(formatExport({ meta: match, messages, log: log2 }, format), redact);
39813
+ if (out === "-") {
39814
+ ctx.output.writeLine(text);
39815
+ return;
39816
+ }
39817
+ const baseDir = out ? dirname25(out) : join55(homedir18(), ".mma", "exports");
39818
+ const path = out ?? join55(baseDir, `${sanitizeFileName(match.name)}-${match.id.slice(0, 8)}.${extensionFor(format)}`);
39819
+ try {
39820
+ mkdirSync26(baseDir, { recursive: true });
39821
+ writeFileSync25(path, text, "utf-8");
39822
+ } catch (e) {
39823
+ ctx.output.writeLine(pc.red(t("export.write_failed", { error: errMsg(e) })), "stderr");
39824
+ return;
39825
+ }
39826
+ ctx.output.writeLine(pc.green(t("export.written", { path })));
39827
+ ctx.output.writeLine(pc.dim(redact ? t("export.redacted") : t("export.raw")));
39828
+ }
39829
+ });
38733
39830
  }
38734
39831
  function registerSkillCommands(ctx) {
38735
39832
  if (!ctx.skillsModule)
@@ -38820,7 +39917,7 @@ function registerSkillCommands(ctx) {
38820
39917
  const subcmd = args[0];
38821
39918
  const handler = subcmd ? skillHandlers[subcmd] : skillHandlers.list;
38822
39919
  if (!handler) {
38823
- ctx.output.writeLine(pc.red(t("repl.skill_unknown_sub", { subcmd })), "stderr");
39920
+ ctx.output.writeLine(pc.yellow(t("repl.unknown_sub", { cmd: "skill", sub: subcmd })));
38824
39921
  ctx.output.writeLine(t("repl.skill_usage"));
38825
39922
  return;
38826
39923
  }
@@ -38849,9 +39946,10 @@ async function runConfigMigrate(ctx) {
38849
39946
  const domainFiles = readdirSync20(join55(configDir, "config")).filter((f) => f.endsWith(".json"));
38850
39947
  ctx.output.writeLine(pc.green(t("config.migrate_done", { count: String(domainFiles.length) })));
38851
39948
  }
38852
- var version2, COMMAND_GROUPS;
39949
+ var version2, COMMAND_GROUPS, SUGAR_HINTS;
38853
39950
  var init_repl_commands = __esm(() => {
38854
39951
  init_colors();
39952
+ init_theme();
38855
39953
  init_version();
38856
39954
  init_table();
38857
39955
  init_i18n();
@@ -38860,6 +39958,8 @@ var init_repl_commands = __esm(() => {
38860
39958
  init_config2();
38861
39959
  init_presets();
38862
39960
  init_budget();
39961
+ init_registry2();
39962
+ init_export();
38863
39963
  init_token_counter();
38864
39964
  init_map_command();
38865
39965
  init_utils();
@@ -38887,9 +39987,20 @@ var init_repl_commands = __esm(() => {
38887
39987
  resume: "session",
38888
39988
  rename: "session",
38889
39989
  delete: "session",
39990
+ export: "session",
38890
39991
  skill: "skill",
38891
39992
  plugins: "agent"
38892
39993
  };
39994
+ SUGAR_HINTS = {
39995
+ model: "/model use <name>",
39996
+ "provider.active": "/provider use <name>",
39997
+ contextWindow: "/context <size>",
39998
+ "contextBudget.systemPrompt": "/context system <share>",
39999
+ "contextBudget.responseReserve": "/context reserve <share>",
40000
+ showReasoning: "/reasoning show | hide",
40001
+ "reasoning.mode": "/reasoning level <level>",
40002
+ "ui.verbosity": "/verbose <level>"
40003
+ };
38893
40004
  });
38894
40005
 
38895
40006
  // node_modules/commander/esm.mjs
@@ -38915,9 +40026,10 @@ init_setup();
38915
40026
  init_setup_order();
38916
40027
  init_i18n();
38917
40028
  init_colors();
40029
+ init_export();
38918
40030
  import { join as join53, dirname as dirname24 } from "path";
38919
40031
  import { homedir as homedir17 } from "os";
38920
- import { existsSync as existsSync59 } from "fs";
40032
+ import { existsSync as existsSync59, mkdirSync as mkdirSync25, writeFileSync as writeFileSync24 } from "fs";
38921
40033
 
38922
40034
  // src/cli/security-commands.ts
38923
40035
  init_bootstrap();
@@ -40182,6 +41294,41 @@ function buildSessionCommands(program2) {
40182
41294
  }
40183
41295
  }
40184
41296
  });
41297
+ session2.command("export").argument("<id>", "Session id (or name prefix)").description(t("cli.export_session")).option("--format <format>", t("cli.export_format"), "md").option("--out <path>", t("cli.export_out")).option("--no-redact", t("cli.export_no_redact")).action(async (id, opts) => {
41298
+ const { sessionManager } = await bootstrap();
41299
+ const meta = sessionManager.get(id) ?? sessionManager.list().find((s) => s.id.startsWith(id)) ?? sessionManager.list().find((s) => s.name.toLowerCase().includes(id.toLowerCase()));
41300
+ if (!meta) {
41301
+ getDefaultChannel().writeLine(t("session.not_found", { id }));
41302
+ return;
41303
+ }
41304
+ const format = opts.format;
41305
+ if (format !== "md" && format !== "json" && format !== "jsonl") {
41306
+ getDefaultChannel().writeLine(t("export.invalid_format", { format }));
41307
+ return;
41308
+ }
41309
+ const messages = sessionManager.getHistory(meta.id);
41310
+ const log2 = sessionManager.loadSessionLog(meta.id);
41311
+ if (format === "jsonl" ? log2.length === 0 : messages.length === 0) {
41312
+ getDefaultChannel().writeLine(t("export.empty"));
41313
+ return;
41314
+ }
41315
+ const text = redactExport(formatExport({ meta, messages, log: log2 }, format), opts.redact);
41316
+ if (opts.out === "-") {
41317
+ getDefaultChannel().writeLine(text);
41318
+ return;
41319
+ }
41320
+ const baseDir = opts.out ? dirname24(opts.out) : join53(homedir17(), ".mma", "exports");
41321
+ const path = opts.out ?? join53(baseDir, `${sanitizeFileName(meta.name)}-${meta.id.slice(0, 8)}.${extensionFor(format)}`);
41322
+ try {
41323
+ mkdirSync25(baseDir, { recursive: true });
41324
+ writeFileSync24(path, text, "utf-8");
41325
+ } catch (e) {
41326
+ getDefaultChannel().writeLine(t("export.write_failed", { error: String(e) }));
41327
+ return;
41328
+ }
41329
+ getDefaultChannel().writeLine(t("export.written", { path }));
41330
+ getDefaultChannel().writeLine(t(opts.redact ? "export.redacted" : "export.raw"));
41331
+ });
40185
41332
  session2.command("delete").argument("<id>", "Session id").description(t("cli.delete_session")).action(async (id) => {
40186
41333
  const { sessionManager } = await bootstrap();
40187
41334
  sessionManager.delete(id);
@@ -41313,11 +42460,13 @@ class LineEditor {
41313
42460
  }
41314
42461
 
41315
42462
  // src/cli/repl.ts
41316
- import { existsSync as existsSync61, mkdirSync as mkdirSync25, readFileSync as readFileSync42, writeFileSync as writeFileSync24 } from "fs";
42463
+ import { existsSync as existsSync61, mkdirSync as mkdirSync27, readFileSync as readFileSync42, writeFileSync as writeFileSync26 } from "fs";
41317
42464
  import { dirname as dirname26, join as join56 } from "path";
41318
- import { homedir as homedir18 } from "os";
42465
+ import { homedir as homedir19 } from "os";
41319
42466
 
41320
42467
  // src/cli/completer.ts
42468
+ init_registry2();
42469
+ init_i18n();
41321
42470
  import { readdirSync as readdirSync19, statSync as statSync11 } from "fs";
41322
42471
  import { join as join54 } from "path";
41323
42472
  function buildSlashCompletions(commands) {
@@ -41422,11 +42571,13 @@ class ProviderArgProvider {
41422
42571
  class SessionNameProvider {
41423
42572
  name = "session-names";
41424
42573
  getNames;
41425
- constructor(getNames) {
42574
+ commands;
42575
+ constructor(getNames, commands = ["/resume"]) {
41426
42576
  this.getNames = getNames;
42577
+ this.commands = commands;
41427
42578
  }
41428
42579
  match(ctx) {
41429
- return ctx.tokenIndex >= 1 && ctx.tokens[0] === "/resume";
42580
+ return ctx.tokenIndex >= 1 && this.commands.includes(ctx.tokens[0]);
41430
42581
  }
41431
42582
  complete(ctx) {
41432
42583
  const names = this.getNames();
@@ -41458,6 +42609,74 @@ class SkillNameProvider {
41458
42609
  }
41459
42610
  }
41460
42611
 
42612
+ class ConfigKeyProvider {
42613
+ name = "config-keys";
42614
+ match(ctx) {
42615
+ return ctx.tokenIndex === 1 && ctx.tokens[0] === "/config";
42616
+ }
42617
+ complete(ctx) {
42618
+ const partial = ctx.partial;
42619
+ const items = allConfigNames().map(({ name, def, alias }) => ({
42620
+ value: name,
42621
+ description: alias ? `${t("config.alias_label")} ${def.key}` : def.domain
42622
+ }));
42623
+ if (!partial.includes(".")) {
42624
+ const groups = [...new Set(canonicalKeys().filter((k) => k.includes(".")).map((k) => k.split(".")[0]))];
42625
+ const groupItems = groups.map((g) => ({
42626
+ value: g,
42627
+ description: t("config.group_label")
42628
+ }));
42629
+ return [...groupItems, ...items].filter((i) => i.value.startsWith(partial));
42630
+ }
42631
+ return items.filter((i) => i.value.startsWith(partial));
42632
+ }
42633
+ }
42634
+
42635
+ class ConfigValueProvider {
42636
+ name = "config-values";
42637
+ match(ctx) {
42638
+ return ctx.tokenIndex === 2 && ctx.tokens[0] === "/config";
42639
+ }
42640
+ complete(ctx) {
42641
+ const def = resolveConfigKey(ctx.tokens[1]);
42642
+ if (!def?.enumValues)
42643
+ return [];
42644
+ return def.enumValues.filter((v) => v.startsWith(ctx.partial)).map((v) => ({ value: v }));
42645
+ }
42646
+ }
42647
+
42648
+ class FlagProvider {
42649
+ name = "provider-add-flags";
42650
+ match(ctx) {
42651
+ return ctx.tokens[0] === "/provider" && ctx.tokens[1] === "add" && ctx.tokenIndex >= 2;
42652
+ }
42653
+ complete(ctx) {
42654
+ const flags = [
42655
+ { value: "--url", description: t("repl.provider_flag_url") },
42656
+ { value: "--key", description: t("repl.provider_flag_key") },
42657
+ { value: "--priority", description: t("repl.provider_flag_priority") },
42658
+ { value: "--context-window", description: t("repl.provider_flag_context_window") },
42659
+ { value: "--rpm", description: t("repl.provider_flag_rpm") },
42660
+ { value: "--parallel", description: t("repl.provider_flag_parallel") }
42661
+ ];
42662
+ return flags.filter((f) => f.value.startsWith(ctx.partial));
42663
+ }
42664
+ }
42665
+
42666
+ class CommandArgProvider {
42667
+ name = "help-command-args";
42668
+ getNames;
42669
+ constructor(getNames) {
42670
+ this.getNames = getNames;
42671
+ }
42672
+ match(ctx) {
42673
+ return ctx.tokenIndex === 1 && ctx.tokens[0] === "/help";
42674
+ }
42675
+ complete(ctx) {
42676
+ return this.getNames().filter((n) => n.startsWith(ctx.partial)).map((n) => ({ value: n }));
42677
+ }
42678
+ }
42679
+
41461
42680
  class Completer {
41462
42681
  providers = [];
41463
42682
  registerProvider(provider) {
@@ -41492,109 +42711,7 @@ init_colors();
41492
42711
  init_string_width();
41493
42712
  init_i18n();
41494
42713
  init_table();
41495
-
41496
- // src/ui/theme.ts
41497
- init_colors();
41498
- var glyph = {
41499
- action: "◇",
41500
- success: "✓",
41501
- failure: "✗",
41502
- warning: "⚠",
41503
- thinking: "✦",
41504
- question: "?",
41505
- user: "↵",
41506
- detail: "↳",
41507
- group: "│",
41508
- metric: "⟡",
41509
- compact: "⟳"
41510
- };
41511
- function resolveVerbosity(ui) {
41512
- if (ui?.verbosity)
41513
- return ui.verbosity;
41514
- return ui?.verbose ? "verbose" : "quiet";
41515
- }
41516
- var CATEGORY_TOOLS = {
41517
- recon: new Set([
41518
- "read_file",
41519
- "list_dir",
41520
- "glob",
41521
- "grep",
41522
- "file_info",
41523
- "project_map",
41524
- "chunk_query",
41525
- "web_search",
41526
- "web_fetch",
41527
- "web_browse",
41528
- "search_history",
41529
- "recall",
41530
- "session_info",
41531
- "process_list",
41532
- "process_log"
41533
- ]),
41534
- mutate: new Set([
41535
- "write_file",
41536
- "edit_file",
41537
- "create_dir",
41538
- "delete_file",
41539
- "move_file",
41540
- "attach_image"
41541
- ]),
41542
- execute: new Set([
41543
- "bash",
41544
- "process_kill",
41545
- "pipeline_run",
41546
- "browser",
41547
- "download_file",
41548
- "subagent",
41549
- "mcp_call"
41550
- ]),
41551
- meta: new Set([
41552
- "plan",
41553
- "todo",
41554
- "verify",
41555
- "lsp_check",
41556
- "enable_tools",
41557
- "set_thinking",
41558
- "load_skill",
41559
- "remember"
41560
- ])
41561
- };
41562
- function toolCategory(tool) {
41563
- for (const [category, tools] of Object.entries(CATEGORY_TOOLS)) {
41564
- if (tools.has(tool))
41565
- return category;
41566
- }
41567
- return "meta";
41568
- }
41569
- function toolAccent(tool) {
41570
- switch (toolCategory(tool)) {
41571
- case "recon":
41572
- return pc.cyan;
41573
- case "mutate":
41574
- return pc.magenta;
41575
- case "execute":
41576
- return pc.blue;
41577
- case "meta":
41578
- default:
41579
- return pc.dim;
41580
- }
41581
- }
41582
- function actionMarkFor(tool) {
41583
- return toolAccent(tool)(glyph.action);
41584
- }
41585
- function successMark() {
41586
- return pc.green(glyph.success);
41587
- }
41588
- function failureMark() {
41589
- return pc.red(glyph.failure);
41590
- }
41591
- function formatDuration(ms) {
41592
- if (ms < 1000)
41593
- return `${ms}ms`;
41594
- return `${(ms / 1000).toFixed(1)}s`;
41595
- }
41596
-
41597
- // src/ui/md-formatter.ts
42714
+ init_theme();
41598
42715
  var KW = /\b(import|export|from|const|let|var|function|return|if|else|for|while|do|switch|case|break|continue|new|class|extends|implements|interface|type|enum|namespace|module|declare|abstract|async|await|yield|throw|try|catch|finally|typeof|instanceof|in|of|as|is|keyof|readonly|static|private|public|protected|get|set|constructor|this|super|void|never|any|unknown|boolean|string|number|symbol|object|true|false|null|undefined|default)\b/g;
41599
42716
  var STRING = /('[^']*'|"[^"]*"|`[^`]*`)/g;
41600
42717
  var COMMENT = /(\/\/[^\n]*|\/\*[\s\S]*?\*\/|#[^\n]*)/g;
@@ -41850,6 +42967,7 @@ function formatWarning(text) {
41850
42967
 
41851
42968
  // src/ui/renderer.ts
41852
42969
  init_spinner();
42970
+ init_theme();
41853
42971
 
41854
42972
  // src/ui/action-log.ts
41855
42973
  var last = null;
@@ -42317,9 +43435,11 @@ ${pc.dim(glyph.metric)} ${model} · ${provider} · ${pc.dim(formatDuration(durat
42317
43435
  }
42318
43436
 
42319
43437
  // src/cli/repl.ts
43438
+ init_theme();
42320
43439
  init_box();
42321
43440
  init_i18n();
42322
43441
  init_repl_commands();
43442
+ init_registry2();
42323
43443
 
42324
43444
  // src/modules/lsp/probe.ts
42325
43445
  init_client2();
@@ -42403,6 +43523,7 @@ init_session_logger();
42403
43523
 
42404
43524
  // src/ui/plan-view.ts
42405
43525
  init_colors();
43526
+ init_theme();
42406
43527
  init_js_identifiers();
42407
43528
  init_i18n();
42408
43529
  function currentStepIndex(plan) {
@@ -42605,6 +43726,7 @@ function createHostBridge(opts) {
42605
43726
  class Repl {
42606
43727
  completer = new Completer;
42607
43728
  modelCache = [];
43729
+ configSnapshot = new Map;
42608
43730
  running = false;
42609
43731
  agentRunning = false;
42610
43732
  inputLocked = false;
@@ -42648,10 +43770,10 @@ class Repl {
42648
43770
  this.execModule = execModule;
42649
43771
  this.slog = new SessionLogger(sessionManager, logger4);
42650
43772
  this.bindSessionOutput();
42651
- this.configDir = configDir || join56(homedir18(), ".mma");
43773
+ this.configDir = configDir || join56(homedir19(), ".mma");
42652
43774
  this.baseDir = baseDir || process.cwd();
42653
43775
  this.noAgentsMd = noAgentsMd === true;
42654
- this.historyPath = historyPath ?? join56(homedir18(), ".mma", "repl-history");
43776
+ this.historyPath = historyPath ?? join56(homedir19(), ".mma", "repl-history");
42655
43777
  this.loadHistory();
42656
43778
  const editorCompleter = (line) => {
42657
43779
  const [matches, partial] = this.completer.complete(line);
@@ -42705,6 +43827,7 @@ class Repl {
42705
43827
  });
42706
43828
  this.agent.setPromptIO?.(this.promptIO);
42707
43829
  registerAllCommands(this);
43830
+ this.configSnapshot = captureConfigSnapshot(this.config);
42708
43831
  this.setupCompleter();
42709
43832
  this.setupListeners();
42710
43833
  if (pluginManager) {
@@ -42765,9 +43888,9 @@ class Repl {
42765
43888
  }
42766
43889
  saveHistory() {
42767
43890
  try {
42768
- mkdirSync25(dirname26(this.historyPath), { recursive: true });
43891
+ mkdirSync27(dirname26(this.historyPath), { recursive: true });
42769
43892
  const allHistory = this.history.slice(-this.maxHistory);
42770
- writeFileSync24(this.historyPath, allHistory.join(`
43893
+ writeFileSync26(this.historyPath, allHistory.join(`
42771
43894
  `), "utf-8");
42772
43895
  } catch (e) {
42773
43896
  this.logger?.debug(`Failed to save REPL history: ${e.message}`);
@@ -42778,14 +43901,59 @@ class Repl {
42778
43901
  const slashCommands = buildSlashCompletions(this.commands);
42779
43902
  this.completer.registerProvider(new SlashCommandProvider(slashCommands));
42780
43903
  if (this.sessionManager) {
42781
- this.completer.registerProvider(new SessionNameProvider(() => this.sessionManager.list().map((s) => s.name)));
43904
+ this.completer.registerProvider(new SessionNameProvider(() => this.sessionManager.list().map((s) => s.name), [
43905
+ "/resume",
43906
+ "/delete",
43907
+ "/export"
43908
+ ]));
42782
43909
  }
42783
43910
  if (this.skillsModule) {
42784
- this.completer.registerProvider(new SubcommandProvider("skill", ["list", "loaded", "load", "unload", "search"]));
43911
+ this.completer.registerProvider(new SubcommandProvider("skill", [
43912
+ { name: "list", description: t("skill.sub_list") },
43913
+ { name: "loaded", description: t("skill.sub_loaded") },
43914
+ { name: "load", description: t("skill.sub_load") },
43915
+ { name: "unload", description: t("skill.sub_unload") },
43916
+ { name: "search", description: t("skill.sub_search") }
43917
+ ]));
42785
43918
  this.completer.registerProvider(new SkillNameProvider(this.skillsModule));
42786
43919
  }
42787
- this.completer.registerProvider(new SubcommandProvider("provider", ["list", "use", "add"]));
42788
- this.completer.registerProvider(new SubcommandProvider("model", ["list", "use"]));
43920
+ this.completer.registerProvider(new SubcommandProvider("provider", [
43921
+ { name: "list", description: t("provider.sub_list") },
43922
+ { name: "use", description: t("provider.sub_use") },
43923
+ { name: "add", description: t("provider.sub_add") }
43924
+ ]));
43925
+ this.completer.registerProvider(new SubcommandProvider("model", [
43926
+ { name: "list", description: t("model.sub_list") },
43927
+ { name: "use", description: t("model.sub_use") }
43928
+ ]));
43929
+ this.completer.registerProvider(new SubcommandProvider("map", [
43930
+ { name: "summary", description: t("map.sub_summary") },
43931
+ { name: "refresh", description: t("map.sub_refresh") },
43932
+ { name: "find", description: t("map.sub_find") }
43933
+ ]));
43934
+ this.completer.registerProvider(new SubcommandProvider("context", [
43935
+ { name: "system", description: t("context.sub_system") },
43936
+ { name: "reserve", description: t("context.sub_reserve") }
43937
+ ]));
43938
+ this.completer.registerProvider(new SubcommandProvider("lsp", [
43939
+ { name: "status", description: t("lsp.sub_status") },
43940
+ { name: "restart", description: t("lsp.sub_restart") },
43941
+ { name: "check", description: t("lsp.sub_check") }
43942
+ ]));
43943
+ this.completer.registerProvider(new SubcommandProvider("reasoning", [
43944
+ { name: "show", description: t("reasoning.sub_show") },
43945
+ { name: "hide", description: t("reasoning.sub_hide") },
43946
+ { name: "level", description: t("reasoning.sub_level") }
43947
+ ]));
43948
+ this.completer.registerProvider(new SubcommandProvider("verbose", [
43949
+ { name: "quiet", description: t("verbose.sub_quiet") },
43950
+ { name: "normal", description: t("verbose.sub_normal") },
43951
+ { name: "verbose", description: t("verbose.sub_verbose") }
43952
+ ]));
43953
+ this.completer.registerProvider(new ConfigKeyProvider);
43954
+ this.completer.registerProvider(new ConfigValueProvider);
43955
+ this.completer.registerProvider(new FlagProvider);
43956
+ this.completer.registerProvider(new CommandArgProvider(() => Array.from(this.commands.entries()).filter(([key, cmd]) => key === cmd.name).map(([, cmd]) => cmd.name)));
42789
43957
  const entryLabels = this.config.provider?.entries?.map((e) => e.label || e.type) ?? [];
42790
43958
  if (entryLabels.length > 0) {
42791
43959
  this.completer.registerProvider(new ProviderArgProvider(entryLabels));
@@ -42794,6 +43962,17 @@ class Repl {
42794
43962
  this.completer.registerProvider(new FilePathProvider(this.baseDir));
42795
43963
  this.refreshModelCache();
42796
43964
  }
43965
+ reregisterCommands() {
43966
+ this.commands.clear();
43967
+ registerAllCommands(this);
43968
+ this.setupCompleter();
43969
+ }
43970
+ getConfigSnapshot() {
43971
+ return this.configSnapshot;
43972
+ }
43973
+ knownModels() {
43974
+ return this.modelCache;
43975
+ }
42797
43976
  refreshModelCache() {
42798
43977
  (async () => {
42799
43978
  try {
@@ -43140,7 +44319,13 @@ ${divider()}`);
43140
44319
  this.runHandledPrompt = false;
43141
44320
  const cmd = this.commands.get(name);
43142
44321
  if (!cmd) {
43143
- this.output.writeLine(`${pc.red(t("cli.unknown_cmd", { name }))} ${t("cli.help_hint")}`);
44322
+ const formerAliases = { use: "resume" };
44323
+ const suggestion = formerAliases[name];
44324
+ if (suggestion) {
44325
+ this.output.writeLine(pc.red(t("cli.unknown_command", { input: `/${name}`, suggestion: `/${suggestion}` })));
44326
+ } else {
44327
+ this.output.writeLine(`${pc.red(t("cli.unknown_cmd", { name }))} ${t("cli.help_hint")}`);
44328
+ }
43144
44329
  return;
43145
44330
  }
43146
44331
  try {
@@ -43151,8 +44336,29 @@ ${divider()}`);
43151
44336
  })), "stderr");
43152
44337
  }
43153
44338
  }
43154
- showHelp() {
44339
+ showHelp(command) {
43155
44340
  const { COMMAND_GROUPS: COMMAND_GROUPS2 } = (init_repl_commands(), __toCommonJS(exports_repl_commands));
44341
+ if (command) {
44342
+ const name = command.replace(/^\//, "");
44343
+ const cmd = this.commands.get(name);
44344
+ if (!cmd) {
44345
+ this.output.writeLine(pc.red(t("repl.help_unknown", { name })));
44346
+ return;
44347
+ }
44348
+ this.output.writeLine(pc.bold(pc.cyan("/" + cmd.name)));
44349
+ this.output.writeLine(` ${cmd.description}`);
44350
+ if (cmd.usage)
44351
+ this.output.writeLine(` ${pc.dim(cmd.usage)}`);
44352
+ if (cmd.aliases?.length) {
44353
+ this.output.writeLine(pc.dim(` ${t("repl.help_aliases", { names: cmd.aliases.join(", ") })}`));
44354
+ }
44355
+ if (cmd.examples?.length) {
44356
+ this.output.writeLine(pc.dim(` ${t("repl.help_examples")}`));
44357
+ for (const ex of cmd.examples)
44358
+ this.output.writeLine(pc.dim(` ${ex}`));
44359
+ }
44360
+ return;
44361
+ }
43156
44362
  const order = ["general", "agent", "session", "skill"];
43157
44363
  const seen = new Set;
43158
44364
  for (const groupKey of order) {
@@ -43363,6 +44569,9 @@ ${line}`);
43363
44569
  }
43364
44570
  }
43365
44571
 
44572
+ // src/cli/main.ts
44573
+ init_theme();
44574
+
43366
44575
  // src/cli/run-result.ts
43367
44576
  init_i18n();
43368
44577
  init_colors();
@@ -43456,7 +44665,7 @@ init_i18n();
43456
44665
  init_colors();
43457
44666
  import { existsSync as existsSync62 } from "fs";
43458
44667
  import { join as join58, dirname as dirname27 } from "path";
43459
- import { homedir as homedir20 } from "os";
44668
+ import { homedir as homedir21 } from "os";
43460
44669
  import { fileURLToPath as fileURLToPath6 } from "url";
43461
44670
 
43462
44671
  // src/modules/updater/index.ts
@@ -43576,10 +44785,10 @@ init_environment();
43576
44785
  init_data_sanitizer();
43577
44786
  init_i18n();
43578
44787
  init_utils();
43579
- import { appendFileSync as appendFileSync8, mkdirSync as mkdirSync26 } from "fs";
44788
+ import { appendFileSync as appendFileSync8, mkdirSync as mkdirSync28 } from "fs";
43580
44789
  import { join as join57 } from "path";
43581
- import { homedir as homedir19 } from "os";
43582
- var CRASH_LOG_DIR = join57(homedir19(), ".mma", "logs");
44790
+ import { homedir as homedir20 } from "os";
44791
+ var CRASH_LOG_DIR = join57(homedir20(), ".mma", "logs");
43583
44792
  var CRASH_LOG_FILE = "crash.jsonl";
43584
44793
  function formatCrashEntry(type2, err) {
43585
44794
  const message = errMsg(err);
@@ -43589,12 +44798,12 @@ function formatCrashEntry(type2, err) {
43589
44798
  type: type2,
43590
44799
  message: sanitizeLogMessage(message),
43591
44800
  stack: sanitizeLogMessage(stack),
43592
- environment: collectEnvironment({ configDir: homedir19(), scanTools: false })
44801
+ environment: collectEnvironment({ configDir: homedir20(), scanTools: false })
43593
44802
  };
43594
44803
  }
43595
44804
  function writeCrashEntry(dir, entry) {
43596
44805
  try {
43597
- mkdirSync26(dir, { recursive: true });
44806
+ mkdirSync28(dir, { recursive: true });
43598
44807
  appendFileSync8(join57(dir, CRASH_LOG_FILE), JSON.stringify(entry) + `
43599
44808
  `, "utf-8");
43600
44809
  } catch {}
@@ -43723,7 +44932,7 @@ async function main() {
43723
44932
  }
43724
44933
  const jsonSink = jsonOpt ? new JsonOutputSink(defaultOutputBus) : undefined;
43725
44934
  const prompt = program2.args.join(" ");
43726
- const mmaDir = process.env.MMA_CONFIG_DIR || join58(homedir20(), ".mma");
44935
+ const mmaDir = process.env.MMA_CONFIG_DIR || join58(homedir21(), ".mma");
43727
44936
  const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
43728
44937
  const virgin = !existsSync62(join58(mmaDir, "config.json")) && !hasDomainFiles2(mmaDir);
43729
44938
  const { agent, config, baseDir, legacyDetected, contextProbe } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete, reasoningLevel, contextLog);
@@ -43811,7 +45020,7 @@ async function main() {
43811
45020
  await updater?.waitForIdle();
43812
45021
  process.exit(exitCode);
43813
45022
  } else {
43814
- const mmaDir = process.env.MMA_CONFIG_DIR || join58(homedir20(), ".mma");
45023
+ const mmaDir = process.env.MMA_CONFIG_DIR || join58(homedir21(), ".mma");
43815
45024
  const legacyConfigPath = join58(mmaDir, "config.json");
43816
45025
  let hasAnyConfig = existsSync62(legacyConfigPath);
43817
45026
  if (!hasAnyConfig) {