micro-models-agent 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -2312,6 +2312,7 @@ var init_defaults = __esm(() => {
2312
2312
  description: "browser + file reading for visual tasks"
2313
2313
  }
2314
2314
  },
2315
+ modelCapabilities: {},
2315
2316
  contextWindow: 32768,
2316
2317
  contextBudget: {
2317
2318
  systemPrompt: 0.1,
@@ -2729,7 +2730,7 @@ Fix the error and re-edit the file (a clean write clears the failure), or mark t
2729
2730
  "cli.config_key": "Config key (e.g., model, provider.baseUrl)",
2730
2731
  "cli.set_value": "Set a config value",
2731
2732
  "cli.set_done": "Set {key}={value}",
2732
- "cli.show_config": "Show current config",
2733
+ "cli.show_config": "Show the effective configuration (secrets redacted; use --show-secrets to reveal)",
2733
2734
  "cli.migrate_config": "Migrate legacy config.json to domain format",
2734
2735
  "cli.manage_models": "Manage models",
2735
2736
  "cli.list_models": "List available models",
@@ -2804,6 +2805,7 @@ Fix the error and re-edit the file (a clean write clears the failure), or mark t
2804
2805
  "cli.show_details": "Show session details",
2805
2806
  "cli.delete_session": "Delete a session",
2806
2807
  "cli.first_run": "First run detected. Running setup wizard...",
2808
+ "cli.first_run_oneshot": "No configuration found — running with defaults. Run `mma` (no arguments) once to set up the wizard.",
2807
2809
  "cli.setup_saved_restart": "Settings saved. Restarting with the new configuration...",
2808
2810
  "cli.unknown_cmd": "Unknown command: {name}.",
2809
2811
  "cli.unknown_command": 'Unknown command "{input}". Did you mean "{suggestion}"?',
@@ -3028,7 +3030,7 @@ Excluded blocks: {count}`,
3028
3030
  "setup.security_configure": " Configure security? (y/N)",
3029
3031
  "setup.security_bash_block": " Block dangerous commands (rm, sudo, ssh, etc.)? (y/N)",
3030
3032
  "setup.security_flags_block": " Block dangerous flags (--force, -rf, etc.)? (y/N)",
3031
- "setup.security_paths_deny": " Allow access to .git/, .env, node_modules/? (y/N)",
3033
+ "setup.security_paths_restrict": " Restrict access to .git/, .env, node_modules/? (Y/n)",
3032
3034
  "skill.already_loaded": 'Skill "{name}" is already loaded',
3033
3035
  "skill.not_found": 'Skill "{name}" not found in any source directory',
3034
3036
  "skill.no_match": "No skills match for: {task}",
@@ -3605,7 +3607,7 @@ var init_ru = __esm(() => {
3605
3607
  "cli.config_key": "Ключ конфига (напр., model, provider.baseUrl)",
3606
3608
  "cli.set_value": "Установить значение",
3607
3609
  "cli.set_done": "Установлено {key}={value}",
3608
- "cli.show_config": "Показать текущий конфиг",
3610
+ "cli.show_config": "Показать действующую конфигурацию (секреты скрыты; используйте --show-secrets)",
3609
3611
  "cli.migrate_config": "Мигрировать legacy config.json в domain-формат",
3610
3612
  "cli.manage_models": "Управление моделями",
3611
3613
  "cli.list_models": "Список доступных моделей",
@@ -3676,6 +3678,7 @@ var init_ru = __esm(() => {
3676
3678
  "cli.show_details": "Показать детали сессии",
3677
3679
  "cli.delete_session": "Удалить сессию",
3678
3680
  "cli.first_run": "Первый запуск. Запускаем мастер настройки...",
3681
+ "cli.first_run_oneshot": "Конфигурация не найдена — запуск с настройками по умолчанию. Запустите `mma` без аргументов один раз для настройки.",
3679
3682
  "cli.setup_saved_restart": "Настройки сохранены. Перезапуск с новой конфигурацией...",
3680
3683
  "cli.unknown_cmd": "Неизвестная команда: {name}.",
3681
3684
  "cli.unknown_command": 'Неизвестная команда "{input}". Возможно, вы имели в виду "{suggestion}"?',
@@ -3904,7 +3907,7 @@ var init_ru = __esm(() => {
3904
3907
  "setup.security_configure": " Настроить безопасность? (y/N)",
3905
3908
  "setup.security_bash_block": " Блокировать опасные команды (rm, sudo, ssh и т.д.)? (y/N)",
3906
3909
  "setup.security_flags_block": " Блокировать опасные флаги (--force, -rf и т.д.)? (y/N)",
3907
- "setup.security_paths_deny": " Разрешить доступ к .git/, .env, node_modules/? (y/N)",
3910
+ "setup.security_paths_restrict": " Ограничить доступ к .git/, .env, node_modules/? (Y/n)",
3908
3911
  "skill.already_loaded": 'Скилл "{name}" уже загружен',
3909
3912
  "skill.not_found": 'Скилл "{name}" не найден ни в одном каталоге',
3910
3913
  "skill.no_match": "Нет скиллов подходящих для: {task}",
@@ -5116,7 +5119,7 @@ var init_domains = __esm(() => {
5116
5119
  "reasoning",
5117
5120
  "hallucination"
5118
5121
  ],
5119
- provider: ["provider", "orchestrator", "retry", "pricing"],
5122
+ provider: ["provider", "orchestrator", "retry", "pricing", "modelCapabilities"],
5120
5123
  moe: ["moe", "experts"],
5121
5124
  security: ["security"],
5122
5125
  browser: ["browser"],
@@ -5324,9 +5327,6 @@ function loadConfig(options) {
5324
5327
  config = deepMerge(config, domainConfig);
5325
5328
  legacyDetected = false;
5326
5329
  }
5327
- if (!globalData && !domainConfig) {
5328
- saveDomainFiles(config, options.configDir);
5329
- }
5330
5330
  const projectData = loadJSON(options.projectConfigPath);
5331
5331
  if (projectData) {
5332
5332
  config = deepMerge(config, projectData);
@@ -7548,7 +7548,7 @@ var init_factory = __esm(() => {
7548
7548
  });
7549
7549
 
7550
7550
  // src/llm/model-loader.ts
7551
- async function getLoadedContextLength(baseUrl, model, timeoutMs = 3000) {
7551
+ async function probeModel(baseUrl, model, timeoutMs = 3000) {
7552
7552
  const base = baseUrl.replace(/\/$/, "").replace(/\/v1\/?$/, "");
7553
7553
  try {
7554
7554
  const response = await fetch(`${base}/api/v1/models`, {
@@ -7562,10 +7562,18 @@ async function getLoadedContextLength(baseUrl, model, timeoutMs = 3000) {
7562
7562
  if (!found)
7563
7563
  return null;
7564
7564
  const loadedInstance = (found.loaded_instances ?? [])[0];
7565
- const actual = loadedInstance?.config?.context_length ?? found.max_context_length;
7566
- if (typeof actual !== "number" || actual <= 0)
7565
+ const contextLength = loadedInstance?.config?.context_length ?? found.max_context_length;
7566
+ if (typeof contextLength !== "number" || contextLength <= 0)
7567
7567
  return null;
7568
- return { model: found.key ?? found.name ?? model, actual };
7568
+ const raw = found.capabilities;
7569
+ let capabilities;
7570
+ if (raw && typeof raw.vision === "boolean") {
7571
+ capabilities = { vision: raw.vision };
7572
+ if (typeof raw.trained_for_tool_use === "boolean") {
7573
+ capabilities.tools = raw.trained_for_tool_use;
7574
+ }
7575
+ }
7576
+ return { model: found.key ?? found.name ?? model, contextLength, capabilities };
7569
7577
  } catch {
7570
7578
  return null;
7571
7579
  }
@@ -15437,6 +15445,15 @@ var init_tool_output = __esm(() => {
15437
15445
  init_i18n();
15438
15446
  });
15439
15447
 
15448
+ // src/core/agent/tool-images.ts
15449
+ function queueToolImages(sink, images) {
15450
+ if (!sink || !images || images.length === 0)
15451
+ return;
15452
+ for (const image of images) {
15453
+ sink.addPendingImage(image);
15454
+ }
15455
+ }
15456
+
15440
15457
  // src/core/agent/tool-batch.ts
15441
15458
  class ToolBatchExecutor {
15442
15459
  deps;
@@ -15566,6 +15583,7 @@ class ToolBatchExecutor {
15566
15583
  arguments: call.arguments
15567
15584
  });
15568
15585
  answeredToolCallIds.add(call.id);
15586
+ queueToolImages(contextManager, result.images);
15569
15587
  const tokensAfterTool = contextManager.getEstimatedTokens();
15570
15588
  const costAfterTool = this.deps.costTracker.total;
15571
15589
  onTool?.({
@@ -21291,6 +21309,119 @@ var init_session = __esm(() => {
21291
21309
  init_i18n();
21292
21310
  });
21293
21311
 
21312
+ // src/llm/image-utils.ts
21313
+ var exports_image_utils = {};
21314
+ __export(exports_image_utils, {
21315
+ readClipboardImage: () => readClipboardImage,
21316
+ loadUrlAsDataUrl: () => loadUrlAsDataUrl,
21317
+ loadFileAsDataUrl: () => loadFileAsDataUrl,
21318
+ detectMime: () => detectMime,
21319
+ bufferToDataUrl: () => bufferToDataUrl
21320
+ });
21321
+ import { readFileSync as readFileSync20 } from "fs";
21322
+ import { extname as extname4 } from "path";
21323
+ function detectMime(filePath) {
21324
+ const ext = extname4(filePath).toLowerCase();
21325
+ return MIME_MAP[ext] ?? "image/png";
21326
+ }
21327
+ async function readClipboardImage() {
21328
+ if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21329
+ try {
21330
+ const img = Bun.Image.fromClipboard();
21331
+ if (img) {
21332
+ const buf = await img.resize(800, 800, { fit: "inside" }).jpeg({ quality: 60 }).buffer();
21333
+ return Buffer.from(buf);
21334
+ }
21335
+ } catch {}
21336
+ }
21337
+ return readClipboardFallback();
21338
+ }
21339
+ async function readClipboardFallback() {
21340
+ const { platform: platform7 } = await import("os");
21341
+ const { execSync } = await import("child_process");
21342
+ const { readFileSync: readFileSync21, unlinkSync: unlinkSync5 } = await import("fs");
21343
+ const { join: join29 } = await import("path");
21344
+ if (platform7() !== "linux")
21345
+ return null;
21346
+ const tmpPath = join29(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
21347
+ const commands = [
21348
+ `wl-paste --type image/png > "${tmpPath}" 2>/dev/null`,
21349
+ `xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`
21350
+ ];
21351
+ for (const cmd of commands) {
21352
+ try {
21353
+ execSync(cmd, { timeout: 5000 });
21354
+ const buf = readFileSync21(tmpPath);
21355
+ unlinkSync5(tmpPath);
21356
+ if (buf.length > 0)
21357
+ return buf;
21358
+ } catch {}
21359
+ }
21360
+ try {
21361
+ unlinkSync5(tmpPath);
21362
+ } catch {}
21363
+ return null;
21364
+ }
21365
+ async function loadFileAsDataUrl(filePath) {
21366
+ const buf = readFileSync20(filePath);
21367
+ if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21368
+ try {
21369
+ const img = new Bun.Image(buf);
21370
+ const { width } = await img.metadata();
21371
+ const targetWidth = Math.min(800, width || 800);
21372
+ const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
21373
+ return { dataUrl };
21374
+ } catch {}
21375
+ }
21376
+ const b64 = buf.toString("base64");
21377
+ const ext = filePath.split(".").pop()?.toLowerCase();
21378
+ const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
21379
+ return { dataUrl: `data:${mime};base64,${b64}` };
21380
+ }
21381
+ async function loadUrlAsDataUrl(url) {
21382
+ const resp = await fetch(url);
21383
+ if (!resp.ok) {
21384
+ throw new Error(`Failed to fetch image: ${resp.status} ${resp.statusText}`);
21385
+ }
21386
+ const buf = Buffer.from(await resp.arrayBuffer());
21387
+ if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21388
+ try {
21389
+ const img = new Bun.Image(buf);
21390
+ const { width } = await img.metadata();
21391
+ const targetWidth = Math.min(800, width || 800);
21392
+ const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
21393
+ return { dataUrl };
21394
+ } catch {}
21395
+ }
21396
+ const b64 = buf.toString("base64");
21397
+ return { dataUrl: `data:image/png;base64,${b64}` };
21398
+ }
21399
+ async function bufferToDataUrl(buf) {
21400
+ if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21401
+ try {
21402
+ const img = new Bun.Image(buf);
21403
+ const { width } = await img.metadata();
21404
+ const targetWidth = Math.min(800, width || 800);
21405
+ const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
21406
+ return { dataUrl };
21407
+ } catch {}
21408
+ }
21409
+ const b64 = buf.toString("base64");
21410
+ return { dataUrl: `data:image/png;base64,${b64}` };
21411
+ }
21412
+ var MIME_MAP;
21413
+ var init_image_utils = __esm(() => {
21414
+ MIME_MAP = {
21415
+ ".png": "image/png",
21416
+ ".jpg": "image/jpeg",
21417
+ ".jpeg": "image/jpeg",
21418
+ ".gif": "image/gif",
21419
+ ".webp": "image/webp",
21420
+ ".bmp": "image/bmp",
21421
+ ".svg": "image/svg+xml"
21422
+ };
21423
+ });
21424
+
21294
21425
  // src/tools/browser.ts
21295
21426
  import { join as join29 } from "path";
21296
21427
  import { mkdirSync as mkdirSync16 } from "fs";
@@ -21327,18 +21458,9 @@ function formatScreenshotForTextModel(snapshot) {
21327
21458
 
21328
21459
  ${snapshot}`;
21329
21460
  }
