micro-models-agent 0.56.0 → 0.56.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +452 -0
  2. package/dist/main.js +225 -100
  3. package/package.json +3 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,452 @@
1
+ # Changelog
2
+
3
+ All notable changes to Micro Models Agent (MMA) will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.56.1] - 2026-08-27
8
+
9
+ ### Added
10
+ - **CLI**: `mma changelog` command — show latest changelog entry or diff between versions (`--from <version>`)
11
+ - **Updater**: show changelog after auto-update install (`updater.installed` message includes new version's changelog)
12
+ - **Docs**: `CHANGELOG.md` included in npm package (visible via `npm info micro-models-agent`)
13
+ - **Docs**: rule to maintain changelog in `AGENTS.md`
14
+
15
+ ## [0.56.0] - 2026-08-27
16
+
17
+ ### Added
18
+ - **MoE**: live re-plan loop in `runWithMoE` — orchestrator re-plans on structural failures
19
+ - **MoE**: scope expansion protocol (`scope_request`) for cross-expert file access
20
+ - **MoE**: Esc/interrupt support in the MoE execution path
21
+ - **MoE**: `input_from` cross-expert data flow for subtask dependencies
22
+ - **LSP**: Angular language server support (`@angular/language-server` for `.html` in Angular workspaces)
23
+ - **Tools**: syntax pre-validation and auto-fix for `write_file`/`edit_file`
24
+
25
+ ### Fixed
26
+ - **Security**: SSRF via IPv6-mapped IPv4 and raw numeric hostnames
27
+ - **Security**: command-validator blacklist bypasses (shell expansions, encoded chars)
28
+ - **Security**: audit webhook retry queue drained forever (infinite loop + duplicate notifications)
29
+ - **Core**: dangling `tool_calls` on interrupt — synthesized tool messages keep assistant/tool pairing
30
+ - **Core**: live reasoning policy + `set_thinking` override integration
31
+ - **Core**: deep-clone config defaults to prevent cross-session mutation
32
+ - **Config**: preserve `provider.fallback` on hot-swap
33
+ - **Tools**: path/scope escape holes in filesystem and read tools closed
34
+ - **Tools**: clear timeout timer & abort listener after race settles; log discarded losers
35
+ - **MCP**: stdio timer leaks + null deref after disconnect; added initialize handshake
36
+ - **LSP**: spawn servers detached on POSIX so `killTree` reaches the whole chain
37
+ - **Browser**: bridge leaked Chromium on SIGTERM and parent death
38
+ - **UI**: diff LCS memory cap; one bad encrypted line no longer kills session history
39
+ - **CLI**: `/config migrate` was unreachable
40
+ - **MoE**: locale-independent artifact path + no retry on user abort
41
+
42
+ ## [0.55.1] - 2026-08-25
43
+
44
+ ### Fixed
45
+ - **LLM**: NDJSON-tolerant stream parser for Ollama backends
46
+ - **LLM**: mid-stream provider errors surfaced (Ollama generation failures)
47
+ - **LLM**: `delta.reasoning` alias for thinking models
48
+ - **LLM**: wire-level debug logging for diagnostics
49
+
50
+ ## [0.55.0] - 2026-08-25
51
+
52
+ ### Added
53
+ - **Certification**: targeted re-run (`--scenarios <ids>`) with result merging
54
+ - **Certification**: per-rep timeout (`--timeout <ms>` + per-scenario `timeoutMs`)
55
+ - **Certification**: audit-gate bypass fix for `--exit-on-complete`
56
+
57
+ ### Fixed
58
+ - **Certification**: stale-label wording (removed impossible user instruction)
59
+ - **Dependencies**: typescript pinned to ^5.9 (TS7 preview breaks `@types/node`)
60
+
61
+ ## [0.54.0] - 2026-08-24
62
+
63
+ ### Added
64
+ - **Certification**: global manifest shipped with package updates (`~/.mma/certifications.json`)
65
+ - **Certification**: bundled manifest + startup sync from package
66
+
67
+ ### Fixed
68
+ - **Certification**: marks only showed when running from repo root
69
+
70
+ ## [0.53.0] - 2026-08-24
71
+
72
+ ### Added
73
+ - **Certification**: qwen3.5-9b certified 20/20
74
+ - **Config**: `provider.maxCompletionTokens` option
75
+ - **Certification**: label text in `mma model list` and REPL `/model`
76
+
77
+ ### Fixed
78
+ - **Certification**: reasoning-heavy models truncated tool_call arguments at default 4096 cap
79
+ - **Certification**: scenario 3.5 prompt disambiguation
80
+
81
+ ## [0.52.0] - 2026-08-23
82
+
83
+ ### Added
84
+ - **Execution**: `FS_MUTATING_TOOLS` — plan auto-advance after bash/download/subagent/mcp/pipeline/browser tools
85
+ - **Config**: `provider.maxCompletionTokens` plumbing through ProviderManager → agent
86
+
87
+ ### Fixed
88
+ - **Execution**: small models create files via bash instead of `write_file`, stalling plans
89
+ - **Execution**: `maxCompletionTokens` was never read from config
90
+
91
+ ## [0.51.0] - 2026-08-23
92
+
93
+ ### Added
94
+ - **Plugin**: `HostBridge` interface for bidirectional agent communication
95
+ - **Plugin**: `onTurnEnd(ctx, TurnSummary)` hook — dispatched once per completed run
96
+ - **Plugin**: `web-ui` example plugin — browser-based chat with SSE streaming
97
+ - **CLI**: REPL bridge wiring for web-ui submit/interrupt
98
+
99
+ ### Fixed
100
+ - **REPL**: `/config migrate` unreachable
101
+
102
+ ## [0.50.3] - 2026-08-22
103
+
104
+ ### Fixed
105
+ - **UI**: REPL line editor scroll-proof rendering (absolute anchoring via DSR query)
106
+
107
+ ## [0.50.2] - 2026-08-22
108
+
109
+ ### Fixed
110
+ - **LLM**: idle stream timeout increased 60s → 180s (LM Studio buffering)
111
+ - **LLM**: stall diagnosis with dedicated message for SSE buffering
112
+ - **LLM**: truncation no longer masquerades as empty response
113
+ - **LLM**: recoverable LLM errors fed back into context instead of dying
114
+ - **Execution**: bash flailing detector (≥4 failed bash attempts in last 6)
115
+
116
+ ## [0.50.1] - 2026-08-21
117
+
118
+ ### Fixed
119
+ - **Security**: no longer silently overrides explicit user opt-out
120
+ - **Lint**: skips missing lint binary (was appending error to every write)
121
+ - **Stuck**: anti-spam for stuck-warnings (dedup via key, decay per-tool counters)
122
+ - **Plan**: unified progress counting (done+skipped as settled)
123
+ - **Plan**: bulk `steps[]` rewrite guarded on progressed plans
124
+ - **Plan**: archived plans answer read-only
125
+ - **Process**: usage hints for `process_kill`/`process_log` without id
126
+
127
+ ## [0.50.0] - 2026-08-21
128
+
129
+ ### Added
130
+ - **Provider**: health probe (`probeProviders()` + `mma provider check`)
131
+ - **Provider**: code-only capability routing (`ProviderManager.pickFor()`)
132
+ - **Provider**: per-entry isolation + priority fallback order
133
+ - **Pricing**: per-provider cost attribution + breakdown
134
+
135
+ ### Fixed
136
+ - **Provider**: transparent failover on 429/5xx/network errors
137
+
138
+ ## [0.49.0] - 2026-08-20
139
+
140
+ ### Fixed
141
+ - **REPL**: stale-plan leaks — plan state no longer persists across sessions
142
+ - **REPL**: plan checklist prints only on plan/todo tool-end events
143
+ - **Plan**: `PlanStore` is single source of truth for UI consumers
144
+
145
+ ## [0.48.3] - 2026-08-20
146
+
147
+ ### Fixed
148
+ - **Hallucination**: dotfile false-positive (`.prettierrc.json` extracted as `prettierrc.json`)
149
+
150
+ ## [0.48.2] - 2026-08-20
151
+
152
+ ### Fixed
153
+ - **REPL**: stale plan printed on first agent run of a session
154
+
155
+ ## [0.47.0] - 2026-08-19
156
+
157
+ ### Added
158
+ - **Session**: startup diagnostics logging (environment, LSP probe, baseline typecheck)
159
+ - **Session**: `logBaselineTypecheck()` for post-mortem visibility
160
+
161
+ ## [0.46.1] - 2026-08-19
162
+
163
+ ### Fixed
164
+ - **LLM**: connection-break hardening (stream-level retry, no-data timeout, `[DONE]` tracking)
165
+ - **LLM**: non-streaming timeout (was missing, could hang forever)
166
+ - **Config**: `retry.maxStreamRetries`, `retry.noDataTimeoutMs` options
167
+
168
+ ## [0.46.0] - 2026-08-19
169
+
170
+ ### Added
171
+ - **Provider**: multi-provider + hot-swap + per-message provenance
172
+ - **Provider**: `ProviderManager` with entry-based config
173
+ - **CLI**: `mma provider add/list/use`
174
+ - **REPL**: `/provider list/use`
175
+
176
+ ### Fixed
177
+ - **Path**: `safeResolvePath` POSIX bug (absolute paths re-resolved against baseDir)
178
+
179
+ ## [0.45.0] - 2026-08-18
180
+
181
+ ### Added
182
+ - **Provider**: provider module Phase 1 (registry, presets, `createProvider()`)
183
+ - **Provider**: `openrouter` preset
184
+ - **Setup**: wizard menu built from `BUILTIN_PROVIDERS`
185
+
186
+ ## [0.44.0] - 2026-08-18
187
+
188
+ ### Added
189
+ - **Plugins**: folder plugins (subdirectory with entry file)
190
+ - **Plugin**: `trace-server` split into folder plugin
191
+
192
+ ### Fixed
193
+ - **Plugin**: template-literal page inlining broke `\n` in JS strings
194
+
195
+ ## [0.43.2] - 2026-08-17
196
+
197
+ ### Fixed
198
+ - **Execution**: plan done-gate on known compile failures (typecheck gate)
199
+ - **Bootstrap**: live getters for `sessionId`/`sessionContext` after session switch
200
+ - **Edit**: `edit_file` "String not found" hint to `read_file` first
201
+
202
+ ## [0.43.1] - 2026-08-17
203
+
204
+ ### Fixed
205
+ - **Agent**: session-interrupt no longer renders tool results after "Session ended"
206
+ - **Executor**: `onAfterTool` plugins skipped when signal is aborted
207
+ - **Lint**: abortable syntax checks on Esc
208
+
209
+ ## [0.43.0] - 2026-08-17
210
+
211
+ ### Added
212
+ - **Plan**: `plan delete <id>` + `plan purge` actions
213
+ - **Plan**: `abort` honors `id` parameter
214
+ - **Plan**: `replan` renumbers ids for kept steps
215
+ - **Audit**: `resolveTestCommand` picks the project's real test runner
216
+
217
+ ### Fixed
218
+ - **Plan**: `switch` to already-active plan is a no-op
219
+ - **Plan**: final audit runs for a completed plan
220
+ - **Plan**: evidence-based plan nudge (replaces iteration counter)
221
+ - **Plan**: `checkPlanAlignment` scans path arguments only
222
+ - **Plan**: `isDepsStep` requires a package-manager verb
223
+ - **Plan**: auto-advance resolves nested files
224
+
225
+ ## [0.42.0] - 2026-08-16
226
+
227
+ ### Added
228
+ - **Execution**: automatic error web search (≥5 same-error repeats → web search → inject results)
229
+ - **Config**: `errorWebSearch` section
230
+
231
+ ### Fixed
232
+ - **Execution**: `onAfterTool` double-counted typecheck + failure errors
233
+
234
+ ## [0.41.1] - 2026-08-16
235
+
236
+ ### Fixed
237
+ - **LSP**: `typescript@5` pin + `--yes` for npx
238
+ - **LSP**: sequential probing (waves) to avoid npx cache lock contention
239
+ - **LSP**: stale config normalization
240
+ - **LSP**: Windows process leak in `shutdown()`
241
+ - **REPL**: non-blocking LSP banner
242
+ - **Bootstrap**: lazy startup health check
243
+
244
+ ## [0.41.0] - 2026-08-15
245
+
246
+ ### Added
247
+ - **REPL**: live plan checklist with progress bar
248
+ - **UI**: tool-to-step binding (`← step N` suffix in tool headers)
249
+ - **UI**: background command output preview
250
+
251
+ ## [0.40.1] - 2026-08-15
252
+
253
+ ### Fixed
254
+ - **LSP**: broken package names (`vscode-html-languageserver` → `vscode-langservers-extracted`)
255
+
256
+ ## [0.40.0] - 2026-08-15
257
+
258
+ ### Added
259
+ - **Tools**: tool-set narrowing (on-demand enable via `enable_tools`)
260
+ - **Config**: `tools.defaultTags` + `tools.enableOnDemand`
261
+ - **Tools**: `alwaysOn` flag for structural tools
262
+
263
+ ## [0.39.1] - 2026-08-14
264
+
265
+ ### Fixed
266
+ - **Plugins**: dedup by version, compatibility gate, source tracking
267
+ - **CLI**: `mma plugins list` command
268
+
269
+ ## [0.38.0] - 2026-08-14
270
+
271
+ ### Added
272
+ - **LSP**: `lsp_check` tool for on-demand diagnostics
273
+ - **Plan**: `plan create` guard (blocks when active plan has progress)
274
+
275
+ ### Fixed
276
+ - **Context**: compaction summary carries plan + read state
277
+ - **Browser**: bridge path resolution in bundled dist
278
+
279
+ ## [0.37.0] - 2026-08-13
280
+
281
+ ### Added
282
+ - **Plugins**: folder plugin support in `PluginLoader`
283
+
284
+ ### Fixed
285
+ - **Plugin**: template-literal page inlining broke `\n` in JS
286
+
287
+ ## [0.36.3] - 2026-08-13
288
+
289
+ ### Fixed
290
+ - **UI**: REPL paste fix (batch single-render, CRLF collapse)
291
+
292
+ ## [0.36.2] - 2026-08-13
293
+
294
+ ### Fixed
295
+ - **Updater**: silent logger, one-shot `process.exit()` killed background install, semver-aware check
296
+
297
+ ## [0.36.1] - 2026-08-13
298
+
299
+ ### Fixed
300
+ - **Audit**: nested-file resolution, non-zero test run blocks
301
+ - **Browser**: bridge diagnostics + one-shot restart
302
+ - **LSP**: per-server disable + initialize retry + CSS timeout
303
+ - **Bash**: npm exec hint
304
+
305
+ ## [0.36.0] - 2026-08-12
306
+
307
+ ### Added
308
+ - **Plan**: model-declared `kind: "create"|"delete"` for steps
309
+ - **Execution**: `FS_MUTATING_TOOLS` for plan auto-advance
310
+ - **Config**: `provider.maxCompletionTokens` option
311
+
312
+ ### Fixed
313
+ - **Context**: compaction preserves session `mission`
314
+ - **Context**: deleted files leave compaction `[Files:]` list
315
+ - **Audit**: final audit runs real `tsc --noEmit --skipLibCheck`
316
+ - **Bash**: repeated forbidden Windows commands hard-stopped after 2 failures
317
+
318
+ ## [0.35.5] - 2026-08-12
319
+
320
+ ### Fixed
321
+ - **Execution**: plan-alignment phantom paths (domains/extensions treated as files)
322
+ - **Execution**: hint/recovery dedup in `StuckDetector`
323
+
324
+ ## [0.35.3] - 2026-08-11
325
+
326
+ ### Fixed
327
+ - **LSP**: workspace root resolved from edited file's project
328
+ - **Audit**: skipped steps treated as terminal
329
+ - **Bash**: Windows hint keys on original command word
330
+ - **Tools**: boundedOutput tools never budget-truncated
331
+
332
+ ## [0.35.0] - 2026-08-11
333
+
334
+ ### Added
335
+ - **Tools**: `download_file` tool for binary file downloads
336
+
337
+ ## [0.34.0] - 2026-08-10
338
+
339
+ ### Added
340
+ - **Indexer**: project profile (`[Stack: ...]` summary from manifest)
341
+ - **Indexer**: dynamic map refresh inside sessions
342
+
343
+ ## [0.33.3] - 2026-08-10
344
+
345
+ ### Fixed
346
+ - **Audit**: nested-file resolution (basename + path-suffix match)
347
+ - **Plan**: `plan show` honors `id` parameter
348
+ - **Context**: compaction interval reset per user turn
349
+ - **Context**: `extractFacts` captures `edit_file` paths + Windows drive-letter paths
350
+ - **Execution**: stuck detector read-only loop detection
351
+ - **Read**: default limit 15 → 300 lines
352
+ - **LSP**: `spawn` on Windows (`.cmd` shim resolution)
353
+ - **Todo**: `todo add` tracks subtasks, `todo done` marks individual sub-task
354
+ - **Memory**: learns repeated per-tool failures (≥3×)
355
+ - **Context**: compaction preserves user's task
356
+
357
+ ## [0.33.2] - 2026-08-09
358
+
359
+ ### Fixed
360
+ - **UI**: REPL conversation layout (user/agent message dividers)
361
+ - **Tools**: `isFilePathLike` uses allow-list of file extensions
362
+
363
+ ## [0.33.1] - 2026-08-09
364
+
365
+ ### Fixed
366
+ - **UI**: REPL line editor redraw fix (no duplicated multi-line input)
367
+ - **Tools**: `web_fetch`/`web_browse` description clarifications
368
+
369
+ ## [0.33.0] - 2026-08-09
370
+
371
+ ### Added
372
+ - **RLM**: pass-by-reference sub-agent results (`ArtifactStore`)
373
+ - **RLM**: chunked parallel queries (`chunk_query` tool)
374
+ - **Config**: `subagent.resultMode`, `maxSummaryChars`, `artifactsDir`, `stableSystemPrompt`
375
+
376
+ ## [0.32.0] - 2026-08-08
377
+
378
+ ### Added
379
+ - **Browser**: driver abstraction (`PlaywrightDriver` + `BridgeDriver`)
380
+ - **Browser**: Node bridge for Bun compatibility
381
+ - **Config**: `browser.maxConsoleLineChars`
382
+
383
+ ## [0.31.0] - 2026-08-08
384
+
385
+ ### Added
386
+ - **Security**: session file encryption (AES-256-GCM)
387
+ - **Security**: audit notifications (file + webhook)
388
+ - **Security**: security policies (strict/balanced/permissive)
389
+ - **CLI**: `mma security` commands
390
+
391
+ ## [0.30.0] - 2026-08-07
392
+
393
+ ### Added
394
+ - **Execution**: final audit gate (runs `bun test` for verification steps)
395
+ - **Execution**: `detectTestResults()` auto-verification in bash
396
+
397
+ ### Fixed
398
+ - **Execution**: empty-CLI entry-point hint
399
+ - **Bash**: Windows anti-patterns documentation
400
+
401
+ ## [0.29.0] - 2026-08-07
402
+
403
+ ### Added
404
+ - **Bash**: smart UTF-8/OEM line decoding (fixes Cyrillic on Windows)
405
+ - **Bash**: behavior-based background detection
406
+ - **Bash**: stuck-detector bash awareness
407
+
408
+ ## [0.28.0] - 2026-08-06
409
+
410
+ ### Fixed
411
+ - **Agent**: double-Esc interrupt fix (Bun readline keypress collapsing)
412
+ - **Agent**: abortable in-flight LLM requests on interrupt
413
+ - **UI**: clipboard paste hint
414
+
415
+ ## [0.27.0] - 2026-08-06
416
+
417
+ ### Fixed
418
+ - **Security**: Windows hardening
419
+ - **Hallucination**: fixes
420
+
421
+ ## [0.26.0] - 2026-08-05
422
+
423
+ ### Added
424
+ - **UI**: opencode-style inline tool headers (`ui.toolStyle`)
425
+ - **UI**: model commentary next to tool calls (`ui.toolComments`)
426
+ - **UI**: diffs without background color
427
+
428
+ ## [0.25.0] - 2026-08-05
429
+
430
+ ### Added
431
+ - **LSP**: LSP module (typescript, CSS, HTML, JSON, Python, Rust, Go)
432
+ - **Execution**: audit gate language-agnostic
433
+ - **Bash**: Windows command hints
434
+
435
+ ## [0.24.0] - 2026-08-04
436
+
437
+ ### Added
438
+ - **Context**: quality formula upgrade (token load + compaction loss + error density + freshness)
439
+ - **Read**: display mode (show only header in REPL, full content to LLM)
440
+
441
+ ## [0.23.0] - 2026-08-04
442
+
443
+ ### Added
444
+ - **Certification**: model certification module (`mma model certify`)
445
+ - **CLI**: cert checkmarks in model lists
446
+
447
+ ## [0.22.0] - 2026-08-03
448
+
449
+ ### Added
450
+ - **Security**: `enabled` flag (disabled by default)
451
+ - **CLI**: init/first-run apply wizard security answers
452
+ - **Prompt**: system prompt & recovery messages cleanup
package/dist/main.js CHANGED
@@ -3126,8 +3126,12 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
3126
3126
  "updater.check_error": "[updater] update check failed: {error}",
3127
3127
  "updater.available": "[updater] Update available: {current} → {latest}. Run `npm install -g micro-models-agent` to upgrade.",
3128
3128
  "updater.installing": "[updater] Installing {latest} globally (current: {current})…",
3129
- "updater.installed": "[updater] Installed {latest}. Restart MMA to use it (was {current}).",
3129
+ "updater.installed": `[updater] Installed {latest}. Restart MMA to use it (was {current}).
3130
+ {changelog}`,
3130
3131
  "updater.install_failed": "[updater] Failed to install {latest}: {error}. Update manually with `npm install -g micro-models-agent`.",
3132
+ "cli.changelog_title": "Changelog (micro-models-agent@{version}):",
3133
+ "cli.changelog_not_found": "Changelog not found for this installation.",
3134
+ "cli.changelog_range": "Changes since {from}:",
3131
3135
  "tools.enable_no_tags": 'enable_tools requires at least one tag in the "tags" array.',
3132
3136
  "tools.enable_no_executor": "Tool executor is not available — cannot enumerate enabled tools.",
3133
3137
  "tools.enable_already_active": "Tool tags already active: {tags}.",
@@ -3889,8 +3893,12 @@ var init_ru = __esm(() => {
3889
3893
  "updater.check_error": "[updater] Ошибка проверки обновления: {error}",
3890
3894
  "updater.available": "[updater] Доступно обновление: {current} → {latest}. Выполните `npm install -g micro-models-agent` для обновления.",
3891
3895
  "updater.installing": "[updater] Установка {latest} глобально (текущая: {current})…",
3892
- "updater.installed": "[updater] Установлена {latest}. Перезапустите MMA для применения (была {current}).",
3896
+ "updater.installed": `[updater] Установлена {latest}. Перезапустите MMA для применения (была {current}).
3897
+ {changelog}`,
3893
3898
  "updater.install_failed": "[updater] Не удалось установить {latest}: {error}. Обновите вручную: `npm install -g micro-models-agent`.",
3899
+ "cli.changelog_title": "Список изменений (micro-models-agent@{version}):",
3900
+ "cli.changelog_not_found": "Changelog не найден для этой установки.",
3901
+ "cli.changelog_range": "Изменения с версии {from}:",
3894
3902
  "tools.enable_no_tags": 'enable_tools требует хотя бы один тег в массиве "tags".',
3895
3903
  "tools.enable_no_executor": "Исполнитель тулов недоступен — невозможно перечислить включённые тулы.",
3896
3904
  "tools.enable_already_active": "Теги тулов уже активны: {tags}.",
@@ -33408,8 +33416,8 @@ var init_scenarios = __esm(() => {
33408
33416
  });
33409
33417
 
33410
33418
  // src/modules/certification/loader.ts
33411
- import { existsSync as existsSync51, readdirSync as readdirSync17, readFileSync as readFileSync32 } from "fs";
33412
- import { join as join44 } from "path";
33419
+ import { existsSync as existsSync52, readdirSync as readdirSync17, readFileSync as readFileSync33 } from "fs";
33420
+ import { join as join45 } from "path";
33413
33421
  function validateScenario(s) {
33414
33422
  const errors2 = [];
33415
33423
  const isSkip = s.mode === "skip";
@@ -33461,12 +33469,12 @@ function loadScenarios(userDir) {
33461
33469
  else
33462
33470
  scenarios.push(s);
33463
33471
  }
33464
- if (userDir && existsSync51(userDir)) {
33472
+ if (userDir && existsSync52(userDir)) {
33465
33473
  for (const file of readdirSync17(userDir)) {
33466
33474
  if (!file.endsWith(".yaml") && !file.endsWith(".yml"))
33467
33475
  continue;
33468
33476
  try {
33469
- const raw = readFileSync32(join44(userDir, file), "utf-8");
33477
+ const raw = readFileSync33(join45(userDir, file), "utf-8");
33470
33478
  const data = $parse(raw);
33471
33479
  const parsed = normalizeScenario(data, file);
33472
33480
  const errs = validateScenario(parsed);
@@ -33529,8 +33537,8 @@ var init_loader3 = __esm(() => {
33529
33537
  });
33530
33538
 
33531
33539
  // src/modules/certification/fact-checker.ts
33532
- import { existsSync as existsSync52, readFileSync as readFileSync33, statSync as statSync9 } from "fs";
33533
- import { join as join45 } from "path";
33540
+ import { existsSync as existsSync53, readFileSync as readFileSync34, statSync as statSync9 } from "fs";
33541
+ import { join as join46 } from "path";
33534
33542
  function checkSandbox(sandboxDir, checks, exitCode, output) {
33535
33543
  const failures = [];
33536
33544
  for (const check of checks) {
@@ -33547,16 +33555,16 @@ function runCheck2(sandboxDir, check, exitCode, output) {
33547
33555
  case "outputContains":
33548
33556
  return output.includes(check.text);
33549
33557
  case "fileExists":
33550
- return isFile(join45(sandboxDir, check.path));
33558
+ return isFile(join46(sandboxDir, check.path));
33551
33559
  case "fileNotExists":
33552
- return !existsSync52(join45(sandboxDir, check.path));
33560
+ return !existsSync53(join46(sandboxDir, check.path));
33553
33561
  case "dirExists":
33554
- return isDir(join45(sandboxDir, check.path));
33562
+ return isDir(join46(sandboxDir, check.path));
33555
33563
  case "fileContent": {
33556
- const abs = join45(sandboxDir, check.path);
33564
+ const abs = join46(sandboxDir, check.path);
33557
33565
  if (!isFile(abs))
33558
33566
  return false;
33559
- const content = readFileSync33(abs, "utf-8");
33567
+ const content = readFileSync34(abs, "utf-8");
33560
33568
  if (check.contains !== undefined)
33561
33569
  return content.includes(check.contains);
33562
33570
  if (check.equals !== undefined)
@@ -33564,10 +33572,10 @@ function runCheck2(sandboxDir, check, exitCode, output) {
33564
33572
  return false;
33565
33573
  }
33566
33574
  case "fileRegex": {
33567
- const abs = join45(sandboxDir, check.path);
33575
+ const abs = join46(sandboxDir, check.path);
33568
33576
  if (!isFile(abs))
33569
33577
  return false;
33570
- return new RegExp(check.pattern).test(readFileSync33(abs, "utf-8"));
33578
+ return new RegExp(check.pattern).test(readFileSync34(abs, "utf-8"));
33571
33579
  }
33572
33580
  default:
33573
33581
  return false;
@@ -33575,14 +33583,14 @@ function runCheck2(sandboxDir, check, exitCode, output) {
33575
33583
  }
33576
33584
  function isFile(p) {
33577
33585
  try {
33578
- return existsSync52(p) && statSync9(p).isFile();
33586
+ return existsSync53(p) && statSync9(p).isFile();
33579
33587
  } catch {
33580
33588
  return false;
33581
33589
  }
33582
33590
  }
33583
33591
  function isDir(p) {
33584
33592
  try {
33585
- return existsSync52(p) && statSync9(p).isDirectory();
33593
+ return existsSync53(p) && statSync9(p).isDirectory();
33586
33594
  } catch {
33587
33595
  return false;
33588
33596
  }
@@ -33613,8 +33621,8 @@ var init_fact_checker = () => {};
33613
33621
 
33614
33622
  // src/modules/certification/runner.ts
33615
33623
  import { spawn as spawn10 } from "child_process";
33616
- import { existsSync as existsSync53, mkdirSync as mkdirSync20, rmSync as rmSync4, cpSync as cpSync2, writeFileSync as writeFileSync20, readdirSync as readdirSync18, readFileSync as readFileSync34 } from "fs";
33617
- import { join as join46, resolve as resolve23, dirname as dirname19, relative as relative7 } from "path";
33624
+ import { existsSync as existsSync54, mkdirSync as mkdirSync20, rmSync as rmSync4, cpSync as cpSync2, writeFileSync as writeFileSync20, readdirSync as readdirSync18, readFileSync as readFileSync35 } from "fs";
33625
+ import { join as join47, resolve as resolve23, dirname as dirname20, relative as relative7 } from "path";
33618
33626
  async function runScenario(scenario, opts) {
33619
33627
  if (scenario.mode === "skip") {
33620
33628
  return {
@@ -33634,7 +33642,7 @@ async function runScenario(scenario, opts) {
33634
33642
  let firstError;
33635
33643
  let lastFailedSandbox;
33636
33644
  for (let i = 1;i <= reps; i++) {
33637
- const sandbox = join46(opts.sandboxBase, `run-${scenario.id}-${i}`);
33645
+ const sandbox = join47(opts.sandboxBase, `run-${scenario.id}-${i}`);
33638
33646
  let failures = [];
33639
33647
  let exitCode = -1;
33640
33648
  let output = "";
@@ -33658,9 +33666,9 @@ async function runScenario(scenario, opts) {
33658
33666
  env3.MMA_PROVIDER_APIKEY = opts.providerKey;
33659
33667
  if (scenario.config && opts.baseConfig) {
33660
33668
  const merged = deepMergeAny(opts.baseConfig, scenario.config);
33661
- const certConfigDir = join46(sandbox, ".mma");
33669
+ const certConfigDir = join47(sandbox, ".mma");
33662
33670
  mkdirSync20(certConfigDir, { recursive: true });
33663
- writeFileSync20(join46(certConfigDir, "config.json"), JSON.stringify(merged, null, 2), "utf-8");
33671
+ writeFileSync20(join47(certConfigDir, "config.json"), JSON.stringify(merged, null, 2), "utf-8");
33664
33672
  env3.MMA_CONFIG_DIR = certConfigDir;
33665
33673
  }
33666
33674
  const res = await runner(env3, opts.mmaRoot, args, timeoutMs);
@@ -33707,12 +33715,12 @@ function prepareSandbox(sandbox, scenario, mmaRoot) {
33707
33715
  rmSync4(sandbox, { recursive: true, force: true });
33708
33716
  mkdirSync20(sandbox, { recursive: true });
33709
33717
  for (const f of scenario.fixtures ?? []) {
33710
- const src = join46(mmaRoot, f.source);
33711
- if (!existsSync53(src)) {
33718
+ const src = join47(mmaRoot, f.source);
33719
+ if (!existsSync54(src)) {
33712
33720
  throw new Error(`fixture missing: ${f.source}`);
33713
33721
  }
33714
- const dest = join46(sandbox, f.dest);
33715
- mkdirSync20(dirname19(dest), { recursive: true });
33722
+ const dest = join47(sandbox, f.dest);
33723
+ mkdirSync20(dirname20(dest), { recursive: true });
33716
33724
  cpSync2(src, dest);
33717
33725
  }
33718
33726
  }
@@ -33724,10 +33732,10 @@ function collectDiagnostics(sandbox) {
33724
33732
  } else {
33725
33733
  lines.push(" Files created: (none)");
33726
33734
  }
33727
- const planPath = join46(sandbox, ".mma", "plans", "active.json");
33728
- if (existsSync53(planPath)) {
33735
+ const planPath = join47(sandbox, ".mma", "plans", "active.json");
33736
+ if (existsSync54(planPath)) {
33729
33737
  try {
33730
- const plan = JSON.parse(readFileSync34(planPath, "utf-8"));
33738
+ const plan = JSON.parse(readFileSync35(planPath, "utf-8"));
33731
33739
  const steps = plan.steps ?? [];
33732
33740
  const done = steps.filter((s) => s.status === "done").length;
33733
33741
  const pending = steps.filter((s) => s.status === "pending" || s.status === "in_progress");
@@ -33746,7 +33754,7 @@ function listFiles(dir, root) {
33746
33754
  for (const entry of readdirSync18(dir, { withFileTypes: true })) {
33747
33755
  if (entry.name === ".mma")
33748
33756
  continue;
33749
- const abs = join46(dir, entry.name);
33757
+ const abs = join47(dir, entry.name);
33750
33758
  const rel = toForwardSlash(relative7(root, abs));
33751
33759
  if (entry.isDirectory()) {
33752
33760
  result.push(...listFiles(abs, root));
@@ -33758,15 +33766,15 @@ function listFiles(dir, root) {
33758
33766
  return result;
33759
33767
  }
33760
33768
  function resolveMmaEntry(mmaRoot) {
33761
- const dev = join46(mmaRoot, "src", "cli", "main.ts");
33762
- if (existsSync53(dev))
33769
+ const dev = join47(mmaRoot, "src", "cli", "main.ts");
33770
+ if (existsSync54(dev))
33763
33771
  return dev;
33764
- return join46(mmaRoot, "dist", "main.js");
33772
+ return join47(mmaRoot, "dist", "main.js");
33765
33773
  }
33766
33774
  function findMmaRoot(fromDir) {
33767
33775
  const candidates = [resolve23(fromDir, "..", "..", ".."), resolve23(fromDir, "..")];
33768
33776
  for (const c of candidates) {
33769
- if (existsSync53(join46(c, "package.json")))
33777
+ if (existsSync54(join47(c, "package.json")))
33770
33778
  return c;
33771
33779
  }
33772
33780
  return process.cwd();
@@ -33834,15 +33842,15 @@ __export(exports_cli, {
33834
33842
  certList: () => certList
33835
33843
  });
33836
33844
  import { rmSync as rmSync5, writeFileSync as writeFileSync21 } from "fs";
33837
- import { join as join47, dirname as dirname20 } from "path";
33845
+ import { join as join48, dirname as dirname21 } from "path";
33838
33846
  import { fileURLToPath as fileURLToPath5 } from "url";
33839
- import { existsSync as existsSync54, readFileSync as readFileSync35 } from "fs";
33847
+ import { existsSync as existsSync55, readFileSync as readFileSync36 } from "fs";
33840
33848
  function readVersion() {
33841
- const candidates = [join47(MMA_ROOT, "package.json")];
33849
+ const candidates = [join48(MMA_ROOT, "package.json")];
33842
33850
  for (const p of candidates) {
33843
- if (existsSync54(p)) {
33851
+ if (existsSync55(p)) {
33844
33852
  try {
33845
- const raw = JSON.parse(readFileSync35(p, "utf-8"));
33853
+ const raw = JSON.parse(readFileSync36(p, "utf-8"));
33846
33854
  if (raw.version)
33847
33855
  return raw.version;
33848
33856
  } catch {}
@@ -33855,7 +33863,7 @@ function parseTags(s) {
33855
33863
  }
33856
33864
  async function certify(opts) {
33857
33865
  const providerUrl = opts.providerUrl || opts.config.provider.baseUrl;
33858
- const { scenarios, errors: errors2 } = loadScenarios(join47(opts.projectDir, ".mma", "certification", "scenarios"));
33866
+ const { scenarios, errors: errors2 } = loadScenarios(join48(opts.projectDir, ".mma", "certification", "scenarios"));
33859
33867
  for (const e of errors2)
33860
33868
  console.error(pc2.yellow(` ${e}`));
33861
33869
  let selected = filterByTags(scenarios, opts.tags);
@@ -33886,7 +33894,7 @@ async function certify(opts) {
33886
33894
  return;
33887
33895
  }
33888
33896
  console.log(t("cli.cert_started", { model: opts.name, provider: providerUrl }));
33889
- const sandboxBase = join47(process.cwd(), ".mma", "certification");
33897
+ const sandboxBase = join48(process.cwd(), ".mma", "certification");
33890
33898
  const results = [];
33891
33899
  const total = selected.length;
33892
33900
  let idx = 0;
@@ -34019,10 +34027,10 @@ function printResults(results) {
34019
34027
  }
34020
34028
  }
34021
34029
  function writeReport(entry, projectDir) {
34022
- const reportDir = join47(projectDir, "certification");
34030
+ const reportDir = join48(projectDir, "certification");
34023
34031
  const ts = entry.certifiedAt.replace(/[:.]/g, "-").slice(0, 19);
34024
34032
  const filename = `report-${entry.model.replace(/[/\\:]/g, "_")}-${ts}.json`;
34025
- const reportPath = join47(reportDir, filename);
34033
+ const reportPath = join48(reportDir, filename);
34026
34034
  const report = {
34027
34035
  model: entry.model,
34028
34036
  providerUrl: entry.providerUrl,
@@ -34055,7 +34063,7 @@ var init_cli = __esm(() => {
34055
34063
  init_loader3();
34056
34064
  init_runner2();
34057
34065
  init_manifest();
34058
- HERE = dirname20(fileURLToPath5(import.meta.url));
34066
+ HERE = dirname21(fileURLToPath5(import.meta.url));
34059
34067
  MMA_ROOT = findMmaRoot(HERE);
34060
34068
  });
34061
34069
 
@@ -34065,8 +34073,8 @@ __export(exports_repl_commands, {
34065
34073
  registerAllCommands: () => registerAllCommands,
34066
34074
  COMMAND_GROUPS: () => COMMAND_GROUPS
34067
34075
  });
34068
- import { join as join49, dirname as dirname22 } from "path";
34069
- import { existsSync as existsSync56 } from "fs";
34076
+ import { join as join50, dirname as dirname23 } from "path";
34077
+ import { existsSync as existsSync57 } from "fs";
34070
34078
  function registerAllCommands(ctx) {
34071
34079
  registerBuiltinCommands(ctx);
34072
34080
  registerMmaCommands(ctx);
@@ -34123,7 +34131,7 @@ function registerMmaCommands(ctx) {
34123
34131
  }
34124
34132
  try {
34125
34133
  const { loadFileAsDataUrl: loadFileAsDataUrl2, loadUrlAsDataUrl: loadUrlAsDataUrl2, readClipboardImage: readClipboardImage2 } = await Promise.resolve().then(() => (init_image_utils(), exports_image_utils));
34126
- const { existsSync: existsSync57 } = await import("fs");
34134
+ const { existsSync: existsSync58 } = await import("fs");
34127
34135
  const { resolve: resolve24 } = await import("path");
34128
34136
  let dataUrl;
34129
34137
  let label;
@@ -34143,7 +34151,7 @@ function registerMmaCommands(ctx) {
34143
34151
  label = source;
34144
34152
  } else {
34145
34153
  const absPath = resolve24(process.cwd(), source);
34146
- if (!existsSync57(absPath)) {
34154
+ if (!existsSync58(absPath)) {
34147
34155
  console.log(pc2.red(t("image.not_found", { path: source })));
34148
34156
  return;
34149
34157
  }
@@ -34246,7 +34254,7 @@ function registerMmaCommands(ctx) {
34246
34254
  console.log(pc2.yellow(t("repl.wizard_running")));
34247
34255
  await ctx.withExclusiveInput(async () => {
34248
34256
  const answers = await runSetup(ctx.rl);
34249
- const configPath = join49(ctx.configDir, "config.json");
34257
+ const configPath = join50(ctx.configDir, "config.json");
34250
34258
  ctx.config.provider.type = answers.provider;
34251
34259
  ctx.config.provider.baseUrl = answers.apiBase;
34252
34260
  ctx.config.provider.apiKey = answers.apiKey;
@@ -34254,7 +34262,7 @@ function registerMmaCommands(ctx) {
34254
34262
  ctx.config.contextWindow = answers.contextWindow;
34255
34263
  ctx.config.maxToolIterations = answers.maxToolIterations;
34256
34264
  ctx.config.locale = answers.locale;
34257
- saveConfig(ctx.config, configPath, dirname22(configPath));
34265
+ saveConfig(ctx.config, configPath, dirname23(configPath));
34258
34266
  await ctx.agent.reconfigure(ctx.config);
34259
34267
  console.log(pc2.green(t("cli.config_saved")));
34260
34268
  });
@@ -34368,8 +34376,8 @@ function registerMmaCommands(ctx) {
34368
34376
  return;
34369
34377
  }
34370
34378
  ctx.config.model = name;
34371
- const configPath = join49(ctx.configDir, "config.json");
34372
- saveConfig(ctx.config, configPath, dirname22(configPath));
34379
+ const configPath = join50(ctx.configDir, "config.json");
34380
+ saveConfig(ctx.config, configPath, dirname23(configPath));
34373
34381
  await ctx.agent.reconfigure(ctx.config);
34374
34382
  console.log(pc2.green(t("repl.model_set", { name })));
34375
34383
  return;
@@ -34393,8 +34401,8 @@ function registerMmaCommands(ctx) {
34393
34401
  return;
34394
34402
  }
34395
34403
  ctx.config.contextWindow = size;
34396
- const configPath = join49(ctx.configDir, "config.json");
34397
- saveConfig(ctx.config, configPath, dirname22(configPath));
34404
+ const configPath = join50(ctx.configDir, "config.json");
34405
+ saveConfig(ctx.config, configPath, dirname23(configPath));
34398
34406
  await ctx.agent.reconfigure(ctx.config);
34399
34407
  console.log(pc2.green(t("cli.context_set", { size })));
34400
34408
  }
@@ -34408,10 +34416,10 @@ function registerMmaCommands(ctx) {
34408
34416
  if (ctx.sessionManager && ctx.config.session.autoSave) {}
34409
34417
  ctx.agent.shutdown();
34410
34418
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
34411
- const { join: join50 } = await import("path");
34419
+ const { join: join51 } = await import("path");
34412
34420
  const configDir = ctx.configDir;
34413
34421
  const baseDir = ctx.baseDir;
34414
- const projectConfigPath = join50(baseDir, ".mmrc");
34422
+ const projectConfigPath = join51(baseDir, ".mmrc");
34415
34423
  const { config: freshConfig } = loadConfig2({ configDir, projectConfigPath });
34416
34424
  Object.assign(ctx.config, freshConfig);
34417
34425
  const { bootstrap: bootstrap2 } = await Promise.resolve().then(() => (init_bootstrap(), exports_bootstrap));
@@ -34765,21 +34773,21 @@ async function runConfigMigrate(ctx) {
34765
34773
  const { hasDomainFiles: hasDomainFiles3 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
34766
34774
  const { loadConfig: loadCfg } = await Promise.resolve().then(() => (init_config2(), exports_config));
34767
34775
  const configDir = ctx.configDir;
34768
- const configPath = join49(configDir, "config.json");
34776
+ const configPath = join50(configDir, "config.json");
34769
34777
  if (hasDomainFiles3(configDir)) {
34770
34778
  console.log(pc2.yellow(t("config.migrate_no_legacy")));
34771
34779
  return;
34772
34780
  }
34773
- if (!existsSync56(configPath)) {
34781
+ if (!existsSync57(configPath)) {
34774
34782
  console.log(pc2.yellow(t("config.migrate_no_legacy")));
34775
34783
  return;
34776
34784
  }
34777
34785
  console.log(t("config.migrate_start"));
34778
- const { config } = loadCfg({ configDir, projectConfigPath: join49(configDir, ".mmrc") });
34786
+ const { config } = loadCfg({ configDir, projectConfigPath: join50(configDir, ".mmrc") });
34779
34787
  saveConfig(config, configPath, configDir);
34780
34788
  const { renameSync: renameSync4, readdirSync: readdirSync19 } = await import("fs");
34781
34789
  renameSync4(configPath, configPath + ".bak");
34782
- const domainFiles = readdirSync19(join49(configDir, "config")).filter((f) => f.endsWith(".json"));
34790
+ const domainFiles = readdirSync19(join50(configDir, "config")).filter((f) => f.endsWith(".json"));
34783
34791
  console.log(pc2.green(t("config.migrate_done", { count: String(domainFiles.length) })));
34784
34792
  }
34785
34793
  var version2, COMMAND_GROUPS;
@@ -34839,9 +34847,9 @@ init_config2();
34839
34847
  init_setup();
34840
34848
  init_i18n();
34841
34849
  init_colors();
34842
- import { join as join48, dirname as dirname21 } from "path";
34850
+ import { join as join49, dirname as dirname22 } from "path";
34843
34851
  import { homedir as homedir16 } from "os";
34844
- import { existsSync as existsSync55 } from "fs";
34852
+ import { existsSync as existsSync56 } from "fs";
34845
34853
 
34846
34854
  // src/cli/security-commands.ts
34847
34855
  init_bootstrap();
@@ -35472,12 +35480,92 @@ function createPluginCommand(program2) {
35472
35480
  // src/cli/commands.ts
35473
35481
  init_presets();
35474
35482
  init_version();
35483
+
35484
+ // src/modules/updater/changelog-reader.ts
35485
+ import { readFileSync as readFileSync32, existsSync as existsSync51 } from "fs";
35486
+ import { dirname as dirname19, join as join44 } from "path";
35487
+ function readChangelog(packageName) {
35488
+ try {
35489
+ let dir = dirname19(import.meta.url);
35490
+ if (dir.startsWith("file://")) {
35491
+ dir = decodeURIComponent(dir.slice(7));
35492
+ }
35493
+ for (let i = 0;i < 10; i++) {
35494
+ const pkgPath = join44(dir, "package.json");
35495
+ if (existsSync51(pkgPath)) {
35496
+ const pkg = JSON.parse(readFileSync32(pkgPath, "utf-8"));
35497
+ if (pkg.name === packageName) {
35498
+ const changelogPath = join44(dir, "CHANGELOG.md");
35499
+ if (existsSync51(changelogPath)) {
35500
+ return readFileSync32(changelogPath, "utf-8");
35501
+ }
35502
+ return null;
35503
+ }
35504
+ }
35505
+ dir = dirname19(dir);
35506
+ }
35507
+ return null;
35508
+ } catch {
35509
+ return null;
35510
+ }
35511
+ }
35512
+ function extractChangelogRange(changelog, fromVersion, toVersion) {
35513
+ const lines = changelog.split(`
35514
+ `);
35515
+ const sections = [];
35516
+ let capturing = false;
35517
+ let foundFrom = false;
35518
+ for (const line of lines) {
35519
+ const match = line.match(/^## \[(\d+\.\d+\.\d+)\]/);
35520
+ if (match) {
35521
+ const ver = match[1];
35522
+ if (ver === fromVersion) {
35523
+ foundFrom = true;
35524
+ break;
35525
+ }
35526
+ if (capturing) {
35527
+ sections.push("");
35528
+ }
35529
+ capturing = true;
35530
+ }
35531
+ if (capturing) {
35532
+ sections.push(line);
35533
+ }
35534
+ }
35535
+ if (!foundFrom)
35536
+ return null;
35537
+ return sections.length > 0 ? sections.join(`
35538
+ `).trim() : null;
35539
+ }
35540
+ function extractLatestChangelog(changelog) {
35541
+ const lines = changelog.split(`
35542
+ `);
35543
+ const sections = [];
35544
+ let capturing = false;
35545
+ let found = false;
35546
+ for (const line of lines) {
35547
+ const match = line.match(/^## \[(\d+\.\d+\.\d+)\]/);
35548
+ if (match) {
35549
+ if (found)
35550
+ break;
35551
+ capturing = true;
35552
+ found = true;
35553
+ }
35554
+ if (capturing) {
35555
+ sections.push(line);
35556
+ }
35557
+ }
35558
+ return sections.length > 0 ? sections.join(`
35559
+ `).trim() : null;
35560
+ }
35561
+
35562
+ // src/cli/commands.ts
35475
35563
  var version = readMmaVersion();
35476
35564
  function createProgram() {
35477
35565
  const program2 = new Command().name("mma").description(t("cli.description")).version(version).option("--no-agents-md", t("cli.no_agents_md")).option("-d, --dir <path>", t("cli.dir")).option("-e, --exit-on-complete", t("cli.exit_on_complete")).option("-j, --json", t("cli.json")).option("--reasoning <level>", t("cli.reasoning_level"), "auto");
35478
35566
  program2.command("init").description(t("cli.init")).action(async () => {
35479
35567
  const answers = await runSetup();
35480
- const configPath = join48(homedir16(), ".mma", "config.json");
35568
+ const configPath = join49(homedir16(), ".mma", "config.json");
35481
35569
  const { config } = await bootstrap();
35482
35570
  config.provider.type = answers.provider;
35483
35571
  config.provider.baseUrl = answers.apiBase;
@@ -35517,12 +35605,12 @@ function createProgram() {
35517
35605
  config.security.paths.denied = [];
35518
35606
  }
35519
35607
  }
35520
- saveConfig(config, configPath, dirname21(configPath));
35608
+ saveConfig(config, configPath, dirname22(configPath));
35521
35609
  console.log(t("cli.config_saved"));
35522
35610
  });
35523
35611
  const configCmd = program2.command("config").description(t("cli.manage_config"));
35524
35612
  configCmd.command("set").argument("<key>", t("cli.config_key")).argument("<value>", "Config value").description(t("cli.set_value")).action(async (key, value) => {
35525
- const configPath = join48(homedir16(), ".mma", "config.json");
35613
+ const configPath = join49(homedir16(), ".mma", "config.json");
35526
35614
  const { config } = await bootstrap();
35527
35615
  const keys = key.split(".");
35528
35616
  let obj = config;
@@ -35542,7 +35630,7 @@ function createProgram() {
35542
35630
  obj[lastKey] = parseFloat(value);
35543
35631
  else
35544
35632
  obj[lastKey] = value;
35545
- saveConfig(config, configPath, dirname21(configPath));
35633
+ saveConfig(config, configPath, dirname22(configPath));
35546
35634
  console.log(t("cli.set_done", { key, value }));
35547
35635
  });
35548
35636
  configCmd.command("show").description(t("cli.show_config")).action(async () => {
@@ -35552,24 +35640,24 @@ function createProgram() {
35552
35640
  configCmd.command("migrate").description(t("cli.migrate_config")).action(async () => {
35553
35641
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
35554
35642
  const { hasDomainFiles: hasDomainFiles3 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
35555
- const configDir = join48(homedir16(), ".mma");
35556
- const configPath = join48(configDir, "config.json");
35643
+ const configDir = join49(homedir16(), ".mma");
35644
+ const configPath = join49(configDir, "config.json");
35557
35645
  if (hasDomainFiles3(configDir)) {
35558
35646
  console.log(pc2.yellow(t("config.migrate_no_legacy")));
35559
35647
  return;
35560
35648
  }
35561
- if (!existsSync55(configPath)) {
35649
+ if (!existsSync56(configPath)) {
35562
35650
  console.log(pc2.yellow(t("config.migrate_no_legacy")));
35563
35651
  return;
35564
35652
  }
35565
35653
  console.log(t("config.migrate_start"));
35566
- const { config } = loadConfig2({ configDir, projectConfigPath: join48(configDir, ".mmrc") });
35654
+ const { config } = loadConfig2({ configDir, projectConfigPath: join49(configDir, ".mmrc") });
35567
35655
  saveConfig(config, configPath, configDir);
35568
35656
  const bakPath = configPath + ".bak";
35569
35657
  const { renameSync: renameSync4 } = await import("fs");
35570
35658
  renameSync4(configPath, bakPath);
35571
35659
  const { readdirSync: readdirSync19 } = await import("fs");
35572
- const domainFiles = readdirSync19(join48(configDir, "config")).filter((f) => f.endsWith(".json"));
35660
+ const domainFiles = readdirSync19(join49(configDir, "config")).filter((f) => f.endsWith(".json"));
35573
35661
  console.log(pc2.green(t("config.migrate_done", { count: String(domainFiles.length) })));
35574
35662
  });
35575
35663
  const model = program2.command("model").description(t("cli.manage_models"));
@@ -35609,10 +35697,10 @@ function createProgram() {
35609
35697
  console.log(t("cli.model_hint"));
35610
35698
  });
35611
35699
  model.command("use").argument("<name>", "Model name").description(t("cli.set_model")).action(async (name) => {
35612
- const configPath = join48(homedir16(), ".mma", "config.json");
35700
+ const configPath = join49(homedir16(), ".mma", "config.json");
35613
35701
  const { config } = await bootstrap();
35614
35702
  config.model = name;
35615
- saveConfig(config, configPath, dirname21(configPath));
35703
+ saveConfig(config, configPath, dirname22(configPath));
35616
35704
  console.log(t("cli.model_set", { name }));
35617
35705
  });
35618
35706
  model.command("certify").argument("<name>", "Model name").option("--provider-url <url>", t("cli.cert_provider_url")).option("--provider-key <key>", t("cli.cert_provider_key")).option("--context-window <n>", t("cli.cert_context_window")).option("--tags <tags>", t("cli.cert_tags"), "core").option("--scenarios <ids>", t("cli.cert_scenarios")).option("--timeout <ms>", t("cli.cert_timeout")).option("--reps <n>", t("cli.cert_reps")).option("--force", t("cli.cert_force")).option("--clean", t("cli.cert_clean")).description(t("cli.certify")).action(async (name, cmdOpts) => {
@@ -35648,7 +35736,7 @@ function createProgram() {
35648
35736
  await uncertify2(name, config, process.cwd());
35649
35737
  });
35650
35738
  program2.command("context").description(t("cli.manage_context")).argument("<size>", "Context window size in tokens").action(async (size) => {
35651
- const configPath = join48(homedir16(), ".mma", "config.json");
35739
+ const configPath = join49(homedir16(), ".mma", "config.json");
35652
35740
  const { config } = await bootstrap();
35653
35741
  const contextWindow = parseInt(size, 10);
35654
35742
  if (isNaN(contextWindow) || contextWindow < 1024) {
@@ -35656,7 +35744,7 @@ function createProgram() {
35656
35744
  return;
35657
35745
  }
35658
35746
  config.contextWindow = contextWindow;
35659
- saveConfig(config, configPath, dirname21(configPath));
35747
+ saveConfig(config, configPath, dirname22(configPath));
35660
35748
  console.log(t("cli.context_set", { size: contextWindow }));
35661
35749
  });
35662
35750
  const provider = program2.command("provider").description(t("cli.manage_providers"));
@@ -35694,7 +35782,7 @@ function createProgram() {
35694
35782
  console.log(t("cli.base_url"), config.provider.baseUrl);
35695
35783
  });
35696
35784
  provider.command("use").argument("<name>", "Provider name").description(t("cli.set_provider")).action(async (name) => {
35697
- const configPath = join48(homedir16(), ".mma", "config.json");
35785
+ const configPath = join49(homedir16(), ".mma", "config.json");
35698
35786
  const { config, agent } = await bootstrap();
35699
35787
  if (config.provider.entries && config.provider.entries.length > 0) {
35700
35788
  try {
@@ -35710,14 +35798,14 @@ function createProgram() {
35710
35798
  if (baseUrl) {
35711
35799
  config.provider.baseUrl = baseUrl;
35712
35800
  }
35713
- saveConfig(config, configPath, dirname21(configPath));
35801
+ saveConfig(config, configPath, dirname22(configPath));
35714
35802
  console.log(t("cli.provider_set", { name }));
35715
35803
  if (baseUrl) {
35716
35804
  console.log(t("cli.provider_base_hint", { baseUrl }));
35717
35805
  }
35718
35806
  });
35719
35807
  provider.command("add").argument("<name>", "Provider type or label").option("--url <url>", "Base URL").option("--key <key>", "API key").option("--priority <n>", "Fallback priority (lower = tried first)").option("--context-window <n>", "Context window override for this entry").option("--rpm <n>", "Max requests per minute for this entry").option("--parallel <n>", "Max parallel tasks for this entry").description(t("cli.add_provider")).action(async (name, opts) => {
35720
- const configPath = join48(homedir16(), ".mma", "config.json");
35808
+ const configPath = join49(homedir16(), ".mma", "config.json");
35721
35809
  const { config } = await bootstrap();
35722
35810
  const entries = Array.isArray(config.provider.entries) ? config.provider.entries : [];
35723
35811
  if (entries.length === 0) {
@@ -35749,7 +35837,7 @@ function createProgram() {
35749
35837
  });
35750
35838
  config.provider.entries = entries;
35751
35839
  config.provider.active = config.provider.active || config.provider.type;
35752
- saveConfig(config, configPath, dirname21(configPath));
35840
+ saveConfig(config, configPath, dirname22(configPath));
35753
35841
  console.log(pc2.green(t("cli.provider_added", { name })));
35754
35842
  console.log(t("cli.provider_switch_hint"));
35755
35843
  });
@@ -35790,6 +35878,31 @@ function createProgram() {
35790
35878
  });
35791
35879
  createSecurityCommand(program2);
35792
35880
  createPluginCommand(program2);
35881
+ program2.command("changelog").description(t("cli.changelog_title", { version })).option("--from <version>", t("cli.changelog_range", { from: "..." })).action((opts) => {
35882
+ console.error("[DEBUG] changelog action called");
35883
+ const changelog = readChangelog("micro-models-agent");
35884
+ if (!changelog) {
35885
+ console.log(pc2.yellow(t("cli.changelog_not_found")));
35886
+ return;
35887
+ }
35888
+ if (opts.from) {
35889
+ const range = extractChangelogRange(changelog, opts.from, version);
35890
+ if (!range) {
35891
+ console.log(pc2.yellow(t("cli.changelog_not_found")));
35892
+ return;
35893
+ }
35894
+ console.log(pc2.cyan(t("cli.changelog_range", { from: opts.from })));
35895
+ console.log(range);
35896
+ } else {
35897
+ const latest = extractLatestChangelog(changelog);
35898
+ if (!latest) {
35899
+ console.log(pc2.yellow(t("cli.changelog_not_found")));
35900
+ return;
35901
+ }
35902
+ console.log(pc2.cyan(t("cli.changelog_title", { version })));
35903
+ console.log(latest);
35904
+ }
35905
+ });
35793
35906
  program2.argument("[prompt...]", "Prompt to execute").description("Run a single prompt").action((prompt) => {});
35794
35907
  return program2;
35795
35908
  }
@@ -36667,8 +36780,8 @@ class LineEditor {
36667
36780
  }
36668
36781
 
36669
36782
  // src/cli/repl.ts
36670
- import { existsSync as existsSync57, readFileSync as readFileSync38, writeFileSync as writeFileSync22 } from "fs";
36671
- import { join as join50 } from "path";
36783
+ import { existsSync as existsSync58, readFileSync as readFileSync39, writeFileSync as writeFileSync22 } from "fs";
36784
+ import { join as join51 } from "path";
36672
36785
  import { homedir as homedir17 } from "os";
36673
36786
 
36674
36787
  // src/cli/completer.ts
@@ -37495,10 +37608,10 @@ class Repl {
37495
37608
  this.envReport = envReport;
37496
37609
  this.execModule = execModule;
37497
37610
  this.slog = new SessionLogger(sessionManager, logger4);
37498
- this.configDir = configDir || join50(homedir17(), ".mma");
37611
+ this.configDir = configDir || join51(homedir17(), ".mma");
37499
37612
  this.baseDir = baseDir || process.cwd();
37500
37613
  this.noAgentsMd = noAgentsMd === true;
37501
- this.historyPath = historyPath ?? join50(homedir17(), ".mma", "repl-history");
37614
+ this.historyPath = historyPath ?? join51(homedir17(), ".mma", "repl-history");
37502
37615
  this.loadHistory();
37503
37616
  this.rl = process.stdin.isTTY ? new LineEditor({
37504
37617
  input: process.stdin,
@@ -37550,9 +37663,9 @@ class Repl {
37550
37663
  }));
37551
37664
  }
37552
37665
  loadHistory() {
37553
- if (existsSync57(this.historyPath)) {
37666
+ if (existsSync58(this.historyPath)) {
37554
37667
  try {
37555
- const raw = readFileSync38(this.historyPath, "utf-8");
37668
+ const raw = readFileSync39(this.historyPath, "utf-8");
37556
37669
  this.history = raw.split(`
37557
37670
  `).filter(Boolean).slice(-this.maxHistory);
37558
37671
  } catch {
@@ -37950,11 +38063,11 @@ ${t("image.clipboard_empty")}`));
37950
38063
  row(t("repl.agents_label"), pc2.red(t("repl.disabled")));
37951
38064
  } else {
37952
38065
  const agentsMdCandidates = [
37953
- join50(this.baseDir, "AGENTS.md"),
37954
- join50(this.baseDir, ".mma", "AGENTS.md"),
37955
- join50(this.configDir, "AGENTS.md")
38066
+ join51(this.baseDir, "AGENTS.md"),
38067
+ join51(this.baseDir, ".mma", "AGENTS.md"),
38068
+ join51(this.configDir, "AGENTS.md")
37956
38069
  ];
37957
- const foundAgents = agentsMdCandidates.filter((p) => existsSync57(p));
38070
+ const foundAgents = agentsMdCandidates.filter((p) => existsSync58(p));
37958
38071
  if (foundAgents.length > 0) {
37959
38072
  for (const p of foundAgents) {
37960
38073
  row(t("repl.agents_label"), pc2.dim(p));
@@ -37965,7 +38078,7 @@ ${t("image.clipboard_empty")}`));
37965
38078
  }
37966
38079
  const meta = this.sessionManager?.getActiveMeta();
37967
38080
  if (meta) {
37968
- const sessionPath = join50(this.configDir, "sessions", meta.id);
38081
+ const sessionPath = join51(this.configDir, "sessions", meta.id);
37969
38082
  row(t("repl.session_label"), `${pc2.cyan(meta.name)} ${pc2.dim(`(${meta.id.slice(0, 12)})`)} — ${meta.messageCount} msgs ${pc2.dim(sessionPath)}`);
37970
38083
  }
37971
38084
  const isTty2 = process.stdout.isTTY === true;
@@ -38080,8 +38193,8 @@ init_setup();
38080
38193
  init_config2();
38081
38194
  init_i18n();
38082
38195
  init_colors();
38083
- import { existsSync as existsSync58 } from "fs";
38084
- import { join as join52, dirname as dirname23 } from "path";
38196
+ import { existsSync as existsSync59 } from "fs";
38197
+ import { join as join53, dirname as dirname24 } from "path";
38085
38198
  import { homedir as homedir19 } from "os";
38086
38199
 
38087
38200
  // src/modules/updater/index.ts
@@ -38165,7 +38278,16 @@ class UpdaterModule {
38165
38278
  }));
38166
38279
  const install = await this.updater.install(result.latest);
38167
38280
  if (install.success) {
38168
- this.logger.info(t("updater.installed", { current: result.current, latest: result.latest }));
38281
+ const changelog = readChangelog("micro-models-agent");
38282
+ const latestEntry = changelog ? extractLatestChangelog(changelog) : null;
38283
+ const changelogBlock = latestEntry ? pc2.cyan(`
38284
+ ${t("cli.changelog_title", { version: result.latest })}
38285
+ `) + latestEntry : "";
38286
+ this.logger.info(t("updater.installed", {
38287
+ current: result.current,
38288
+ latest: result.latest,
38289
+ changelog: changelogBlock
38290
+ }));
38169
38291
  } else {
38170
38292
  this.logger.warn(t("updater.install_failed", {
38171
38293
  current: result.current,
@@ -38186,9 +38308,9 @@ init_environment();
38186
38308
  init_data_sanitizer();
38187
38309
  init_i18n();
38188
38310
  import { appendFileSync as appendFileSync7, mkdirSync as mkdirSync21 } from "fs";
38189
- import { join as join51 } from "path";
38311
+ import { join as join52 } from "path";
38190
38312
  import { homedir as homedir18 } from "os";
38191
- var CRASH_LOG_DIR = join51(homedir18(), ".mma", "logs");
38313
+ var CRASH_LOG_DIR = join52(homedir18(), ".mma", "logs");
38192
38314
  var CRASH_LOG_FILE = "crash.jsonl";
38193
38315
  function formatCrashEntry(type2, err) {
38194
38316
  const message = err instanceof Error ? err.message : String(err);
@@ -38204,7 +38326,7 @@ function formatCrashEntry(type2, err) {
38204
38326
  function writeCrashEntry(dir, entry) {
38205
38327
  try {
38206
38328
  mkdirSync21(dir, { recursive: true });
38207
- appendFileSync7(join51(dir, CRASH_LOG_FILE), JSON.stringify(entry) + `
38329
+ appendFileSync7(join52(dir, CRASH_LOG_FILE), JSON.stringify(entry) + `
38208
38330
  `, "utf-8");
38209
38331
  } catch {}
38210
38332
  }
@@ -38258,6 +38380,9 @@ async function main() {
38258
38380
  const jsonMode = opts.json === true;
38259
38381
  const reasoningLevel = opts.reasoning;
38260
38382
  const isSubcommand = program2.args.length > 0 && cmdNames.has(program2.args[0]);
38383
+ console.error("[DEBUG] program.args:", program2.args);
38384
+ console.error("[DEBUG] cmdNames:", [...cmdNames]);
38385
+ console.error("[DEBUG] isSubcommand:", isSubcommand);
38261
38386
  if (isSubcommand) {
38262
38387
  return;
38263
38388
  }
@@ -38313,9 +38438,9 @@ async function main() {
38313
38438
  await updater?.waitForIdle();
38314
38439
  process.exit(exitCode);
38315
38440
  } else {
38316
- const mmaDir = join52(homedir19(), ".mma");
38317
- const legacyConfigPath = join52(mmaDir, "config.json");
38318
- let hasAnyConfig = existsSync58(legacyConfigPath);
38441
+ const mmaDir = join53(homedir19(), ".mma");
38442
+ const legacyConfigPath = join53(mmaDir, "config.json");
38443
+ let hasAnyConfig = existsSync59(legacyConfigPath);
38319
38444
  if (!hasAnyConfig) {
38320
38445
  try {
38321
38446
  const { hasDomainFiles: hasDomainFiles3 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
@@ -38378,7 +38503,7 @@ async function main() {
38378
38503
  config.security.paths.denied = [];
38379
38504
  }
38380
38505
  }
38381
- saveConfig(config, legacyConfigPath, dirname23(legacyConfigPath));
38506
+ saveConfig(config, legacyConfigPath, dirname24(legacyConfigPath));
38382
38507
  await agent.reconfigure(config);
38383
38508
  }
38384
38509
  startAutoUpdate(config);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "micro-models-agent",
3
- "version": "0.56.0",
3
+ "version": "0.56.2",
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": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "files": [
10
10
  "dist/",
11
- "bin/"
11
+ "bin/",
12
+ "CHANGELOG.md"
12
13
  ],
13
14
  "engines": {
14
15
  "node": ">=20"