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.
- package/README.md +312 -312
- package/bin/mma.mjs +8 -8
- package/dist/main.js +190 -120
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,312 +1,312 @@
|
|
|
1
|
-
# MMA — Micro Models Agent v2
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/micro-models-agent)
|
|
4
|
-
[](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
|
+
[](https://www.npmjs.com/package/micro-models-agent)
|
|
4
|
+
[](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:
|
|
2129
|
-
baseDelay:
|
|
2130
|
-
maxDelay:
|
|
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({
|
|
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 =
|
|
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
|
-
|
|
5668
|
-
|
|
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: {
|
|
6956
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9347
|
-
|
|
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
|
|
15698
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
15799
|
-
width += 2;
|
|
15865
|
+
if (codePoint >= 8203 && codePoint <= 8207 || codePoint === 65279) {
|
|
15800
15866
|
continue;
|
|
15801
15867
|
}
|
|
15802
|
-
|
|
15803
|
-
|
|
15804
|
-
|
|
15805
|
-
|
|
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
|
|
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
|
-
|
|
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
|