micro-models-agent 0.24.2 → 0.24.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +312 -312
  2. package/bin/mma.mjs +8 -8
  3. package/dist/main.js +190 -120
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -1,312 +1,312 @@
1
- # MMA — Micro Models Agent v2
2
-
3
- [![npm version](https://img.shields.io/npm/v/micro-models-agent)](https://www.npmjs.com/package/micro-models-agent)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
-
6
- **MMA** — модульный AI-coding агент, оптимизированный для **небольших локальных языковых моделей** (9B параметров, 32K–64K контекст). Работает на ноутбуке, облачное API не требуется.
7
-
8
- ```bash
9
- npm install -g micro-models-agent
10
- mma "перепиши модуль авторизации на JWT"
11
- ```
12
-
13
- Создан и протестирован с [Qwen3.5-9B](https://qwen.readthedocs.io/) через LM Studio / Ollama / llama.cpp.
14
-
15
- ---
16
-
17
- ## Зачем MMA?
18
-
19
- Большинство AI-агентов для кода (Devin, Cursor, Copilot) завязаны на облачные модели или дорогие API. MMA работает иначе:
20
-
21
- - **Создан для моделей 9B** — оптимизирован под Qwen3.5-9B на обычном железе
22
- - **Не требует облака** — полностью работает офлайн с любым OpenAI-совместимым бэкендом
23
- - **Выживает в узком контексте** — умное sliding-window compaction держит 32K-модели продуктивными
24
- - **Отлавливает галлюцинации** — 3-стадийный пайплайн валидации ловит выдумки, к которым склонны маленькие модели
25
- - **Agent-Level MoE** — иерархическая декомпозиция задач: маленькие модели справляются со сложной многошаговой работой
26
-
27
- ---
28
-
29
- ## Возможности
30
-
31
- - **6-состояний цикл агента** (INIT → THINK → ACT → OBSERVE → OUTPUT → ERROR) — чистый, предсказуемый, отлаживаемый
32
- - **Управление бюджетом контекста** — динамический бюджет под модель, sliding-window compaction с извлечением фактов/решений/ошибок
33
- - **Детекция галлюцинаций** — фактическая (пути файлов), согласованности (откат решений), уверенности (короткие/повторяющиеся ответы)
34
- - **Гарантии выполнения** — авто-планирование через LLM (без keyword-эвристик), stuck-detection, off-track предупреждения, авто-продвижение плана
35
- - **Agent-Level MoE** — Router + экспертные сабагенты с фильтрацией инструментов, изолированным контекстом, файловым скоупом и топологическим параллельным выполнением
36
- - **20+ инструментов** — файловая система, шелл, веб-поиск/fetch, браузер (Playwright), планирование, взаимодействие, сабагенты, MCP, пайплайны
37
- - **13 модулей** — скиллы, плагины, MCP-клиент, пайплайны, indexer проекта, постоянная память, контекст, сессии, браузер, выполнение, детекция галлюцинаций, апдейтер, профиль пользователя
38
- - **MCP-клиент** — подключение к любому MCP-серверу (stdio или SSE)
39
- - **YAML-пайплайны** — DAG-движок с параллельными волнами, зависимостями и повторными попытками
40
- - **Управление сессиями** — постоянные JSONL-сессии с REPL-командами
41
- - **Карта проекта** — автоматический обход файлов + извлечение экспортов + дисковый кэш
42
- - **i18n** — все строки интерфейса через `t()`, включены английский и русский
43
- - **8K–120K контекст** — адаптируется под любое контекстное окно модели
44
- - **Без TUI** — минимальный CLI + REPL с markdown→ANSI форматированием
45
- - **llama.cpp / Jinja proven** — проверено на граничных случаях Jinja-шаблонов (system-first, streaming fallback, явный `stream: false`)
46
-
47
- ---
48
-
49
- ## Установка
50
-
51
- ### Требования
52
-
53
- - **Среда выполнения:** [Bun](https://bun.sh) (рекомендуется) или Node.js ≥ 20
54
- - **LLM-бэкенд:** Любой OpenAI-совместимый сервер (LM Studio, Ollama, vLLM, llama.cpp, Together AI)
55
-
56
- ### Через npm (рекомендуется)
57
-
58
- ```bash
59
- npm install -g micro-models-agent@latest
60
- ```
61
-
62
- После установки команда `mma` будет доступна в терминале. Если после установки `mma` не находится — попробуйте переоткрыть терминал или выполните:
63
-
64
- ```bash
65
- # PowerShell
66
- npm uninstall -g micro-models-agent
67
- npm install -g micro-models-agent@latest
68
-
69
- # Проверка
70
- mma --version
71
- ```
72
-
73
- ### Из исходников
74
-
75
- ```bash
76
- git clone https://github.com/your-org/micro-models-agent
77
- cd micro-models-agent
78
- bun install
79
- bun run build:prod
80
- ```
81
-
82
- ---
83
-
84
- ## Быстрый старт
85
-
86
- ### 1. Запустите LLM-бэкенд
87
-
88
- Направьте MMA на любой OpenAI-совместимый эндпоинт. Пример с LM Studio:
89
-
90
- ```bash
91
- # LM Studio слушает http://localhost:1234 по умолчанию
92
- ```
93
-
94
- ### 2. Запустите мастер настройки
95
-
96
- ```bash
97
- bun run mma init
98
- ```
99
-
100
- Сканирует локальные порты, находит модель, тестирует соединение и записывает конфиг.
101
-
102
- ### 3. Используйте агента
103
-
104
- ```bash
105
- # Одноразовый режим
106
- bun run mma "создай REST API на Express и добавь тесты"
107
-
108
- # Интерактивный REPL
109
- bun run dev
110
- ```
111
-
112
- ---
113
-
114
- ## Использование
115
-
116
- ### CLI
117
-
118
- ```bash
119
- bun run mma "<prompt>"
120
-
121
- # Подкоманды
122
- bun run mma init # Интерактивный мастер настройки
123
- bun run mma config set model qwen3.5-9b
124
- bun run mma config show
125
- bun run mma model list # Список доступных моделей
126
- bun run mma model use qwen3.5-9b # Переключить модель
127
- bun run mma provider list # Список провайдеров
128
- bun run mma session list # Список сессий
129
- bun run mma session show <id> # Детали сессии
130
- bun run mma session delete <id> # Удалить сессию
131
- ```
132
-
133
- ### REPL
134
-
135
- ```bash
136
- bun run dev
137
- ```
138
-
139
- | Команда | Алиасы | Описание |
140
- |---------|--------|----------|
141
- | `/help` | | Показать справку |
142
- | `/sessions` | `/ls` | Список сессий (* = активная) |
143
- | `/new <name>` | `/create` | Создать новую сессию |
144
- | `/resume <id\|name>` | `/switch`, `/use` | Переключиться на сессию |
145
- | `/rename <name>` | | Переименовать текущую сессию |
146
- | `/delete <id>` | `/rm` | Удалить сессию |
147
- | `/config set <key> <value>` | | Установить значение конфига |
148
- | `/exit` | | Выйти из REPL |
149
-
150
- ### Разработка
151
-
152
- ```bash
153
- bun run mma "почини страницу логина" # Запуск агента
154
- bun run dev # Watch-режим (авто-перезапуск при изменениях)
155
- bun run build:prod # Сборка для публикации
156
- bun test # Запуск тестов
157
- bun run typecheck # Проверка типов
158
- ```
159
-
160
- ---
161
-
162
- ## Конфигурация
163
-
164
- 3-слойный конфиг: `defaults.ts` → `~/.mma/config.json` (глобальный) → `.mmrc` (проект) + переменные окружения.
165
-
166
- Ключевые опции (полный список в `src/config/defaults.ts`):
167
-
168
- | Опция | По умолчанию | Описание |
169
- |-------|-------------|----------|
170
- | `model` | `qwen3.5-9b` | Имя модели для бэкенда |
171
- | `provider.baseUrl` | `http://localhost:1234/v1` | URL OpenAI-совместимого API |
172
- | `contextWindow` | `32768` | Контекстное окно модели в токенах |
173
- | `autoPlan` | `true` | Авто-создание планов для многошаговых задач |
174
- | `maxToolIterations` | `25` | Макс. вызовов инструментов за запуск |
175
- | `stuckThreshold` | `8` | Итераций без прогресса до stuck-detection |
176
- | `moe.enabled` | `false` | Включить Agent-Level MoE |
177
- | `locale` | `en` | Язык интерфейса (`en` / `ru`) |
178
- | `logLevel` | `info` | Уровень логирования |
179
-
180
- ### Agent-Level MoE
181
-
182
- ```json
183
- {
184
- "moe": { "enabled": true },
185
- "orchestrator": {
186
- "model": "qwen3-70b-414k",
187
- "provider": { "baseUrl": "http://localhost:1234/v1" }
188
- },
189
- "experts": {
190
- "code": { "model": "qwen3.5-9b", "tool_tags": ["file", "code", "shell"], "max_attempts": 3 },
191
- "research": { "model": "qwen3.5-9b", "tool_tags": ["research"], "max_attempts": 3 },
192
- "browser": { "model": "qwen3.5-9b", "tool_tags": ["browser", "vision"], "max_attempts": 3 }
193
- }
194
- }
195
- ```
196
-
197
- ---
198
-
199
- ## Архитектура
200
-
201
- ```
202
- CLI (main.ts → commands.ts / repl.ts / setup.ts)
203
- → Core (agent.ts state machine → prompt-builder.ts)
204
- → LLM Layer (provider.ts → openai-compat.ts → response.ts → token-counter.ts)
205
- → Tools (registry.ts → executor.ts → 20+ tools)
206
- → Modules (skills, plugins, mcp, pipelines, indexer, memory, context, session,
207
- hallucination, execution, updater, user-profile, browser)
208
- ```
209
-
210
- ### Структура проекта
211
-
212
- ```
213
- src/
214
- ├── core/ # Цикл агента, сборщик промптов, типы
215
- ├── llm/ # Абстракция провайдера, OpenAI-совместимость, стриминг, подсчёт токенов
216
- ├── tools/ # 20+ инструментов с реестром, исполнителем, скоуп-гардами
217
- ├── modules/ # 13 модулей (скиллы, плагины, mcp, пайплайны, indexer, память, контекст, сессии, галлюцинации, выполнение, апдейтер, профиль, браузер)
218
- ├── cli/ # Точка входа, команды, REPL, мастер настройки
219
- ├── config/ # 3-слойный конфиг: defaults → глобальный → проект
220
- ├── i18n/ # en.json + ru.json + t()
221
- ├── ui/ # Markdown→ANSI форматтер
222
- └── logger/ # Структурированный логгер с уровнями и дочерними логгерами
223
- ```
224
-
225
- ---
226
-
227
- ## Инструменты
228
-
229
- | Инструмент | Описание |
230
- |------------|----------|
231
- | `read_file` | Чтение файла с offset/limit |
232
- | `write_file` | Создание/перезапись с созданием директорий |
233
- | `edit_file` | Поиск-и-замена в существующих файлах |
234
- | `glob` | Поиск по glob-паттернам |
235
- | `grep` | Поиск по содержимому через ripgrep |
236
- | `list_dir` | Список содержимого директории |
237
- | `create_dir` | Создание директории (рекурсивно) |
238
- | `delete_file` | Удаление файла или пустой директории |
239
- | `move_file` | Перемещение/переименование файла или директории |
240
- | `file_info` | Метаданные файла/директории |
241
- | `bash` | Выполнение shell-команд |
242
- | `subagent` | Изолированный сабагент со скоупом |
243
- | `web_search` | Поиск в интернете |
244
- | `web_fetch` | Загрузка веб-страницы (15K символов, 15s таймаут) |
245
- | `web_browse` | Браузер на Playwright (клик, ввод, скролл, скриншот) |
246
- | `plan` | Создание/обновление/отмена планов |
247
- | `todo` | Отслеживание задач |
248
- | `question` | Задать вопрос пользователю |
249
- | `approve` | Запросить подтверждение пользователя |
250
- | `load_skill` | Загрузить скилл во время выполнения |
251
- | `pipeline_run` | Выполнить YAML-пайплайн |
252
- | `mcp_call` | Вызвать инструмент MCP-сервера |
253
- | `search_history` | Поиск по истории сессий |
254
- | `project_map` | Запрос карты проекта (summary, refresh, find) |
255
-
256
- ---
257
-
258
- ## Тестирование
259
-
260
- ```bash
261
- bun test # Модульные + компонентные тесты
262
- bun test tests/agent.test.ts # Один файл
263
- bun run test:integration # Интеграционные тесты (требуют LLM-бэкенд)
264
- bun run typecheck # Проверка типов
265
- ```
266
-
267
- ### Тестирование агента из консоли (agent-driven)
268
-
269
- MMA можно тестировать в одноразовом headless-режиме без интерактивного REPL — это удобно для проверки фич и регрессий из терминала или другим агентом:
270
-
271
- ```bash
272
- # Одноразовый прогон: без AGENTS.md, выход сразу после ответа, песочница
273
- bun run mma "<промпт>" --no-agents-md --exit-on-complete -d <путь-к-песочнице>
274
- ```
275
-
276
- - **Песочница** — всегда внутри `_testing/` в корне проекта (например `_testing/<case-name>/`), никогда в корне или в `src/`. Папка `_testing/` добавлена в `.gitignore`.
277
- - **`--no-agents-md`** — не грузить AGENTS.md проекта в системный промпт (чистая среда).
278
- - **`--exit-on-complete`** — выйти сразу после первого финального ответа; интерактивные тулзы (`question`/`approve`) не блокируют stdin, а возвращают ошибку.
279
- - **`-d <dir>`** — рабочая директория агента (туда он создаёт файлы).
280
- - Каждый прогон сохраняется в сессию `~/.mma/sessions/<id>/` — можно посмотреть через `bun run mma session list` / `session show <id>`.
281
-
282
- ---
283
-
284
- ## Принципы дизайна
285
-
286
- 1. **Ни одного файла >300 строк** — разделяй при приближении к лимиту
287
- 2. **Маленькие файлы, одна ответственность** — каждый файл делает одно дело
288
- 3. **KISS state machine** — никаких god-объектов, никакой вложенности if-else в цикле агента
289
- 4. **Изоляция ошибок плагинов** — падение одного плагина не останавливает другие
290
- 5. **Безопасность путей** — все файловые инструменты проверяют `targetPath.startsWith(baseDir)`
291
- 6. **TDD** — сначала тест, потом реализация, потом коммит
292
- 7. **Никаких захардкоженных строк** — весь текст через `t()` i18n
293
- 8. **Никакого keyword matching** — LLM решает когда планировать, не эвристики
294
- 9. **Без TUI** — минимальный CLI + REPL с markdown→ANSI
295
-
296
- ---
297
-
298
- ## Лицензия
299
-
300
- MIT
301
-
302
- ---
303
-
304
- ## Благодарности
305
-
306
- - [Qwen3.5-9B](https://qwen.readthedocs.io/) — основная целевая модель
307
- - [LM Studio](https://lmstudio.ai/) — рекомендуемый локальный инференс-сервер
308
- - [llama.cpp](https://github.com/ggerganov/llama.cpp) — инференс-движок
309
-
310
- ---
311
-
312
- **Сделано для локальных моделей. Работает везде.**
1
+ # MMA — Micro Models Agent v2
2
+
3
+ [![npm version](https://img.shields.io/npm/v/micro-models-agent)](https://www.npmjs.com/package/micro-models-agent)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
+
6
+ **MMA** — модульный AI-coding агент, оптимизированный для **небольших локальных языковых моделей** (9B параметров, 32K–64K контекст). Работает на ноутбуке, облачное API не требуется.
7
+
8
+ ```bash
9
+ npm install -g micro-models-agent
10
+ mma "перепиши модуль авторизации на JWT"
11
+ ```
12
+
13
+ Создан и протестирован с [Qwen3.5-9B](https://qwen.readthedocs.io/) через LM Studio / Ollama / llama.cpp.
14
+
15
+ ---
16
+
17
+ ## Зачем MMA?
18
+
19
+ Большинство AI-агентов для кода (Devin, Cursor, Copilot) завязаны на облачные модели или дорогие API. MMA работает иначе:
20
+
21
+ - **Создан для моделей 9B** — оптимизирован под Qwen3.5-9B на обычном железе
22
+ - **Не требует облака** — полностью работает офлайн с любым OpenAI-совместимым бэкендом
23
+ - **Выживает в узком контексте** — умное sliding-window compaction держит 32K-модели продуктивными
24
+ - **Отлавливает галлюцинации** — 3-стадийный пайплайн валидации ловит выдумки, к которым склонны маленькие модели
25
+ - **Agent-Level MoE** — иерархическая декомпозиция задач: маленькие модели справляются со сложной многошаговой работой
26
+
27
+ ---
28
+
29
+ ## Возможности
30
+
31
+ - **6-состояний цикл агента** (INIT → THINK → ACT → OBSERVE → OUTPUT → ERROR) — чистый, предсказуемый, отлаживаемый
32
+ - **Управление бюджетом контекста** — динамический бюджет под модель, sliding-window compaction с извлечением фактов/решений/ошибок
33
+ - **Детекция галлюцинаций** — фактическая (пути файлов), согласованности (откат решений), уверенности (короткие/повторяющиеся ответы)
34
+ - **Гарантии выполнения** — авто-планирование через LLM (без keyword-эвристик), stuck-detection, off-track предупреждения, авто-продвижение плана
35
+ - **Agent-Level MoE** — Router + экспертные сабагенты с фильтрацией инструментов, изолированным контекстом, файловым скоупом и топологическим параллельным выполнением
36
+ - **20+ инструментов** — файловая система, шелл, веб-поиск/fetch, браузер (Playwright), планирование, взаимодействие, сабагенты, MCP, пайплайны
37
+ - **13 модулей** — скиллы, плагины, MCP-клиент, пайплайны, indexer проекта, постоянная память, контекст, сессии, браузер, выполнение, детекция галлюцинаций, апдейтер, профиль пользователя
38
+ - **MCP-клиент** — подключение к любому MCP-серверу (stdio или SSE)
39
+ - **YAML-пайплайны** — DAG-движок с параллельными волнами, зависимостями и повторными попытками
40
+ - **Управление сессиями** — постоянные JSONL-сессии с REPL-командами
41
+ - **Карта проекта** — автоматический обход файлов + извлечение экспортов + дисковый кэш
42
+ - **i18n** — все строки интерфейса через `t()`, включены английский и русский
43
+ - **8K–120K контекст** — адаптируется под любое контекстное окно модели
44
+ - **Без TUI** — минимальный CLI + REPL с markdown→ANSI форматированием
45
+ - **llama.cpp / Jinja proven** — проверено на граничных случаях Jinja-шаблонов (system-first, streaming fallback, явный `stream: false`)
46
+
47
+ ---
48
+
49
+ ## Установка
50
+
51
+ ### Требования
52
+
53
+ - **Среда выполнения:** [Bun](https://bun.sh) (рекомендуется) или Node.js ≥ 20
54
+ - **LLM-бэкенд:** Любой OpenAI-совместимый сервер (LM Studio, Ollama, vLLM, llama.cpp, Together AI)
55
+
56
+ ### Через npm (рекомендуется)
57
+
58
+ ```bash
59
+ npm install -g micro-models-agent@latest
60
+ ```
61
+
62
+ После установки команда `mma` будет доступна в терминале. Если после установки `mma` не находится — попробуйте переоткрыть терминал или выполните:
63
+
64
+ ```bash
65
+ # PowerShell
66
+ npm uninstall -g micro-models-agent
67
+ npm install -g micro-models-agent@latest
68
+
69
+ # Проверка
70
+ mma --version
71
+ ```
72
+
73
+ ### Из исходников
74
+
75
+ ```bash
76
+ git clone https://github.com/your-org/micro-models-agent
77
+ cd micro-models-agent
78
+ bun install
79
+ bun run build:prod
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Быстрый старт
85
+
86
+ ### 1. Запустите LLM-бэкенд
87
+
88
+ Направьте MMA на любой OpenAI-совместимый эндпоинт. Пример с LM Studio:
89
+
90
+ ```bash
91
+ # LM Studio слушает http://localhost:1234 по умолчанию
92
+ ```
93
+
94
+ ### 2. Запустите мастер настройки
95
+
96
+ ```bash
97
+ bun run mma init
98
+ ```
99
+
100
+ Сканирует локальные порты, находит модель, тестирует соединение и записывает конфиг.
101
+
102
+ ### 3. Используйте агента
103
+
104
+ ```bash
105
+ # Одноразовый режим
106
+ bun run mma "создай REST API на Express и добавь тесты"
107
+
108
+ # Интерактивный REPL
109
+ bun run dev
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Использование
115
+
116
+ ### CLI
117
+
118
+ ```bash
119
+ bun run mma "<prompt>"
120
+
121
+ # Подкоманды
122
+ bun run mma init # Интерактивный мастер настройки
123
+ bun run mma config set model qwen3.5-9b
124
+ bun run mma config show
125
+ bun run mma model list # Список доступных моделей
126
+ bun run mma model use qwen3.5-9b # Переключить модель
127
+ bun run mma provider list # Список провайдеров
128
+ bun run mma session list # Список сессий
129
+ bun run mma session show <id> # Детали сессии
130
+ bun run mma session delete <id> # Удалить сессию
131
+ ```
132
+
133
+ ### REPL
134
+
135
+ ```bash
136
+ bun run dev
137
+ ```
138
+
139
+ | Команда | Алиасы | Описание |
140
+ |---------|--------|----------|
141
+ | `/help` | | Показать справку |
142
+ | `/sessions` | `/ls` | Список сессий (* = активная) |
143
+ | `/new <name>` | `/create` | Создать новую сессию |
144
+ | `/resume <id\|name>` | `/switch`, `/use` | Переключиться на сессию |
145
+ | `/rename <name>` | | Переименовать текущую сессию |
146
+ | `/delete <id>` | `/rm` | Удалить сессию |
147
+ | `/config set <key> <value>` | | Установить значение конфига |
148
+ | `/exit` | | Выйти из REPL |
149
+
150
+ ### Разработка
151
+
152
+ ```bash
153
+ bun run mma "почини страницу логина" # Запуск агента
154
+ bun run dev # Watch-режим (авто-перезапуск при изменениях)
155
+ bun run build:prod # Сборка для публикации
156
+ bun test # Запуск тестов
157
+ bun run typecheck # Проверка типов
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Конфигурация
163
+
164
+ 3-слойный конфиг: `defaults.ts` → `~/.mma/config.json` (глобальный) → `.mmrc` (проект) + переменные окружения.
165
+
166
+ Ключевые опции (полный список в `src/config/defaults.ts`):
167
+
168
+ | Опция | По умолчанию | Описание |
169
+ |-------|-------------|----------|
170
+ | `model` | `qwen3.5-9b` | Имя модели для бэкенда |
171
+ | `provider.baseUrl` | `http://localhost:1234/v1` | URL OpenAI-совместимого API |
172
+ | `contextWindow` | `32768` | Контекстное окно модели в токенах |
173
+ | `autoPlan` | `true` | Авто-создание планов для многошаговых задач |
174
+ | `maxToolIterations` | `25` | Макс. вызовов инструментов за запуск |
175
+ | `stuckThreshold` | `8` | Итераций без прогресса до stuck-detection |
176
+ | `moe.enabled` | `false` | Включить Agent-Level MoE |
177
+ | `locale` | `en` | Язык интерфейса (`en` / `ru`) |
178
+ | `logLevel` | `info` | Уровень логирования |
179
+
180
+ ### Agent-Level MoE
181
+
182
+ ```json
183
+ {
184
+ "moe": { "enabled": true },
185
+ "orchestrator": {
186
+ "model": "qwen3-70b-414k",
187
+ "provider": { "baseUrl": "http://localhost:1234/v1" }
188
+ },
189
+ "experts": {
190
+ "code": { "model": "qwen3.5-9b", "tool_tags": ["file", "code", "shell"], "max_attempts": 3 },
191
+ "research": { "model": "qwen3.5-9b", "tool_tags": ["research"], "max_attempts": 3 },
192
+ "browser": { "model": "qwen3.5-9b", "tool_tags": ["browser", "vision"], "max_attempts": 3 }
193
+ }
194
+ }
195
+ ```
196
+
197
+ ---
198
+
199
+ ## Архитектура
200
+
201
+ ```
202
+ CLI (main.ts → commands.ts / repl.ts / setup.ts)
203
+ → Core (agent.ts state machine → prompt-builder.ts)
204
+ → LLM Layer (provider.ts → openai-compat.ts → response.ts → token-counter.ts)
205
+ → Tools (registry.ts → executor.ts → 20+ tools)
206
+ → Modules (skills, plugins, mcp, pipelines, indexer, memory, context, session,
207
+ hallucination, execution, updater, user-profile, browser)
208
+ ```
209
+
210
+ ### Структура проекта
211
+
212
+ ```
213
+ src/
214
+ ├── core/ # Цикл агента, сборщик промптов, типы
215
+ ├── llm/ # Абстракция провайдера, OpenAI-совместимость, стриминг, подсчёт токенов
216
+ ├── tools/ # 20+ инструментов с реестром, исполнителем, скоуп-гардами
217
+ ├── modules/ # 13 модулей (скиллы, плагины, mcp, пайплайны, indexer, память, контекст, сессии, галлюцинации, выполнение, апдейтер, профиль, браузер)
218
+ ├── cli/ # Точка входа, команды, REPL, мастер настройки
219
+ ├── config/ # 3-слойный конфиг: defaults → глобальный → проект
220
+ ├── i18n/ # en.json + ru.json + t()
221
+ ├── ui/ # Markdown→ANSI форматтер
222
+ └── logger/ # Структурированный логгер с уровнями и дочерними логгерами
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Инструменты
228
+
229
+ | Инструмент | Описание |
230
+ |------------|----------|
231
+ | `read_file` | Чтение файла с offset/limit |
232
+ | `write_file` | Создание/перезапись с созданием директорий |
233
+ | `edit_file` | Поиск-и-замена в существующих файлах |
234
+ | `glob` | Поиск по glob-паттернам |
235
+ | `grep` | Поиск по содержимому через ripgrep |
236
+ | `list_dir` | Список содержимого директории |
237
+ | `create_dir` | Создание директории (рекурсивно) |
238
+ | `delete_file` | Удаление файла или пустой директории |
239
+ | `move_file` | Перемещение/переименование файла или директории |
240
+ | `file_info` | Метаданные файла/директории |
241
+ | `bash` | Выполнение shell-команд |
242
+ | `subagent` | Изолированный сабагент со скоупом |
243
+ | `web_search` | Поиск в интернете |
244
+ | `web_fetch` | Загрузка веб-страницы (15K символов, 15s таймаут) |
245
+ | `web_browse` | Браузер на Playwright (клик, ввод, скролл, скриншот) |
246
+ | `plan` | Создание/обновление/отмена планов |
247
+ | `todo` | Отслеживание задач |
248
+ | `question` | Задать вопрос пользователю |
249
+ | `approve` | Запросить подтверждение пользователя |
250
+ | `load_skill` | Загрузить скилл во время выполнения |
251
+ | `pipeline_run` | Выполнить YAML-пайплайн |
252
+ | `mcp_call` | Вызвать инструмент MCP-сервера |
253
+ | `search_history` | Поиск по истории сессий |
254
+ | `project_map` | Запрос карты проекта (summary, refresh, find) |
255
+
256
+ ---
257
+
258
+ ## Тестирование
259
+
260
+ ```bash
261
+ bun test # Модульные + компонентные тесты
262
+ bun test tests/agent.test.ts # Один файл
263
+ bun run test:integration # Интеграционные тесты (требуют LLM-бэкенд)
264
+ bun run typecheck # Проверка типов
265
+ ```
266
+
267
+ ### Тестирование агента из консоли (agent-driven)
268
+
269
+ MMA можно тестировать в одноразовом headless-режиме без интерактивного REPL — это удобно для проверки фич и регрессий из терминала или другим агентом:
270
+
271
+ ```bash
272
+ # Одноразовый прогон: без AGENTS.md, выход сразу после ответа, песочница
273
+ bun run mma "<промпт>" --no-agents-md --exit-on-complete -d <путь-к-песочнице>
274
+ ```
275
+
276
+ - **Песочница** — всегда внутри `_testing/` в корне проекта (например `_testing/<case-name>/`), никогда в корне или в `src/`. Папка `_testing/` добавлена в `.gitignore`.
277
+ - **`--no-agents-md`** — не грузить AGENTS.md проекта в системный промпт (чистая среда).
278
+ - **`--exit-on-complete`** — выйти сразу после первого финального ответа; интерактивные тулзы (`question`/`approve`) не блокируют stdin, а возвращают ошибку.
279
+ - **`-d <dir>`** — рабочая директория агента (туда он создаёт файлы).
280
+ - Каждый прогон сохраняется в сессию `~/.mma/sessions/<id>/` — можно посмотреть через `bun run mma session list` / `session show <id>`.
281
+
282
+ ---
283
+
284
+ ## Принципы дизайна
285
+
286
+ 1. **Ни одного файла >300 строк** — разделяй при приближении к лимиту
287
+ 2. **Маленькие файлы, одна ответственность** — каждый файл делает одно дело
288
+ 3. **KISS state machine** — никаких god-объектов, никакой вложенности if-else в цикле агента
289
+ 4. **Изоляция ошибок плагинов** — падение одного плагина не останавливает другие
290
+ 5. **Безопасность путей** — все файловые инструменты проверяют `targetPath.startsWith(baseDir)`
291
+ 6. **TDD** — сначала тест, потом реализация, потом коммит
292
+ 7. **Никаких захардкоженных строк** — весь текст через `t()` i18n
293
+ 8. **Никакого keyword matching** — LLM решает когда планировать, не эвристики
294
+ 9. **Без TUI** — минимальный CLI + REPL с markdown→ANSI
295
+
296
+ ---
297
+
298
+ ## Лицензия
299
+
300
+ MIT
301
+
302
+ ---
303
+
304
+ ## Благодарности
305
+
306
+ - [Qwen3.5-9B](https://qwen.readthedocs.io/) — основная целевая модель
307
+ - [LM Studio](https://lmstudio.ai/) — рекомендуемый локальный инференс-сервер
308
+ - [llama.cpp](https://github.com/ggerganov/llama.cpp) — инференс-движок
309
+
310
+ ---
311
+
312
+ **Сделано для локальных моделей. Работает везде.**
package/bin/mma.mjs CHANGED
@@ -1,8 +1,8 @@
1
- #!/usr/bin/env node
2
- import { fileURLToPath, pathToFileURL } from "node:url";
3
- import { dirname, join } from "node:path";
4
-
5
- const __dirname = dirname(fileURLToPath(import.meta.url));
6
- const main = join(__dirname, "..", "dist", "main.js");
7
-
8
- await import(pathToFileURL(main).href);
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath, pathToFileURL } from "node:url";
3
+ import { dirname, join } from "node:path";
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const main = join(__dirname, "..", "dist", "main.js");
7
+
8
+ await import(pathToFileURL(main).href);
package/dist/main.js CHANGED
@@ -2125,9 +2125,9 @@ var init_defaults = __esm(() => {
2125
2125
  offloadKvCacheToGpu: true
2126
2126
  },
2127
2127
  retry: {
2128
- maxRetries: 3,
2129
- baseDelay: 1000,
2130
- maxDelay: 30000
2128
+ maxRetries: 5,
2129
+ baseDelay: 5000,
2130
+ maxDelay: 120000
2131
2131
  },
2132
2132
  maxToolIterations: 1000,
2133
2133
  stuckThreshold: 8,
@@ -4884,7 +4884,8 @@ function runCommand(command, options = {}) {
4884
4884
  cwd,
4885
4885
  callId,
4886
4886
  timeoutMs = 120000,
4887
- maxBuffer = 10 * 1024 * 1024
4887
+ maxBuffer = 10 * 1024 * 1024,
4888
+ onOutput
4888
4889
  } = options;
4889
4890
  return new Promise((resolve) => {
4890
4891
  let timedOut = false;
@@ -4903,6 +4904,7 @@ function runCommand(command, options = {}) {
4903
4904
  const decodeChunk = (chunk) => decoder ? decoder.decode(chunk, { stream: true }) : chunk.toString("utf-8");
4904
4905
  child.stdout?.on("data", (chunk) => {
4905
4906
  const text = decodeChunk(chunk);
4907
+ onOutput?.(text);
4906
4908
  const next = stdoutRef.value.length + text.length;
4907
4909
  if (next > maxBuffer) {
4908
4910
  stdoutRef.value = stdoutRef.value.slice(stdoutRef.value.length + text.length - maxBuffer) + text;
@@ -4912,6 +4914,7 @@ function runCommand(command, options = {}) {
4912
4914
  });
4913
4915
  child.stderr?.on("data", (chunk) => {
4914
4916
  const text = decodeChunk(chunk);
4917
+ onOutput?.(text);
4915
4918
  const next = stderrRef.value.length + text.length;
4916
4919
  if (next > maxBuffer) {
4917
4920
  stderrRef.value = stderrRef.value.slice(stderrRef.value.length + text.length - maxBuffer) + text;
@@ -4945,7 +4948,13 @@ function runCommand(command, options = {}) {
4945
4948
  clearTimeout(timer);
4946
4949
  if (callId)
4947
4950
  activeChildren.delete(callId);
4948
- resolve({ stdout: stdoutRef.value, stderr: stderrRef.value, code, signal, timedOut });
4951
+ resolve({
4952
+ stdout: stdoutRef.value,
4953
+ stderr: stderrRef.value,
4954
+ code,
4955
+ signal,
4956
+ timedOut
4957
+ });
4949
4958
  });
4950
4959
  });
4951
4960
  }
@@ -4954,11 +4963,61 @@ var init_runner = __esm(() => {
4954
4963
  activeChildren = new Map;
4955
4964
  });
4956
4965
 
4966
+ // src/tools/cache.ts
4967
+ class ToolResultCache {
4968
+ cache = new Map;
4969
+ maxEntries;
4970
+ defaultTtlMs;
4971
+ constructor(maxEntries = 50, defaultTtlMs = 60000) {
4972
+ this.maxEntries = maxEntries;
4973
+ this.defaultTtlMs = defaultTtlMs;
4974
+ }
4975
+ get(key) {
4976
+ const entry = this.cache.get(key);
4977
+ if (!entry)
4978
+ return;
4979
+ if (Date.now() > entry.expiresAt) {
4980
+ this.cache.delete(key);
4981
+ return;
4982
+ }
4983
+ this.cache.delete(key);
4984
+ this.cache.set(key, entry);
4985
+ return entry.value;
4986
+ }
4987
+ set(key, value, ttlMs) {
4988
+ if (this.cache.size >= this.maxEntries) {
4989
+ const firstKey = this.cache.keys().next().value;
4990
+ if (firstKey !== undefined) {
4991
+ this.cache.delete(firstKey);
4992
+ }
4993
+ }
4994
+ this.cache.set(key, {
4995
+ value,
4996
+ expiresAt: Date.now() + (ttlMs ?? this.defaultTtlMs)
4997
+ });
4998
+ }
4999
+ has(key) {
5000
+ return this.get(key) !== undefined;
5001
+ }
5002
+ clear() {
5003
+ this.cache.clear();
5004
+ }
5005
+ get size() {
5006
+ return this.cache.size;
5007
+ }
5008
+ }
5009
+ function hashKey(toolName, args) {
5010
+ const payload = toolName + JSON.stringify(args);
5011
+ const hash = Bun.hash(payload).toString(16).slice(0, 16);
5012
+ return hash;
5013
+ }
5014
+
4957
5015
  // src/tools/executor.ts
4958
5016
  class ToolExecutor {
4959
5017
  registry;
4960
5018
  ctx;
4961
5019
  pluginManager;
5020
+ cache = new ToolResultCache(50, CACHE_TTL_MS);
4962
5021
  constructor(registry, ctx, pluginManager) {
4963
5022
  this.registry = registry;
4964
5023
  this.ctx = ctx;
@@ -4973,6 +5032,18 @@ class ToolExecutor {
4973
5032
  toolCallId: call.id
4974
5033
  };
4975
5034
  }
5035
+ if (CACHEABLE_TOOLS.has(call.name)) {
5036
+ const key = hashKey(call.name, call.arguments);
5037
+ const cached = this.cache.get(key);
5038
+ if (cached !== undefined) {
5039
+ this.ctx.logger.debug(`Tool ${call.name}: CACHE HIT`);
5040
+ return {
5041
+ success: true,
5042
+ output: cached,
5043
+ toolCallId: call.id
5044
+ };
5045
+ }
5046
+ }
4976
5047
  for (const plugin of this.pluginManager.getAllPlugins()) {
4977
5048
  if (plugin.onBeforeTool) {
4978
5049
  try {
@@ -5043,6 +5114,11 @@ class ToolExecutor {
5043
5114
  }
5044
5115
  }
5045
5116
  }
5117
+ if (result.success && CACHEABLE_TOOLS.has(call.name)) {
5118
+ const key = hashKey(call.name, call.arguments);
5119
+ this.cache.set(key, result.output);
5120
+ this.ctx.logger.debug(`Tool ${call.name}: CACHED`);
5121
+ }
5046
5122
  this.ctx.logger.debug(`Tool ${call.name}: ${result.success ? "OK" : "FAIL"}`);
5047
5123
  return result;
5048
5124
  }
@@ -5052,6 +5128,9 @@ class ToolExecutor {
5052
5128
  getRegistry() {
5053
5129
  return this.registry;
5054
5130
  }
5131
+ getContext() {
5132
+ return this.ctx;
5133
+ }
5055
5134
  setScope(scope) {
5056
5135
  this.ctx.scope = scope;
5057
5136
  }
@@ -5059,10 +5138,11 @@ class ToolExecutor {
5059
5138
  this.ctx.llmProvider = provider;
5060
5139
  }
5061
5140
  }
5062
- var TOOL_EXECUTION_TIMEOUT_MS = 60000;
5141
+ var TOOL_EXECUTION_TIMEOUT_MS = 60000, CACHEABLE_TOOLS, CACHE_TTL_MS = 60000;
5063
5142
  var init_executor = __esm(() => {
5064
5143
  init_runner();
5065
5144
  init_i18n();
5145
+ CACHEABLE_TOOLS = new Set(["web_fetch"]);
5066
5146
  });
5067
5147
 
5068
5148
  // src/modules/security/path-validator.ts
@@ -5593,7 +5673,7 @@ function safeResolvePath(baseDir, userPath) {
5593
5673
  var init_path_utils = () => {};
5594
5674
 
5595
5675
  // src/tools/preview.ts
5596
- var MAX_PREVIEW_LINES = 15;
5676
+ var MAX_PREVIEW_LINES = 100;
5597
5677
 
5598
5678
  // src/tools/read-file.ts
5599
5679
  import { readFileSync as readFileSync5, existsSync as existsSync9 } from "fs";
@@ -5664,8 +5744,13 @@ var init_read_file = __esm(() => {
5664
5744
  remaining: String(total - end),
5665
5745
  next: String(end + 1)
5666
5746
  }) : "";
5667
- return { success: true, output: `${header}
5668
- ${selected}${truncated}` };
5747
+ const display = truncated ? `${header}${truncated}` : header;
5748
+ return {
5749
+ success: true,
5750
+ output: `${header}
5751
+ ${selected}${truncated}`,
5752
+ display
5753
+ };
5669
5754
  }
5670
5755
  };
5671
5756
  });
@@ -6952,8 +7037,14 @@ var init_bash = __esm(() => {
6952
7037
  type: "object",
6953
7038
  properties: {
6954
7039
  command: { type: "string", description: "Shell command to execute" },
6955
- workdir: { type: "string", description: "Working directory (default: baseDir)" },
6956
- background: { type: "boolean", description: "Start the command in the background and return immediately with a process id (default: auto-detect long-running commands)" }
7040
+ workdir: {
7041
+ type: "string",
7042
+ description: "Working directory (default: baseDir)"
7043
+ },
7044
+ background: {
7045
+ type: "boolean",
7046
+ description: "Start the command in the background and return immediately with a process id (default: auto-detect long-running commands)"
7047
+ }
6957
7048
  },
6958
7049
  required: ["command"]
6959
7050
  },
@@ -7000,7 +7091,8 @@ ${t("proc.manage_hint", {
7000
7091
  const res = await runCommand(command, {
7001
7092
  cwd: workdir,
7002
7093
  callId: ctx.activeCallId,
7003
- timeoutMs: BASH_TIMEOUT_MS
7094
+ timeoutMs: BASH_TIMEOUT_MS,
7095
+ onOutput: ctx.onMeta
7004
7096
  });
7005
7097
  const parts = [res.stdout.trimEnd(), res.stderr.trimEnd()].filter(Boolean);
7006
7098
  let output = parts.join(`
@@ -9060,6 +9152,16 @@ var init_store = __esm(() => {
9060
9152
 
9061
9153
  // src/core/agent.ts
9062
9154
  import { join as join10 } from "path";
9155
+ function canRunInParallel(calls) {
9156
+ if (calls.length <= 1)
9157
+ return false;
9158
+ for (const call of calls) {
9159
+ if (SIDE_EFFECT_TOOLS.has(call.name) || WRITE_TOOLS.has(call.name)) {
9160
+ return false;
9161
+ }
9162
+ }
9163
+ return true;
9164
+ }
9063
9165
 
9064
9166
  class Agent {
9065
9167
  deps;
@@ -9322,7 +9424,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
9322
9424
  });
9323
9425
  const summaries = [];
9324
9426
  let anyToolFailed = false;
9325
- for (const call of toolCalls) {
9427
+ const executeToolCall = async (call) => {
9326
9428
  this.setScope();
9327
9429
  const startTime = Date.now();
9328
9430
  pluginManager.runOnToolCall({
@@ -9333,20 +9435,38 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
9333
9435
  onTool?.({ type: "start", tool: call.name, args: call.arguments });
9334
9436
  slog.logToolCall(call, iteration);
9335
9437
  const tokensBeforeTool = contextManager.getEstimatedTokens();
9438
+ const toolCtx = this.deps.toolExecutor?.getContext?.();
9439
+ const prevOnMeta = toolCtx?.onMeta;
9440
+ if (toolCtx) {
9441
+ toolCtx.onMeta = onMeta;
9442
+ }
9336
9443
  const result = await toolExecutor.execute(call, this.abortController?.signal);
9444
+ if (toolCtx) {
9445
+ toolCtx.onMeta = prevOnMeta;
9446
+ }
9337
9447
  const duration = Date.now() - startTime;
9448
+ pluginManager.runOnToolEnd({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments }, result, duration);
9449
+ return { call, result, duration, tokensBefore: tokensBeforeTool };
9450
+ };
9451
+ const processToolResult = (entry) => {
9452
+ const {
9453
+ call,
9454
+ result,
9455
+ duration,
9456
+ tokensBefore: tokensBeforeTool
9457
+ } = entry;
9338
9458
  if (!result.success)
9339
9459
  anyToolFailed = true;
9340
- pluginManager.runOnToolEnd({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments }, result, duration);
9341
9460
  if (result.display) {
9342
9461
  onMeta?.(`
9343
9462
  ` + result.display + `
9344
9463
  `);
9345
- }
9346
- const metaOut = pluginManager.runOnMeta({ iteration, logger }, result.output);
9347
- onMeta?.(`
9464
+ } else {
9465
+ const metaOut = pluginManager.runOnMeta({ iteration, logger }, result.output);
9466
+ onMeta?.(`
9348
9467
  ` + pc.dim(metaOut) + `
9349
9468
  `);
9469
+ }
9350
9470
  if (result.diff) {
9351
9471
  onMeta?.(`
9352
9472
  ` + result.diff + `
@@ -9380,6 +9500,17 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
9380
9500
  contextManager.compact();
9381
9501
  logger.debug("Context compacted after tool result");
9382
9502
  }
9503
+ };
9504
+ if (canRunInParallel(toolCalls)) {
9505
+ const entries = await Promise.all(toolCalls.map(executeToolCall));
9506
+ for (const entry of entries) {
9507
+ processToolResult(entry);
9508
+ }
9509
+ } else {
9510
+ for (const call of toolCalls) {
9511
+ const entry = await executeToolCall(call);
9512
+ processToolResult(entry);
9513
+ }
9383
9514
  }
9384
9515
  if (anyToolFailed) {
9385
9516
  consecutiveToolFailures++;
@@ -9590,7 +9721,7 @@ ${taskReminder}</system-summary>`
9590
9721
  });
9591
9722
  }
9592
9723
  }
9593
- var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000;
9724
+ var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000, WRITE_TOOLS, SIDE_EFFECT_TOOLS;
9594
9725
  var init_agent = __esm(() => {
9595
9726
  init_i18n();
9596
9727
  init_colors();
@@ -9598,6 +9729,14 @@ var init_agent = __esm(() => {
9598
9729
  init_processes();
9599
9730
  init_agent_moe();
9600
9731
  init_store();
9732
+ WRITE_TOOLS = new Set([
9733
+ "write_file",
9734
+ "edit_file",
9735
+ "delete_file",
9736
+ "move_file",
9737
+ "create_dir"
9738
+ ]);
9739
+ SIDE_EFFECT_TOOLS = new Set(["bash", "subagent", "pipeline_run"]);
9601
9740
  });
9602
9741
 
9603
9742
  // src/modules/context/manager.ts
@@ -15303,6 +15442,7 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
15303
15442
  lines.push(``, `Windows environment — use Windows-compatible commands:`, `- Use "dir" instead of "ls". Use "dir /b" for bare listing.`, `- Use "type" or "Get-Content" instead of "cat".`, `- Use "cd" instead of "pwd". Use "echo %cd%" to print working directory.`, `- Use "copy" instead of "cp", "move" instead of "mv", "del" instead of "rm".`, `- Do not use "mkdir -p" — Windows mkdir creates intermediate dirs by default. Use the create_dir tool instead.`, `- Use forward slashes (/) or escaped backslashes (\\\\) in file paths.`, `- Your working directory is: ${baseDir}`);
15304
15443
  }
15305
15444
  lines.push(``, `Bash tool rules:`, `- Use the "workdir" parameter to run commands in a specific directory. Prefer workdir over "cd dir && cmd" chaining.`, `- Run one command per tool call. Split multi-step shell operations into separate bash calls.`, `- Background processes (dev servers, watchers, long-running npm install): use the "background: true" parameter or the tool will auto-detect and background them. Check output with process_log.`);
15445
+ lines.push(``, `Parallel tool calls:`, `- When you need to read multiple files, call read_file for ALL of them in a SINGLE response (one tool_calls array with multiple read_file calls). They will execute in parallel — much faster than reading one at a time.`, `- Example: to read 3 files, return one assistant message with 3 read_file tool_calls. Do NOT read them one by one in separate iterations.`, `- This works for: read_file, glob, grep, file_info, list_dir — any read-only operations.`, `- Do NOT parallelize write operations (write_file, edit_file, delete_file, bash) — these run sequentially.`);
15306
15446
  lines.push(``, `=== DEVELOPMENT RULES — follow these strictly ===`, ``, `1. DEPENDENCIES FIRST: Before writing any source code, ALWAYS install project dependencies (e.g., "npm install", "pip install -r requirements.txt", "cargo build", "go mod tidy"). Verify the package manager's lock file or dependency directory exists. Never write code that imports/uses packages that aren't installed yet.`, `2. TOOLKIT/FWK FIRST: If the task specifies a framework or UI library, initialize and configure it BEFORE writing application code. Run its project init command first, then add components/modules. Never write your own version of what the framework already provides.`, `3. ONE STEP AT A TIME: Follow the plan sequentially. Complete step N before starting step N+1. When a step is done: verify the deliverables exist and have real content (not empty), then call "plan update step=N status=done". Do not redo completed work.`, `4. VERIFY YOUR WORK: After creating/modifying files, verify they exist on disk. After installing dependencies, verify the package manager completed successfully. After any command, check its output for errors. Don't assume operations succeeded.`, `5. NO PREMATURE WORK: Do not create files for future steps. Do not add imports/references to packages or modules that haven't been installed yet. Do not reference files or components that don't exist yet. Build incrementally — one layer at a time.`, `6. WHEN STUCK: If a command fails 2+ times, STOP and try a different approach. Write files directly instead of using commands. Ask the user for help. Never repeat the same failing command more than twice.`, ``, `=== PLAN QUALITY RULES — your plan MUST follow these ===`, ``, `- Each step must describe CONCRETE deliverables: exact filenames with paths, exact packages to install, exact CLI commands to run. Avoid vague steps — be specific.`, `- A step like "Настройка проекта" or "Setup the project" is too vague — describe what exactly needs to be configured or set up.`, `- A step like "Создать src/components/Header.tsx с навигацией и логотипом, добавить в src/App.tsx импорт <Header />" is GOOD.`, `- Include file extensions (.tsx, .css, .json) and directory paths. Every step must mention at least one file or command.`, `- The plan must cover EVERYTHING needed: init → deps → framework setup → code → verification.`, `- Number of steps: 5-8 for a typical task. Too few means you're being vague. Too many means you're over-splitting.`);
15307
15447
  if (config.autoPlan) {
15308
15448
  lines.push(``, `Plan rule (MANDATORY): For ANY task that requires creating files, installing packages, or multiple actions — you MUST create a plan using the "plan" tool BEFORE starting work. Each step must describe a concrete deliverable (specific files to create, packages to install, commands to run). Do not combine unrelated work into one step.`);
@@ -15594,7 +15734,7 @@ function ansiRegex({ onlyFirst = false } = {}) {
15594
15734
  return new RegExp(pattern, onlyFirst ? undefined : "g");
15595
15735
  }
15596
15736
 
15597
- // node_modules/strip-ansi/index.js
15737
+ // node_modules/string-width/node_modules/strip-ansi/index.js
15598
15738
  function stripAnsi(string) {
15599
15739
  if (typeof string !== "string") {
15600
15740
  throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
@@ -15693,135 +15833,65 @@ var init_get_east_asian_width = __esm(() => {
15693
15833
  init_lookup();
15694
15834
  });
15695
15835
 
15836
+ // node_modules/emoji-regex/index.js
15837
+ var require_emoji_regex = __commonJS((exports, module) => {
15838
+ module.exports = () => {
15839
+ return /[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26D3\uFE0F?(?:\u200D\uD83D\uDCA5)?|\u26F9(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF43\uDF45-\uDF4A\uDF4C-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDF44(?:\u200D\uD83D\uDFEB)?|\uDF4B(?:\u200D\uD83D\uDFE9)?|\uDFC3(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E-\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4\uDEB5](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE41\uDE43\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED8\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC08(?:\u200D\u2B1B)?|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC26(?:\u200D(?:\u2B1B|\uD83D\uDD25))?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFE])))?))?|\uDD75(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?|\uDE42(?:\u200D[\u2194\u2195]\uFE0F?)?|\uDEB6(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3C-\uDD3E\uDDB8\uDDB9\uDDCD\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE8A\uDE8E-\uDEC2\uDEC6\uDEC8\uDECD-\uDEDC\uDEDF-\uDEEA\uDEEF]|\uDDCE(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1|\uDDD1\u200D\uD83E\uDDD2(?:\u200D\uD83E\uDDD2)?|\uDDD2(?:\u200D\uD83E\uDDD2)?))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)/g;
15840
+ };
15841
+ });
15842
+
15696
15843
  // node_modules/string-width/index.js
15697
- function isDoubleWidthNonRgiEmojiSequence(segment) {
15698
- if (segment.length > 50) {
15699
- return false;
15700
- }
15701
- if (unqualifiedKeycapRegex.test(segment)) {
15702
- return true;
15703
- }
15704
- if (segment.includes("‍")) {
15705
- const pictographics = segment.match(extendedPictographicRegex);
15706
- return pictographics !== null && pictographics.length >= 2;
15707
- }
15708
- return false;
15709
- }
15710
- function baseVisible(segment) {
15711
- return segment.replace(leadingNonPrintingRegex, "");
15712
- }
15713
- function isZeroWidthCluster(segment) {
15714
- return zeroWidthClusterRegex.test(segment);
15715
- }
15716
- function isHangulLeadingJamo(codePoint) {
15717
- return codePoint >= 4352 && codePoint <= 4447 || codePoint >= 43360 && codePoint <= 43388;
15718
- }
15719
- function isHangulVowelJamo(codePoint) {
15720
- return codePoint >= 4448 && codePoint <= 4519 || codePoint >= 55216 && codePoint <= 55238;
15721
- }
15722
- function isHangulTrailingJamo(codePoint) {
15723
- return codePoint >= 4520 && codePoint <= 4607 || codePoint >= 55243 && codePoint <= 55291;
15724
- }
15725
- function isHangulJamo(codePoint) {
15726
- return isHangulLeadingJamo(codePoint) || isHangulVowelJamo(codePoint) || isHangulTrailingJamo(codePoint);
15727
- }
15728
- function hangulClusterWidth(visibleSegment, eastAsianWidthOptions) {
15729
- const codePoints = [];
15730
- for (const character of visibleSegment) {
15731
- if (zeroWidthClusterRegex.test(character)) {
15732
- continue;
15733
- }
15734
- codePoints.push(character.codePointAt(0));
15735
- }
15736
- if (codePoints.length === 0) {
15737
- return;
15738
- }
15739
- let width = 0;
15740
- for (let index = 0;index < codePoints.length; index++) {
15741
- const codePoint = codePoints[index];
15742
- if (!isHangulJamo(codePoint)) {
15743
- if (width === 0) {
15744
- return;
15745
- }
15746
- for (let remaining = index;remaining < codePoints.length; remaining++) {
15747
- width += eastAsianWidth(codePoints[remaining], eastAsianWidthOptions);
15748
- }
15749
- return width;
15750
- }
15751
- if (isHangulLeadingJamo(codePoint) && isHangulVowelJamo(codePoints[index + 1])) {
15752
- width += 2;
15753
- index += isHangulTrailingJamo(codePoints[index + 2]) ? 2 : 1;
15754
- continue;
15755
- }
15756
- width += eastAsianWidth(codePoint, eastAsianWidthOptions);
15757
- }
15758
- return width;
15759
- }
15760
- function trailingWidth(visibleSegment, eastAsianWidthOptions) {
15761
- let extra = 0;
15762
- let first = true;
15763
- for (const character of visibleSegment) {
15764
- if (first) {
15765
- first = false;
15766
- continue;
15767
- }
15768
- if (spacingMarkRegex.test(character) || character >= "＀" && character <= "￯") {
15769
- extra += eastAsianWidth(character.codePointAt(0), eastAsianWidthOptions);
15770
- }
15771
- }
15772
- return extra;
15773
- }
15774
- function stringWidth(input, options = {}) {
15775
- if (typeof input !== "string" || input.length === 0) {
15844
+ function stringWidth(string, options = {}) {
15845
+ if (typeof string !== "string" || string.length === 0) {
15776
15846
  return 0;
15777
15847
  }
15778
15848
  const {
15779
15849
  ambiguousIsNarrow = true,
15780
15850
  countAnsiEscapeCodes = false
15781
15851
  } = options;
15782
- let string = input;
15783
- if (!countAnsiEscapeCodes && (string.includes("\x1B") || string.includes("›"))) {
15852
+ if (!countAnsiEscapeCodes) {
15784
15853
  string = stripAnsi(string);
15785
15854
  }
15786
15855
  if (string.length === 0) {
15787
15856
  return 0;
15788
15857
  }
15789
- if (/^[\u0020-\u007E]*$/.test(string)) {
15790
- return string.length;
15791
- }
15792
15858
  let width = 0;
15793
15859
  const eastAsianWidthOptions = { ambiguousAsWide: !ambiguousIsNarrow };
15794
- for (const { segment } of segmenter.segment(string)) {
15795
- if (isZeroWidthCluster(segment)) {
15860
+ for (const { segment: character } of segmenter.segment(string)) {
15861
+ const codePoint = character.codePointAt(0);
15862
+ if (codePoint <= 31 || codePoint >= 127 && codePoint <= 159) {
15796
15863
  continue;
15797
15864
  }
15798
- if (rgiEmojiRegex.test(segment) || isDoubleWidthNonRgiEmojiSequence(segment)) {
15799
- width += 2;
15865
+ if (codePoint >= 8203 && codePoint <= 8207 || codePoint === 65279) {
15800
15866
  continue;
15801
15867
  }
15802
- const visibleSegment = baseVisible(segment);
15803
- const hangulWidth = hangulClusterWidth(visibleSegment, eastAsianWidthOptions);
15804
- if (hangulWidth !== undefined) {
15805
- width += hangulWidth;
15868
+ if (codePoint >= 768 && codePoint <= 879 || codePoint >= 6832 && codePoint <= 6911 || codePoint >= 7616 && codePoint <= 7679 || codePoint >= 8400 && codePoint <= 8447 || codePoint >= 65056 && codePoint <= 65071) {
15869
+ continue;
15870
+ }
15871
+ if (codePoint >= 55296 && codePoint <= 57343) {
15872
+ continue;
15873
+ }
15874
+ if (codePoint >= 65024 && codePoint <= 65039) {
15875
+ continue;
15876
+ }
15877
+ if (defaultIgnorableCodePointRegex.test(character)) {
15878
+ continue;
15879
+ }
15880
+ if (import_emoji_regex.default().test(character)) {
15881
+ width += 2;
15806
15882
  continue;
15807
15883
  }
15808
- const codePoint = visibleSegment.codePointAt(0);
15809
15884
  width += eastAsianWidth(codePoint, eastAsianWidthOptions);
15810
- width += trailingWidth(visibleSegment, eastAsianWidthOptions);
15811
15885
  }
15812
15886
  return width;
15813
15887
  }
15814
- var segmenter, zeroWidthClusterRegex, leadingNonPrintingRegex, spacingMarkRegex, rgiEmojiRegex, unqualifiedKeycapRegex, extendedPictographicRegex;
15888
+ var import_emoji_regex, segmenter, defaultIgnorableCodePointRegex;
15815
15889
  var init_string_width = __esm(() => {
15816
15890
  init_strip_ansi();
15817
15891
  init_get_east_asian_width();
15892
+ import_emoji_regex = __toESM(require_emoji_regex(), 1);
15818
15893
  segmenter = new Intl.Segmenter;
15819
- zeroWidthClusterRegex = /^(?:\p{Default_Ignorable_Code_Point}|\p{Control}|\p{Format}|\p{Nonspacing_Mark}|\p{Enclosing_Mark}|\p{Surrogate})+$/v;
15820
- leadingNonPrintingRegex = /^[\p{Default_Ignorable_Code_Point}\p{Control}\p{Format}\p{Nonspacing_Mark}\p{Enclosing_Mark}\p{Surrogate}]+/v;
15821
- spacingMarkRegex = /\p{Spacing_Mark}/v;
15822
- rgiEmojiRegex = /^\p{RGI_Emoji}$/v;
15823
- unqualifiedKeycapRegex = /^[\d#*]\u20E3$/;
15824
- extendedPictographicRegex = /\p{Extended_Pictographic}/gu;
15894
+ defaultIgnorableCodePointRegex = /^\p{Default_Ignorable_Code_Point}$/u;
15825
15895
  });
15826
15896
 
15827
15897
  // src/ui/table.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "micro-models-agent",
3
- "version": "0.24.2",
3
+ "version": "0.24.4",
4
4
  "description": "Micro Models Agent (MMA) — LLM agent harness for small models (Qwen3.5-9B, 32K-64K context)",
5
5
  "type": "module",
6
6
  "bin": {