21330
- function modelSupportsVision(model) {
21331
- const m = model.toLowerCase();
21332
- return /vision|\bvl\b|qwen.*vl|gpt-4|claude|gemini|llava|pixtral/.test(m);
21333
- }
21334
- function formatScreenshotResult(snapshot, screenshot, supportsVision) {
21335
- if (!supportsVision) {
21336
- return formatScreenshotForTextModel(snapshot);
21337
- }
21338
- const base642 = screenshot.toString("base64");
21339
- return `${snapshot}
21340
-
21341
- [Screenshot: data:image/png;base64,${base642}]`;
21461
+ async function screenshotToImagePart(screenshot) {
21462
+ const { dataUrl } = await bufferToDataUrl(screenshot);
21463
+ return { type: "image_url", image_url: { url: dataUrl } };
21342
21464
  }
21343
21465
  function createBrowserTool() {
21344
21466
  return {
@@ -21403,12 +21525,15 @@ function createBrowserTool() {
21403
21525
  }
21404
21526
  const s = getSession(ctx);
21405
21527
  const result = await serialize(() => s.execute(action, args));
21406
- const supportsVision = modelSupportsVision(ctx.config.model);
21407
21528
  if (result.screenshot) {
21408
- return {
21409
- success: result.success,
21410
- output: formatScreenshotResult(result.output, result.screenshot, supportsVision)
21411
- };
21529
+ if (ctx.capabilities?.vision) {
21530
+ return {
21531
+ success: result.success,
21532
+ output: result.output,
21533
+ images: [await screenshotToImagePart(result.screenshot)]
21534
+ };
21535
+ }
21536
+ return { success: result.success, output: formatScreenshotForTextModel(result.output) };
21412
21537
  }
21413
21538
  return { success: result.success, output: result.output };
21414
21539
  }
@@ -21427,125 +21552,13 @@ var session = null, sessionFactory, actionChain;
21427
21552
  var init_browser = __esm(() => {
21428
21553
  init_session();
21429
21554
  init_types();
21555
+ init_image_utils();
21430
21556
  init_i18n();
21431
21557
  init_output();
21432
21558
  init_utils();
21433
21559
  actionChain = Promise.resolve();
21434
21560
  });
21435
21561
 
21436
- // src/llm/image-utils.ts
21437
- var exports_image_utils = {};
21438
- __export(exports_image_utils, {
21439
- readClipboardImage: () => readClipboardImage,
21440
- loadUrlAsDataUrl: () => loadUrlAsDataUrl,
21441
- loadFileAsDataUrl: () => loadFileAsDataUrl,
21442
- detectMime: () => detectMime,
21443
- bufferToDataUrl: () => bufferToDataUrl
21444
- });
21445
- import { readFileSync as readFileSync20 } from "fs";
21446
- import { extname as extname4 } from "path";
21447
- function detectMime(filePath) {
21448
- const ext = extname4(filePath).toLowerCase();
21449
- return MIME_MAP[ext] ?? "image/png";
21450
- }
21451
- async function readClipboardImage() {
21452
- if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21453
- try {
21454
- const img = Bun.Image.fromClipboard();
21455
- if (img) {
21456
- const buf = await img.resize(800, 800, { fit: "inside" }).jpeg({ quality: 60 }).buffer();
21457
- return Buffer.from(buf);
21458
- }
21459
- } catch {}
21460
- }
21461
- return readClipboardFallback();
21462
- }
21463
- async function readClipboardFallback() {
21464
- const { platform: platform7 } = await import("os");
21465
- const { execSync } = await import("child_process");
21466
- const { readFileSync: readFileSync21, unlinkSync: unlinkSync5 } = await import("fs");
21467
- const { join: join30 } = await import("path");
21468
- if (platform7() !== "linux")
21469
- return null;
21470
- const tmpPath = join30(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
21471
- const commands = [
21472
- `wl-paste --type image/png > "${tmpPath}" 2>/dev/null`,
21473
- `xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`
21474
- ];
21475
- for (const cmd of commands) {
21476
- try {
21477
- execSync(cmd, { timeout: 5000 });
21478
- const buf = readFileSync21(tmpPath);
21479
- unlinkSync5(tmpPath);
21480
- if (buf.length > 0)
21481
- return buf;
21482
- } catch {}
21483
- }
21484
- try {
21485
- unlinkSync5(tmpPath);
21486
- } catch {}
21487
- return null;
21488
- }
21489
- async function loadFileAsDataUrl(filePath) {
21490
- const buf = readFileSync20(filePath);
21491
- if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21492
- try {
21493
- const img = new Bun.Image(buf);
21494
- const { width } = await img.metadata();
21495
- const targetWidth = Math.min(800, width || 800);
21496
- const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
21497
- return { dataUrl };
21498
- } catch {}
21499
- }
21500
- const b64 = buf.toString("base64");
21501
- const ext = filePath.split(".").pop()?.toLowerCase();
21502
- const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
21503
- return { dataUrl: `data:${mime};base64,${b64}` };
21504
- }
21505
- async function loadUrlAsDataUrl(url) {
21506
- const resp = await fetch(url);
21507
- if (!resp.ok) {
21508
- throw new Error(`Failed to fetch image: ${resp.status} ${resp.statusText}`);
21509
- }
21510
- const buf = Buffer.from(await resp.arrayBuffer());
21511
- if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21512
- try {
21513
- const img = new Bun.Image(buf);
21514
- const { width } = await img.metadata();
21515
- const targetWidth = Math.min(800, width || 800);
21516
- const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
21517
- return { dataUrl };
21518
- } catch {}
21519
- }
21520
- const b64 = buf.toString("base64");
21521
- return { dataUrl: `data:image/png;base64,${b64}` };
21522
- }
21523
- async function bufferToDataUrl(buf) {
21524
- if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
21525
- try {
21526
- const img = new Bun.Image(buf);
21527
- const { width } = await img.metadata();
21528
- const targetWidth = Math.min(800, width || 800);
21529
- const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
21530
- return { dataUrl };
21531
- } catch {}
21532
- }
21533
- const b64 = buf.toString("base64");
21534
- return { dataUrl: `data:image/png;base64,${b64}` };
21535
- }
21536
- var MIME_MAP;
21537
- var init_image_utils = __esm(() => {
21538
- MIME_MAP = {
21539
- ".png": "image/png",
21540
- ".jpg": "image/jpeg",
21541
- ".jpeg": "image/jpeg",
21542
- ".gif": "image/gif",
21543
- ".webp": "image/webp",
21544
- ".bmp": "image/bmp",
21545
- ".svg": "image/svg+xml"
21546
- };
21547
- });
21548
-
21549
21562
  // src/tools/attach-image.ts
