micro-models-agent 0.31.0 → 0.33.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
@@ -2162,7 +2162,7 @@ var init_defaults = __esm(() => {
2162
2162
  init_config();
2163
2163
  DEFAULTS = {
2164
2164
  version: "2.0.0",
2165
- model: "qwen3.5-9b",
2165
+ model: "qwen/qwen3.5-9b",
2166
2166
  provider: {
2167
2167
  type: "openai-compat",
2168
2168
  baseUrl: "http://localhost:1234/v1",
@@ -2177,18 +2177,18 @@ var init_defaults = __esm(() => {
2177
2177
  },
2178
2178
  experts: {
2179
2179
  code: {
2180
- model: "qwen3.5-9b",
2180
+ model: "qwen/qwen3.5-9b",
2181
2181
  tool_tags: ["file", "code", "shell"],
2182
2182
  max_attempts: 3
2183
2183
  },
2184
- research: { model: "qwen3.5-9b", tool_tags: ["research"], max_attempts: 3 },
2184
+ research: { model: "qwen/qwen3.5-9b", tool_tags: ["research"], max_attempts: 3 },
2185
2185
  browser: {
2186
- model: "qwen3.5-9b",
2186
+ model: "qwen/qwen3.5-9b",
2187
2187
  tool_tags: ["browser", "vision"],
2188
2188
  max_attempts: 3
2189
2189
  },
2190
2190
  vision: {
2191
- model: "qwen3.5-9b",
2191
+ model: "qwen/qwen3.5-9b",
2192
2192
  tool_tags: ["browser", "vision", "file"],
2193
2193
  max_attempts: 2
2194
2194
  }
@@ -2225,7 +2225,8 @@ var init_defaults = __esm(() => {
2225
2225
  headless: true,
2226
2226
  maxElements: 30,
2227
2227
  maxContentChars: 2500,
2228
- maxConsoleEntries: 20,
2228
+ maxConsoleEntries: 40,
2229
+ maxConsoleLineChars: 400,
2229
2230
  viewportWidth: 1280,
2230
2231
  viewportHeight: 720,
2231
2232
  navigationTimeout: 15000
@@ -2457,7 +2458,7 @@ Command: {command}`,
2457
2458
  "plan.step_status": "Step {step}: {status}",
2458
2459
  "plan.no_active": "No active plan",
2459
2460
  "plan.step_not_found": "Step not found",
2460
- "plan.step_already_done": "Step {step} is already marked done. Stop re-marking it either continue with the next step or investigate why progress appears stuck.",
2461
+ "plan.step_already_done": "Step {step} is already done — nothing to do. Plan progress:",
2461
2462
  "plan.order_blocked": 'Cannot mark step {step} done: step {first} ("{desc}") is not finished yet. Complete earlier steps first, or mark step {first} status=skipped if it is not needed.',
2462
2463
  "plan.show_header": "Plan status:",
2463
2464
  "plan.show_empty": "(plan has no steps)",
@@ -3034,7 +3035,7 @@ var init_ru = __esm(() => {
3034
3035
  "plan.step_status": "Шаг {step}: {status}",
3035
3036
  "plan.no_active": "Нет активного плана",
3036
3037
  "plan.step_not_found": "Шаг не найден",
3037
- "plan.step_already_done": "Шаг {step} уже отмечен выполненным. Прекратите повторную отметку переходите к следующему шагу или выясните, почему прогресс заблокирован.",
3038
+ "plan.step_already_done": "Шаг {step} уже выполненничего делать не нужно. Прогресс плана:",
3038
3039
  "plan.order_blocked": "Нельзя отметить шаг {step} выполненным: шаг {first} («{desc}») ещё не завершён. Сначала завершите предыдущие шаги или отметьте шаг {first} status=skipped, если он не нужен.",
3039
3040
  "plan.show_header": "Статус плана:",
3040
3041
  "plan.show_empty": "(в плане нет шагов)",
@@ -10434,6 +10435,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10434
10435
  this.abortController = new AbortController;
10435
10436
  let iteration = 0;
10436
10437
  let lastText = "";
10438
+ let lastModelText = "";
10437
10439
  let lastForcedCompactionIteration = -FORCED_COMPACTION_COOLDOWN;
10438
10440
  let hallucinationRetries = 0;
10439
10441
  let lastToolSignature = "";
@@ -10450,6 +10452,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10450
10452
  let emptyResponseExhausted = false;
10451
10453
  let auditFailed = false;
10452
10454
  let lastAuditSummary = "";
10455
+ let suppressRepetitionRetry = false;
10453
10456
  let repeatedToolCount = 0;
10454
10457
  const MAX_REPEATED_TOOL_CALLS = 2;
10455
10458
  const allToolsForBudget = toolExecutor.getToolDefinitions(this.deps.toolTags);
@@ -10737,9 +10740,12 @@ ${taskReminder}</system-summary>`
10737
10740
  `));
10738
10741
  }
10739
10742
  }
10743
+ suppressRepetitionRetry = false;
10740
10744
  continue;
10741
10745
  }
10742
- hallucinationDetector.getConfidenceCheck().setPreviousResponse(lastText);
10746
+ hallucinationDetector.getConfidenceCheck().setPreviousResponse(lastModelText);
10747
+ if (textContent)
10748
+ lastModelText = textContent;
10743
10749
  const hallucinationResult = await hallucinationDetector.validate(textContent);
10744
10750
  if (hallucinationResult.status === "block") {
10745
10751
  logger.warn(`Response blocked: ${hallucinationResult.reason}`);
@@ -10771,30 +10777,45 @@ ${warnLine}
10771
10777
  lastText = textContent;
10772
10778
  break;
10773
10779
  }
10774
- if (hallucinationRetries >= MAX_HALLUCINATION_RETRIES) {
10775
- logger.warn(`Hallucination retries exhausted (${MAX_HALLUCINATION_RETRIES}), returning error`);
10776
- return {
10777
- success: false,
10778
- text: lastText,
10779
- error: t("error.response_blocked", {
10780
- reason: t("hall.max_retries_exhausted")
10781
- }),
10782
- iterationCount: iteration
10783
- };
10784
- }
10785
- hallucinationRetries++;
10786
- logger.warn(`Hallucination retry (${hallucinationRetries}/${MAX_HALLUCINATION_RETRIES}): ${hallucinationResult.reason}`);
10787
- if (textContent) {
10780
+ if (suppressRepetitionRetry && hallucinationResult.kind === "repetition") {
10781
+ suppressRepetitionRetry = false;
10782
+ logger.warn(`Audit-triggered re-answer repetition — not counted as hallucination retry`);
10783
+ const warnLine = `${t("hall.uncertainty_prefix").trim()} ${hallucinationResult.reason ?? ""}`;
10784
+ if (onMeta) {
10785
+ onMeta(`
10786
+ ${pc2.yellow(warnLine)}
10787
+ `);
10788
+ } else if (onChunk) {
10789
+ onChunk(`
10790
+ ${warnLine}
10791
+ `);
10792
+ }
10793
+ } else {
10794
+ if (hallucinationRetries >= MAX_HALLUCINATION_RETRIES) {
10795
+ logger.warn(`Hallucination retries exhausted (${MAX_HALLUCINATION_RETRIES}), returning error`);
10796
+ return {
10797
+ success: false,
10798
+ text: lastText,
10799
+ error: t("error.response_blocked", {
10800
+ reason: t("hall.max_retries_exhausted")
10801
+ }),
10802
+ iterationCount: iteration
10803
+ };
10804
+ }
10805
+ hallucinationRetries++;
10806
+ logger.warn(`Hallucination retry (${hallucinationRetries}/${MAX_HALLUCINATION_RETRIES}): ${hallucinationResult.reason}`);
10807
+ if (textContent) {
10808
+ contextManager.addMessage({
10809
+ role: "assistant",
10810
+ content: textContent
10811
+ });
10812
+ }
10788
10813
  contextManager.addMessage({
10789
- role: "assistant",
10790
- content: textContent
10814
+ role: "user",
10815
+ content: `<system-summary>[Retry context: ${hallucinationResult.reason}. Original task: "${input}". You must either call a needed tool or provide a substantive response. Empty replies are not allowed.]</system-summary>`
10791
10816
  });
10817
+ continue;
10792
10818
  }
10793
- contextManager.addMessage({
10794
- role: "user",
10795
- content: `<system-summary>[Retry context: ${hallucinationResult.reason}. Original task: "${input}". You must either call a needed tool or provide a substantive response. Empty replies are not allowed.]</system-summary>`
10796
- });
10797
- continue;
10798
10819
  }
10799
10820
  if (textContent) {
10800
10821
  contextManager.addMessage({ role: "assistant", content: textContent });
@@ -10846,6 +10867,7 @@ ${warnLine}
10846
10867
  auditFailed = true;
10847
10868
  break;
10848
10869
  }
10870
+ suppressRepetitionRetry = true;
10849
10871
  continue;
10850
10872
  }
10851
10873
  }
@@ -11295,13 +11317,18 @@ class ConfidenceCheck {
11295
11317
  }
11296
11318
  validate(response) {
11297
11319
  if (!response || response.length < MIN_CHARS) {
11298
- return { status: "retry", reason: t("hall.short_response") };
11320
+ return {
11321
+ status: "retry",
11322
+ kind: "short",
11323
+ reason: t("hall.short_response")
11324
+ };
11299
11325
  }
11300
11326
  const wordCount = response.split(/\s+/).filter(Boolean).length;
11301
11327
  const hasStructure = /```|^\s*[-*]\s|^\s*\d+\.\s|<[^>]+>/m.test(response);
11302
11328
  if (wordCount < MIN_WORDS && !hasStructure) {
11303
11329
  return {
11304
11330
  status: "warn",
11331
+ kind: "short",
11305
11332
  reason: t("hall.short_response")
11306
11333
  };
11307
11334
  }
@@ -11310,6 +11337,7 @@ class ConfidenceCheck {
11310
11337
  if (overlap > 0.5) {
11311
11338
  return {
11312
11339
  status: "retry",
11340
+ kind: "repetition",
11313
11341
  reason: t("hall.repetitive", { pct: Math.round(overlap * 100) })
11314
11342
  };
11315
11343
  }
@@ -11321,6 +11349,7 @@ class ConfidenceCheck {
11321
11349
  if (diversity < 0.25) {
11322
11350
  return {
11323
11351
  status: "warn",
11352
+ kind: "diversity",
11324
11353
  reason: t("hall.repetitive", {
11325
11354
  pct: Math.round((1 - diversity) * 100)
11326
11355
  })
@@ -11349,7 +11378,25 @@ function isJsMemberAccess(candidate) {
11349
11378
  const m = lower.match(/^([\w-]+)\./);
11350
11379
  return m !== null && JS_GLOBALS.has(m[1]);
11351
11380
  }
11352
- var JS_GLOBALS;
11381
+ function isFilePathLike(s) {
11382
+ const lower = s.toLowerCase();
11383
+ if (TECH_NAMES.has(lower))
11384
+ return false;
11385
+ if (!s.includes("/") && !s.includes("\\") && !/^\w+\.[a-z]{2,4}$/i.test(s))
11386
+ return false;
11387
+ if (isJsMemberAccess(s))
11388
+ return false;
11389
+ const ext = s.split(".").pop()?.toLowerCase() || "";
11390
+ return !NON_FILE_EXTENSIONS.has(ext);
11391
+ }
11392
+ function extractFileLikeTokens(text) {
11393
+ const matches = text.match(FILE_TOKEN_RE) || [];
11394
+ return [...new Set(matches.filter(isFilePathLike))];
11395
+ }
11396
+ function stripUrls(text) {
11397
+ return text.replace(/\bhttps?:\/\/[^\s"'<>]+/gi, " ");
11398
+ }
11399
+ var JS_GLOBALS, NON_FILE_EXTENSIONS, TECH_NAMES, FILE_TOKEN_RE;
11353
11400
  var init_js_identifiers = __esm(() => {
11354
11401
  JS_GLOBALS = new Set([
11355
11402
  "process",
@@ -11407,22 +11454,46 @@ var init_js_identifiers = __esm(() => {
11407
11454
  "deno",
11408
11455
  "node"
11409
11456
  ]);
11457
+ NON_FILE_EXTENSIONS = new Set([
11458
+ "com",
11459
+ "org",
11460
+ "net",
11461
+ "gov",
11462
+ "edu",
11463
+ "io",
11464
+ "dev",
11465
+ "app",
11466
+ "co",
11467
+ "ai",
11468
+ "ru",
11469
+ "de",
11470
+ "fr",
11471
+ "uk",
11472
+ "us",
11473
+ "es",
11474
+ "it",
11475
+ "jp",
11476
+ "cn",
11477
+ "br"
11478
+ ]);
11479
+ TECH_NAMES = new Set([
11480
+ "node.js",
11481
+ "react.js",
11482
+ "vue.js",
11483
+ "next.js",
11484
+ "angular.js",
11485
+ "express.js",
11486
+ "deno.js",
11487
+ "qwik.js",
11488
+ "svelte.js",
11489
+ "jquery.js"
11490
+ ]);
11491
+ FILE_TOKEN_RE = /\b(?:[a-zA-Z]:[\\/])?[\w./\\-]+\.[a-z]{2,6}\b/gi;
11410
11492
  });
11411
11493
 
11412
11494
  // src/modules/hallucination/factual.ts
11413
11495
  import { existsSync as existsSync22, readdirSync as readdirSync5 } from "fs";
11414
11496
  import { resolve as resolve12, isAbsolute as isAbsolute2, join as join13 } from "path";
11415
- function looksLikeFilePath(s) {
11416
- const lower = s.toLowerCase();
11417
- if (TECH_NAMES.has(lower))
11418
- return false;
11419
- if (!s.includes("/") && !s.includes("\\") && !s.match(/^\w+\.[a-z]{2,4}$/i))
11420
- return false;
11421
- if (isJsMemberAccess(s))
11422
- return false;
11423
- const ext = s.split(".").pop()?.toLowerCase() || "";
11424
- return !NON_FILE_EXTENSIONS.has(ext);
11425
- }
11426
11497
 
11427
11498
  class FactualCheck {
11428
11499
  baseDir;
@@ -11521,12 +11592,10 @@ class FactualCheck {
11521
11592
  return count;
11522
11593
  }
11523
11594
  extractFilePaths(text) {
11524
- const pattern = /\b(?:[a-zA-Z]:[\\/])?[\w./\\-]+\.[a-z]{2,6}\b/gi;
11525
- const matches = text.match(pattern) || [];
11526
- return [...new Set(matches)].filter(looksLikeFilePath);
11595
+ return extractFileLikeTokens(stripUrls(text));
11527
11596
  }
11528
11597
  }
11529
- var IGNORED_DIRS, NON_FILE_EXTENSIONS, TECH_NAMES;
11598
+ var IGNORED_DIRS;
11530
11599
  var init_factual = __esm(() => {
11531
11600
  init_i18n();
11532
11601
  init_js_identifiers();
@@ -11539,40 +11608,6 @@ var init_factual = __esm(() => {
11539
11608
  ".mma",
11540
11609
  "_testing"
11541
11610
  ]);
11542
- NON_FILE_EXTENSIONS = new Set([
11543
- "com",
11544
- "org",
11545
- "net",
11546
- "gov",
11547
- "edu",
11548
- "io",
11549
- "dev",
11550
- "app",
11551
- "co",
11552
- "ai",
11553
- "ru",
11554
- "de",
11555
- "fr",
11556
- "uk",
11557
- "us",
11558
- "es",
11559
- "it",
11560
- "jp",
11561
- "cn",
11562
- "br"
11563
- ]);
11564
- TECH_NAMES = new Set([
11565
- "node.js",
11566
- "react.js",
11567
- "vue.js",
11568
- "next.js",
11569
- "angular.js",
11570
- "express.js",
11571
- "deno.js",
11572
- "qwik.js",
11573
- "svelte.js",
11574
- "jquery.js"
11575
- ]);
11576
11611
  });
11577
11612
 
11578
11613
  // src/modules/hallucination/llm-judge.ts
@@ -13670,6 +13705,306 @@ var init_recall = __esm(() => {
13670
13705
  };
13671
13706
  });
13672
13707
 
13708
+ // src/modules/browser/bridge-client.ts
13709
+ var exports_bridge_client = {};
13710
+ __export(exports_bridge_client, {
13711
+ BridgeDriver: () => BridgeDriver
13712
+ });
13713
+ import { spawn as spawn4 } from "child_process";
13714
+ import { createInterface } from "readline";
13715
+ import { dirname as dirname6, join as join17 } from "path";
13716
+ import { fileURLToPath } from "url";
13717
+ function bridgeScriptPath() {
13718
+ return join17(dirname6(fileURLToPath(import.meta.url)), "bridge-server.mjs");
13719
+ }
13720
+
13721
+ class BridgeDriver {
13722
+ proc = null;
13723
+ rl = null;
13724
+ pending = new Map;
13725
+ nextId = 1;
13726
+ lastUrl = "";
13727
+ console = new ConsoleBuffer(500);
13728
+ network = [];
13729
+ maxConsoleLineChars;
13730
+ constructor(opts = {}) {
13731
+ this.maxConsoleLineChars = opts.maxConsoleLineChars ?? 400;
13732
+ }
13733
+ async send(cmd, params = {}) {
13734
+ if (!this.proc || !this.rl)
13735
+ throw new Error("Bridge is not running");
13736
+ const id = this.nextId++;
13737
+ const response = await new Promise((resolve15, reject) => {
13738
+ const timer = setTimeout(() => {
13739
+ this.pending.delete(id);
13740
+ reject(new Error(`Bridge timeout waiting for "${cmd}"`));
13741
+ }, BRIDGE_REQUEST_TIMEOUT_MS);
13742
+ this.pending.set(id, (resp) => {
13743
+ clearTimeout(timer);
13744
+ resolve15(resp);
13745
+ });
13746
+ this.proc.stdin.write(JSON.stringify({ id, cmd, params }) + `
13747
+ `);
13748
+ });
13749
+ this.drainEvents(response);
13750
+ if (!response.ok)
13751
+ throw new Error(response.error || `Bridge command failed: ${cmd}`);
13752
+ return response;
13753
+ }
13754
+ drainEvents(resp) {
13755
+ if (Array.isArray(resp.console)) {
13756
+ for (const entry of resp.console) {
13757
+ this.console.add(entry.type, entry.text);
13758
+ }
13759
+ }
13760
+ if (Array.isArray(resp.network)) {
13761
+ for (const err of resp.network)
13762
+ this.network.push(err);
13763
+ }
13764
+ if (resp.data && typeof resp.data.url === "string") {
13765
+ this.lastUrl = resp.data.url;
13766
+ }
13767
+ }
13768
+ async launch(cfg) {
13769
+ this.spawnBridge();
13770
+ await this.send("launch", {
13771
+ headless: cfg.headless,
13772
+ viewport: cfg.viewport,
13773
+ timeoutMs: cfg.timeoutMs,
13774
+ maxConsoleLineChars: this.maxConsoleLineChars
13775
+ });
13776
+ }
13777
+ spawnBridge() {
13778
+ const script = bridgeScriptPath();
13779
+ const proc = spawn4("node", [script], { stdio: ["pipe", "pipe", "pipe"], windowsHide: true });
13780
+ this.proc = proc;
13781
+ proc.stderr.on("data", () => {});
13782
+ const rl = createInterface({ input: proc.stdout });
13783
+ this.rl = rl;
13784
+ rl.on("line", (line) => {
13785
+ let resp;
13786
+ try {
13787
+ resp = JSON.parse(line);
13788
+ } catch {
13789
+ return;
13790
+ }
13791
+ const resolver = this.pending.get(resp.id);
13792
+ if (resolver) {
13793
+ this.pending.delete(resp.id);
13794
+ resolver(resp);
13795
+ }
13796
+ });
13797
+ proc.on("exit", () => {
13798
+ for (const [, resolver] of this.pending) {
13799
+ resolver({ id: -1, ok: false, error: "Bridge process exited" });
13800
+ }
13801
+ this.pending.clear();
13802
+ this.proc = null;
13803
+ this.rl = null;
13804
+ });
13805
+ }
13806
+ async goto(url, timeoutMs) {
13807
+ await this.send("goto", { url, timeoutMs });
13808
+ }
13809
+ async evaluate(js) {
13810
+ const resp = await this.send("evaluate", { js });
13811
+ return resp.data?.result;
13812
+ }
13813
+ async content() {
13814
+ const resp = await this.send("content");
13815
+ return String(resp.data?.html ?? "");
13816
+ }
13817
+ async title() {
13818
+ const resp = await this.send("title");
13819
+ return String(resp.data?.title ?? "");
13820
+ }
13821
+ url() {
13822
+ return this.lastUrl;
13823
+ }
13824
+ async waitForLoad(timeoutMs) {
13825
+ await this.send("waitLoad", { timeoutMs });
13826
+ }
13827
+ async screenshot() {
13828
+ const resp = await this.send("screenshot");
13829
+ return Buffer.from(String(resp.data?.base64 ?? ""), "base64");
13830
+ }
13831
+ async cookies() {
13832
+ const resp = await this.send("cookies");
13833
+ return resp.data?.cookies ?? [];
13834
+ }
13835
+ async addCookies(cookies) {
13836
+ await this.send("setCookies", { cookies });
13837
+ }
13838
+ async goBack(timeoutMs) {
13839
+ await this.send("back", { timeoutMs });
13840
+ }
13841
+ async goForward(timeoutMs) {
13842
+ await this.send("forward", { timeoutMs });
13843
+ }
13844
+ resetEventLog() {
13845
+ this.console.clear();
13846
+ this.network = [];
13847
+ }
13848
+ getConsole() {
13849
+ return this.console.getAll();
13850
+ }
13851
+ getNetworkErrors() {
13852
+ return this.network.slice(-15);
13853
+ }
13854
+ async close() {
13855
+ try {
13856
+ if (this.proc)
13857
+ await this.send("close");
13858
+ } catch {}
13859
+ this.dispose();
13860
+ }
13861
+ dispose() {
13862
+ if (this.proc) {
13863
+ this.proc.kill();
13864
+ this.proc = null;
13865
+ }
13866
+ if (this.rl) {
13867
+ this.rl.close();
13868
+ this.rl = null;
13869
+ }
13870
+ }
13871
+ }
13872
+ var BRIDGE_REQUEST_TIMEOUT_MS = 30000;
13873
+ var init_bridge_client = __esm(() => {
13874
+ init_session();
13875
+ });
13876
+
13877
+ // src/modules/browser/driver.ts
13878
+ import { chromium } from "playwright";
13879
+
13880
+ class PlaywrightDriver {
13881
+ browser = null;
13882
+ context = null;
13883
+ page = null;
13884
+ console = new ConsoleBuffer;
13885
+ network = [];
13886
+ async launch(cfg) {
13887
+ if (this.browser)
13888
+ return;
13889
+ this.browser = await chromium.launch({
13890
+ headless: cfg.headless,
13891
+ timeout: cfg.timeoutMs
13892
+ });
13893
+ this.context = await this.browser.newContext({ viewport: cfg.viewport });
13894
+ this.page = await this.context.newPage();
13895
+ this.page.setDefaultTimeout(cfg.timeoutMs);
13896
+ this.page.on("console", (msg) => this.console.add(msg.type(), msg.text()));
13897
+ this.page.on("pageerror", (err) => this.console.add("pageerror", `Page error: ${err.message}`));
13898
+ this.page.on("requestfailed", (req) => {
13899
+ const failure = req.failure();
13900
+ this.network.push({
13901
+ method: req.method(),
13902
+ url: req.url(),
13903
+ error: failure?.errorText || "unknown"
13904
+ });
13905
+ });
13906
+ }
13907
+ async goto(url, timeoutMs) {
13908
+ this.requirePage();
13909
+ this.console.clear();
13910
+ this.network = [];
13911
+ await this.page.goto(url, { waitUntil: "domcontentloaded", timeout: timeoutMs });
13912
+ }
13913
+ async evaluate(js) {
13914
+ return this.requirePage().evaluate(js);
13915
+ }
13916
+ async content() {
13917
+ return this.requirePage().content();
13918
+ }
13919
+ async title() {
13920
+ return this.requirePage().title();
13921
+ }
13922
+ url() {
13923
+ return this.page?.url() || "";
13924
+ }
13925
+ async waitForLoad(timeoutMs) {
13926
+ await this.requirePage().waitForLoadState("domcontentloaded", { timeout: timeoutMs }).catch(() => {});
13927
+ }
13928
+ async screenshot() {
13929
+ return this.requirePage().screenshot({ type: "png", fullPage: false });
13930
+ }
13931
+ async cookies() {
13932
+ if (!this.context)
13933
+ return [];
13934
+ return this.context.cookies();
13935
+ }
13936
+ async addCookies(cookies) {
13937
+ if (!this.context || cookies.length === 0)
13938
+ return;
13939
+ await this.context.addCookies(cookies);
13940
+ }
13941
+ async goBack(timeoutMs) {
13942
+ await this.requirePage().goBack({ waitUntil: "domcontentloaded", timeout: timeoutMs }).catch(() => {});
13943
+ }
13944
+ async goForward(timeoutMs) {
13945
+ await this.requirePage().goForward({ waitUntil: "domcontentloaded", timeout: timeoutMs }).catch(() => {});
13946
+ }
13947
+ resetEventLog() {
13948
+ this.console.clear();
13949
+ this.network = [];
13950
+ }
13951
+ getConsole() {
13952
+ return this.console.getAll();
13953
+ }
13954
+ getNetworkErrors() {
13955
+ return this.network.slice(-15);
13956
+ }
13957
+ async close() {
13958
+ if (this.page)
13959
+ await this.page.close().catch(() => {});
13960
+ if (this.context)
13961
+ await this.context.close().catch(() => {});
13962
+ if (this.browser)
13963
+ await this.browser.close().catch(() => {});
13964
+ this.page = null;
13965
+ this.context = null;
13966
+ this.browser = null;
13967
+ }
13968
+ dispose() {
13969
+ this.close().catch(() => {});
13970
+ }
13971
+ requirePage() {
13972
+ if (!this.page)
13973
+ throw new Error("No page open");
13974
+ return this.page;
13975
+ }
13976
+ }
13977
+ function createBrowserDriver(config) {
13978
+ const common = {
13979
+ headless: config.headless,
13980
+ viewport: { width: config.viewportWidth, height: config.viewportHeight },
13981
+ timeoutMs: config.navigationTimeout
13982
+ };
13983
+ return (async () => {
13984
+ if (process.versions.bun) {
13985
+ return launchBridge(config, common);
13986
+ }
13987
+ const direct = new PlaywrightDriver;
13988
+ try {
13989
+ await direct.launch({ ...common, timeoutMs: DIRECT_LAUNCH_TIMEOUT_MS });
13990
+ return direct;
13991
+ } catch {
13992
+ direct.dispose();
13993
+ }
13994
+ return launchBridge(config, common);
13995
+ })();
13996
+ }
13997
+ async function launchBridge(config, common) {
13998
+ const { BridgeDriver: BridgeDriver2 } = await Promise.resolve().then(() => (init_bridge_client(), exports_bridge_client));
13999
+ const bridge = new BridgeDriver2({ maxConsoleLineChars: config.maxConsoleLineChars });
14000
+ await bridge.launch(common);
14001
+ return bridge;
14002
+ }
14003
+ var DIRECT_LAUNCH_TIMEOUT_MS = 8000;
14004
+ var init_driver = __esm(() => {
14005
+ init_session();
14006
+ });
14007
+
13673
14008
  // src/modules/browser/types.ts
13674
14009
  var DEFAULT_BROWSER_CONFIG;
13675
14010
  var init_types = __esm(() => {
@@ -13677,7 +14012,8 @@ var init_types = __esm(() => {
13677
14012
  headless: true,
13678
14013
  maxElements: 30,
13679
14014
  maxContentChars: 2500,
13680
- maxConsoleEntries: 20,
14015
+ maxConsoleEntries: 40,
14016
+ maxConsoleLineChars: 400,
13681
14017
  screenshotMaxWidth: 1280,
13682
14018
  cookieDir: "",
13683
14019
  viewportWidth: 1280,
@@ -13915,15 +14251,15 @@ function buildTextExtractionScript() {
13915
14251
 
13916
14252
  // src/modules/browser/cookie-store.ts
13917
14253
  import { readFile, writeFile, mkdir } from "fs/promises";
13918
- import { join as join17 } from "path";
14254
+ import { join as join18 } from "path";
13919
14255
 
13920
14256
  class CookieStore {
13921
14257
  filePath;
13922
14258
  constructor(cookieDir) {
13923
- this.filePath = join17(cookieDir, "cookies.json");
14259
+ this.filePath = join18(cookieDir, "cookies.json");
13924
14260
  }
13925
14261
  async save(cookies) {
13926
- await mkdir(join17(this.filePath, ".."), { recursive: true });
14262
+ await mkdir(join18(this.filePath, ".."), { recursive: true });
13927
14263
  await writeFile(this.filePath, JSON.stringify(cookies, null, 2), "utf-8");
13928
14264
  }
13929
14265
  async load() {
@@ -13941,10 +14277,6 @@ class CookieStore {
13941
14277
  var init_cookie_store = () => {};
13942
14278
 
13943
14279
  // src/modules/browser/session.ts
13944
- import {
13945
- chromium
13946
- } from "playwright";
13947
-
13948
14280
  class ConsoleBuffer {
13949
14281
  entries = [];
13950
14282
  maxEntries;
@@ -13998,9 +14330,7 @@ class BrowserActionTracker {
13998
14330
  }
13999
14331
 
14000
14332
  class BrowserSession {
14001
- browser = null;
14002
- context = null;
14003
- page = null;
14333
+ driver = null;
14004
14334
  config;
14005
14335
  cookieStore;
14006
14336
  state = {
@@ -14010,12 +14340,9 @@ class BrowserSession {
14010
14340
  elementCount: 0
14011
14341
  };
14012
14342
  actionTracker = new BrowserActionTracker(3);
14013
- consoleBuffer;
14014
- networkErrors = [];
14015
14343
  constructor(config) {
14016
14344
  this.config = config;
14017
14345
  this.cookieStore = new CookieStore(config.cookieDir);
14018
- this.consoleBuffer = new ConsoleBuffer(config.maxConsoleEntries);
14019
14346
  }
14020
14347
  getState() {
14021
14348
  return { ...this.state };
@@ -14061,7 +14388,7 @@ class BrowserSession {
14061
14388
  };
14062
14389
  }
14063
14390
  if (action !== "open") {
14064
- const warning = this.actionTracker.record(action, args, this.page?.url() || "");
14391
+ const warning = this.actionTracker.record(action, args, this.driver?.url() || "");
14065
14392
  if (warning && result.success) {
14066
14393
  result.output = result.output + `
14067
14394
 
@@ -14076,62 +14403,37 @@ class BrowserSession {
14076
14403
  };
14077
14404
  }
14078
14405
  }
14079
- async ensurePage() {
14080
- if (!this.page) {
14406
+ async ensureDriver() {
14407
+ if (!this.driver) {
14081
14408
  return { success: false, output: t("browser.no_page") };
14082
14409
  }
14083
14410
  return null;
14084
14411
  }
14085
14412
  async launch() {
14086
- if (this.browser)
14413
+ if (this.driver)
14087
14414
  return;
14088
- this.browser = await chromium.launch({ headless: this.config.headless });
14089
- this.context = await this.browser.newContext({
14090
- viewport: {
14091
- width: this.config.viewportWidth,
14092
- height: this.config.viewportHeight
14093
- }
14094
- });
14415
+ this.driver = await createBrowserDriver(this.config);
14095
14416
  const savedCookies = await this.cookieStore.load();
14096
14417
  if (savedCookies.length > 0) {
14097
- await this.context.addCookies(savedCookies.map((c) => ({
14098
- ...c,
14099
- sameSite: c.sameSite
14100
- })));
14418
+ await this.driver.addCookies(savedCookies);
14101
14419
  }
14102
- this.page = await this.context.newPage();
14103
- this.page.setDefaultTimeout(this.config.navigationTimeout);
14104
- this.page.on("console", (msg) => {
14105
- this.consoleBuffer.add(msg.type(), msg.text());
14106
- });
14107
- this.page.on("pageerror", (err) => {
14108
- this.consoleBuffer.add("pageerror", `Page error: ${err.message}`);
14109
- });
14110
- this.page.on("requestfailed", (req) => {
14111
- const failure = req.failure();
14112
- this.networkErrors.push({
14113
- method: req.method(),
14114
- url: req.url(),
14115
- error: failure?.errorText || "unknown"
14116
- });
14117
- });
14118
14420
  }
14119
14421
  async saveCookies() {
14120
- if (!this.context)
14422
+ if (!this.driver)
14121
14423
  return;
14122
- const cookies = await this.context.cookies();
14424
+ const cookies = await this.driver.cookies();
14123
14425
  await this.cookieStore.save(cookies);
14124
14426
  }
14125
14427
  async takeSnapshot() {
14126
- if (!this.page)
14428
+ if (!this.driver)
14127
14429
  return "No page open.";
14128
- await this.page.evaluate(buildIndexInjectionScript());
14129
- const html = await this.page.content();
14130
- const url = this.page.url();
14131
- const title = await this.page.title();
14430
+ await this.driver.evaluate(buildIndexInjectionScript());
14431
+ const html = await this.driver.content();
14432
+ const url = this.driver.url();
14433
+ const title = await this.driver.title();
14132
14434
  let content = "";
14133
14435
  try {
14134
- content = await this.page.evaluate(buildTextExtractionScript());
14436
+ content = String(await this.driver.evaluate(buildTextExtractionScript()));
14135
14437
  } catch {
14136
14438
  content = "";
14137
14439
  }
@@ -14149,8 +14451,8 @@ class BrowserSession {
14149
14451
  title,
14150
14452
  elements,
14151
14453
  content,
14152
- console: this.consoleBuffer.getAll(),
14153
- networkErrors: this.networkErrors.slice(-5),
14454
+ console: this.driver.getConsole(),
14455
+ networkErrors: this.driver.getNetworkErrors(),
14154
14456
  truncated
14155
14457
  });
14156
14458
  }
@@ -14162,16 +14464,12 @@ class BrowserSession {
14162
14464
  url = isLocalhost ? "http://" + url : "https://" + url;
14163
14465
  }
14164
14466
  await this.launch();
14165
- if (!this.page)
14467
+ if (!this.driver)
14166
14468
  return { success: false, output: t("browser.create_page_failed") };
14167
14469
  this.actionTracker.reset();
14168
- this.consoleBuffer.clear();
14169
- this.networkErrors = [];
14470
+ this.driver.resetEventLog();
14170
14471
  try {
14171
- await this.page.goto(url, {
14172
- waitUntil: "domcontentloaded",
14173
- timeout: this.config.navigationTimeout
14174
- });
14472
+ await this.driver.goto(url, this.config.navigationTimeout);
14175
14473
  } catch (err) {
14176
14474
  return {
14177
14475
  success: false,
@@ -14183,18 +14481,17 @@ class BrowserSession {
14183
14481
  return { success: true, output: snapshot };
14184
14482
  }
14185
14483
  async click(target) {
14186
- const err = await this.ensurePage();
14484
+ const err = await this.ensureDriver();
14187
14485
  if (err)
14188
14486
  return err;
14189
- if (!this.page)
14487
+ if (!this.driver)
14190
14488
  return { success: false, output: "No page" };
14191
14489
  if (!target || target < 1) {
14192
14490
  return { success: false, output: t("browser.invalid_target") };
14193
14491
  }
14194
14492
  try {
14195
- const script = buildClickScript(target);
14196
- await this.page.evaluate(script);
14197
- await this.page.waitForLoadState("domcontentloaded", { timeout: 5000 }).catch(() => {});
14493
+ await this.driver.evaluate(buildClickScript(target));
14494
+ await this.driver.waitForLoad(5000);
14198
14495
  await new Promise((r) => setTimeout(r, 500));
14199
14496
  const snapshot = await this.takeSnapshot();
14200
14497
  await this.saveCookies();
@@ -14207,10 +14504,10 @@ class BrowserSession {
14207
14504
  }
14208
14505
  }
14209
14506
  async type(target, text) {
14210
- const err = await this.ensurePage();
14507
+ const err = await this.ensureDriver();
14211
14508
  if (err)
14212
14509
  return err;
14213
- if (!this.page)
14510
+ if (!this.driver)
14214
14511
  return { success: false, output: "No page" };
14215
14512
  if (!target || target < 1) {
14216
14513
  return { success: false, output: t("browser.invalid_target_short") };
@@ -14219,8 +14516,7 @@ class BrowserSession {
14219
14516
  return { success: false, output: t("browser.text_required") };
14220
14517
  }
14221
14518
  try {
14222
- const script = buildTypeScript(target, text);
14223
- await this.page.evaluate(script);
14519
+ await this.driver.evaluate(buildTypeScript(target, text));
14224
14520
  const snapshot = await this.takeSnapshot();
14225
14521
  return { success: true, output: snapshot };
14226
14522
  } catch (err2) {
@@ -14231,17 +14527,17 @@ class BrowserSession {
14231
14527
  }
14232
14528
  }
14233
14529
  async scroll(direction) {
14234
- const err = await this.ensurePage();
14530
+ const err = await this.ensureDriver();
14235
14531
  if (err)
14236
14532
  return err;
14237
- if (!this.page)
14533
+ if (!this.driver)
14238
14534
  return { success: false, output: "No page" };
14239
14535
  const dir = direction;
14240
14536
  if (!["up", "down", "top", "bottom"].includes(dir)) {
14241
14537
  return { success: false, output: t("browser.direction_invalid") };
14242
14538
  }
14243
14539
  try {
14244
- await this.page.evaluate(buildScrollScript(dir));
14540
+ await this.driver.evaluate(buildScrollScript(dir));
14245
14541
  await new Promise((r) => setTimeout(r, 300));
14246
14542
  const snapshot = await this.takeSnapshot();
14247
14543
  return { success: true, output: snapshot };
@@ -14253,52 +14549,46 @@ class BrowserSession {
14253
14549
  }
14254
14550
  }
14255
14551
  async back() {
14256
- const err = await this.ensurePage();
14552
+ const err = await this.ensureDriver();
14257
14553
  if (err)
14258
14554
  return err;
14259
- if (!this.page)
14555
+ if (!this.driver)
14260
14556
  return { success: false, output: "No page" };
14261
- await this.page.goBack({
14262
- waitUntil: "domcontentloaded",
14263
- timeout: this.config.navigationTimeout
14264
- }).catch(() => {});
14557
+ await this.driver.goBack(this.config.navigationTimeout);
14265
14558
  await new Promise((r) => setTimeout(r, 300));
14266
14559
  const snapshot = await this.takeSnapshot();
14267
14560
  return { success: true, output: snapshot };
14268
14561
  }
14269
14562
  async forward() {
14270
- const err = await this.ensurePage();
14563
+ const err = await this.ensureDriver();
14271
14564
  if (err)
14272
14565
  return err;
14273
- if (!this.page)
14566
+ if (!this.driver)
14274
14567
  return { success: false, output: "No page" };
14275
- await this.page.goForward({
14276
- waitUntil: "domcontentloaded",
14277
- timeout: this.config.navigationTimeout
14278
- }).catch(() => {});
14568
+ await this.driver.goForward(this.config.navigationTimeout);
14279
14569
  await new Promise((r) => setTimeout(r, 300));
14280
14570
  const snapshot = await this.takeSnapshot();
14281
14571
  return { success: true, output: snapshot };
14282
14572
  }
14283
14573
  async screenshot() {
14284
- const err = await this.ensurePage();
14574
+ const err = await this.ensureDriver();
14285
14575
  if (err)
14286
14576
  return err;
14287
- if (!this.page)
14577
+ if (!this.driver)
14288
14578
  return { success: false, output: "No page" };
14289
- const buffer = await this.page.screenshot({ type: "png", fullPage: false });
14579
+ const buffer = await this.driver.screenshot();
14290
14580
  const snapshot = await this.takeSnapshot();
14291
14581
  return { success: true, output: snapshot, screenshot: buffer };
14292
14582
  }
14293
14583
  async snapshot() {
14294
- const err = await this.ensurePage();
14584
+ const err = await this.ensureDriver();
14295
14585
  if (err)
14296
14586
  return err;
14297
14587
  const snap = await this.takeSnapshot();
14298
14588
  return { success: true, output: snap };
14299
14589
  }
14300
14590
  async wait(ms) {
14301
- const err = await this.ensurePage();
14591
+ const err = await this.ensureDriver();
14302
14592
  if (err)
14303
14593
  return err;
14304
14594
  await new Promise((r) => setTimeout(r, Math.min(ms, 1e4)));
@@ -14307,40 +14597,34 @@ class BrowserSession {
14307
14597
  }
14308
14598
  async close() {
14309
14599
  await this.saveCookies();
14310
- if (this.page) {
14311
- await this.page.close().catch(() => {});
14312
- this.page = null;
14313
- }
14314
- if (this.context) {
14315
- await this.context.close().catch(() => {});
14316
- this.context = null;
14317
- }
14318
- if (this.browser) {
14319
- await this.browser.close().catch(() => {});
14320
- this.browser = null;
14600
+ if (this.driver) {
14601
+ await this.driver.close();
14602
+ this.driver = null;
14321
14603
  }
14322
14604
  this.state = { isOpen: false, url: null, title: null, elementCount: 0 };
14323
14605
  return { success: true, output: t("browser.closed") };
14324
14606
  }
14325
14607
  }
14326
- var CONSOLE_MAX_LINE = 200;
14608
+ var CONSOLE_MAX_LINE = 400;
14327
14609
  var init_session = __esm(() => {
14610
+ init_driver();
14328
14611
  init_snapshot();
14329
14612
  init_cookie_store();
14330
14613
  init_i18n();
14331
14614
  });
14332
14615
 
14333
14616
  // src/tools/browser.ts
14334
- import { join as join18 } from "path";
14617
+ import { join as join19 } from "path";
14335
14618
  function getSession(ctx) {
14336
14619
  if (!session) {
14337
- const cookieDir = join18(ctx.baseDir, ".mma", "browser");
14620
+ const cookieDir = join19(ctx.baseDir, ".mma", "browser");
14338
14621
  session = new BrowserSession({
14339
14622
  ...DEFAULT_BROWSER_CONFIG,
14340
14623
  headless: ctx.config.browser?.headless ?? true,
14341
14624
  maxElements: ctx.config.browser?.maxElements ?? 30,
14342
14625
  maxContentChars: ctx.config.browser?.maxContentChars ?? 2500,
14343
- maxConsoleEntries: ctx.config.browser?.maxConsoleEntries ?? 20,
14626
+ maxConsoleEntries: ctx.config.browser?.maxConsoleEntries ?? 40,
14627
+ maxConsoleLineChars: ctx.config.browser?.maxConsoleLineChars ?? 400,
14344
14628
  navigationTimeout: ctx.config.browser?.navigationTimeout ?? 15000,
14345
14629
  viewportWidth: ctx.config.browser?.viewportWidth ?? 1280,
14346
14630
  viewportHeight: ctx.config.browser?.viewportHeight ?? 720,
@@ -14467,8 +14751,8 @@ async function readClipboardFallback() {
14467
14751
  const { platform: platform3 } = await import("os");
14468
14752
  const { execSync } = await import("child_process");
14469
14753
  const { readFileSync: readFileSync15, unlinkSync: unlinkSync4 } = await import("fs");
14470
- const { join: join19 } = await import("path");
14471
- const tmpPath = join19(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
14754
+ const { join: join20 } = await import("path");
14755
+ const tmpPath = join20(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
14472
14756
  try {
14473
14757
  if (platform3() === "linux") {
14474
14758
  execSync(`xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`, { timeout: 5000 });
@@ -14748,7 +15032,7 @@ class ModuleRegistry {
14748
15032
 
14749
15033
  // src/modules/plugins/loader.ts
14750
15034
  import { readdirSync as readdirSync7, existsSync as existsSync25, statSync as statSync5 } from "fs";
14751
- import { join as join19 } from "path";
15035
+ import { join as join20 } from "path";
14752
15036
 
14753
15037
  class PluginLoader {
14754
15038
  loadFromDir(dirPath, pluginManager, logger) {
@@ -14756,7 +15040,7 @@ class PluginLoader {
14756
15040
  return;
14757
15041
  const entries = readdirSync7(dirPath);
14758
15042
  for (const entry of entries) {
14759
- const fullPath = join19(dirPath, entry);
15043
+ const fullPath = join20(dirPath, entry);
14760
15044
  if (!statSync5(fullPath).isFile())
14761
15045
  continue;
14762
15046
  if (!entry.endsWith(".ts") && !entry.endsWith(".js"))
@@ -14779,9 +15063,9 @@ var init_loader = __esm(() => {
14779
15063
  });
14780
15064
 
14781
15065
  // src/modules/plugins/builtin/lint-on-write.ts
14782
- import { spawn as spawn4, execSync } from "child_process";
15066
+ import { spawn as spawn5, execSync } from "child_process";
14783
15067
  import { existsSync as existsSync26, readFileSync as readFileSync15 } from "fs";
14784
- import { resolve as resolve16, extname as extname4, join as join20 } from "path";
15068
+ import { resolve as resolve16, extname as extname4, join as join21 } from "path";
14785
15069
  import { platform as platform3 } from "os";
14786
15070
  function contentHash(content) {
14787
15071
  let h = 5381;
@@ -14883,7 +15167,7 @@ class LintOnWritePlugin {
14883
15167
  }
14884
15168
  async runProjectLint(ctx, result) {
14885
15169
  try {
14886
- const packageJsonPath = join20(ctx.baseDir, "package.json");
15170
+ const packageJsonPath = join21(ctx.baseDir, "package.json");
14887
15171
  if (!existsSync26(packageJsonPath)) {
14888
15172
  return;
14889
15173
  }
@@ -14903,7 +15187,7 @@ class LintOnWritePlugin {
14903
15187
  }
14904
15188
  }
14905
15189
  async runProjectTypeCheck(ctx, result) {
14906
- const tsconfigPath = join20(ctx.baseDir, "tsconfig.json");
15190
+ const tsconfigPath = join21(ctx.baseDir, "tsconfig.json");
14907
15191
  if (!existsSync26(tsconfigPath)) {
14908
15192
  return;
14909
15193
  }
@@ -14946,7 +15230,7 @@ class LintOnWritePlugin {
14946
15230
  }
14947
15231
  function runAsync(command, cwd, timeoutMs) {
14948
15232
  return new Promise((resolve17, reject) => {
14949
- const child = spawn4(command, {
15233
+ const child = spawn5(command, {
14950
15234
  cwd,
14951
15235
  shell: true,
14952
15236
  windowsHide: true,
@@ -15147,7 +15431,7 @@ var init_tracker = () => {};
15147
15431
 
15148
15432
  // src/modules/execution/auditor.ts
15149
15433
  import { existsSync as existsSync27, readdirSync as readdirSync8 } from "fs";
15150
- import { resolve as resolve17, join as join21 } from "path";
15434
+ import { resolve as resolve17, join as join22 } from "path";
15151
15435
  function findTestFile(dir, depth = 0) {
15152
15436
  if (depth > 5)
15153
15437
  return null;
@@ -15158,7 +15442,7 @@ function findTestFile(dir, depth = 0) {
15158
15442
  return null;
15159
15443
  }
15160
15444
  for (const e of entries) {
15161
- const full = join21(dir, e.name);
15445
+ const full = join22(dir, e.name);
15162
15446
  if (e.isDirectory()) {
15163
15447
  if (SKIP_DIRS.has(e.name))
15164
15448
  continue;
@@ -15230,10 +15514,7 @@ class Auditor {
15230
15514
  }
15231
15515
  async audit(plan) {
15232
15516
  const allStepText = plan.steps.map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
15233
- const fileMatches = allStepText.match(/\b[\w./-]+\.[a-z]+/gi) || [];
15234
- const uniqueFiles = [
15235
- ...new Set(fileMatches.filter((f) => !isJsMemberAccess(f)))
15236
- ];
15517
+ const uniqueFiles = extractFileLikeTokens(stripUrls(allStepText));
15237
15518
  const missingFiles = [];
15238
15519
  const existingFiles = [];
15239
15520
  for (const filePath of uniqueFiles) {
@@ -15318,7 +15599,7 @@ var init_auditor = __esm(() => {
15318
15599
 
15319
15600
  // src/modules/execution/plan-store.ts
15320
15601
  import { readFileSync as readFileSync16, writeFileSync as writeFileSync9, mkdirSync as mkdirSync13, existsSync as existsSync28, readdirSync as readdirSync9, rmSync } from "fs";
15321
- import { join as join22 } from "path";
15602
+ import { join as join23 } from "path";
15322
15603
  function readPlanFile(path, fallbackBaseDir) {
15323
15604
  try {
15324
15605
  const raw = readFileSync16(path, "utf-8");
@@ -15346,7 +15627,7 @@ function listDir(dir, baseDir) {
15346
15627
  if (!existsSync28(dir))
15347
15628
  return [];
15348
15629
  const files = readdirSync9(dir).filter((f) => f.endsWith(".json"));
15349
- return files.map((f) => readPlanFile(join22(dir, f), baseDir)).filter((p) => p !== null);
15630
+ return files.map((f) => readPlanFile(join23(dir, f), baseDir)).filter((p) => p !== null);
15350
15631
  }
15351
15632
  function toMeta(plan, status) {
15352
15633
  return {
@@ -15367,21 +15648,21 @@ class PlanStore {
15367
15648
  archiveDir;
15368
15649
  legacyPath;
15369
15650
  constructor(baseDir) {
15370
- const mmaDir = join22(baseDir, ".mma");
15651
+ const mmaDir = join23(baseDir, ".mma");
15371
15652
  if (!existsSync28(mmaDir))
15372
15653
  mkdirSync13(mmaDir, { recursive: true });
15373
15654
  this.baseDir = baseDir;
15374
- this.plansDir = join22(mmaDir, "plans");
15375
- this.draftsDir = join22(this.plansDir, "drafts");
15376
- this.archiveDir = join22(this.plansDir, "archive");
15377
- this.legacyPath = join22(mmaDir, LEGACY_FILE);
15655
+ this.plansDir = join23(mmaDir, "plans");
15656
+ this.draftsDir = join23(this.plansDir, "drafts");
15657
+ this.archiveDir = join23(this.plansDir, "archive");
15658
+ this.legacyPath = join23(mmaDir, LEGACY_FILE);
15378
15659
  for (const dir of [this.plansDir, this.draftsDir, this.archiveDir]) {
15379
15660
  if (!existsSync28(dir))
15380
15661
  mkdirSync13(dir, { recursive: true });
15381
15662
  }
15382
15663
  }
15383
15664
  activePath() {
15384
- return join22(this.plansDir, "active.json");
15665
+ return join23(this.plansDir, "active.json");
15385
15666
  }
15386
15667
  saveActive(plan) {
15387
15668
  writePlanFile(this.activePath(), plan);
@@ -15411,14 +15692,14 @@ class PlanStore {
15411
15692
  rmSync(p, { force: true });
15412
15693
  }
15413
15694
  saveDraft(plan) {
15414
- writePlanFile(join22(this.draftsDir, `${plan.id}.json`), plan);
15695
+ writePlanFile(join23(this.draftsDir, `${plan.id}.json`), plan);
15415
15696
  }
15416
15697
  loadDraft(id) {
15417
- const p = join22(this.draftsDir, `${id}.json`);
15698
+ const p = join23(this.draftsDir, `${id}.json`);
15418
15699
  return existsSync28(p) ? readPlanFile(p, this.baseDir) : null;
15419
15700
  }
15420
15701
  removeDraft(id) {
15421
- const p = join22(this.draftsDir, `${id}.json`);
15702
+ const p = join23(this.draftsDir, `${id}.json`);
15422
15703
  if (existsSync28(p))
15423
15704
  rmSync(p, { force: true });
15424
15705
  }
@@ -15426,7 +15707,7 @@ class PlanStore {
15426
15707
  return listDir(this.draftsDir, this.baseDir);
15427
15708
  }
15428
15709
  archivePlan(plan) {
15429
- writePlanFile(join22(this.archiveDir, `${plan.id}.json`), plan);
15710
+ writePlanFile(join23(this.archiveDir, `${plan.id}.json`), plan);
15430
15711
  this.removeDraft(plan.id);
15431
15712
  const active = this.loadActive();
15432
15713
  if (active && active.id === plan.id) {
@@ -15437,7 +15718,7 @@ class PlanStore {
15437
15718
  return listDir(this.archiveDir, this.baseDir);
15438
15719
  }
15439
15720
  removeArchived(id) {
15440
- const p = join22(this.archiveDir, `${id}.json`);
15721
+ const p = join23(this.archiveDir, `${id}.json`);
15441
15722
  if (existsSync28(p))
15442
15723
  rmSync(p, { force: true });
15443
15724
  }
@@ -15781,11 +16062,15 @@ ${display}`,
15781
16062
  }
15782
16063
  const status = String(args.status || "done");
15783
16064
  if (status === "done" && target.status === "done") {
16065
+ const progress2 = this.tracker.getProgressString();
16066
+ const display2 = this.tracker.toPromptBlock();
15784
16067
  return {
15785
- success: false,
15786
- output: t("plan.step_already_done", {
16068
+ success: true,
16069
+ output: `${t("plan.step_already_done", {
15787
16070
  step: String(stepId)
15788
- })
16071
+ })}
16072
+ ${progress2}`,
16073
+ display: display2
15789
16074
  };
15790
16075
  }
15791
16076
  if (status === "done") {
@@ -16304,7 +16589,7 @@ import {
16304
16589
  readdirSync as readdirSync10,
16305
16590
  unlinkSync as unlinkSync4
16306
16591
  } from "fs";
16307
- import { join as join23 } from "path";
16592
+ import { join as join24 } from "path";
16308
16593
  import { homedir as homedir8 } from "os";
16309
16594
 
16310
16595
  class SessionFileEncryptor {
@@ -16313,7 +16598,7 @@ class SessionFileEncryptor {
16313
16598
  constructor(config) {
16314
16599
  this.config = { ...DEFAULT_SESSION_ENCRYPTION, ...config };
16315
16600
  this.encryptor = new ConfigEncryptor({
16316
- keyPath: config?.keyPath || join23(homedir8(), ".mma", ".session-encryption-key")
16601
+ keyPath: config?.keyPath || join24(homedir8(), ".mma", ".session-encryption-key")
16317
16602
  });
16318
16603
  }
16319
16604
  isEnabled() {
@@ -16399,7 +16684,7 @@ class SessionFileEncryptor {
16399
16684
  return;
16400
16685
  const files = readdirSync10(sessionDir);
16401
16686
  for (const file of files) {
16402
- const filePath = join23(sessionDir, file);
16687
+ const filePath = join24(sessionDir, file);
16403
16688
  if (existsSync30(filePath) && !file.endsWith(".enc")) {
16404
16689
  try {
16405
16690
  const content = readFileSync18(filePath, "utf8");
@@ -16416,7 +16701,7 @@ class SessionFileEncryptor {
16416
16701
  const files = readdirSync10(sessionDir);
16417
16702
  for (const file of files) {
16418
16703
  if (file.endsWith(".enc")) {
16419
- const encFilePath = join23(sessionDir, file);
16704
+ const encFilePath = join24(sessionDir, file);
16420
16705
  const decFilePath = encFilePath.slice(0, -4);
16421
16706
  try {
16422
16707
  const content = readFileSync18(encFilePath, "utf8");
@@ -16449,7 +16734,7 @@ import {
16449
16734
  writeFileSync as writeFileSync11,
16450
16735
  appendFileSync as appendFileSync6
16451
16736
  } from "fs";
16452
- import { join as join24 } from "path";
16737
+ import { join as join25 } from "path";
16453
16738
  import { gzipSync } from "zlib";
16454
16739
 
16455
16740
  class SessionStore {
@@ -16463,7 +16748,7 @@ class SessionStore {
16463
16748
  }
16464
16749
  }
16465
16750
  getSessionDir(id) {
16466
- return join24(this.baseDir, id);
16751
+ return join25(this.baseDir, id);
16467
16752
  }
16468
16753
  updateEncryption(config) {
16469
16754
  if (config?.enabled) {
@@ -16479,16 +16764,16 @@ class SessionStore {
16479
16764
  mkdirSync14(this.baseDir, { recursive: true });
16480
16765
  }
16481
16766
  sessionDir(id) {
16482
- return join24(this.baseDir, id);
16767
+ return join25(this.baseDir, id);
16483
16768
  }
16484
16769
  metaPath(id) {
16485
- return join24(this.sessionDir(id), "meta.json");
16770
+ return join25(this.sessionDir(id), "meta.json");
16486
16771
  }
16487
16772
  historyPath(id) {
16488
- return join24(this.sessionDir(id), "history.jsonl");
16773
+ return join25(this.sessionDir(id), "history.jsonl");
16489
16774
  }
16490
16775
  sessionLogPath(id) {
16491
- return join24(this.sessionDir(id), "session.jsonl");
16776
+ return join25(this.sessionDir(id), "session.jsonl");
16492
16777
  }
16493
16778
  sessionExists(id) {
16494
16779
  return existsSync31(this.metaPath(id));
@@ -16642,7 +16927,7 @@ class SessionStore {
16642
16927
  if (existsSync31(historyPath)) {
16643
16928
  const content = readFileSync19(historyPath, "utf-8");
16644
16929
  const compressed = gzipSync(content);
16645
- const gzPath = join24(this.baseDir, `${session2.id}.jsonl.gz`);
16930
+ const gzPath = join25(this.baseDir, `${session2.id}.jsonl.gz`);
16646
16931
  writeFileSync11(gzPath, compressed);
16647
16932
  rmSync2(historyPath);
16648
16933
  }
@@ -16853,7 +17138,7 @@ class ProfileCompressor {
16853
17138
 
16854
17139
  // src/modules/user-profile/profile.ts
16855
17140
  import { readFileSync as readFileSync20, writeFileSync as writeFileSync12, existsSync as existsSync32, mkdirSync as mkdirSync15 } from "fs";
16856
- import { join as join25 } from "path";
17141
+ import { join as join26 } from "path";
16857
17142
  import { homedir as homedir9, hostname, platform as platform4, type } from "os";
16858
17143
  import { env } from "process";
16859
17144
 
@@ -16880,10 +17165,10 @@ class UserProfile {
16880
17165
  if (!existsSync32(this.profileDir)) {
16881
17166
  mkdirSync15(this.profileDir, { recursive: true });
16882
17167
  }
16883
- writeFileSync12(join25(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
17168
+ writeFileSync12(join26(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
16884
17169
  }
16885
17170
  load() {
16886
- const path = join25(this.profileDir, "profile.json");
17171
+ const path = join26(this.profileDir, "profile.json");
16887
17172
  if (!existsSync32(path))
16888
17173
  return null;
16889
17174
  try {
@@ -16923,7 +17208,7 @@ var init_profile = () => {};
16923
17208
 
16924
17209
  // src/modules/skills/loader.ts
16925
17210
  import { readdirSync as readdirSync12, readFileSync as readFileSync21, existsSync as existsSync33, statSync as statSync6 } from "fs";
16926
- import { join as join26 } from "path";
17211
+ import { join as join27 } from "path";
16927
17212
 
16928
17213
  class SkillsLoader {
16929
17214
  loadFromDir(dirPath) {
@@ -16936,7 +17221,7 @@ class SkillsLoader {
16936
17221
  scanDir(dirPath, skills) {
16937
17222
  const entries = readdirSync12(dirPath);
16938
17223
  for (const entry of entries) {
16939
- const fullPath = join26(dirPath, entry);
17224
+ const fullPath = join27(dirPath, entry);
16940
17225
  const stat = statSync6(fullPath);
16941
17226
  if (stat.isDirectory()) {
16942
17227
  this.scanDir(fullPath, skills);
@@ -17173,12 +17458,14 @@ var init_browser2 = __esm(() => {
17173
17458
  init_module4();
17174
17459
  init_session();
17175
17460
  init_cookie_store();
17461
+ init_driver();
17462
+ init_bridge_client();
17176
17463
  init_snapshot();
17177
17464
  init_types();
17178
17465
  });
17179
17466
 
17180
17467
  // src/modules/lsp/client.ts
17181
- import { spawn as spawn5, execSync as execSync2 } from "child_process";
17468
+ import { spawn as spawn6, execSync as execSync2 } from "child_process";
17182
17469
  import { resolve as resolve19 } from "path";
17183
17470
 
17184
17471
  class LspClient {
@@ -17242,7 +17529,7 @@ class LspClient {
17242
17529
  }
17243
17530
  return new Promise((resolve20, reject) => {
17244
17531
  const args = config.args ?? [];
17245
- const proc = spawn5(config.command, args, {
17532
+ const proc = spawn6(config.command, args, {
17246
17533
  stdio: ["pipe", "pipe", "pipe"],
17247
17534
  env: { ...process.env, ...config.env },
17248
17535
  cwd: baseDir
@@ -17503,7 +17790,7 @@ var init_lsp = __esm(() => {
17503
17790
 
17504
17791
  // src/modules/indexer/walker.ts
17505
17792
  import { readdirSync as readdirSync13, readFileSync as readFileSync22, statSync as statSync7, existsSync as existsSync35, watch } from "fs";
17506
- import { join as join27, relative as relative2, extname as extname5 } from "path";
17793
+ import { join as join28, relative as relative2, extname as extname5 } from "path";
17507
17794
 
17508
17795
  class Indexer {
17509
17796
  baseDir;
@@ -17541,7 +17828,7 @@ class Indexer {
17541
17828
  for (const entry of entries) {
17542
17829
  if (count >= this.MAX_FILES)
17543
17830
  return;
17544
- const fullPath = join27(dir, entry);
17831
+ const fullPath = join28(dir, entry);
17545
17832
  const relPath = relative2(this.baseDir, fullPath);
17546
17833
  const stat = statSync7(fullPath);
17547
17834
  if (stat.isDirectory()) {
@@ -17605,13 +17892,13 @@ var init_walker = __esm(() => {
17605
17892
 
17606
17893
  // src/modules/indexer/cache.ts
17607
17894
  import { readFileSync as readFileSync23, writeFileSync as writeFileSync13, existsSync as existsSync36, mkdirSync as mkdirSync16, rmSync as rmSync3 } from "fs";
17608
- import { join as join28 } from "path";
17895
+ import { join as join29 } from "path";
17609
17896
 
17610
17897
  class IndexCache {
17611
17898
  cachePath;
17612
17899
  cache = null;
17613
17900
  constructor(cacheDir) {
17614
- this.cachePath = join28(cacheDir, "index-cache.json");
17901
+ this.cachePath = join29(cacheDir, "index-cache.json");
17615
17902
  }
17616
17903
  load() {
17617
17904
  if (this.cache)
@@ -17627,7 +17914,7 @@ class IndexCache {
17627
17914
  }
17628
17915
  save(result) {
17629
17916
  this.cache = result;
17630
- const dir = join28(this.cachePath, "..");
17917
+ const dir = join29(this.cachePath, "..");
17631
17918
  if (!existsSync36(dir))
17632
17919
  mkdirSync16(dir, { recursive: true });
17633
17920
  writeFileSync13(this.cachePath, JSON.stringify(result), "utf-8");
@@ -17644,7 +17931,7 @@ class IndexCache {
17644
17931
  var init_cache = () => {};
17645
17932
 
17646
17933
  // src/modules/indexer/module.ts
17647
- import { dirname as dirname7 } from "path";
17934
+ import { dirname as dirname8 } from "path";
17648
17935
 
17649
17936
  class IndexerModule {
17650
17937
  name = "indexer";
@@ -17761,7 +18048,7 @@ ${t("indexer.and_more", { count: result.files.length - 100 })}` : "";
17761
18048
  const counts = {};
17762
18049
  for (const f of result.files) {
17763
18050
  const normalized = f.path.replace(/\\/g, "/");
17764
- const dir = dirname7(normalized);
18051
+ const dir = dirname8(normalized);
17765
18052
  const key = dir === "." ? "(root)" : dir;
17766
18053
  counts[key] = (counts[key] || 0) + 1;
17767
18054
  }
@@ -17992,13 +18279,13 @@ var init_mcp = __esm(() => {
17992
18279
 
17993
18280
  // src/modules/memory/module.ts
17994
18281
  import { homedir as homedir10 } from "os";
17995
- import { join as join29 } from "path";
18282
+ import { join as join30 } from "path";
17996
18283
 
17997
18284
  class MemoryModule {
17998
18285
  name = "memory";
17999
18286
  store;
18000
18287
  constructor(memoryDir) {
18001
- const dir = memoryDir || join29(homedir10(), ".mma", "memory");
18288
+ const dir = memoryDir || join30(homedir10(), ".mma", "memory");
18002
18289
  this.store = new MemoryStore(dir);
18003
18290
  }
18004
18291
  getSystemPromptBlock() {
@@ -18046,7 +18333,7 @@ __export(exports_bootstrap, {
18046
18333
  bootstrap: () => bootstrap
18047
18334
  });
18048
18335
  import { homedir as homedir11 } from "os";
18049
- import { join as join30, resolve as resolve21 } from "path";
18336
+ import { join as join31, resolve as resolve21 } from "path";
18050
18337
  import { existsSync as existsSync37, readFileSync as readFileSync24, writeFileSync as writeFileSync14 } from "fs";
18051
18338
  function buildSystemInfo(config, baseDir, profileCompressed) {
18052
18339
  const now = new Date().toISOString().replace("T", " ").slice(0, 19);
@@ -18072,8 +18359,8 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
18072
18359
  `);
18073
18360
  }
18074
18361
  async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
18075
- const dir = configDir || join30(homedir11(), ".mma");
18076
- const projectConfigPath = projectDir ? join30(projectDir, ".mmrc") : join30(process.cwd(), ".mmrc");
18362
+ const dir = configDir || join31(homedir11(), ".mma");
18363
+ const projectConfigPath = projectDir ? join31(projectDir, ".mmrc") : join31(process.cwd(), ".mmrc");
18077
18364
  const config = loadConfig({ configDir: dir, projectConfigPath });
18078
18365
  setLocale(config.locale);
18079
18366
  try {
@@ -18083,7 +18370,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
18083
18370
  }
18084
18371
  } catch {}
18085
18372
  const logger = new Logger(config.logLevel);
18086
- logger.setLogDir(join30(dir, "logs"));
18373
+ logger.setLogDir(join31(dir, "logs"));
18087
18374
  logger.debug("MMA bootstrap", {
18088
18375
  version: config.version,
18089
18376
  model: config.model
@@ -18105,7 +18392,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
18105
18392
  logger.info(`Model ${config.model} loaded in ${loadResult.loadTime}s`);
18106
18393
  }
18107
18394
  }
18108
- const profile = new UserProfile(join30(dir));
18395
+ const profile = new UserProfile(join31(dir));
18109
18396
  profile.load() || profile.collect();
18110
18397
  profile.save();
18111
18398
  const llmProvider = new OpenAICompatProvider({
@@ -18117,7 +18404,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
18117
18404
  rateLimits: config.security?.rateLimits
18118
18405
  });
18119
18406
  const baseDir = projectDir ? resolve21(projectDir) : process.cwd();
18120
- const projectMapCacheDir = join30(baseDir, ".mma");
18407
+ const projectMapCacheDir = join31(baseDir, ".mma");
18121
18408
  const indexerModule = new IndexerModule({
18122
18409
  baseDir,
18123
18410
  cacheDir: projectMapCacheDir
@@ -18128,9 +18415,9 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
18128
18415
  logger.warn(`Project indexing failed: ${err.message}`);
18129
18416
  }
18130
18417
  const skillsLoader = new SkillsLoader;
18131
- const builtinDir = join30(import.meta.dirname, "skills", "builtin");
18132
- const globalDir = join30(homedir11(), ".agents", "skills");
18133
- const projectSkillsDir = join30(baseDir, ".mma", "skills");
18418
+ const builtinDir = join31(import.meta.dirname, "skills", "builtin");
18419
+ const globalDir = join31(homedir11(), ".agents", "skills");
18420
+ const projectSkillsDir = join31(baseDir, ".mma", "skills");
18134
18421
  const availableSkills = skillsLoader.loadFromAllSources(builtinDir, globalDir, projectSkillsDir);
18135
18422
  const skillsBudget = Math.floor(config.contextWindow * config.skills.budget);
18136
18423
  const skillsModule = new SkillsModule(availableSkills, skillsBudget);
@@ -18144,11 +18431,11 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
18144
18431
  essential: true,
18145
18432
  estimatedTokens: Math.ceil(systemInfoContent.length / 4)
18146
18433
  };
18147
- const agentsMdGlobal = join30(dir, "AGENTS.md");
18434
+ const agentsMdGlobal = join31(dir, "AGENTS.md");
18148
18435
  if (!existsSync37(agentsMdGlobal)) {
18149
18436
  writeFileSync14(agentsMdGlobal, "", "utf-8");
18150
18437
  }
18151
- const sessionDir = join30(dir, "sessions");
18438
+ const sessionDir = join31(dir, "sessions");
18152
18439
  const sessionStore = new SessionStore(sessionDir);
18153
18440
  sessionStore.init();
18154
18441
  const sessionManager = new SessionManager(sessionStore, {
@@ -18215,7 +18502,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
18215
18502
  const mcpModule = new MCPModule(config);
18216
18503
  await mcpModule.initialize();
18217
18504
  moduleRegistry.register(mcpModule);
18218
- const memoryModule = new MemoryModule(join30(dir, "memory"));
18505
+ const memoryModule = new MemoryModule(join31(dir, "memory"));
18219
18506
  moduleRegistry.register(memoryModule);
18220
18507
  if (config.browser.enabled) {
18221
18508
  const browserModule = new BrowserModule;
@@ -18265,8 +18552,8 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
18265
18552
  pluginManager.register(plugin);
18266
18553
  pluginManager.register(plugin2);
18267
18554
  const pluginLoader = new PluginLoader;
18268
- const globalPluginsDir = join30(homedir11(), ".mma", "plugins");
18269
- const projectPluginsDir = join30(baseDir, ".mma", "plugins");
18555
+ const globalPluginsDir = join31(homedir11(), ".mma", "plugins");
18556
+ const projectPluginsDir = join31(baseDir, ".mma", "plugins");
18270
18557
  pluginLoader.loadFromDir(globalPluginsDir, pluginManager, logger);
18271
18558
  pluginLoader.loadFromDir(projectPluginsDir, pluginManager, logger);
18272
18559
  contextManager.onCompact = (summary) => {
@@ -18284,9 +18571,9 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
18284
18571
  const skipAgentsMd = noAgentsMd === true;
18285
18572
  if (!skipAgentsMd) {
18286
18573
  const agentsMdCandidates = [
18287
- join30(baseDir, "AGENTS.md"),
18288
- join30(baseDir, ".mma", "AGENTS.md"),
18289
- join30(dir, "AGENTS.md")
18574
+ join31(baseDir, "AGENTS.md"),
18575
+ join31(baseDir, ".mma", "AGENTS.md"),
18576
+ join31(dir, "AGENTS.md")
18290
18577
  ];
18291
18578
  for (const p of agentsMdCandidates) {
18292
18579
  if (existsSync37(p)) {
@@ -19043,7 +19330,7 @@ async function runSetup(externalRl) {
19043
19330
  const idx = Math.max(0, Math.min(models.length - 1, (parseInt(choice) || 1) - 1));
19044
19331
  model = models[idx];
19045
19332
  } else {
19046
- model = await ask(rl, t("setup.model_name"), "qwen3.5-9b");
19333
+ model = await ask(rl, t("setup.model_name"), "qwen/qwen3.5-9b");
19047
19334
  }
19048
19335
  const connected = await withSpinner(t("setup.testing_spinner", { model }), () => testChat(apiBase, apiKey, model));
19049
19336
  if (connected) {
@@ -19141,7 +19428,7 @@ __export(exports_manifest, {
19141
19428
  });
19142
19429
  import { existsSync as existsSync38, readFileSync as readFileSync25, mkdirSync as mkdirSync17, writeFileSync as writeFileSync15 } from "fs";
19143
19430
  import { homedir as homedir13 } from "os";
19144
- import { join as join32 } from "path";
19431
+ import { join as join33 } from "path";
19145
19432
  function readManifest(path = MANIFEST_PATH) {
19146
19433
  try {
19147
19434
  if (existsSync38(path)) {
@@ -19152,7 +19439,7 @@ function readManifest(path = MANIFEST_PATH) {
19152
19439
  return { version: 1, certifications: [] };
19153
19440
  }
19154
19441
  function saveManifest(m, path = MANIFEST_PATH) {
19155
- mkdirSync17(join32(homedir13(), ".mma"), { recursive: true });
19442
+ mkdirSync17(join33(homedir13(), ".mma"), { recursive: true });
19156
19443
  writeFileSync15(path, JSON.stringify(m, null, 2), "utf-8");
19157
19444
  }
19158
19445
  function upsertCertification(entry, path = MANIFEST_PATH) {
@@ -19187,7 +19474,7 @@ function getCertMark(model, providerUrl, currentVersion, path = MANIFEST_PATH) {
19187
19474
  }
19188
19475
  var MANIFEST_PATH;
19189
19476
  var init_manifest = __esm(() => {
19190
- MANIFEST_PATH = join32(homedir13(), ".mma", "certifications.json");
19477
+ MANIFEST_PATH = join33(homedir13(), ".mma", "certifications.json");
19191
19478
  });
19192
19479
 
19193
19480
  // node_modules/yaml/dist/nodes/identity.js
@@ -26311,7 +26598,7 @@ var init_scenarios = __esm(() => {
26311
26598
 
26312
26599
  // src/modules/certification/loader.ts
26313
26600
  import { existsSync as existsSync39, readdirSync as readdirSync14, readFileSync as readFileSync26 } from "fs";
26314
- import { join as join33 } from "path";
26601
+ import { join as join34 } from "path";
26315
26602
  function validateScenario(s) {
26316
26603
  const errors2 = [];
26317
26604
  const isSkip = s.mode === "skip";
@@ -26365,7 +26652,7 @@ function loadScenarios(userDir) {
26365
26652
  if (!file.endsWith(".yaml") && !file.endsWith(".yml"))
26366
26653
  continue;
26367
26654
  try {
26368
- const raw = readFileSync26(join33(userDir, file), "utf-8");
26655
+ const raw = readFileSync26(join34(userDir, file), "utf-8");
26369
26656
  const data = $parse(raw);
26370
26657
  const parsed = normalizeScenario(data, file);
26371
26658
  const errs = validateScenario(parsed);
@@ -26419,7 +26706,7 @@ var init_loader3 = __esm(() => {
26419
26706
 
26420
26707
  // src/modules/certification/fact-checker.ts
26421
26708
  import { existsSync as existsSync40, readFileSync as readFileSync27, statSync as statSync8 } from "fs";
26422
- import { join as join34 } from "path";
26709
+ import { join as join35 } from "path";
26423
26710
  function checkSandbox(sandboxDir, checks, exitCode, output) {
26424
26711
  const failures = [];
26425
26712
  for (const check of checks) {
@@ -26436,13 +26723,13 @@ function runCheck(sandboxDir, check, exitCode, output) {
26436
26723
  case "outputContains":
26437
26724
  return output.includes(check.text);
26438
26725
  case "fileExists":
26439
- return isFile(join34(sandboxDir, check.path));
26726
+ return isFile(join35(sandboxDir, check.path));
26440
26727
  case "fileNotExists":
26441
- return !existsSync40(join34(sandboxDir, check.path));
26728
+ return !existsSync40(join35(sandboxDir, check.path));
26442
26729
  case "dirExists":
26443
- return isDir(join34(sandboxDir, check.path));
26730
+ return isDir(join35(sandboxDir, check.path));
26444
26731
  case "fileContent": {
26445
- const abs = join34(sandboxDir, check.path);
26732
+ const abs = join35(sandboxDir, check.path);
26446
26733
  if (!isFile(abs))
26447
26734
  return false;
26448
26735
  const content = readFileSync27(abs, "utf-8");
@@ -26453,7 +26740,7 @@ function runCheck(sandboxDir, check, exitCode, output) {
26453
26740
  return false;
26454
26741
  }
26455
26742
  case "fileRegex": {
26456
- const abs = join34(sandboxDir, check.path);
26743
+ const abs = join35(sandboxDir, check.path);
26457
26744
  if (!isFile(abs))
26458
26745
  return false;
26459
26746
  return new RegExp(check.pattern).test(readFileSync27(abs, "utf-8"));
@@ -26501,10 +26788,10 @@ function describe(check) {
26501
26788
  var init_fact_checker = () => {};
26502
26789
 
26503
26790
  // src/modules/certification/runner.ts
26504
- import { spawn as spawn6 } from "child_process";
26791
+ import { spawn as spawn7 } from "child_process";
26505
26792
  import { existsSync as existsSync41, mkdirSync as mkdirSync18, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
26506
26793
  import { platform as platform5 } from "os";
26507
- import { join as join35, resolve as resolve22, dirname as dirname8 } from "path";
26794
+ import { join as join36, resolve as resolve22, dirname as dirname9 } from "path";
26508
26795
  async function runScenario(scenario, opts) {
26509
26796
  if (scenario.mode === "skip") {
26510
26797
  return {
@@ -26523,7 +26810,7 @@ async function runScenario(scenario, opts) {
26523
26810
  let passed = 0;
26524
26811
  let firstError;
26525
26812
  for (let i = 1;i <= reps; i++) {
26526
- const sandbox = join35(opts.sandboxBase, `run-${scenario.id}-${i}`);
26813
+ const sandbox = join36(opts.sandboxBase, `run-${scenario.id}-${i}`);
26527
26814
  let failures = [];
26528
26815
  let exitCode = -1;
26529
26816
  let output = "";
@@ -26584,20 +26871,20 @@ function prepareSandbox(sandbox, scenario, mmaRoot) {
26584
26871
  rmSync4(sandbox, { recursive: true, force: true });
26585
26872
  mkdirSync18(sandbox, { recursive: true });
26586
26873
  for (const f of scenario.fixtures ?? []) {
26587
- const src = join35(mmaRoot, f.source);
26874
+ const src = join36(mmaRoot, f.source);
26588
26875
  if (!existsSync41(src)) {
26589
26876
  throw new Error(`fixture missing: ${f.source}`);
26590
26877
  }
26591
- const dest = join35(sandbox, f.dest);
26592
- mkdirSync18(dirname8(dest), { recursive: true });
26878
+ const dest = join36(sandbox, f.dest);
26879
+ mkdirSync18(dirname9(dest), { recursive: true });
26593
26880
  cpSync2(src, dest);
26594
26881
  }
26595
26882
  }
26596
26883
  function resolveMmaEntry(mmaRoot) {
26597
- const dev = join35(mmaRoot, "src", "cli", "main.ts");
26884
+ const dev = join36(mmaRoot, "src", "cli", "main.ts");
26598
26885
  if (existsSync41(dev))
26599
26886
  return dev;
26600
- return join35(mmaRoot, "dist", "main.js");
26887
+ return join36(mmaRoot, "dist", "main.js");
26601
26888
  }
26602
26889
  function findMmaRoot(fromDir) {
26603
26890
  const candidates = [
@@ -26605,7 +26892,7 @@ function findMmaRoot(fromDir) {
26605
26892
  resolve22(fromDir, "..")
26606
26893
  ];
26607
26894
  for (const c of candidates) {
26608
- if (existsSync41(join35(c, "package.json")))
26895
+ if (existsSync41(join36(c, "package.json")))
26609
26896
  return c;
26610
26897
  }
26611
26898
  return process.cwd();
@@ -26615,7 +26902,7 @@ function killTree2(child) {
26615
26902
  if (!pid)
26616
26903
  return;
26617
26904
  if (platform5() === "win32") {
26618
- spawn6("taskkill", ["/pid", String(pid), "/T", "/F"], {
26905
+ spawn7("taskkill", ["/pid", String(pid), "/T", "/F"], {
26619
26906
  windowsHide: true,
26620
26907
  stdio: "ignore"
26621
26908
  });
@@ -26630,7 +26917,7 @@ function killTree2(child) {
26630
26917
  }
26631
26918
  }
26632
26919
  var defaultRunner = (env2, cwd, args, timeoutMs) => new Promise((resolvePromise) => {
26633
- const child = spawn6(process.execPath, args, {
26920
+ const child = spawn7(process.execPath, args, {
26634
26921
  cwd,
26635
26922
  env: env2,
26636
26923
  windowsHide: true,
@@ -26673,11 +26960,11 @@ __export(exports_cli, {
26673
26960
  });
26674
26961
  import { rmSync as rmSync5 } from "fs";
26675
26962
  import { homedir as homedir14 } from "os";
26676
- import { join as join36, dirname as dirname9 } from "path";
26677
- import { fileURLToPath } from "url";
26963
+ import { join as join37, dirname as dirname10 } from "path";
26964
+ import { fileURLToPath as fileURLToPath2 } from "url";
26678
26965
  import { existsSync as existsSync42, readFileSync as readFileSync28 } from "fs";
26679
26966
  function readVersion() {
26680
- const candidates = [join36(MMA_ROOT, "package.json")];
26967
+ const candidates = [join37(MMA_ROOT, "package.json")];
26681
26968
  for (const p of candidates) {
26682
26969
  if (existsSync42(p)) {
26683
26970
  try {
@@ -26717,7 +27004,7 @@ async function certify(opts) {
26717
27004
  return;
26718
27005
  }
26719
27006
  console.log(t("cli.cert_started", { model: opts.name, provider: providerUrl }));
26720
- const sandboxBase = join36(process.cwd(), ".mma", "certification");
27007
+ const sandboxBase = join37(process.cwd(), ".mma", "certification");
26721
27008
  const results = [];
26722
27009
  const total = selected.length;
26723
27010
  let idx = 0;
@@ -26829,9 +27116,9 @@ var init_cli = __esm(() => {
26829
27116
  init_loader3();
26830
27117
  init_runner2();
26831
27118
  init_manifest();
26832
- HERE = dirname9(fileURLToPath(import.meta.url));
27119
+ HERE = dirname10(fileURLToPath2(import.meta.url));
26833
27120
  MMA_ROOT = findMmaRoot(HERE);
26834
- USER_SCENARIO_DIR = join36(homedir14(), ".mma", "certification", "scenarios");
27121
+ USER_SCENARIO_DIR = join37(homedir14(), ".mma", "certification", "scenarios");
26835
27122
  });
26836
27123
 
26837
27124
  // src/cli/repl-commands.ts
@@ -26840,15 +27127,15 @@ __export(exports_repl_commands, {
26840
27127
  registerAllCommands: () => registerAllCommands,
26841
27128
  COMMAND_GROUPS: () => COMMAND_GROUPS
26842
27129
  });
26843
- import { join as join38, dirname as dirname11 } from "path";
27130
+ import { join as join39, dirname as dirname12 } from "path";
26844
27131
  import { homedir as homedir16 } from "os";
26845
27132
  import { existsSync as existsSync44, readFileSync as readFileSync30 } from "fs";
26846
- import { fileURLToPath as fileURLToPath3 } from "url";
27133
+ import { fileURLToPath as fileURLToPath4 } from "url";
26847
27134
  function readVersion3() {
26848
- const here = dirname11(fileURLToPath3(import.meta.url));
27135
+ const here = dirname12(fileURLToPath4(import.meta.url));
26849
27136
  const candidates = [
26850
- join38(here, "..", "..", "package.json"),
26851
- join38(here, "..", "package.json")
27137
+ join39(here, "..", "..", "package.json"),
27138
+ join39(here, "..", "package.json")
26852
27139
  ];
26853
27140
  for (const p of candidates) {
26854
27141
  if (existsSync44(p)) {
@@ -27016,7 +27303,7 @@ function registerMmaCommands(ctx) {
27016
27303
  console.log(pc2.yellow(t("repl.wizard_running")));
27017
27304
  await ctx.withExclusiveInput(async () => {
27018
27305
  const answers = await runSetup(ctx.rl);
27019
- const configPath = join38(homedir16(), ".mma", "config.json");
27306
+ const configPath = join39(homedir16(), ".mma", "config.json");
27020
27307
  ctx.config.provider.type = answers.provider;
27021
27308
  ctx.config.provider.baseUrl = answers.apiBase;
27022
27309
  ctx.config.provider.apiKey = answers.apiKey;
@@ -27070,7 +27357,7 @@ Excluded blocks: ${info.excluded.length}`));
27070
27357
  return;
27071
27358
  }
27072
27359
  ctx.config.provider.type = name;
27073
- const configPath = join38(homedir16(), ".mma", "config.json");
27360
+ const configPath = join39(homedir16(), ".mma", "config.json");
27074
27361
  saveConfig(ctx.config, configPath);
27075
27362
  await ctx.agent.reconfigure(ctx.config);
27076
27363
  console.log(pc2.green(t("repl.provider_set", { name })));
@@ -27126,7 +27413,7 @@ Excluded blocks: ${info.excluded.length}`));
27126
27413
  return;
27127
27414
  }
27128
27415
  ctx.config.model = name;
27129
- const configPath = join38(homedir16(), ".mma", "config.json");
27416
+ const configPath = join39(homedir16(), ".mma", "config.json");
27130
27417
  saveConfig(ctx.config, configPath);
27131
27418
  await ctx.agent.reconfigure(ctx.config);
27132
27419
  console.log(pc2.green(t("repl.model_set", { name })));
@@ -27151,7 +27438,7 @@ Excluded blocks: ${info.excluded.length}`));
27151
27438
  return;
27152
27439
  }
27153
27440
  ctx.config.contextWindow = size;
27154
- const configPath = join38(homedir16(), ".mma", "config.json");
27441
+ const configPath = join39(homedir16(), ".mma", "config.json");
27155
27442
  saveConfig(ctx.config, configPath);
27156
27443
  await ctx.agent.reconfigure(ctx.config);
27157
27444
  console.log(pc2.green(t("cli.context_set", { size })));
@@ -27170,10 +27457,10 @@ Excluded blocks: ${info.excluded.length}`));
27170
27457
  ctx.agent.shutdown();
27171
27458
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
27172
27459
  const { homedir: homedir17 } = await import("os");
27173
- const { join: join39 } = await import("path");
27460
+ const { join: join40 } = await import("path");
27174
27461
  const configDir = ctx.configDir;
27175
27462
  const baseDir = ctx.baseDir;
27176
- const projectConfigPath = join39(baseDir, ".mmrc");
27463
+ const projectConfigPath = join40(baseDir, ".mmrc");
27177
27464
  const freshConfig = loadConfig2({ configDir, projectConfigPath });
27178
27465
  Object.assign(ctx.config, freshConfig);
27179
27466
  const { bootstrap: bootstrap2 } = await Promise.resolve().then(() => (init_bootstrap(), exports_bootstrap));
@@ -27473,14 +27760,14 @@ init_bootstrap();
27473
27760
  init_config2();
27474
27761
  init_setup();
27475
27762
  init_i18n();
27476
- import { join as join37, dirname as dirname10 } from "path";
27763
+ import { join as join38, dirname as dirname11 } from "path";
27477
27764
  import { homedir as homedir15 } from "os";
27478
27765
  import { existsSync as existsSync43, readFileSync as readFileSync29 } from "fs";
27479
27766
 
27480
27767
  // src/cli/security-commands.ts
27481
27768
  init_bootstrap();
27482
27769
  init_config2();
27483
- import { join as join31 } from "path";
27770
+ import { join as join32 } from "path";
27484
27771
  import { homedir as homedir12 } from "os";
27485
27772
 
27486
27773
  // src/modules/security/security-policies.ts
@@ -28010,7 +28297,7 @@ function createSecurityCommand(program2) {
28010
28297
  }
28011
28298
  });
28012
28299
  securityCmd.command("set-policy").argument("<preset>", t("cli.security.preset")).description(t("cli.security.set_policy")).action(async (preset) => {
28013
- const configPath = join31(homedir12(), ".mma", "config.json");
28300
+ const configPath = join32(homedir12(), ".mma", "config.json");
28014
28301
  const { config: appConfig } = await bootstrap();
28015
28302
  const validPresets = ["strict", "balanced", "permissive"];
28016
28303
  if (!validPresets.includes(preset)) {
@@ -28025,7 +28312,7 @@ function createSecurityCommand(program2) {
28025
28312
  console.log(t("cli.security.policy_description", { description: policy.description }));
28026
28313
  });
28027
28314
  securityCmd.command("enable-encryption").description(t("cli.security.enable_encryption")).action(async () => {
28028
- const configPath = join31(homedir12(), ".mma", "config.json");
28315
+ const configPath = join32(homedir12(), ".mma", "config.json");
28029
28316
  const { config: appConfig } = await bootstrap();
28030
28317
  appConfig.security = appConfig.security || {};
28031
28318
  appConfig.security.sessionEncryption = {
@@ -28037,7 +28324,7 @@ function createSecurityCommand(program2) {
28037
28324
  console.log(t("cli.security.encryption_enabled"));
28038
28325
  });
28039
28326
  securityCmd.command("disable-encryption").description(t("cli.security.disable_encryption")).action(async () => {
28040
- const configPath = join31(homedir12(), ".mma", "config.json");
28327
+ const configPath = join32(homedir12(), ".mma", "config.json");
28041
28328
  const { config: appConfig } = await bootstrap();
28042
28329
  appConfig.security = appConfig.security || {};
28043
28330
  appConfig.security.sessionEncryption = {
@@ -28049,7 +28336,7 @@ function createSecurityCommand(program2) {
28049
28336
  console.log(t("cli.security.encryption_disabled"));
28050
28337
  });
28051
28338
  securityCmd.command("enable-audit").description(t("cli.security.enable_audit")).action(async () => {
28052
- const configPath = join31(homedir12(), ".mma", "config.json");
28339
+ const configPath = join32(homedir12(), ".mma", "config.json");
28053
28340
  const { config: appConfig } = await bootstrap();
28054
28341
  appConfig.security = appConfig.security || {};
28055
28342
  appConfig.security.auditNotifier = {
@@ -28063,7 +28350,7 @@ function createSecurityCommand(program2) {
28063
28350
  console.log(t("cli.security.audit_enabled"));
28064
28351
  });
28065
28352
  securityCmd.command("disable-audit").description(t("cli.security.disable_audit")).action(async () => {
28066
- const configPath = join31(homedir12(), ".mma", "config.json");
28353
+ const configPath = join32(homedir12(), ".mma", "config.json");
28067
28354
  const { config: appConfig } = await bootstrap();
28068
28355
  appConfig.security = appConfig.security || {};
28069
28356
  appConfig.security.auditNotifier = {
@@ -28095,12 +28382,12 @@ function createSecurityCommand(program2) {
28095
28382
  }
28096
28383
 
28097
28384
  // src/cli/commands.ts
28098
- import { fileURLToPath as fileURLToPath2 } from "url";
28385
+ import { fileURLToPath as fileURLToPath3 } from "url";
28099
28386
  function readVersion2() {
28100
- const here = dirname10(fileURLToPath2(import.meta.url));
28387
+ const here = dirname11(fileURLToPath3(import.meta.url));
28101
28388
  const candidates = [
28102
- join37(here, "..", "..", "package.json"),
28103
- join37(here, "..", "package.json")
28389
+ join38(here, "..", "..", "package.json"),
28390
+ join38(here, "..", "package.json")
28104
28391
  ];
28105
28392
  for (const p of candidates) {
28106
28393
  if (existsSync43(p)) {
@@ -28118,7 +28405,7 @@ function createProgram() {
28118
28405
  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"));
28119
28406
  program2.command("init").description(t("cli.init")).action(async () => {
28120
28407
  const answers = await runSetup();
28121
- const configPath = join37(homedir15(), ".mma", "config.json");
28408
+ const configPath = join38(homedir15(), ".mma", "config.json");
28122
28409
  const { config } = await bootstrap();
28123
28410
  config.provider.type = answers.provider;
28124
28411
  config.provider.baseUrl = answers.apiBase;
@@ -28163,7 +28450,7 @@ function createProgram() {
28163
28450
  });
28164
28451
  const configCmd = program2.command("config").description(t("cli.manage_config"));
28165
28452
  configCmd.command("set").argument("<key>", t("cli.config_key")).argument("<value>", "Config value").description(t("cli.set_value")).action(async (key, value) => {
28166
- const configPath = join37(homedir15(), ".mma", "config.json");
28453
+ const configPath = join38(homedir15(), ".mma", "config.json");
28167
28454
  const { config } = await bootstrap();
28168
28455
  const keys = key.split(".");
28169
28456
  let obj = config;
@@ -28226,7 +28513,7 @@ function createProgram() {
28226
28513
  console.log(t("cli.model_hint"));
28227
28514
  });
28228
28515
  model.command("use").argument("<name>", "Model name").description(t("cli.set_model")).action(async (name) => {
28229
- const configPath = join37(homedir15(), ".mma", "config.json");
28516
+ const configPath = join38(homedir15(), ".mma", "config.json");
28230
28517
  const { config } = await bootstrap();
28231
28518
  config.model = name;
28232
28519
  saveConfig(config, configPath);
@@ -28262,7 +28549,7 @@ function createProgram() {
28262
28549
  await uncertify2(name, config);
28263
28550
  });
28264
28551
  program2.command("context").description(t("cli.manage_context")).argument("<size>", "Context window size in tokens").action(async (size) => {
28265
- const configPath = join37(homedir15(), ".mma", "config.json");
28552
+ const configPath = join38(homedir15(), ".mma", "config.json");
28266
28553
  const { config } = await bootstrap();
28267
28554
  const contextWindow = parseInt(size, 10);
28268
28555
  if (isNaN(contextWindow) || contextWindow < 1024) {
@@ -28280,7 +28567,7 @@ function createProgram() {
28280
28567
  console.log(t("cli.base_url"), config.provider.baseUrl);
28281
28568
  });
28282
28569
  provider.command("use").argument("<name>", "Provider name").description(t("cli.set_provider")).action(async (name) => {
28283
- const configPath = join37(homedir15(), ".mma", "config.json");
28570
+ const configPath = join38(homedir15(), ".mma", "config.json");
28284
28571
  const { config } = await bootstrap();
28285
28572
  config.provider.type = name;
28286
28573
  saveConfig(config, configPath);
@@ -28331,11 +28618,701 @@ init_bootstrap();
28331
28618
 
28332
28619
  // src/cli/repl.ts
28333
28620
  init_colors();
28621
+ import * as readline3 from "readline";
28622
+
28623
+ // src/ui/line-editor.ts
28624
+ init_string_width();
28334
28625
  import * as readline2 from "readline";
28626
+
28627
+ // src/ui/line-math.ts
28628
+ init_string_width();
28629
+ var ANSI_RE = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
28630
+ function stripAnsi2(s) {
28631
+ return s.replace(ANSI_RE, "");
28632
+ }
28633
+ function charLen(s) {
28634
+ return Array.from(s).length;
28635
+ }
28636
+ function wrapLine(line, width) {
28637
+ const w = width > 0 ? width : 80;
28638
+ const chars = Array.from(line);
28639
+ const rows = [];
28640
+ let start = 0;
28641
+ let cells = 0;
28642
+ for (let i = 0;i < chars.length; i++) {
28643
+ const ch = chars[i];
28644
+ if (ch === `
28645
+ `) {
28646
+ rows.push({ text: chars.slice(start, i).join(""), charStart: start });
28647
+ start = i + 1;
28648
+ cells = 0;
28649
+ continue;
28650
+ }
28651
+ const cw = stringWidth(ch);
28652
+ if (cells + cw > w && cells > 0) {
28653
+ rows.push({ text: chars.slice(start, i).join(""), charStart: start });
28654
+ start = i;
28655
+ cells = cw;
28656
+ } else {
28657
+ cells += cw;
28658
+ }
28659
+ }
28660
+ rows.push({ text: chars.slice(start).join(""), charStart: start });
28661
+ return rows;
28662
+ }
28663
+ function buildLayout(lines, width) {
28664
+ const rows = [];
28665
+ for (let b = 0;b < lines.length; b++) {
28666
+ for (const r of wrapLine(lines[b], width)) {
28667
+ rows.push({ bufRow: b, charStart: r.charStart, text: r.text });
28668
+ }
28669
+ }
28670
+ return rows;
28671
+ }
28672
+ function layoutIndexFor(rows, bufRow, col) {
28673
+ let idx = 0;
28674
+ for (let i = 0;i < rows.length; i++) {
28675
+ if (rows[i].bufRow === bufRow && rows[i].charStart <= col)
28676
+ idx = i;
28677
+ }
28678
+ return idx;
28679
+ }
28680
+ function visualColAt(rows, idx, col) {
28681
+ const r = rows[idx];
28682
+ const upTo = col - r.charStart;
28683
+ return stringWidth(Array.from(r.text).slice(0, upTo).join(""));
28684
+ }
28685
+ function charOffsetFor(rows, idx, visualCol) {
28686
+ const r = rows[idx];
28687
+ const chars = Array.from(r.text);
28688
+ let cells = 0;
28689
+ for (let i = 0;i < chars.length; i++) {
28690
+ const cw = stringWidth(chars[i]);
28691
+ if (cells + cw > visualCol)
28692
+ return r.charStart + i;
28693
+ cells += cw;
28694
+ }
28695
+ return r.charStart + chars.length;
28696
+ }
28697
+
28698
+ // src/ui/line-editor.ts
28699
+ class LineEditor {
28700
+ input;
28701
+ output;
28702
+ onKeyInput;
28703
+ promptStr;
28704
+ completer;
28705
+ history;
28706
+ historySize;
28707
+ lines = [""];
28708
+ row = 0;
28709
+ col = 0;
28710
+ historyIndex = -1;
28711
+ stash = null;
28712
+ isDone = false;
28713
+ isPaste = false;
28714
+ lastWasCR = false;
28715
+ questionCb = null;
28716
+ rawMode = false;
28717
+ listeners = {};
28718
+ constructor(opts) {
28719
+ this.input = opts.input;
28720
+ this.output = opts.output;
28721
+ this.promptStr = opts.prompt ?? "";
28722
+ this.completer = opts.completer;
28723
+ this.history = opts.history ?? [];
28724
+ this.historySize = opts.historySize ?? 50;
28725
+ if (this.history.length > this.historySize) {
28726
+ this.history = this.history.slice(-this.historySize);
28727
+ }
28728
+ if (this.input.isTTY) {
28729
+ this.rawMode = typeof this.input.setRawMode === "function";
28730
+ if (this.rawMode)
28731
+ this.input.setRawMode(true);
28732
+ this.enableTerminalProtocols();
28733
+ readline2.emitKeypressEvents(this.input);
28734
+ this.input.on("keypress", (str, key) => this.onKey(str, key));
28735
+ } else {
28736
+ this.input.on("data", (buf) => this.onData(buf));
28737
+ this.input.on("end", () => {
28738
+ const last = this.lines.join(`
28739
+ `);
28740
+ this.lines = [""];
28741
+ this.row = 0;
28742
+ this.col = 0;
28743
+ if (last)
28744
+ this.emitLine(last);
28745
+ this.emitClose();
28746
+ });
28747
+ }
28748
+ }
28749
+ on(event, listener) {
28750
+ if (event === "line") {
28751
+ this.listeners.line = listener;
28752
+ } else {
28753
+ this.listeners.close = listener;
28754
+ }
28755
+ return this;
28756
+ }
28757
+ setPrompt(prompt) {
28758
+ this.promptStr = prompt;
28759
+ if (this.input.isTTY)
28760
+ this.render();
28761
+ }
28762
+ prompt(_preserveCursor) {
28763
+ if (this.input.isTTY)
28764
+ this.render();
28765
+ }
28766
+ write(s) {
28767
+ this.insertText(s);
28768
+ }
28769
+ reset() {
28770
+ this.lines = [""];
28771
+ this.row = 0;
28772
+ this.col = 0;
28773
+ this.historyIndex = -1;
28774
+ this.stash = null;
28775
+ if (this.input.isTTY)
28776
+ this.render();
28777
+ }
28778
+ question(query, cb) {
28779
+ this.questionCb = cb;
28780
+ this.promptStr = query;
28781
+ this.lines = [""];
28782
+ this.row = 0;
28783
+ this.col = 0;
28784
+ if (this.input.isTTY)
28785
+ this.render();
28786
+ }
28787
+ close() {
28788
+ if (this.isDone)
28789
+ return;
28790
+ this.isDone = true;
28791
+ this.disableTerminalProtocols();
28792
+ if (this.rawMode && this.input.setRawMode) {
28793
+ this.input.setRawMode(false);
28794
+ }
28795
+ this.emitClose();
28796
+ }
28797
+ onData(buf) {
28798
+ if (this.isDone)
28799
+ return;
28800
+ let text = buf.toString("utf-8");
28801
+ text = text.replace(/\r\n/g, `
28802
+ `).replace(/\r/g, `
28803
+ `);
28804
+ const parts = text.split(`
28805
+ `);
28806
+ for (let i = 0;i < parts.length; i++) {
28807
+ if (i < parts.length - 1) {
28808
+ this.emitLine(parts[i]);
28809
+ this.lines = [""];
28810
+ this.row = 0;
28811
+ this.col = 0;
28812
+ } else if (parts[i]) {
28813
+ this.emitLine(parts[i]);
28814
+ this.lines = [""];
28815
+ this.row = 0;
28816
+ this.col = 0;
28817
+ }
28818
+ }
28819
+ }
28820
+ onKey(str, key) {
28821
+ if (this.isDone)
28822
+ return;
28823
+ if (this.onKeyInput && this.onKeyInput(str, key))
28824
+ return;
28825
+ const k = key;
28826
+ const name = k?.name ?? "undefined";
28827
+ const seq2 = k?.sequence ?? str ?? "";
28828
+ if (name === "undefined" && /^\x1b\[\d+(?:;\d+)?u$/.test(seq2)) {
28829
+ const m = seq2.match(/^\x1b\[(\d+)(?:;(\d+))?u$/);
28830
+ if (m && Number(m[1]) === 13 && Number(m[2] ?? 0) !== 0) {
28831
+ this.insertNewline();
28832
+ }
28833
+ return;
28834
+ }
28835
+ if (name === "paste-start" || seq2 === "\x1B[200~") {
28836
+ this.isPaste = true;
28837
+ return;
28838
+ }
28839
+ if (name === "paste-end" || seq2 === "\x1B[201~") {
28840
+ this.isPaste = false;
28841
+ this.render();
28842
+ return;
28843
+ }
28844
+ if (this.isPaste) {
28845
+ if (name === "return" || seq2 === "\r" || seq2 === `
28846
+ `) {
28847
+ this.insertText(`
28848
+ `);
28849
+ } else if (str) {
28850
+ this.insertText(str);
28851
+ }
28852
+ this.render();
28853
+ return;
28854
+ }
28855
+ if (name === "return" && (seq2 === "\x1B\r" || k.meta)) {
28856
+ this.insertNewline();
28857
+ return;
28858
+ }
28859
+ if (name === "undefined" && seq2 === "\x1B\r") {
28860
+ this.insertNewline();
28861
+ return;
28862
+ }
28863
+ if (k?.ctrl && name === "j") {
28864
+ this.insertNewline();
28865
+ return;
28866
+ }
28867
+ if (name === "escape")
28868
+ return;
28869
+ switch (name) {
28870
+ case "return":
28871
+ this.lastWasCR = true;
28872
+ this.submit();
28873
+ return;
28874
+ case "enter":
28875
+ if (this.lastWasCR) {
28876
+ this.lastWasCR = false;
28877
+ return;
28878
+ }
28879
+ this.insertNewline();
28880
+ return;
28881
+ case "backspace":
28882
+ this.lastWasCR = false;
28883
+ this.backspace();
28884
+ return;
28885
+ case "delete":
28886
+ this.lastWasCR = false;
28887
+ this.deleteForward();
28888
+ return;
28889
+ case "left":
28890
+ this.lastWasCR = false;
28891
+ this.moveLeft();
28892
+ return;
28893
+ case "right":
28894
+ this.lastWasCR = false;
28895
+ this.moveRight();
28896
+ return;
28897
+ case "up":
28898
+ this.lastWasCR = false;
28899
+ this.moveUp();
28900
+ return;
28901
+ case "down":
28902
+ this.lastWasCR = false;
28903
+ this.moveDown();
28904
+ return;
28905
+ case "home":
28906
+ this.lastWasCR = false;
28907
+ this.moveHome();
28908
+ return;
28909
+ case "end":
28910
+ this.lastWasCR = false;
28911
+ this.moveEnd();
28912
+ return;
28913
+ case "pageup":
28914
+ this.lastWasCR = false;
28915
+ this.moveToStart();
28916
+ return;
28917
+ case "pagedown":
28918
+ this.lastWasCR = false;
28919
+ this.moveToEnd();
28920
+ return;
28921
+ case "tab":
28922
+ this.lastWasCR = false;
28923
+ this.tabComplete();
28924
+ return;
28925
+ }
28926
+ if (k?.ctrl) {
28927
+ this.lastWasCR = false;
28928
+ switch (name) {
28929
+ case "c":
28930
+ this.ctrlC();
28931
+ return;
28932
+ case "d":
28933
+ this.ctrlD();
28934
+ return;
28935
+ case "a":
28936
+ this.moveHome();
28937
+ return;
28938
+ case "e":
28939
+ this.moveEnd();
28940
+ return;
28941
+ case "u":
28942
+ this.killToStart();
28943
+ return;
28944
+ case "k":
28945
+ this.killToEnd();
28946
+ return;
28947
+ case "w":
28948
+ this.killWordBefore();
28949
+ return;
28950
+ case "l":
28951
+ this.clearScreen();
28952
+ return;
28953
+ default:
28954
+ return;
28955
+ }
28956
+ }
28957
+ if (k?.meta) {
28958
+ this.lastWasCR = false;
28959
+ switch (name) {
28960
+ case "b":
28961
+ this.moveWordLeft();
28962
+ return;
28963
+ case "f":
28964
+ this.moveWordRight();
28965
+ return;
28966
+ case "d":
28967
+ this.killWordAfter();
28968
+ return;
28969
+ default:
28970
+ return;
28971
+ }
28972
+ }
28973
+ if (str && !/^[\u0000-\u001f\u007f]/.test(str)) {
28974
+ this.lastWasCR = false;
28975
+ this.insertText(str);
28976
+ }
28977
+ }
28978
+ submit() {
28979
+ if (this.questionCb) {
28980
+ const cb = this.questionCb;
28981
+ this.questionCb = null;
28982
+ const answer = this.lines.join(`
28983
+ `).trim();
28984
+ this.lines = [""];
28985
+ this.row = 0;
28986
+ this.col = 0;
28987
+ this.render();
28988
+ cb(answer);
28989
+ return;
28990
+ }
28991
+ const text = this.lines.join(`
28992
+ `);
28993
+ if (text.trim()) {
28994
+ this.history.push(text);
28995
+ if (this.history.length > this.historySize) {
28996
+ this.history = this.history.slice(-this.historySize);
28997
+ }
28998
+ }
28999
+ this.historyIndex = -1;
29000
+ this.stash = null;
29001
+ this.lines = [""];
29002
+ this.row = 0;
29003
+ this.col = 0;
29004
+ this.render();
29005
+ this.emitLine(text);
29006
+ }
29007
+ ctrlC() {
29008
+ this.lines = [""];
29009
+ this.row = 0;
29010
+ this.col = 0;
29011
+ this.render();
29012
+ process.emit("SIGINT");
29013
+ }
29014
+ ctrlD() {
29015
+ if (this.lines.length === 1 && this.lines[0] === "") {
29016
+ this.emitClose();
29017
+ } else {
29018
+ this.deleteForward();
29019
+ }
29020
+ }
29021
+ currentChars() {
29022
+ return Array.from(this.lines[this.row]);
29023
+ }
29024
+ setCurrentChars(chars) {
29025
+ this.lines[this.row] = chars.join("");
29026
+ }
29027
+ insertText(text) {
29028
+ const sanitized = text.replace(/\r\n/g, `
29029
+ `).replace(/\r/g, "");
29030
+ const chars = this.currentChars();
29031
+ const t2 = Array.from(sanitized);
29032
+ chars.splice(this.col, 0, ...t2);
29033
+ this.setCurrentChars(chars);
29034
+ this.col += t2.length;
29035
+ this.render();
29036
+ }
29037
+ insertNewline() {
29038
+ const chars = this.currentChars();
29039
+ const rest = chars.slice(this.col).join("");
29040
+ this.setCurrentChars(chars.slice(0, this.col));
29041
+ this.lines.splice(this.row + 1, 0, rest);
29042
+ this.row++;
29043
+ this.col = 0;
29044
+ this.render();
29045
+ }
29046
+ backspace() {
29047
+ const chars = this.currentChars();
29048
+ if (this.col > 0) {
29049
+ chars.splice(this.col - 1, 1);
29050
+ this.setCurrentChars(chars);
29051
+ this.col--;
29052
+ } else if (this.row > 0) {
29053
+ const prevLen = charLen(this.lines[this.row - 1]);
29054
+ this.lines[this.row - 1] = this.lines[this.row - 1] + this.lines[this.row];
29055
+ this.lines.splice(this.row, 1);
29056
+ this.row--;
29057
+ this.col = prevLen;
29058
+ }
29059
+ this.render();
29060
+ }
29061
+ deleteForward() {
29062
+ const chars = this.currentChars();
29063
+ if (this.col < chars.length) {
29064
+ chars.splice(this.col, 1);
29065
+ this.setCurrentChars(chars);
29066
+ } else if (this.row < this.lines.length - 1) {
29067
+ this.lines[this.row] = this.lines[this.row] + this.lines[this.row + 1];
29068
+ this.lines.splice(this.row + 1, 1);
29069
+ }
29070
+ this.render();
29071
+ }
29072
+ killToStart() {
29073
+ const chars = this.currentChars();
29074
+ this.setCurrentChars(chars.slice(this.col));
29075
+ this.col = 0;
29076
+ this.render();
29077
+ }
29078
+ killToEnd() {
29079
+ const chars = this.currentChars();
29080
+ this.setCurrentChars(chars.slice(0, this.col));
29081
+ this.render();
29082
+ }
29083
+ killWordBefore() {
29084
+ const chars = this.currentChars();
29085
+ let i = this.col;
29086
+ while (i > 0 && /\s/.test(chars[i - 1]))
29087
+ i--;
29088
+ while (i > 0 && !/\s/.test(chars[i - 1]))
29089
+ i--;
29090
+ chars.splice(i, this.col - i);
29091
+ this.setCurrentChars(chars);
29092
+ this.col = i;
29093
+ this.render();
29094
+ }
29095
+ killWordAfter() {
29096
+ const chars = this.currentChars();
29097
+ const n = chars.length;
29098
+ let i = this.col;
29099
+ while (i < n && /\s/.test(chars[i]))
29100
+ i++;
29101
+ while (i < n && !/\s/.test(chars[i]))
29102
+ i++;
29103
+ chars.splice(this.col, i - this.col);
29104
+ this.setCurrentChars(chars);
29105
+ this.render();
29106
+ }
29107
+ moveLeft() {
29108
+ if (this.col > 0) {
29109
+ this.col--;
29110
+ } else if (this.row > 0) {
29111
+ this.row--;
29112
+ this.col = charLen(this.lines[this.row]);
29113
+ }
29114
+ this.render();
29115
+ }
29116
+ moveRight() {
29117
+ if (this.col < charLen(this.lines[this.row])) {
29118
+ this.col++;
29119
+ } else if (this.row < this.lines.length - 1) {
29120
+ this.row++;
29121
+ this.col = 0;
29122
+ }
29123
+ this.render();
29124
+ }
29125
+ moveUp() {
29126
+ const layout = this.layout();
29127
+ const idx = layoutIndexFor(layout, this.row, this.col);
29128
+ const vcol = visualColAt(layout, idx, this.col);
29129
+ if (idx > 0) {
29130
+ const target = layout[idx - 1];
29131
+ this.row = target.bufRow;
29132
+ this.col = charOffsetFor(layout, idx - 1, vcol);
29133
+ this.render();
29134
+ } else {
29135
+ this.historyPrev();
29136
+ }
29137
+ }
29138
+ moveDown() {
29139
+ const layout = this.layout();
29140
+ const idx = layoutIndexFor(layout, this.row, this.col);
29141
+ const vcol = visualColAt(layout, idx, this.col);
29142
+ if (idx < layout.length - 1) {
29143
+ const target = layout[idx + 1];
29144
+ this.row = target.bufRow;
29145
+ this.col = charOffsetFor(layout, idx + 1, vcol);
29146
+ this.render();
29147
+ } else {
29148
+ this.historyNext();
29149
+ }
29150
+ }
29151
+ moveHome() {
29152
+ this.col = 0;
29153
+ this.render();
29154
+ }
29155
+ moveEnd() {
29156
+ this.col = charLen(this.lines[this.row]);
29157
+ this.render();
29158
+ }
29159
+ moveToStart() {
29160
+ this.row = 0;
29161
+ this.col = 0;
29162
+ this.render();
29163
+ }
29164
+ moveToEnd() {
29165
+ this.row = this.lines.length - 1;
29166
+ this.col = charLen(this.lines[this.row]);
29167
+ this.render();
29168
+ }
29169
+ moveWordLeft() {
29170
+ const chars = this.currentChars();
29171
+ let i = this.col;
29172
+ while (i > 0 && /\s/.test(chars[i - 1]))
29173
+ i--;
29174
+ while (i > 0 && !/\s/.test(chars[i - 1]))
29175
+ i--;
29176
+ this.col = i;
29177
+ this.render();
29178
+ }
29179
+ moveWordRight() {
29180
+ const chars = this.currentChars();
29181
+ const n = chars.length;
29182
+ let i = this.col;
29183
+ while (i < n && !/\s/.test(chars[i]))
29184
+ i++;
29185
+ while (i < n && /\s/.test(chars[i]))
29186
+ i++;
29187
+ this.col = i;
29188
+ this.render();
29189
+ }
29190
+ historyPrev() {
29191
+ if (this.history.length === 0)
29192
+ return;
29193
+ if (this.historyIndex === -1) {
29194
+ this.stash = {
29195
+ lines: this.lines.slice(),
29196
+ row: this.row,
29197
+ col: this.col
29198
+ };
29199
+ this.historyIndex = 0;
29200
+ } else if (this.historyIndex < this.history.length - 1) {
29201
+ this.historyIndex++;
29202
+ } else {
29203
+ return;
29204
+ }
29205
+ this.applyHistoryEntry();
29206
+ }
29207
+ historyNext() {
29208
+ if (this.historyIndex === -1)
29209
+ return;
29210
+ if (this.historyIndex > 0) {
29211
+ this.historyIndex--;
29212
+ this.applyHistoryEntry();
29213
+ } else if (this.stash) {
29214
+ this.lines = this.stash.lines.slice();
29215
+ this.row = this.stash.row;
29216
+ this.col = this.stash.col;
29217
+ this.historyIndex = -1;
29218
+ this.stash = null;
29219
+ this.render();
29220
+ }
29221
+ }
29222
+ applyHistoryEntry() {
29223
+ const entry = this.history[this.history.length - 1 - this.historyIndex];
29224
+ this.lines = entry.split(`
29225
+ `);
29226
+ if (this.lines.length === 0)
29227
+ this.lines = [""];
29228
+ this.row = this.lines.length - 1;
29229
+ this.col = charLen(this.lines[this.row]);
29230
+ this.render();
29231
+ }
29232
+ tabComplete() {
29233
+ if (!this.completer)
29234
+ return;
29235
+ const line = this.lines[this.row];
29236
+ const [matches, partial] = this.completer(line);
29237
+ if (matches.length === 0) {
29238
+ this.output.write("\x07");
29239
+ return;
29240
+ }
29241
+ if (matches.length === 1) {
29242
+ const from = line.lastIndexOf(partial);
29243
+ const head = line.slice(0, from);
29244
+ const tail = line.slice(from + partial.length);
29245
+ this.lines[this.row] = head + matches[0] + tail;
29246
+ this.col = charLen(head) + charLen(matches[0]);
29247
+ this.render();
29248
+ return;
29249
+ }
29250
+ const lines = matches.map((m) => " " + m);
29251
+ this.output.write(`\r
29252
+ ` + lines.join(`\r
29253
+ `) + `\r
29254
+ `);
29255
+ this.output.write(`\x1B[${lines.length + 1}A`);
29256
+ this.render();
29257
+ }
29258
+ clearScreen() {
29259
+ this.output.write("\x1B[2J\x1B[H");
29260
+ this.render();
29261
+ }
29262
+ columns() {
29263
+ return this.output.columns || 80;
29264
+ }
29265
+ layout() {
29266
+ return buildLayout(this.lines, this.columns());
29267
+ }
29268
+ render() {
29269
+ if (!this.input.isTTY || this.isDone)
29270
+ return;
29271
+ const layout = this.layout();
29272
+ const promptWidth = stringWidth(stripAnsi2(this.promptStr));
29273
+ const idx = layoutIndexFor(layout, this.row, this.col);
29274
+ const vcol = visualColAt(layout, idx, this.col);
29275
+ const rowsAfter = layout.length - idx - 1;
29276
+ const ccol = (idx === 0 ? promptWidth : 0) + vcol;
29277
+ const out = ["\r"];
29278
+ for (let i = 0;i < layout.length; i++) {
29279
+ out.push("\x1B[2K");
29280
+ if (i === 0)
29281
+ out.push(this.promptStr);
29282
+ out.push(layout[i].text);
29283
+ if (i < layout.length - 1)
29284
+ out.push(`\r
29285
+ `);
29286
+ }
29287
+ out.push("\x1B[J");
29288
+ if (rowsAfter > 0)
29289
+ out.push(`\x1B[${rowsAfter}A`);
29290
+ out.push("\r");
29291
+ if (ccol > 0)
29292
+ out.push(`\x1B[${ccol}C`);
29293
+ this.output.write(out.join(""));
29294
+ }
29295
+ enableTerminalProtocols() {
29296
+ this.output.write("\x1B[?2004h");
29297
+ this.output.write("\x1B[>1u");
29298
+ }
29299
+ disableTerminalProtocols() {
29300
+ this.output.write("\x1B[?2004l");
29301
+ this.output.write("\x1B[<u");
29302
+ }
29303
+ emitLine(line) {
29304
+ this.listeners.line?.(line);
29305
+ }
29306
+ emitClose() {
29307
+ this.listeners.close?.();
29308
+ }
29309
+ }
29310
+
29311
+ // src/cli/repl.ts
28335
29312
  import { existsSync as existsSync45, readFileSync as readFileSync31, writeFileSync as writeFileSync16 } from "fs";
28336
- import { join as join39, dirname as dirname12 } from "path";
29313
+ import { join as join40, dirname as dirname13 } from "path";
28337
29314
  import { homedir as homedir17 } from "os";
28338
- import { fileURLToPath as fileURLToPath4 } from "url";
29315
+ import { fileURLToPath as fileURLToPath5 } from "url";
28339
29316
 
28340
29317
  // src/cli/completer.ts
28341
29318
  class SlashCommandProvider {
@@ -28633,14 +29610,6 @@ class FormattingStream {
28633
29610
  return result;
28634
29611
  }
28635
29612
  }
28636
- function formatMarkdown(text) {
28637
- const lines = [];
28638
- const stream = new FormattingStream((l) => lines.push(l));
28639
- stream.write(text);
28640
- stream.flush();
28641
- return lines.join(`
28642
- `);
28643
- }
28644
29613
 
28645
29614
  // src/ui/renderer.ts
28646
29615
  init_spinner();
@@ -28845,10 +29814,10 @@ init_box();
28845
29814
  init_i18n();
28846
29815
  init_repl_commands();
28847
29816
  function readVersion4() {
28848
- const here = dirname12(fileURLToPath4(import.meta.url));
29817
+ const here = dirname13(fileURLToPath5(import.meta.url));
28849
29818
  const candidates = [
28850
- join39(here, "..", "..", "package.json"),
28851
- join39(here, "..", "package.json")
29819
+ join40(here, "..", "..", "package.json"),
29820
+ join40(here, "..", "package.json")
28852
29821
  ];
28853
29822
  for (const p of candidates) {
28854
29823
  if (existsSync45(p)) {
@@ -28893,6 +29862,7 @@ class Repl {
28893
29862
  baseDir;
28894
29863
  noAgentsMd;
28895
29864
  pendingClipboardImage = null;
29865
+ exitOnClose = false;
28896
29866
  rl;
28897
29867
  agent;
28898
29868
  config;
@@ -28900,19 +29870,32 @@ class Repl {
28900
29870
  skillsModule;
28901
29871
  pluginManager;
28902
29872
  logger;
28903
- constructor(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger) {
29873
+ constructor(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger, exitOnClose) {
28904
29874
  this.agent = agent;
28905
29875
  this.config = config;
29876
+ this.exitOnClose = exitOnClose === true;
28906
29877
  this.sessionManager = sessionManager;
28907
29878
  this.skillsModule = skillsModule;
28908
29879
  this.pluginManager = pluginManager;
28909
29880
  this.logger = logger;
28910
- this.configDir = configDir || join39(homedir17(), ".mma");
29881
+ this.configDir = configDir || join40(homedir17(), ".mma");
28911
29882
  this.baseDir = baseDir || process.cwd();
28912
29883
  this.noAgentsMd = noAgentsMd === true;
28913
- this.historyPath = join39(homedir17(), ".mma", "repl-history");
29884
+ this.historyPath = join40(homedir17(), ".mma", "repl-history");
28914
29885
  this.loadHistory();
28915
- this.rl = readline2.createInterface({
29886
+ this.rl = process.stdin.isTTY ? new LineEditor({
29887
+ input: process.stdin,
29888
+ output: process.stdout,
29889
+ prompt: pc2.cyan("> "),
29890
+ history: this.history,
29891
+ historySize: this.maxHistory,
29892
+ completer: (line) => {
29893
+ const [matches, partial] = this.completer.complete(line);
29894
+ if (matches.length > 0)
29895
+ return [matches, partial];
29896
+ return [[], line];
29897
+ }
29898
+ }) : readline3.createInterface({
28916
29899
  input: process.stdin,
28917
29900
  output: process.stdout,
28918
29901
  prompt: pc2.cyan("> "),
@@ -29029,46 +30012,22 @@ class Repl {
29029
30012
  });
29030
30013
  this.rl.on("close", () => {
29031
30014
  this.running = false;
30015
+ this.saveHistory();
30016
+ this.agent.shutdown();
30017
+ if (this.exitOnClose)
30018
+ process.exit(0);
29032
30019
  });
29033
- if (process.stdin.isTTY) {
29034
- readline2.emitKeypressEvents(process.stdin);
29035
- process.stdin.on("keypress", async (str, key) => {
29036
- if (key.name === "escape") {
29037
- const escBytes = key.sequence ? (key.sequence.match(/\x1b/g) || []).length : 1;
29038
- const now = Date.now();
29039
- const withinWindow = now - this.lastEscTime < this.doubleEscDelay;
29040
- this.lastEscTime = now;
29041
- if (escBytes >= 2 || withinWindow) {
29042
- this.lastEscTime = 0;
29043
- if (this.agentRunning) {
29044
- process.stdout.write(pc2.yellow(`
29045
- ${t("repl.interrupt")}
29046
- `));
29047
- this.agent.shutdown();
29048
- }
29049
- }
29050
- return;
29051
- }
29052
- if (key.ctrl && key.name === "v" && !this.agentRunning) {
29053
- try {
29054
- const { readClipboardImage: readClipboardImage2, bufferToDataUrl: bufferToDataUrl2 } = await Promise.resolve().then(() => (init_image_utils(), exports_image_utils));
29055
- const clipBuf = await readClipboardImage2();
29056
- if (clipBuf) {
29057
- const { dataUrl } = await bufferToDataUrl2(clipBuf);
29058
- this.pendingClipboardImage = dataUrl;
29059
- const sizeKb = Math.round(dataUrl.length * 3 / 4 / 1024);
29060
- console.log(pc2.green(`
29061
- ${t("image.attached", { source: "clipboard", size: `${sizeKb} KB` })}`));
29062
- this.rl.prompt();
29063
- } else {
29064
- console.log(pc2.yellow(`
29065
- ${t("image.clipboard_empty")}`));
29066
- this.rl.prompt();
29067
- }
29068
- } catch {}
29069
- }
30020
+ if (this.exitOnClose && !process.stdin.isTTY) {
30021
+ process.stdin.on("end", () => {
30022
+ this.running = false;
30023
+ this.saveHistory();
30024
+ this.agent.shutdown();
30025
+ process.exit(0);
29070
30026
  });
29071
30027
  }
30028
+ if (this.rl instanceof LineEditor) {
30029
+ this.rl.onKeyInput = (str, key) => this.handleSpecialKey(str, key);
30030
+ }
29072
30031
  let forceExitTimer = null;
29073
30032
  process.on("SIGINT", () => {
29074
30033
  if (this.agentRunning) {
@@ -29084,6 +30043,51 @@ ${t("image.clipboard_empty")}`));
29084
30043
  }
29085
30044
  });
29086
30045
  }
30046
+ handleSpecialKey(str, key) {
30047
+ if (key.name === "escape") {
30048
+ const escBytes = key.sequence ? (key.sequence.match(/\x1b/g) || []).length : 1;
30049
+ const now = Date.now();
30050
+ const withinWindow = now - this.lastEscTime < this.doubleEscDelay;
30051
+ this.lastEscTime = now;
30052
+ if (escBytes >= 2 || withinWindow) {
30053
+ this.lastEscTime = 0;
30054
+ if (this.agentRunning) {
30055
+ process.stdout.write(pc2.yellow(`
30056
+ ${t("repl.interrupt")}
30057
+ `));
30058
+ this.agent.shutdown();
30059
+ }
30060
+ }
30061
+ return true;
30062
+ }
30063
+ if (key.ctrl && key.name === "v" && !this.agentRunning) {
30064
+ this.pasteClipboardImage();
30065
+ return true;
30066
+ }
30067
+ return false;
30068
+ }
30069
+ async pasteClipboardImage() {
30070
+ try {
30071
+ const { readClipboardImage: readClipboardImage2, bufferToDataUrl: bufferToDataUrl2 } = await Promise.resolve().then(() => (init_image_utils(), exports_image_utils));
30072
+ const clipBuf = await readClipboardImage2();
30073
+ if (clipBuf) {
30074
+ const { dataUrl } = await bufferToDataUrl2(clipBuf);
30075
+ this.pendingClipboardImage = dataUrl;
30076
+ const sizeKb = Math.round(dataUrl.length * 3 / 4 / 1024);
30077
+ console.log(pc2.green(`
30078
+ ${t("image.attached", { source: "clipboard", size: `${sizeKb} KB` })}`));
30079
+ if (this.rl instanceof LineEditor)
30080
+ this.rl.reset();
30081
+ this.rl.prompt();
30082
+ } else {
30083
+ console.log(pc2.yellow(`
30084
+ ${t("image.clipboard_empty")}`));
30085
+ if (this.rl instanceof LineEditor)
30086
+ this.rl.reset();
30087
+ this.rl.prompt();
30088
+ }
30089
+ } catch {}
30090
+ }
29087
30091
  isMultiLineInput(line) {
29088
30092
  if (line.endsWith("\\"))
29089
30093
  return true;
@@ -29109,6 +30113,8 @@ ${t("image.clipboard_empty")}`));
29109
30113
  this.pendingClipboardImage = null;
29110
30114
  }
29111
30115
  this.logger?.logREPL("user", input);
30116
+ if (this.rl instanceof LineEditor)
30117
+ this.rl.reset();
29112
30118
  process.stdout.write(`
29113
30119
  ` + pc2.green(t("repl.agent")));
29114
30120
  const renderer = new Renderer({
@@ -29264,9 +30270,9 @@ ${t("image.clipboard_empty")}`));
29264
30270
  row(t("repl.agents_label"), pc2.red(t("repl.disabled")));
29265
30271
  } else {
29266
30272
  const agentsMdCandidates = [
29267
- join39(this.baseDir, "AGENTS.md"),
29268
- join39(this.baseDir, ".mma", "AGENTS.md"),
29269
- join39(this.configDir, "AGENTS.md")
30273
+ join40(this.baseDir, "AGENTS.md"),
30274
+ join40(this.baseDir, ".mma", "AGENTS.md"),
30275
+ join40(this.configDir, "AGENTS.md")
29270
30276
  ];
29271
30277
  const foundAgents = agentsMdCandidates.filter((p) => existsSync45(p));
29272
30278
  if (foundAgents.length > 0) {
@@ -29279,7 +30285,7 @@ ${t("image.clipboard_empty")}`));
29279
30285
  }
29280
30286
  const meta = this.sessionManager?.getActiveMeta();
29281
30287
  if (meta) {
29282
- const sessionPath = join39(this.configDir, "sessions", meta.id);
30288
+ const sessionPath = join40(this.configDir, "sessions", meta.id);
29283
30289
  row(t("repl.session_label"), `${pc2.cyan(meta.name)} ${pc2.dim(`(${meta.id.slice(0, 12)})`)} — ${meta.messageCount} msgs ${pc2.dim(sessionPath)}`);
29284
30290
  }
29285
30291
  const headerWidth = Math.max(50, Math.min(96, process.stdout.columns || 96));
@@ -29300,15 +30306,30 @@ ${t("image.clipboard_empty")}`));
29300
30306
  }
29301
30307
  }
29302
30308
 
30309
+ // src/cli/run-result.ts
30310
+ init_i18n();
30311
+ init_colors();
30312
+ function printRunResult(result, flush) {
30313
+ flush();
30314
+ if (result.success) {
30315
+ if (!result.text) {
30316
+ console.log(pc2.yellow(t("cli.no_output")));
30317
+ }
30318
+ return 0;
30319
+ }
30320
+ console.error(`${t("error.prefix")}${result.error}`);
30321
+ return 1;
30322
+ }
30323
+
29303
30324
  // src/cli/main.ts
29304
30325
  init_setup();
29305
30326
  init_config2();
29306
30327
  init_i18n();
29307
30328
  init_colors();
29308
30329
  import { existsSync as existsSync46, readFileSync as readFileSync32 } from "fs";
29309
- import { join as join40, dirname as dirname13 } from "path";
30330
+ import { join as join41, dirname as dirname14 } from "path";
29310
30331
  import { homedir as homedir18 } from "os";
29311
- import { fileURLToPath as fileURLToPath5 } from "url";
30332
+ import { fileURLToPath as fileURLToPath6 } from "url";
29312
30333
 
29313
30334
  // src/modules/updater/checker.ts
29314
30335
  var defaultRunner2 = async (command, args, options) => {
@@ -29457,10 +30478,10 @@ class UpdaterModule {
29457
30478
  }
29458
30479
  // src/cli/main.ts
29459
30480
  function readVersion5() {
29460
- const here = dirname13(fileURLToPath5(import.meta.url));
30481
+ const here = dirname14(fileURLToPath6(import.meta.url));
29461
30482
  const candidates = [
29462
- join40(here, "..", "..", "package.json"),
29463
- join40(here, "..", "package.json")
30483
+ join41(here, "..", "..", "package.json"),
30484
+ join41(here, "..", "package.json")
29464
30485
  ];
29465
30486
  for (const p of candidates) {
29466
30487
  if (existsSync46(p)) {
@@ -29532,28 +30553,19 @@ async function main() {
29532
30553
  }
29533
30554
  });
29534
30555
  renderer.flush();
29535
- if (result.success) {
29536
- if (!result.text) {
29537
- console.log(pc2.yellow(t("cli.no_output")));
29538
- }
29539
- } else {
29540
- console.error(`${t("error.prefix")}${result.error}`);
29541
- if (result.text) {
29542
- console.log(formatMarkdown(result.text));
29543
- }
29544
- process.exit(1);
29545
- }
30556
+ const exitCode = printRunResult(result, () => renderer.flush());
29546
30557
  agent.shutdown();
30558
+ process.exit(exitCode);
29547
30559
  } else {
29548
- const configPath = join40(homedir18(), ".mma", "config.json");
30560
+ const configPath = join41(homedir18(), ".mma", "config.json");
29549
30561
  if (!existsSync46(configPath)) {
29550
30562
  console.log(pc2.yellow(`
29551
30563
  ` + t("cli.first_run") + `
29552
30564
  `));
29553
30565
  const answers = await runSetup();
29554
30566
  const config2 = loadConfig({
29555
- configDir: join40(homedir18(), ".mma"),
29556
- projectConfigPath: projectDir ? join40(projectDir, ".mmrc") : join40(process.cwd(), ".mmrc")
30567
+ configDir: join41(homedir18(), ".mma"),
30568
+ projectConfigPath: projectDir ? join41(projectDir, ".mmrc") : join41(process.cwd(), ".mmrc")
29557
30569
  });
29558
30570
  config2.provider.type = answers.provider;
29559
30571
  config2.provider.baseUrl = answers.apiBase;
@@ -29606,7 +30618,7 @@ async function main() {
29606
30618
  logger
29607
30619
  } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
29608
30620
  startAutoUpdate(config);
29609
- const repl = new Repl(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger);
30621
+ const repl = new Repl(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger, true);
29610
30622
  repl.start();
29611
30623
  }
29612
30624
  }