21550
21563
  import { existsSync as existsSync35 } from "fs";
21551
21564
  import { resolve as resolve17 } from "path";
@@ -25292,7 +25305,7 @@ class BrowserModule {
25292
25305
  "Workflow: open(url) → read snapshot → click/type on element numbers → read updated snapshot.",
25293
25306
  'Each snapshot includes a "Content" section with the visible page text, a "Console" section with browser messages, and network errors. Use these to understand the page, detect JS errors, and verify the result of your actions.',
25294
25307
  'Always start with "open" action. Use element numbers from snapshot for click/type targets.',
25295
- 'Use "screenshot" action only if the model supports vision. Otherwise rely on the text snapshot.'
25308
+ 'The "screenshot" action is delivered automatically: a vision model receives the image, a text-only model receives the text snapshot instead. You do not need to decide — just call it and read what you get.'
25296
25309
  ].join(`
25297
25310
  `),
25298
25311
  priority: "normal",
@@ -27705,6 +27718,19 @@ var init_context_log = __esm(() => {
27705
27718
  SEP2 = "─".repeat(78);
27706
27719
  });
27707
27720
 
27721
+ // src/modules/providers/capabilities.ts
27722
+ function resolveModelCapabilities(preset, probe, override) {
27723
+ const base = preset ? { vision: preset.vision, tools: preset.tools } : { ...DEFAULT_MODEL_CAPABILITIES };
27724
+ return { ...base, ...probe ?? {}, ...override ?? {} };
27725
+ }
27726
+ var DEFAULT_MODEL_CAPABILITIES;
27727
+ var init_capabilities = __esm(() => {
27728
+ DEFAULT_MODEL_CAPABILITIES = {
27729
+ vision: false,
27730
+ tools: false
27731
+ };
27732
+ });
27733
+
27708
27734
  // src/core/environment.ts
27709
27735
  import { existsSync as existsSync50, readFileSync as readFileSync32, readdirSync as readdirSync16 } from "fs";
27710
27736
  import { spawnSync as spawnSync3 } from "child_process";
@@ -28336,7 +28362,9 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
28336
28362
  lines.push(`Runtime verification: when a bug is described in terms of runtime behavior (what happens when the app runs), reproduce it — run the app / build / browser tool — and confirm the fix against actual runtime output before calling "plan update step=N status=done". Check existing servers first with process_list/process_log: a stale dev server started BEFORE your change still runs the old pipeline and keeps failing even after the files on disk are fixed. Restart or kill it (process_kill) and verify the exact endpoint/port the user reported — a fresh server on a new port does NOT prove the user's error is gone.`);
28337
28363
  const sec = config.security;
28338
28364
  if (sec?.enabled) {
28339
- const secLines = [`SECURITY RESTRICTIONS (enforced — blocked calls return "[SECURITY BLOCKED]", do not retry them):`];
28365
+ const secLines = [
28366
+ `SECURITY RESTRICTIONS (enforced — blocked calls return "[SECURITY BLOCKED]", do not retry them):`
28367
+ ];
28340
28368
  if (sec.bash?.enabled && sec.bash.blacklist.length > 0) {
28341
28369
  const cmds = sec.bash.blacklist.slice(0, 15).join(", ");
28342
28370
  secLines.push(`Forbidden bash commands: ${cmds}${sec.bash.blacklist.length > 15 ? ", …" : ""}.`);
@@ -28418,19 +28446,23 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
28418
28446
  logger4.info(t("env.model_loaded", { model: config.model, seconds: String(loadResult.loadTime) }));
28419
28447
  }
28420
28448
  }
28449
+ const capabilitiesRef = {};
28421
28450
  const contextProbePromise = (async () => {
28422
28451
  try {
28423
- const probe = await getLoadedContextLength(config.provider.baseUrl, config.model);
28452
+ const probe = await probeModel(config.provider.baseUrl, config.model);
28424
28453
  if (!probe)
28425
28454
  return null;
28426
- if (probe.actual < config.contextWindow) {
28427
- logger4.logSilent("warn", `Context probe: model "${probe.model}" is loaded with ${probe.actual} tokens, but contextWindow is configured as ${config.contextWindow} — overflow/429 risk. Lower contextWindow or reload the model with a larger context.`);
28428
- } else if (probe.actual > config.contextWindow) {
28429
- logger4.logSilent("info", `Context probe: model "${probe.model}" supports ${probe.actual} tokens — consider "mma context ${probe.actual}" to use the full window.`);
28455
+ if (probe.capabilities)
28456
+ capabilitiesRef.probe = probe.capabilities;
28457
+ const contextProbe = { model: probe.model, actual: probe.contextLength };
28458
+ if (probe.contextLength < config.contextWindow) {
28459
+ logger4.logSilent("warn", `Context probe: model "${probe.model}" is loaded with ${probe.contextLength} tokens, but contextWindow is configured as ${config.contextWindow} — overflow/429 risk. Lower contextWindow or reload the model with a larger context.`);
28460
+ } else if (probe.contextLength > config.contextWindow) {
28461
+ logger4.logSilent("info", `Context probe: model "${probe.model}" supports ${probe.contextLength} tokens — consider "mma context ${probe.contextLength}" to use the full window.`);
28430
28462
  } else {
28431
- logger4.logSilent("debug", `Context probe: configured contextWindow matches loaded context (${probe.actual})`);
28463
+ logger4.logSilent("debug", `Context probe: configured contextWindow matches loaded context (${probe.contextLength})`);
28432
28464
  }
28433
- return probe;
28465
+ return contextProbe;
28434
28466
  } catch {
28435
28467
  return null;
28436
28468
  }
@@ -28609,6 +28641,14 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
28609
28641
  get: () => sessionManager,
28610
28642
  configurable: true
28611
28643
  });
28644
+ Object.defineProperty(toolCtx, "capabilities", {
28645
+ get: () => {
28646
+ const cfg = configRef.current;
28647
+ const spec = BUILTIN_PROVIDERS.find((p) => p.type === cfg.provider.type);
28648
+ return resolveModelCapabilities(spec?.capabilities, capabilitiesRef.probe, cfg.modelCapabilities?.[cfg.model]);
28649
+ },
28650
+ configurable: true
28651
+ });
28612
28652
  const toolExecutor = new ToolExecutor(toolRegistry, toolCtx, pluginManager);
28613
28653
  toolCtx.llmProvider = llmProvider;
28614
28654
  toolCtx.toolExecutor = toolExecutor;
@@ -28832,6 +28872,7 @@ var init_bootstrap = __esm(() => {
28832
28872
  init_i18n();
28833
28873
  init_agent();
28834
28874
  init_context_log();
28875
+ init_capabilities();
28835
28876
  init_version();
28836
28877
  init_environment();
28837
28878
  init_utils();
@@ -29592,11 +29633,12 @@ async function runSetup(externalRl) {
29592
29633
  const configureSecurity = await ask(rl, t("setup.security_configure"), "n");
29593
29634
  let securityBashBlock = false;
29594
29635
  let securityFlagsBlock = false;
29595
- let securityPathsDeny = true;
29636
+ let securityPathsRestrict = false;
29596
29637
  if (configureSecurity.toLowerCase() === "y" || configureSecurity.toLowerCase() === "yes") {
29597
29638
  securityBashBlock = (await ask(rl, t("setup.security_bash_block"), "n")).toLowerCase() === "y";
29598
29639
  securityFlagsBlock = (await ask(rl, t("setup.security_flags_block"), "n")).toLowerCase() === "y";
29599
- securityPathsDeny = (await ask(rl, t("setup.security_paths_deny"), "Y")).toLowerCase() !== "n";
29640
+ const pathsAnswer = (await ask(rl, t("setup.security_paths_restrict"), "Y")).toLowerCase();
29641
+ securityPathsRestrict = pathsAnswer !== "n" && pathsAnswer !== "no";
29600
29642
  }
29601
29643
  if (ownRl) {
29602
29644
  rl.close();
@@ -29611,7 +29653,7 @@ async function runSetup(externalRl) {
29611
29653
  locale,
29612
29654
  securityBashBlock,
29613
29655
  securityFlagsBlock,
29614
- securityPathsDeny
29656
+ securityPathsRestrict
29615
29657
  };
29616
29658
  getDefaultChannel().writeLine(pc.green(pc.bold(t("setup.complete"))));
29617
29659
  const summary = [
@@ -29652,6 +29694,73 @@ var init_setup = __esm(() => {
29652
29694
  ];
29653
29695
  });
29654
29696
 
29697
+ // src/cli/setup-order.ts
29698
+ function applyAnswersToConfig(config, a) {
29699
+ config.provider = {
29700
+ ...config.provider,
29701
+ type: a.provider,
29702
+ baseUrl: a.apiBase,
29703
+ apiKey: a.apiKey
29704
+ };
29705
+ config.model = a.model;
29706
+ config.contextWindow = a.contextWindow;
29707
+ config.maxToolIterations = a.maxToolIterations;
29708
+ config.locale = a.locale;
29709
+ if ((config.provider.entries?.length ?? 0) > 0) {
29710
+ const entries = config.provider.entries ?? [];
29711
+ const activeName = config.provider.active || entries[0]?.label || entries[0]?.type;
29712
+ const idx = entries.findIndex((e) => e.label === activeName || e.type === activeName);
29713
+ const target = idx >= 0 ? idx : 0;
29714
+ const current = entries[target];
29715
+ const keepLabel = Boolean(current.label && current.label !== current.type);
29716
+ const updated = {
29717
+ ...current,
29718
+ type: a.provider,
29719
+ baseUrl: a.apiBase,
29720
+ apiKey: a.apiKey,
29721
+ contextWindow: a.contextWindow,
29722
+ ...keepLabel ? {} : { label: a.provider }
29723
+ };
29724
+ const next = entries.slice();
29725
+ next[target] = updated;
29726
+ config.provider.entries = next;
29727
+ config.provider.active = updated.label || updated.type;
29728
+ }
29729
+ if (config.security) {
29730
+ config.security.enabled = true;
29731
+ config.security.bash.enabled = true;
29732
+ config.security.bash.blockDangerousFlags = a.securityFlagsBlock || config.security.bash.blockDangerousFlags;
29733
+ if (a.securityBashBlock) {
29734
+ config.security.bash.blacklist = [
29735
+ ...new Set([...config.security.bash.blacklist, ...SECURITY_BASH_BLOCKLIST])
29736
+ ];
29737
+ }
29738
+ config.security.paths.enabled = a.securityPathsRestrict;
29739
+ }
29740
+ return config;
29741
+ }
29742
+ var SECURITY_BASH_BLOCKLIST;
29743
+ var init_setup_order = __esm(() => {
29744
+ SECURITY_BASH_BLOCKLIST = [
29745
+ "rm",
29746
+ "dd",
29747
+ "chmod",
29748
+ "wget",
29749
+ "curl",
29750
+ "scp",
29751
+ "ssh",
29752
+ "nc",
29753
+ "netcat",
29754
+ "sudo",
29755
+ "su",
29756
+ "kill",
29757
+ "pkill",
29758
+ "killall",
29759
+ "shutdown",
29760
+ "reboot"
29761
+ ];
29762
+ });
29763
+
29655
29764
  // src/modules/indexer/map-command.ts
29656
29765
  async function runMapAction(module, action, query) {
29657
29766
  const summary = async () => {
@@ -37947,7 +38056,7 @@ __export(exports_repl_commands, {
37947
38056
  registerAllCommands: () => registerAllCommands,
37948
38057
  COMMAND_GROUPS: () => COMMAND_GROUPS
37949
38058
  });
37950
- import { join as join54, dirname as dirname25 } from "path";
38059
+ import { join as join55, dirname as dirname25 } from "path";
37951
38060
  import { existsSync as existsSync60 } from "fs";
37952
38061
  function registerAllCommands(ctx) {
37953
38062
  registerBuiltinCommands(ctx);
@@ -38148,14 +38257,8 @@ function registerConfigCommands(ctx) {
38148
38257
  ctx.output.writeLine(pc.yellow(t("repl.wizard_running")));
38149
38258
  await ctx.withExclusiveInput(async () => {
38150
38259
  const answers = await runSetup(ctx.rl);
38151
- const configPath = join54(ctx.configDir, "config.json");
38152
- ctx.config.provider.type = answers.provider;
38153
- ctx.config.provider.baseUrl = answers.apiBase;
38154
- ctx.config.provider.apiKey = answers.apiKey;
38155
- ctx.config.model = answers.model;
38156
- ctx.config.contextWindow = answers.contextWindow;
38157
- ctx.config.maxToolIterations = answers.maxToolIterations;
38158
- ctx.config.locale = answers.locale;
38260
+ const configPath = join55(ctx.configDir, "config.json");
38261
+ applyAnswersToConfig(ctx.config, answers);
38159
38262
  saveConfig(ctx.config, configPath, dirname25(configPath));
38160
38263
  await ctx.agent.reconfigure(ctx.config);
38161
38264
  ctx.output.writeLine(pc.green(t("cli.config_saved")));
@@ -38279,7 +38382,7 @@ function registerProviderCommands(ctx) {
38279
38382
  });
38280
38383
  ctx.config.provider.entries = entries;
38281
38384
  ctx.config.provider.active = ctx.config.provider.active || ctx.config.provider.type;
38282
- const configPath = join54(ctx.configDir, "config.json");
38385
+ const configPath = join55(ctx.configDir, "config.json");
38283
38386
  saveConfig(ctx.config, configPath, dirname25(configPath));
38284
38387
  await ctx.agent.reconfigure(ctx.config);
38285
38388
  ctx.output.writeLine(pc.green(t("cli.provider_added", { name })));
@@ -38346,7 +38449,7 @@ function registerProviderCommands(ctx) {
38346
38449
  return;
38347
38450
  }
38348
38451
  ctx.config.model = name;
38349
- const configPath = join54(ctx.configDir, "config.json");
38452
+ const configPath = join55(ctx.configDir, "config.json");
38350
38453
  saveConfig(ctx.config, configPath, dirname25(configPath));
38351
38454
  await ctx.agent.reconfigure(ctx.config);
38352
38455
  ctx.output.writeLine(pc.green(t("repl.model_set", { name })));
@@ -38375,7 +38478,7 @@ function registerProviderCommands(ctx) {
38375
38478
  usage: t("repl.context_usage"),
38376
38479
  action: async (args) => {
38377
38480
  const save = async () => {
38378
- const configPath = join54(ctx.configDir, "config.json");
38481
+ const configPath = join55(ctx.configDir, "config.json");
38379
38482
  saveConfig(ctx.config, configPath, dirname25(configPath));
38380
38483
  await ctx.agent.reconfigure(ctx.config);
38381
38484
  };
@@ -38768,11 +38871,11 @@ function registerSkillCommands(ctx) {
38768
38871
  });
38769
38872
  }
38770
38873
  async function runConfigMigrate(ctx) {
38771
- const { hasDomainFiles: hasDomainFiles3 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
38874
+ const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
38772
38875
  const { loadConfig: loadCfg } = await Promise.resolve().then(() => (init_config2(), exports_config));
38773
38876
  const configDir = ctx.configDir;
38774
- const configPath = join54(configDir, "config.json");
38775
- if (hasDomainFiles3(configDir)) {
38877
+ const configPath = join55(configDir, "config.json");
38878
+ if (hasDomainFiles2(configDir)) {
38776
38879
  ctx.output.writeLine(pc.yellow(t("config.migrate_no_legacy")));
38777
38880
  return;
38778
38881
  }
@@ -38781,11 +38884,11 @@ async function runConfigMigrate(ctx) {
38781
38884
  return;
38782
38885
  }
38783
38886
  ctx.output.writeLine(t("config.migrate_start"));
38784
- const { config } = loadCfg({ configDir, projectConfigPath: join54(configDir, ".mmrc") });
38887
+ const { config } = loadCfg({ configDir, projectConfigPath: join55(configDir, ".mmrc") });
38785
38888
  saveConfig(config, configPath, configDir);
38786
- const { renameSync: renameSync4, readdirSync: readdirSync19 } = await import("fs");
38889
+ const { renameSync: renameSync4, readdirSync: readdirSync20 } = await import("fs");
38787
38890
  renameSync4(configPath, configPath + ".bak");
38788
- const domainFiles = readdirSync19(join54(configDir, "config")).filter((f) => f.endsWith(".json"));
38891
+ const domainFiles = readdirSync20(join55(configDir, "config")).filter((f) => f.endsWith(".json"));
38789
38892
  ctx.output.writeLine(pc.green(t("config.migrate_done", { count: String(domainFiles.length) })));
38790
38893
  }
38791
38894
  var version2, COMMAND_GROUPS;
@@ -38795,6 +38898,7 @@ var init_repl_commands = __esm(() => {
38795
38898
  init_table();
38796
38899
  init_i18n();
38797
38900
  init_setup();
38901
+ init_setup_order();
38798
38902
  init_config2();
38799
38903
  init_presets();
38800
38904
  init_budget();
@@ -38850,6 +38954,7 @@ var {
38850
38954
  init_bootstrap();
38851
38955
  init_config2();
38852
38956
  init_setup();
38957
+ init_setup_order();
38853
38958
  init_i18n();
38854
38959
  init_colors();
38855
38960
  import { join as join53, dirname as dirname24 } from "path";
@@ -39363,7 +39468,7 @@ function createSecurityCommand(program2) {
39363
39468
  const auditNotifier = security.auditNotifier || {};
39364
39469
  getDefaultChannel().writeLine(t("cli.security.current_policy"));
39365
39470
  getDefaultChannel().writeLine(` ${t("cli.security.bash_enabled")}: ${bash.blacklist?.length > 0 ? t("cli.yes") : t("cli.no")}`);
39366
- getDefaultChannel().writeLine(` ${t("cli.security.path_validation")}: ${paths.denied?.length > 0 ? t("cli.yes") : t("cli.no")}`);
39471
+ getDefaultChannel().writeLine(` ${t("cli.security.path_validation")}: ${paths.enabled ? t("cli.yes") : t("cli.no")}`);
39367
39472
  getDefaultChannel().writeLine(` ${t("cli.security.network_validation")}: ${network.deniedDomains?.length > 0 || network.allowedDomains?.length > 0 ? t("cli.yes") : t("cli.no")}`);
39368
39473
  getDefaultChannel().writeLine(` ${t("cli.security.content_scanning")}: ${contentScan.enabled ? t("cli.yes") : t("cli.no")}`);
39369
39474
  getDefaultChannel().writeLine(` ${t("cli.security.max_recursion")}: ${security.maxRecursionDepth ?? 3}`);
@@ -39671,50 +39776,60 @@ function extractLatestChangelog(changelog) {
39671
39776
 
39672
39777
  // src/cli/commands.ts
39673
39778
  init_output();
39779
+
39780
+ // src/config/redact.ts
39781
+ var SECRET_KEYS = new Set([
39782
+ "apiKey",
39783
+ "api_key",
39784
+ "apikey",
39785
+ "token",
39786
+ "tokens",
39787
+ "secret",
39788
+ "password",
39789
+ "authorization",
39790
+ "webhookUrl"
39791
+ ]);
39792
+ var SECRET_MAPS = new Set(["headers", "env"]);
39793
+ var MASK = "***";
39794
+ function walk(value, parentKey, depth = 0) {
39795
+ if (depth > 100)
39796
+ return value;
39797
+ if (typeof value === "string") {
39798
+ return parentKey && SECRET_KEYS.has(parentKey) ? MASK : value;
39799
+ }
39800
+ if (Array.isArray(value)) {
39801
+ return value.map((v) => walk(v, parentKey, depth + 1));
39802
+ }
39803
+ if (value && typeof value === "object" && !(value instanceof RegExp)) {
39804
+ if (parentKey && SECRET_MAPS.has(parentKey)) {
39805
+ const masked = {};
39806
+ for (const k of Object.keys(value))
39807
+ masked[k] = MASK;
39808
+ return masked;
39809
+ }
39810
+ const out = {};
39811
+ for (const [k, v] of Object.entries(value)) {
39812
+ out[k] = walk(v, k, depth + 1);
39813
+ }
39814
+ return out;
39815
+ }
39816
+ return value;
39817
+ }
39818
+ function redactConfigSecrets(config) {
39819
+ return walk(config);
39820
+ }
39821
+
39822
+ // src/cli/commands.ts
39674
39823
  var version = readMmaVersion();
39824
+ function configForDisplay(config, showSecrets) {
39825
+ return showSecrets ? config : redactConfigSecrets(config);
39826
+ }
39675
39827
  function buildInitCommand(program2) {
39676
39828
  program2.command("init").description(t("cli.init")).action(async () => {
39677
39829
  const answers = await runSetup();
39678
39830
  const configPath = join53(homedir17(), ".mma", "config.json");
39679
39831
  const { config } = await bootstrap();
39680
- config.provider.type = answers.provider;
39681
- config.provider.baseUrl = answers.apiBase;
39682
- config.provider.apiKey = answers.apiKey;
39683
- config.model = answers.model;
39684
- config.contextWindow = answers.contextWindow;
39685
- config.maxToolIterations = answers.maxToolIterations;
39686
- config.locale = answers.locale;
39687
- if (config.security) {
39688
- config.security.enabled = true;
39689
- config.security.bash.enabled = true;
39690
- config.security.bash.blockDangerousFlags = answers.securityFlagsBlock || config.security.bash.blockDangerousFlags;
39691
- if (answers.securityBashBlock) {
39692
- config.security.bash.blacklist = [
39693
- ...new Set([
39694
- ...config.security.bash.blacklist,
39695
- "rm",
39696
- "dd",
39697
- "chmod",
39698
- "wget",
39699
- "curl",
39700
- "scp",
39701
- "ssh",
39702
- "nc",
39703
- "netcat",
39704
- "sudo",
39705
- "su",
39706
- "kill",
39707
- "pkill",
39708
- "killall",
39709
- "shutdown",
39710
- "reboot"
39711
- ])
39712
- ];
39713
- }
39714
- if (!answers.securityPathsDeny) {
39715
- config.security.paths.denied = [];
39716
- }
39717
- }
39832
+ applyAnswersToConfig(config, answers);
39718
39833
  saveConfig(config, configPath, dirname24(configPath));
39719
39834
  getDefaultChannel().writeLine(t("cli.config_saved"));
39720
39835
  });
@@ -39745,16 +39860,16 @@ function buildConfigCommands(program2) {
39745
39860
  saveConfig(config, configPath, dirname24(configPath));
39746
39861
  getDefaultChannel().writeLine(t("cli.set_done", { key, value }));
39747
39862
  });
39748
- configCmd.command("show").description(t("cli.show_config")).action(async () => {
39863
+ configCmd.command("show").description(t("cli.show_config")).option("--show-secrets", "Print secrets (API keys, tokens) unredacted").action(async (opts) => {
39749
39864
  const { config } = await bootstrap();
39750
- writeMachineJson(config);
39865
+ writeMachineJson(configForDisplay(config, opts.showSecrets));
39751
39866
  });
39752
39867
  configCmd.command("migrate").description(t("cli.migrate_config")).action(async () => {
39753
39868
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
39754
- const { hasDomainFiles: hasDomainFiles3 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
39869
+ const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
39755
39870
  const configDir = join53(homedir17(), ".mma");
39756
39871
  const configPath = join53(configDir, "config.json");
39757
- if (hasDomainFiles3(configDir)) {
39872
+ if (hasDomainFiles2(configDir)) {
39758
39873
  getDefaultChannel().writeLine(pc.yellow(t("config.migrate_no_legacy")));
39759
39874
  return;
39760
39875
  }
@@ -40176,6 +40291,7 @@ import * as readline2 from "readline";
40176
40291
 
40177
40292
  // src/ui/line-editor.ts
40178
40293
  init_string_width();
40294
+ init_colors();
40179
40295
  import * as readline from "readline";
40180
40296
 
40181
40297
  // src/ui/line-math.ts
@@ -40288,6 +40404,7 @@ class LineEditor {
40288
40404
  pasteLastWasCR = false;
40289
40405
  lastWasCR = false;
40290
40406
  questionCb = null;
40407
+ completionSession = null;
40291
40408
  rawMode = false;
40292
40409
  listeners = {};
40293
40410
  dsrDataHandler;
@@ -40421,6 +40538,7 @@ class LineEditor {
40421
40538
  this.col = 0;
40422
40539
  this.historyIndex = -1;
40423
40540
  this.stash = null;
40541
+ this.completionSession = null;
40424
40542
  this.invalidateAnchor();
40425
40543
  if (this.input.isTTY)
40426
40544
  this.render();
@@ -40431,6 +40549,7 @@ class LineEditor {
40431
40549
  this.lines = [""];
40432
40550
  this.row = 0;
40433
40551
  this.col = 0;
40552
+ this.completionSession = null;
40434
40553
  this.invalidateAnchor();
40435
40554
  if (this.input.isTTY)
40436
40555
  this.render();
@@ -40496,6 +40615,27 @@ class LineEditor {
40496
40615
  }
40497
40616
  return;
40498
40617
  }
40618
+ if (this.completionSession) {
40619
+ const ck = key;
40620
+ const plain = !ck.ctrl && !ck.meta;
40621
+ if (ck?.name === "down" && plain) {
40622
+ this.cycleCompletion(1);
40623
+ return;
40624
+ }
40625
+ if (ck?.name === "up" && plain) {
40626
+ this.cycleCompletion(-1);
40627
+ return;
40628
+ }
40629
+ if (ck?.name === "tab") {
40630
+ this.cycleCompletion(1);
40631
+ return;
40632
+ }
40633
+ if (ck?.name === "escape") {
40634
+ this.cancelCompletion();
40635
+ return;
40636
+ }
40637
+ this.completionSession = null;
40638
+ }
40499
40639
  if (this.onKeyInput && this.onKeyInput(str, key))
40500
40640
  return;
40501
40641
  const k = key;
@@ -40674,6 +40814,7 @@ class LineEditor {
40674
40814
  }
40675
40815
  }
40676
40816
  submit() {
40817
+ this.completionSession = null;
40677
40818
  if (this.questionCb) {
40678
40819
  const cb = this.questionCb;
40679
40820
  this.questionCb = null;
@@ -40729,6 +40870,7 @@ class LineEditor {
40729
40870
  this.lines = [""];
40730
40871
  this.row = 0;
40731
40872
  this.col = 0;
40873
+ this.completionSession = null;
40732
40874
  this.invalidateAnchor();
40733
40875
  this.render();
40734
40876
  if (this.onInterrupt) {
@@ -40753,6 +40895,7 @@ class LineEditor {
40753
40895
  this.lines[this.row] = chars.join("");
40754
40896
  }
40755
40897
  insertText(text) {
40898
+ this.completionSession = null;
40756
40899
  const sanitized = text.replace(/\r\n/g, `
40757
40900
  `).replace(/\r/g, "");
40758
40901
  const chars = this.currentChars();
@@ -40967,6 +41110,7 @@ class LineEditor {
40967
41110
  }
40968
41111
  }
40969
41112
  applyHistoryEntry() {
41113
+ this.completionSession = null;
40970
41114
  const entry = this.history[this.history.length - 1 - this.historyIndex];
40971
41115
  this.lines = entry.split(`
40972
41116
  `);
@@ -40976,6 +41120,49 @@ class LineEditor {
40976
41120
  this.col = charLen(this.lines[this.row]);
40977
41121
  this.render();
40978
41122
  }
41123
+ replaceCompletionToken(from, removeText, insertText) {
41124
+ const line = this.lines[this.row];
41125
+ const head = line.slice(0, from);
41126
+ const tail = line.slice(from + removeText.length);
41127
+ this.lines[this.row] = head + insertText + tail;
41128
+ this.col = charLen(head) + charLen(insertText);
41129
+ }
41130
+ partialStart(line, partial) {
41131
+ const chars = Array.from(line);
41132
+ const beforeCursor = chars.slice(0, this.col).join("");
41133
+ let from = beforeCursor.lastIndexOf(partial);
41134
+ if (from === -1)
41135
+ from = line.lastIndexOf(partial);
41136
+ return from;
41137
+ }
41138
+ applyCompletionCandidate(index) {
41139
+ const s = this.completionSession;
41140
+ if (!s)
41141
+ return;
41142
+ const chosen = s.items[index].value;
41143
+ this.replaceCompletionToken(s.from, s.inserted, chosen);
41144
+ s.index = index;
41145
+ s.inserted = chosen;
41146
+ this.render();
41147
+ }
41148
+ cycleCompletion(delta) {
41149
+ const s = this.completionSession;
41150
+ if (!s)
41151
+ return;
41152
+ const n = s.items.length;
41153
+ if (n === 0)
41154
+ return;
41155
+ const next = (s.index + delta + n) % n;
41156
+ this.applyCompletionCandidate(next);
41157
+ }
41158
+ cancelCompletion() {
41159
+ const s = this.completionSession;
41160
+ if (!s)
41161
+ return;
41162
+ this.replaceCompletionToken(s.from, s.inserted, s.original);
41163
+ this.completionSession = null;
41164
+ this.render();
41165
+ }
40979
41166
  tabComplete() {
40980
41167
  if (!this.completer)
40981
41168
  return;
@@ -40986,28 +41173,53 @@ class LineEditor {
40986
41173
  return;
40987
41174
  }
40988
41175
  if (matches.length === 1) {
40989
- const chars = Array.from(line);
40990
- const beforeCursor = chars.slice(0, this.col).join("");
40991
- let from = beforeCursor.lastIndexOf(partial);
40992
- if (from === -1)
40993
- from = line.lastIndexOf(partial);
40994
- if (from === -1) {
41176
+ const from2 = this.partialStart(line, partial);
41177
+ if (from2 === -1) {
40995
41178
  this.output.write("\x07");
40996
41179
  return;
40997
41180
  }
40998
- const head = line.slice(0, from);
40999
- const tail = line.slice(from + partial.length);
41000
- this.lines[this.row] = head + matches[0] + tail;
41001
- this.col = charLen(head) + charLen(matches[0]);
41181
+ this.replaceCompletionToken(from2, partial, matches[0].value);
41002
41182
  this.render();
41003
41183
  return;
41004
41184
  }
41005
- const rows = matches.map((m) => " " + m);
41185
+ const from = this.partialStart(line, partial);
41186
+ if (from === -1) {
41187
+ this.output.write("\x07");
41188
+ return;
41189
+ }
41190
+ const width = this.columns();
41191
+ const limit = width - 1;
41192
+ const rows = matches.map((m) => {
41193
+ const plain = m.description ? ` ${m.value} ${m.description}` : ` ${m.value}`;
41194
+ if (limit <= 0)
41195
+ return "";
41196
+ if (stringWidth(plain) <= limit) {
41197
+ return m.description ? ` ${m.value} ${pc.dim(m.description)}` : ` ${m.value}`;
41198
+ }
41199
+ let acc = "";
41200
+ let w = 0;
41201
+ for (const ch of plain) {
41202
+ const cw = stringWidth(ch);
41203
+ if (w + cw > limit - 1)
41204
+ break;
41205
+ acc += ch;
41206
+ w += cw;
41207
+ }
41208
+ return acc + "…";
41209
+ });
41006
41210
  this.output.write(`\r
41007
41211
  ` + rows.join(`\r
41008
41212
  `) + `\r
41009
41213
  `);
41010
41214
  this.prevCursorRow += rows.length + 1;
41215
+ this.completionSession = {
41216
+ items: matches,
41217
+ index: 0,
41218
+ from,
41219
+ original: partial,
41220
+ inserted: matches[0].value
41221
+ };
41222
+ this.replaceCompletionToken(from, partial, matches[0].value);
41011
41223
  this.render();
41012
41224
  }
41013
41225
  clearScreen() {
@@ -41144,10 +41356,19 @@ class LineEditor {
41144
41356
 
41145
41357
  // src/cli/repl.ts
41146
41358
  import { existsSync as existsSync61, mkdirSync as mkdirSync25, readFileSync as readFileSync42, writeFileSync as writeFileSync24 } from "fs";
41147
- import { dirname as dirname26, join as join55 } from "path";
41359
+ import { dirname as dirname26, join as join56 } from "path";
41148
41360
  import { homedir as homedir18 } from "os";
41149
41361
 
41150
41362
  // src/cli/completer.ts
41363
+ import { readdirSync as readdirSync19, statSync as statSync11 } from "fs";
41364
+ import { join as join54 } from "path";
41365
+ function buildSlashCompletions(commands) {
41366
+ return Array.from(commands.entries()).map(([name, cmd]) => ({
41367
+ name,
41368
+ description: cmd.description
41369
+ }));
41370
+ }
41371
+
41151
41372
  class SlashCommandProvider {
41152
41373
  name = "slash-commands";
41153
41374
  commands;
@@ -41158,7 +41379,7 @@ class SlashCommandProvider {
41158
41379
  return ctx.tokenIndex === 0 && ctx.line.startsWith("/");
41159
41380
  }
41160
41381
  complete(ctx) {
41161
- return this.commands.filter((c) => c.startsWith(ctx.partial.slice(1))).map((c) => `/${c}`);
41382
+ return this.commands.filter((c) => c.name.startsWith(ctx.partial.slice(1))).map((c) => ({ value: `/${c.name}`, description: c.description }));
41162
41383
  }
41163
41384
  }
41164
41385
 
@@ -41168,13 +41389,13 @@ class SubcommandProvider {
41168
41389
  subcommands;
41169
41390
  constructor(command, subcommands) {
41170
41391
  this.command = command;
41171
- this.subcommands = subcommands;
41392
+ this.subcommands = subcommands.map((s) => typeof s === "string" ? { name: s } : s);
41172
41393
  }
41173
41394
  match(ctx) {
41174
41395
  return ctx.tokenIndex === 1 && ctx.tokens[0] === `/${this.command}`;
41175
41396
  }
41176
41397
  complete(ctx) {
41177
- return this.subcommands.filter((s) => s.startsWith(ctx.partial));
41398
+ return this.subcommands.filter((s) => s.name.startsWith(ctx.partial)).map((s) => ({ value: s.name, description: s.description }));
41178
41399
  }
41179
41400
  }
41180
41401
 
@@ -41188,9 +41409,44 @@ class ModelArgProvider {
41188
41409
  return ctx.tokenIndex === 2 && ctx.tokens[0] === "/model" && ctx.tokens[1] === "use";
41189
41410
  }
41190
41411
  complete(ctx) {
41191
- return this.getModels().filter((m) => m.startsWith(ctx.partial));
41412
+ return this.getModels().filter((m) => m.startsWith(ctx.partial)).map((m) => ({ value: m }));
41413
+ }
41414
+ }
41415
+
41416
+ class FilePathProvider {
41417
+ name = "file-paths";
41418
+ root;
41419
+ constructor(root) {
41420
+ this.root = root;
41421
+ }
41422
+ match(ctx) {
41423
+ return ctx.tokenIndex >= 1 && (ctx.partial.includes("/") || ctx.partial.includes("\\"));
41424
+ }
41425
+ complete(ctx) {
41426
+ try {
41427
+ const cwd = this.root ?? process.cwd();
41428
+ const partialPath = ctx.partial;
41429
+ const lastSlash = Math.max(partialPath.lastIndexOf("/"), partialPath.lastIndexOf("\\"));
41430
+ const dirPath = lastSlash > 0 ? partialPath.slice(0, lastSlash) : ".";
41431
+ const prefix = lastSlash > 0 ? partialPath.slice(0, lastSlash + 1) : "";
41432
+ const fullPath = join54(cwd, dirPath);
41433
+ const entries = readdirSync19(fullPath);
41434
+ const matches = [];
41435
+ for (const entry of entries) {
41436
+ const entryPath = join54(fullPath, entry);
41437
+ const stat2 = statSync11(entryPath);
41438
+ const completion = prefix + entry + (stat2.isDirectory() ? "/" : "");
41439
+ if (completion.startsWith(ctx.partial)) {
41440
+ matches.push(completion);
41441
+ }
41442
+ }
41443
+ return matches.slice(0, 20).map((m) => ({ value: m }));
41444
+ } catch {
41445
+ return [];
41446
+ }
41192
41447
  }
41193
41448
  }
41449
+
41194
41450
  class ProviderArgProvider {
41195
41451
  name = "provider-args";
41196
41452
  providers;
@@ -41201,7 +41457,7 @@ class ProviderArgProvider {
41201
41457
  return ctx.tokenIndex === 2 && ctx.tokens[0] === "/provider" && ctx.tokens[1] === "use";
41202
41458
  }
41203
41459
  complete(ctx) {
41204
- return this.providers.filter((p) => p.startsWith(ctx.partial));
41460
+ return this.providers.filter((p) => p.startsWith(ctx.partial)).map((p) => ({ value: p }));
41205
41461
  }
41206
41462
  }
41207
41463
 
@@ -41216,9 +41472,8 @@ class SessionNameProvider {
41216
41472
  }
41217
41473
  complete(ctx) {
41218
41474
  const names = this.getNames();
41219
- if (!ctx.partial)
41220
- return names;
41221
- return names.filter((n) => n.toLowerCase().includes(ctx.partial.toLowerCase()));
41475
+ const filtered = !ctx.partial ? names : names.filter((n) => n.toLowerCase().includes(ctx.partial.toLowerCase()));
41476
+ return filtered.map((n) => ({ value: n }));
41222
41477
  }
41223
41478
  }
41224
41479
 
@@ -41240,9 +41495,8 @@ class SkillNameProvider {
41240
41495
  if (!source)
41241
41496
  return [];
41242
41497
  const names = source();
41243
- if (!ctx.partial)
41244
- return names;
41245
- return names.filter((n) => n.toLowerCase().includes(ctx.partial.toLowerCase()));
41498
+ const filtered = !ctx.partial ? names : names.filter((n) => n.toLowerCase().includes(ctx.partial.toLowerCase()));
41499
+ return filtered.map((n) => ({ value: n }));
41246
41500
  }
41247
41501
  }
41248
41502
 
@@ -41354,19 +41608,22 @@ function toolCategory(tool) {
41354
41608
  }
41355
41609
  return "meta";
41356
41610
  }
41357
- function actionMarkFor(tool) {
41611
+ function toolAccent(tool) {
41358
41612
  switch (toolCategory(tool)) {
41359
41613
  case "recon":
41360
- return pc.cyan(glyph.action);
41614
+ return pc.cyan;
41361
41615
  case "mutate":
41362
- return pc.magenta(glyph.action);
41616
+ return pc.magenta;
41363
41617
  case "execute":
41364
- return pc.blue(glyph.action);
41618
+ return pc.blue;
41365
41619
  case "meta":
41366
41620
  default:
41367
- return pc.dim(glyph.action);
41621
+ return pc.dim;
41368
41622
  }
41369
41623
  }
41624
+ function actionMarkFor(tool) {
41625
+ return toolAccent(tool)(glyph.action);
41626
+ }
41370
41627
  function successMark() {
41371
41628
  return pc.green(glyph.success);
41372
41629
  }
@@ -41861,11 +42118,7 @@ class Renderer {
41861
42118
  meta(chunk) {
41862
42119
  this.spinner.stop();
41863
42120
  if (this.thoughtStarted) {
41864
- if (!this.thoughtHeaderPrinted) {
41865
- this.channel.writeRaw(`
41866
- ${pc.magenta(glyph.thinking)} ${pc.dim(this.thinkingLabel)} `);
41867
- this.thoughtHeaderPrinted = true;
41868
- }
42121
+ this.writeThoughtHeader();
41869
42122
  this.channel.writeRaw(pc.dim(chunk));
41870
42123
  } else if (this.card) {
41871
42124
  this.writeInlineBody(chunk);
@@ -41901,13 +42154,16 @@ ${pc.magenta(glyph.thinking)} ${pc.dim(this.thinkingLabel)} `);
41901
42154
  reasoning(chunk) {
41902
42155
  this.spinner.stop();
41903
42156
  this.flushRecon();
41904
- if (this.thoughtStarted && !this.thoughtHeaderPrinted) {
41905
- this.channel.writeRaw(`
41906
- ${pc.magenta(glyph.thinking)} ${pc.dim(this.thinkingLabel)} `);
41907
- this.thoughtHeaderPrinted = true;
41908
- }
42157
+ this.writeThoughtHeader();
41909
42158
  this.channel.writeRaw(pc.dim(chunk));
41910
42159
  }
42160
+ writeThoughtHeader() {
42161
+ if (!this.thoughtStarted || this.thoughtHeaderPrinted)
42162
+ return;
42163
+ this.channel.writeRaw(`
42164
+ ${pc.magenta(glyph.thinking)} ${pc.magenta(this.thinkingLabel)} ${pc.dim("·")} `);
42165
+ this.thoughtHeaderPrinted = true;
42166
+ }
41911
42167
  thinkingStart() {
41912
42168
  this.flushRecon();
41913
42169
  this.thoughtStarted = true;
@@ -41946,6 +42202,7 @@ ${pc.magenta(glyph.thinking)} ${pc.dim(this.thinkingLabel)} `);
41946
42202
  const summary = humanArgs(tool, displayArgs);
41947
42203
  const label = friendlyTool(tool);
41948
42204
  const mark = actionMarkFor(tool);
42205
+ const accent = toolAccent(tool);
41949
42206
  const deferred = this.verbosity === "quiet";
41950
42207
  this.card = {
41951
42208
  tool,
@@ -41964,7 +42221,7 @@ ${pc.dim(glyph.detail)} ${pc.cyan(stepContext)}
41964
42221
  `);
41965
42222
  }
41966
42223
  if (!deferred) {
41967
- this.channel.writeRaw(`${mark} ${label}${summary ? ` ${pc.dim(summary)}` : ""}
42224
+ this.channel.writeRaw(`${mark} ${accent(label)}${summary ? ` ${pc.dim("·")} ${pc.dim(summary)}` : ""}
41968
42225
  `);
41969
42226
  }
41970
42227
  if (!SPINNERLESS_TOOLS.has(tool)) {
@@ -41981,7 +42238,8 @@ ${pc.dim(glyph.detail)} ${pc.cyan(stepContext)}
41981
42238
  this.reconBuffer = [];
41982
42239
  if (items.length === 1) {
41983
42240
  const it = items[0];
41984
- const head = `${it.mark} ${it.label}${it.summary ? ` ${pc.dim(it.summary)}` : ""}`;
42241
+ const accent = toolAccent(it.tool);
42242
+ const head = `${it.mark} ${accent(it.label)}${it.summary ? ` ${pc.dim("·")} ${pc.dim(it.summary)}` : ""}`;
41985
42243
  this.channel.writeRaw(`${head} ${successMark()} ${pc.dim(formatDuration(it.duration))}
41986
42244
  `);
41987
42245
  return;
@@ -42018,13 +42276,19 @@ ${pc.dim(glyph.detail)} ${pc.cyan(stepContext)}
42018
42276
  }
42019
42277
  if (deferred) {
42020
42278
  if (!error && RECON_TOOLS.has(this.card.tool)) {
42021
- this.reconBuffer.push({ mark: leadMark, label, summary, duration });
42279
+ this.reconBuffer.push({
42280
+ tool: this.card.tool,
42281
+ mark: leadMark,
42282
+ label,
42283
+ summary,
42284
+ duration
42285
+ });
42022
42286
  this.recordLast(label, summary, error, duration);
42023
42287
  this.card = null;
42024
42288
  return;
42025
42289
  }
42026
42290
  this.flushRecon();
42027
- const head = `${leadMark} ${label}${summary ? ` ${pc.dim(summary)}` : ""}`;
42291
+ const head = `${leadMark} ${toolAccent(this.card.tool)(label)}${summary ? ` ${pc.dim("·")} ${pc.dim(summary)}` : ""}`;
42028
42292
  this.channel.writeRaw(`${head} ${tail}
42029
42293
  `);
42030
42294
  if (error && firstBody) {
@@ -42049,6 +42313,7 @@ ${pc.dim(glyph.detail)} ${pc.cyan(stepContext)}
42049
42313
  const firstBody = this.card?.firstBody ?? "";
42050
42314
  const status = !error ? "ok" : firstBody.startsWith("⚠") ? "warn" : "error";
42051
42315
  recordLastAction({
42316
+ tool: this.card?.tool ?? "",
42052
42317
  label,
42053
42318
  args: summary,
42054
42319
  status,
@@ -42425,10 +42690,10 @@ class Repl {
42425
42690
  this.execModule = execModule;
42426
42691
  this.slog = new SessionLogger(sessionManager, logger4);
42427
42692
  this.bindSessionOutput();
42428
- this.configDir = configDir || join55(homedir18(), ".mma");
42693
+ this.configDir = configDir || join56(homedir18(), ".mma");
42429
42694
  this.baseDir = baseDir || process.cwd();
42430
42695
  this.noAgentsMd = noAgentsMd === true;
42431
- this.historyPath = historyPath ?? join55(homedir18(), ".mma", "repl-history");
42696
+ this.historyPath = historyPath ?? join56(homedir18(), ".mma", "repl-history");
42432
42697
  this.loadHistory();
42433
42698
  const editorCompleter = (line) => {
42434
42699
  const [matches, partial] = this.completer.complete(line);
@@ -42436,6 +42701,10 @@ class Repl {
42436
42701
  return [matches, partial];
42437
42702
  return [[], line];
42438
42703
  };
42704
+ const readlineCompleter = (line) => {
42705
+ const [items, partial] = editorCompleter(line);
42706
+ return [items.map((i) => i.value), partial];
42707
+ };
42439
42708
  if (process.stdin.isTTY) {
42440
42709
  this.rl = new LineEditor({
42441
42710
  input: process.stdin,
@@ -42453,7 +42722,7 @@ class Repl {
42453
42722
  history: this.history,
42454
42723
  historySize: this.maxHistory,
42455
42724
  tabSize: 2,
42456
- completer: editorCompleter
42725
+ completer: readlineCompleter
42457
42726
  });
42458
42727
  }
42459
42728
  this.output = output ?? getDefaultChannel();
@@ -42508,7 +42777,7 @@ class Repl {
42508
42777
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
42509
42778
  const { bootstrap: bootstrap2 } = await Promise.resolve().then(() => (init_bootstrap(), exports_bootstrap));
42510
42779
  this.agent.shutdown();
42511
- const projectConfigPath = join55(this.baseDir, ".mmrc");
42780
+ const projectConfigPath = join56(this.baseDir, ".mmrc");
42512
42781
  const { config: freshConfig } = loadConfig2({
42513
42782
  configDir: this.configDir,
42514
42783
  projectConfigPath
@@ -42548,7 +42817,7 @@ class Repl {
42548
42817
  }
42549
42818
  setupCompleter() {
42550
42819
  this.completer.reset();
42551
- const slashCommands = Array.from(this.commands.keys());
42820
+ const slashCommands = buildSlashCompletions(this.commands);
42552
42821
  this.completer.registerProvider(new SlashCommandProvider(slashCommands));
42553
42822
  if (this.sessionManager) {
42554
42823
  this.completer.registerProvider(new SessionNameProvider(() => this.sessionManager.list().map((s) => s.name)));
@@ -42564,6 +42833,7 @@ class Repl {
42564
42833
  this.completer.registerProvider(new ProviderArgProvider(entryLabels));
42565
42834
  }
42566
42835
  this.completer.registerProvider(new ModelArgProvider(() => this.modelCache));
42836
+ this.completer.registerProvider(new FilePathProvider(this.baseDir));
42567
42837
  this.refreshModelCache();
42568
42838
  }
42569
42839
  refreshModelCache() {
@@ -42744,7 +43014,8 @@ ${t("repl.interrupt")}`));
42744
43014
  return;
42745
43015
  }
42746
43016
  const mark = last2.status === "error" ? pc.red(glyph.failure) : last2.status === "warn" ? pc.yellow(glyph.warning) : pc.green(glyph.success);
42747
- const head = `${pc.cyan(glyph.action)} ${last2.label}${last2.args ? ` ${pc.dim(last2.args)}` : ""} ${mark} ${pc.dim(`${last2.durationMs}ms`)}`;
43017
+ const accent = toolAccent(last2.tool);
43018
+ const head = `${accent(glyph.action)} ${accent(last2.label)}${last2.args ? ` ${pc.dim("·")} ${pc.dim(last2.args)}` : ""} ${mark} ${pc.dim(`${last2.durationMs}ms`)}`;
42748
43019
  const lines = [head, ...last2.body.map((l) => ` ${l}`)];
42749
43020
  this.output.writeLine(`
42750
43021
  ${lines.join(`
@@ -43022,9 +43293,9 @@ ${divider()}`);
43022
43293
  row(t("repl.agents_label"), pc.red(t("repl.disabled")));
43023
43294
  } else {
43024
43295
  const agentsMdCandidates = [
43025
- join55(this.baseDir, "AGENTS.md"),
43026
- join55(this.baseDir, ".mma", "AGENTS.md"),
43027
- join55(this.configDir, "AGENTS.md")
43296
+ join56(this.baseDir, "AGENTS.md"),
43297
+ join56(this.baseDir, ".mma", "AGENTS.md"),
43298
+ join56(this.configDir, "AGENTS.md")
43028
43299
  ];
43029
43300
  const foundAgents = agentsMdCandidates.filter((p) => existsSync61(p));
43030
43301
  if (foundAgents.length > 0) {
@@ -43037,7 +43308,7 @@ ${divider()}`);
43037
43308
  }
43038
43309
  const meta = this.sessionManager?.getActiveMeta();
43039
43310
  if (meta) {
43040
- const sessionPath = join55(this.configDir, "sessions", meta.id);
43311
+ const sessionPath = join56(this.configDir, "sessions", meta.id);
43041
43312
  row(t("repl.session_label"), `${pc.cyan(meta.name)} ${pc.dim(`(${meta.id.slice(0, 12)})`)} — ${meta.messageCount} msgs ${pc.dim(sessionPath)}`);
43042
43313
  }
43043
43314
  const isTty2 = process.stdout.isTTY === true;
@@ -43049,9 +43320,9 @@ ${divider()}`);
43049
43320
  try {
43050
43321
  const { existsSync: exists } = await import("fs");
43051
43322
  const { join: pathJoin } = await import("path");
43052
- const { hasDomainFiles: hasDomainFiles3 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
43323
+ const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
43053
43324
  const legacyPath = pathJoin(this.configDir, "config.json");
43054
- if (exists(legacyPath) && !hasDomainFiles3(this.configDir)) {
43325
+ if (exists(legacyPath) && !hasDomainFiles2(this.configDir)) {
43055
43326
  this.output.writeLine(pc.yellow(` ${t("config.legacy_hint")}`));
43056
43327
  this.output.writeLine("");
43057
43328
  }
@@ -43166,51 +43437,7 @@ function printRunResult(result, flush, channel = getDefaultChannel()) {
43166
43437
 
43167
43438
  // src/cli/main.ts
43168
43439
  init_setup();
43169
-
43170
- // src/cli/setup-order.ts
43171
- var SECURITY_BASH_BLOCKLIST = [
43172
- "rm",
43173
- "dd",
43174
- "chmod",
43175
- "wget",
43176
- "curl",
43177
- "scp",
43178
- "ssh",
43179
- "nc",
43180
- "netcat",
43181
- "sudo",
43182
- "su",
43183
- "kill",
43184
- "pkill",
43185
- "killall",
43186
- "shutdown",
43187
- "reboot"
43188
- ];
43189
- function applyAnswersToConfig(config, a) {
43190
- config.provider = {
43191
- ...config.provider,
43192
- type: a.provider,
43193
- baseUrl: a.apiBase,
43194
- apiKey: a.apiKey
43195
- };
43196
- config.model = a.model;
43197
- config.contextWindow = a.contextWindow;
43198
- config.maxToolIterations = a.maxToolIterations;
43199
- config.locale = a.locale;
43200
- if (config.security) {
43201
- config.security.enabled = true;
43202
- config.security.bash.enabled = true;
43203
- config.security.bash.blockDangerousFlags = a.securityFlagsBlock || config.security.bash.blockDangerousFlags;
43204
- if (a.securityBashBlock) {
43205
- config.security.bash.blacklist = [
43206
- ...new Set([...config.security.bash.blacklist, ...SECURITY_BASH_BLOCKLIST])
43207
- ];
43208
- }
43209
- if (!a.securityPathsDeny)
43210
- config.security.paths.denied = [];
43211
- }
43212
- return config;
43213
- }
43440
+ init_setup_order();
43214
43441
 
43215
43442
  // src/cli/relaunch.ts
43216
43443
  init_output();
@@ -43270,7 +43497,7 @@ init_config2();
43270
43497
  init_i18n();
43271
43498
  init_colors();
43272
43499
  import { existsSync as existsSync62 } from "fs";
43273
- import { join as join57, dirname as dirname27 } from "path";
43500
+ import { join as join58, dirname as dirname27 } from "path";
43274
43501
  import { homedir as homedir20 } from "os";
43275
43502
  import { fileURLToPath as fileURLToPath6 } from "url";
43276
43503
 
@@ -43392,9 +43619,9 @@ init_data_sanitizer();
43392
43619
  init_i18n();
43393
43620
  init_utils();
43394
43621
  import { appendFileSync as appendFileSync8, mkdirSync as mkdirSync26 } from "fs";
43395
- import { join as join56 } from "path";
43622
+ import { join as join57 } from "path";
43396
43623
  import { homedir as homedir19 } from "os";
43397
- var CRASH_LOG_DIR = join56(homedir19(), ".mma", "logs");
43624
+ var CRASH_LOG_DIR = join57(homedir19(), ".mma", "logs");
43398
43625
  var CRASH_LOG_FILE = "crash.jsonl";
43399
43626
  function formatCrashEntry(type2, err) {
43400
43627
  const message = errMsg(err);
@@ -43410,7 +43637,7 @@ function formatCrashEntry(type2, err) {
43410
43637
  function writeCrashEntry(dir, entry) {
43411
43638
  try {
43412
43639
  mkdirSync26(dir, { recursive: true });
43413
- appendFileSync8(join56(dir, CRASH_LOG_FILE), JSON.stringify(entry) + `
43640
+ appendFileSync8(join57(dir, CRASH_LOG_FILE), JSON.stringify(entry) + `
43414
43641
  `, "utf-8");
43415
43642
  } catch {}
43416
43643
  }
@@ -43538,6 +43765,9 @@ async function main() {
43538
43765
  }
43539
43766
  const jsonSink = jsonOpt ? new JsonOutputSink(defaultOutputBus) : undefined;
43540
43767
  const prompt = program2.args.join(" ");
43768
+ const mmaDir = process.env.MMA_CONFIG_DIR || join58(homedir20(), ".mma");
43769
+ const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
43770
+ const virgin = !existsSync62(join58(mmaDir, "config.json")) && !hasDomainFiles2(mmaDir);
43541
43771
  const { agent, config, baseDir, legacyDetected, contextProbe } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete, reasoningLevel, contextLog);
43542
43772
  if (verboseOpt)
43543
43773
  config.ui.verbosity = "verbose";
@@ -43545,6 +43775,9 @@ async function main() {
43545
43775
  if (legacyDetected) {
43546
43776
  getDefaultChannel().writeLine(pc.yellow(` ${t("config.legacy_hint")}`), "stderr");
43547
43777
  }
43778
+ if (virgin && !jsonOpt) {
43779
+ getDefaultChannel().writeLine(pc.yellow(` ${t("cli.first_run_oneshot")}`), "stderr");
43780
+ }
43548
43781
  if (contextProbe && !jsonOpt) {
43549
43782
  try {
43550
43783
  const probe = await contextProbe;
@@ -43620,16 +43853,16 @@ async function main() {
43620
43853
  await updater?.waitForIdle();
43621
43854
  process.exit(exitCode);
43622
43855
  } else {
43623
- const mmaDir = process.env.MMA_CONFIG_DIR || join57(homedir20(), ".mma");
43624
- const legacyConfigPath = join57(mmaDir, "config.json");
43856
+ const mmaDir = process.env.MMA_CONFIG_DIR || join58(homedir20(), ".mma");
43857
+ const legacyConfigPath = join58(mmaDir, "config.json");
43625
43858
  let hasAnyConfig = existsSync62(legacyConfigPath);
43626
43859
  if (!hasAnyConfig) {
43627
43860
  try {
43628
- const { hasDomainFiles: hasDomainFiles3 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
43629
- hasAnyConfig = hasDomainFiles3(mmaDir);
43861
+ const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
43862
+ hasAnyConfig = hasDomainFiles2(mmaDir);
43630
43863
  } catch {}
43631
43864
  }
43632
- const projectConfigPath = projectDir ? join57(projectDir, ".mmrc") : join57(process.cwd(), ".mmrc");
43865
+ const projectConfigPath = projectDir ? join58(projectDir, ".mmrc") : join58(process.cwd(), ".mmrc");
43633
43866
  if (!hasAnyConfig && !exitOnComplete && process.env.MMA_POST_SETUP !== "1") {
43634
43867
  getDefaultChannel().writeLine(`
43635
43868
  ` + t("cli.first_run") + `