micro-models-agent 0.36.0 → 0.36.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.js +439 -184
  2. package/package.json +45 -45
package/dist/main.js CHANGED
@@ -2142,7 +2142,7 @@ var init_config = __esm(() => {
2142
2142
  css: {
2143
2143
  command: "npx",
2144
2144
  args: ["vscode-css-languageserver", "--stdio"],
2145
- timeout: 1e4,
2145
+ timeout: 20000,
2146
2146
  autoInstall: true,
2147
2147
  workspaceMarkers: ["package.json"]
2148
2148
  }
@@ -2785,6 +2785,7 @@ Use this knowledge to answer the user's question.`,
2785
2785
  {hints}`,
2786
2786
  "exec.file_rewrite_warning": "⚠️ File {file} has been rewritten {count} times. Consider a different approach — the current fix strategy is not working.",
2787
2787
  "exec.forbidden_cmd": 'STOP using "{cmd}" via the bash tool — it is not a native Windows cmd.exe command and has failed repeatedly this session. Use the dedicated tool instead: grep → the grep tool, ls/dir → list_dir, find → glob, rm → delete_file, sed → edit_file, touch → write_file, which → `where`, cp/mv → move_file, diff → read_file. Do NOT call bash for this purpose again.',
2788
+ "exec.npm_exec_hint": '"could not determine executable to run" — no "bin" for that package/script. Use "npm run <script>" (script must exist in package.json) or "bunx <pkg>" for a package that declares a bin.',
2788
2789
  "hall.max_retries_exhausted": "Model returned empty or insufficient responses after multiple retries",
2789
2790
  "hall.short_response": "Response too short or empty",
2790
2791
  "hall.repetitive": "Response too repetitive ({pct}% overlap)",
@@ -3383,6 +3384,7 @@ var init_ru = __esm(() => {
3383
3384
  {hints}`,
3384
3385
  "exec.file_rewrite_warning": "⚠️ Файл {file} был перезаписан {count} раз. Попробуйте другой подход — текущая стратегия исправлений не работает.",
3385
3386
  "exec.forbidden_cmd": 'ПРЕКРАТИ использовать "{cmd}" через bash — это не команда Windows cmd.exe, и она уже неоднократно падала в этой сессии. Используй предназначенный тул: grep → тул grep, ls/dir → list_dir, find → glob, rm → delete_file, sed → edit_file, touch → write_file, which → `where`, cp/mv → move_file, diff → read_file. Больше не вызывай bash для этого.',
3387
+ "exec.npm_exec_hint": '"could not determine executable to run" — у пакета/скрипта нет "bin". Используй "npm run <script>" (скрипт должен быть в package.json) или "bunx <pkg>" для пакета с объявленным bin.',
3386
3388
  "hall.max_retries_exhausted": "Модель вернула пустой или недостаточный ответ после нескольких попыток",
3387
3389
  "hall.short_response": "Слишком короткий или пустой ответ",
3388
3390
  "hall.repetitive": "Слишком повторяющийся ответ ({pct}% совпадение)",
@@ -7555,7 +7557,15 @@ function emptyCliRunHint(command, output, code) {
7555
7557
  return null;
7556
7558
  return "the command exited 0 but printed NOTHING to stdout. If this should run a CLI program, the file probably has no entry point: read it with read_file and check the code actually calls its main function with command-line arguments (e.g. main(process.argv[2])) and prints results with console.log.";
7557
7559
  }
7558
- var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, bashGraceMs, FAILING_FIRST_WORDS, HARD_BLOCK_THRESHOLD = 3, UNIX_TO_WIN_HINTS, UNIX_TO_WIN_TRANSLATE, NEVER_TOOL_CALLS, CLI_FILE_RUN_RE, bashTool;
7560
+ function npmExecHint(output) {
7561
+ if (NPM_EXEC_RE.test(output)) {
7562
+ return `${output}
7563
+
7564
+ Hint: ${t("exec.npm_exec_hint")}`;
7565
+ }
7566
+ return output;
7567
+ }
7568
+ var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, bashGraceMs, FAILING_FIRST_WORDS, HARD_BLOCK_THRESHOLD = 3, UNIX_TO_WIN_HINTS, UNIX_TO_WIN_TRANSLATE, NEVER_TOOL_CALLS, CLI_FILE_RUN_RE, NPM_EXEC_RE, bashTool;
7559
7569
  var init_bash = __esm(() => {
7560
7570
  init_command_validator();
7561
7571
  init_audit_log();
@@ -7620,6 +7630,7 @@ var init_bash = __esm(() => {
7620
7630
  "awk"
7621
7631
  ]);
7622
7632
  CLI_FILE_RUN_RE = /\b(bun|node|deno|python|python3|tsx|ts-node|php|ruby|go\s+run)\S*\s+(run\s+)?["']?[\w./\\-]+\.(ts|js|tsx|jsx|mjs|cjs|py)\b/;
7633
+ NPM_EXEC_RE = /could not determine executable to run/i;
7623
7634
  bashTool = {
7624
7635
  name: "bash",
7625
7636
  description: `Execute a shell command and return its output. Use for running tests, build, git, and shell operations. Commands that are still running after a few seconds are automatically moved to the background and return a process id — manage them with process_list, process_log, process_kill. Set background=true to return a process id immediately for commands you know are long-running (dev servers, watchers).
@@ -7716,6 +7727,7 @@ ${output2}`;
7716
7727
  if (!output2 && code !== 0) {
7717
7728
  output2 = `(exit code ${code})`;
7718
7729
  }
7730
+ output2 = npmExecHint(output2);
7719
7731
  if (platform2() === "win32") {
7720
7732
  const originalFirstWord = originalCommand.trim().split(/\s+/)[0]?.split(/[\\/]/).pop();
7721
7733
  if (originalFirstWord && originalFirstWord in UNIX_TO_WIN_HINTS) {
@@ -7897,6 +7909,12 @@ var init_process_kill = __esm(() => {
7897
7909
  });
7898
7910
 
7899
7911
  // src/core/prompt-builder.ts
7912
+ function blockLabel(content) {
7913
+ const firstLine = content.split(`
7914
+ `)[0].trim();
7915
+ return firstLine.length > 70 ? firstLine.slice(0, 67) + "..." : firstLine;
7916
+ }
7917
+
7900
7918
  class PromptBuilder {
7901
7919
  blocks = [];
7902
7920
  budget;
@@ -7925,24 +7943,41 @@ class PromptBuilder {
7925
7943
  let usedTokens = 0;
7926
7944
  const included = [];
7927
7945
  const excluded = [];
7946
+ const blocks = [];
7928
7947
  for (const block of essential) {
7929
7948
  included.push(block.content);
7930
7949
  usedTokens += block.estimatedTokens;
7950
+ blocks.push({
7951
+ label: blockLabel(block.content),
7952
+ priority: block.priority,
7953
+ essential: true,
7954
+ tokens: block.estimatedTokens,
7955
+ included: true
7956
+ });
7931
7957
  }
7932
7958
  for (const block of nonEssential) {
7933
7959
  const tokens = block.estimatedTokens;
7934
- if (usedTokens + tokens <= this.budget) {
7960
+ const fits = usedTokens + tokens <= this.budget;
7961
+ if (fits) {
7935
7962
  included.push(block.content);
7936
7963
  usedTokens += tokens;
7937
7964
  } else {
7938
7965
  excluded.push(block.content);
7939
7966
  }
7967
+ blocks.push({
7968
+ label: blockLabel(block.content),
7969
+ priority: block.priority,
7970
+ essential: false,
7971
+ tokens,
7972
+ included: fits
7973
+ });
7940
7974
  }
7941
7975
  return {
7942
7976
  prompt: included.join(`
7943
7977
 
7944
7978
  `),
7945
- excluded
7979
+ excluded,
7980
+ blocks
7946
7981
  };
7947
7982
  }
7948
7983
  }
@@ -8066,14 +8101,54 @@ class SessionLogger {
8066
8101
  iteration
8067
8102
  });
8068
8103
  }
8069
- logCompaction(content, iteration, contextTokens, contextLimit) {
8104
+ logCompaction(info) {
8070
8105
  this.session?.appendLog({
8071
8106
  ts: new Date().toISOString(),
8072
8107
  type: "compaction",
8073
- content,
8108
+ content: `${info.reason}, iteration ${info.iteration}`,
8109
+ iteration: info.iteration,
8110
+ reason: info.reason,
8111
+ tokensBefore: info.tokensBefore,
8112
+ tokensAfter: info.tokensAfter,
8113
+ qualityBefore: info.qualityBefore,
8114
+ qualityAfter: info.qualityAfter,
8115
+ messagesBefore: info.messagesBefore,
8116
+ messagesAfter: info.messagesAfter,
8117
+ removedTurns: info.removedTurns,
8118
+ keptTurns: info.keptTurns,
8119
+ summary: info.summary
8120
+ });
8121
+ }
8122
+ logContext(info) {
8123
+ this.session?.appendLog({
8124
+ ts: new Date().toISOString(),
8125
+ type: "context",
8126
+ content: info.kind === "start" ? "context snapshot at session start" : `context at iteration ${info.iteration}`,
8127
+ iteration: info.iteration,
8128
+ window: info.window,
8129
+ systemBudget: info.systemBudget,
8130
+ reserveBudget: info.reserveBudget,
8131
+ historyBudget: info.historyBudget,
8132
+ systemTokens: info.systemTokens,
8133
+ toolTokens: info.toolTokens,
8134
+ contextTokens: info.tokens,
8135
+ quality: info.quality,
8136
+ messageCount: info.messageCount,
8137
+ compactionCount: info.compactionCount,
8138
+ iterationsSinceCompaction: info.iterationsSinceCompaction,
8139
+ blocks: info.blocks
8140
+ });
8141
+ }
8142
+ logLlmUsage(iteration, usage) {
8143
+ this.session?.appendLog({
8144
+ ts: new Date().toISOString(),
8145
+ type: "llm_usage",
8074
8146
  iteration,
8075
- ...contextTokens !== undefined ? { contextTokens } : {},
8076
- ...contextLimit !== undefined ? { contextLimit } : {}
8147
+ promptTokens: usage.promptTokens,
8148
+ completionTokens: usage.completionTokens,
8149
+ totalTokens: usage.totalTokens,
8150
+ source: usage.source,
8151
+ durationMs: usage.durationMs
8077
8152
  });
8078
8153
  }
8079
8154
  logError(message) {
@@ -10372,7 +10447,36 @@ class Agent {
10372
10447
  if (pluginBlocks.length > 0) {
10373
10448
  builder.addBlocks(pluginBlocks);
10374
10449
  }
10375
- return builder.build();
10450
+ const result = builder.build();
10451
+ return {
10452
+ prompt: result.prompt,
10453
+ excluded: result.excluded,
10454
+ blocks: result.blocks
10455
+ };
10456
+ }
10457
+ logContextStat(kind, iteration, slog, blocks) {
10458
+ const cm = this.deps.contextManager;
10459
+ if (typeof cm.getSnapshot !== "function")
10460
+ return;
10461
+ const snap = cm.getSnapshot();
10462
+ const history = cm.getActiveHistory();
10463
+ const systemMsg = history.find((m) => m.role === "system");
10464
+ slog.logContext({
10465
+ kind,
10466
+ iteration,
10467
+ window: snap.window,
10468
+ systemBudget: snap.budget.systemPrompt,
10469
+ reserveBudget: snap.budget.responseReserve,
10470
+ historyBudget: snap.budget.history,
10471
+ systemTokens: kind === "start" && systemMsg && typeof systemMsg.content === "string" ? this.deps.llmProvider.countTokens(systemMsg.content) : undefined,
10472
+ toolTokens: snap.toolTokens,
10473
+ tokens: snap.tokens,
10474
+ quality: snap.quality,
10475
+ messageCount: snap.messageCount,
10476
+ compactionCount: snap.compactionCount,
10477
+ iterationsSinceCompaction: snap.iterationsSinceCompaction,
10478
+ blocks
10479
+ });
10376
10480
  }
10377
10481
  getSystemPromptInfo() {
10378
10482
  const { prompt, excluded } = this.buildSystemPrompt();
@@ -10532,27 +10636,47 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10532
10636
  }
10533
10637
  });
10534
10638
  if (contextManager.needsCompaction()) {
10535
- contextManager.compact();
10536
- logger.debug("Context compacted");
10537
- slog.logCompaction(`regular compaction, iteration ${iteration}`, iteration);
10639
+ const result = contextManager.compact();
10640
+ if (result) {
10641
+ logger.debug("Context compacted");
10642
+ slog.logCompaction({ reason: "interval", iteration, ...result });
10643
+ }
10538
10644
  }
10539
10645
  const currentTokens = contextManager.getEstimatedTokens();
10540
10646
  const budget2 = contextManager.getBudget();
10541
10647
  const quality = contextManager.getQuality();
10542
10648
  if (quality < QUALITY_TRIGGER_THRESHOLD && contextManager.getCompactionCount() > 0 && iteration - lastForcedCompactionIteration >= FORCED_COMPACTION_COOLDOWN) {
10543
10649
  lastForcedCompactionIteration = iteration;
10544
- contextManager.compact();
10650
+ const result = contextManager.compact();
10545
10651
  logger.warn(`Low context quality (${quality}%) — forced compaction`);
10546
- slog.logCompaction(`quality-triggered compaction (${quality}% < ${QUALITY_TRIGGER_THRESHOLD}%), iteration ${iteration}`, iteration, currentTokens, budget2.history);
10652
+ if (result) {
10653
+ slog.logCompaction({
10654
+ reason: `quality-triggered (${quality}% < ${QUALITY_TRIGGER_THRESHOLD}%)`,
10655
+ iteration,
10656
+ ...result
10657
+ });
10658
+ }
10547
10659
  }
10548
10660
  if (currentTokens > budget2.history) {
10549
- contextManager.compact();
10661
+ const result = contextManager.compact();
10550
10662
  logger.warn(`Context overflow (${currentTokens} > ${budget2.history}), forced compaction`);
10551
- slog.logCompaction(`forced compaction (${currentTokens} > ${budget2.history}), iteration ${iteration}`, iteration, currentTokens, budget2.history);
10663
+ if (result) {
10664
+ slog.logCompaction({
10665
+ reason: `overflow (${currentTokens} > ${budget2.history})`,
10666
+ iteration,
10667
+ ...result
10668
+ });
10669
+ }
10552
10670
  }
10553
10671
  this.refreshSystemPrompt();
10554
10672
  const history = contextManager.getActiveHistory();
10555
10673
  slog.logToolDefs(allToolsForBudget.length, allToolsForBudget.map((t2) => t2.name), iteration);
10674
+ if (iteration === 1) {
10675
+ const { blocks } = this.buildSystemPrompt();
10676
+ this.logContextStat("start", iteration, slog, blocks);
10677
+ } else {
10678
+ this.logContextStat("iteration", iteration, slog);
10679
+ }
10556
10680
  let textContent = "";
10557
10681
  let reasoningContent = "";
10558
10682
  const toolCalls = [];
@@ -10561,6 +10685,8 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10561
10685
  const textChunks = [];
10562
10686
  this.emitPhase(iteration, "thinking", onPhase);
10563
10687
  const llmStart = Date.now();
10688
+ const promptBefore = apiPromptTokens;
10689
+ const completionBefore = apiCompletionTokens;
10564
10690
  logger.logLLMRequest(config.model, history.length, input, "agent");
10565
10691
  try {
10566
10692
  for await (const chunk of llmProvider.chat(history, allToolsForBudget, this.abortController?.signal)) {
@@ -10624,6 +10750,20 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10624
10750
  }
10625
10751
  apiCompletionChars += (textContent || reasoningContent).length;
10626
10752
  logger.logLLMResponse(config.model, (textContent || reasoningContent).length, Date.now() - llmStart, undefined, "agent");
10753
+ {
10754
+ const usagePrompt = apiPromptTokens - promptBefore;
10755
+ const usageCompletion = apiCompletionTokens - completionBefore;
10756
+ const source = usagePrompt > 0 || usageCompletion > 0 ? "api" : "estimate";
10757
+ const prompt = source === "api" ? usagePrompt : contextManager.getEstimatedTokens();
10758
+ const completion = source === "api" ? usageCompletion : Math.ceil((textContent || reasoningContent).length / 4);
10759
+ slog.logLlmUsage(iteration, {
10760
+ promptTokens: prompt,
10761
+ completionTokens: completion,
10762
+ totalTokens: prompt + completion,
10763
+ source,
10764
+ durationMs: Date.now() - llmStart
10765
+ });
10766
+ }
10627
10767
  if (this.shutdownRequested) {
10628
10768
  break;
10629
10769
  }
@@ -10741,8 +10881,15 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10741
10881
  slog.logToolResult(call, result, duration, iteration);
10742
10882
  }
10743
10883
  if (contextManager.needsCompaction()) {
10744
- contextManager.compact();
10745
- logger.debug("Context compacted after tool result");
10884
+ const result2 = contextManager.compact();
10885
+ if (result2) {
10886
+ logger.debug("Context compacted after tool result");
10887
+ slog.logCompaction({
10888
+ reason: "after_tool",
10889
+ iteration,
10890
+ ...result2
10891
+ });
10892
+ }
10746
10893
  }
10747
10894
  if (!result.success) {
10748
10895
  const key = call.name;
@@ -11350,9 +11497,12 @@ class ContextManager {
11350
11497
  return totalTokens > this.budget.history * this.compactionThreshold;
11351
11498
  }
11352
11499
  compact() {
11500
+ const tokensBefore = this.getEstimatedTokens();
11501
+ const qualityBefore = this.getQuality();
11502
+ const messagesBefore = this.messages.length;
11353
11503
  this.iterationsSinceCompaction = 0;
11354
11504
  if (this.messages.length <= KEEP_LAST_N * 2)
11355
- return;
11505
+ return null;
11356
11506
  this.compactionCount++;
11357
11507
  const cutoff = this.messages.length - KEEP_LAST_N * 2;
11358
11508
  const oldTurns = this.messages.slice(0, cutoff);
@@ -11406,6 +11556,29 @@ ${lines.join(`
11406
11556
  if (this.onCompact) {
11407
11557
  this.onCompact(summary);
11408
11558
  }
11559
+ return {
11560
+ removedTurns: oldTurns.length,
11561
+ keptTurns: freshRecent.length,
11562
+ tokensBefore,
11563
+ tokensAfter: this.getEstimatedTokens(),
11564
+ qualityBefore,
11565
+ qualityAfter: this.getQuality(),
11566
+ messagesBefore,
11567
+ messagesAfter: this.messages.length,
11568
+ summary: this.compactedBlock ?? ""
11569
+ };
11570
+ }
11571
+ getSnapshot() {
11572
+ return {
11573
+ window: this.contextWindow,
11574
+ budget: { ...this.budget },
11575
+ tokens: this.getEstimatedTokens(),
11576
+ toolTokens: this.toolTokens,
11577
+ messageCount: this.messages.length,
11578
+ quality: this.getQuality(),
11579
+ compactionCount: this.compactionCount,
11580
+ iterationsSinceCompaction: this.iterationsSinceCompaction
11581
+ };
11409
11582
  }
11410
11583
  updateSystemPrompt(content) {
11411
11584
  const idx = this.messages.findIndex((m) => m.role === "system");
@@ -14216,6 +14389,7 @@ class BridgeDriver {
14216
14389
  proc = null;
14217
14390
  rl = null;
14218
14391
  pending = new Map;
14392
+ lastStderr = [];
14219
14393
  nextId = 1;
14220
14394
  lastUrl = "";
14221
14395
  console = new ConsoleBuffer(500);
@@ -14224,9 +14398,13 @@ class BridgeDriver {
14224
14398
  constructor(opts = {}) {
14225
14399
  this.maxConsoleLineChars = opts.maxConsoleLineChars ?? 400;
14226
14400
  }
14227
- async send(cmd, params = {}) {
14228
- if (!this.proc || !this.rl)
14229
- throw new Error("Bridge is not running");
14401
+ async send(cmd, params = {}, retried = false) {
14402
+ if (!this.proc || !this.rl) {
14403
+ if (retried || cmd === "close")
14404
+ throw new Error("Bridge is not running");
14405
+ this.spawnBridge();
14406
+ return this.send(cmd, params, true);
14407
+ }
14230
14408
  const id = this.nextId++;
14231
14409
  const response = await new Promise((resolve15, reject) => {
14232
14410
  const timer = setTimeout(() => {
@@ -14272,7 +14450,10 @@ class BridgeDriver {
14272
14450
  const script = bridgeScriptPath();
14273
14451
  const proc = spawn4("node", [script], { stdio: ["pipe", "pipe", "pipe"], windowsHide: true });
14274
14452
  this.proc = proc;
14275
- proc.stderr.on("data", () => {});
14453
+ this.lastStderr = [];
14454
+ proc.stderr.on("data", (chunk) => {
14455
+ this.lastStderr = [...this.lastStderr.slice(-4), chunk.toString()];
14456
+ });
14276
14457
  const rl = createInterface({ input: proc.stdout });
14277
14458
  this.rl = rl;
14278
14459
  rl.on("line", (line) => {
@@ -14288,15 +14469,28 @@ class BridgeDriver {
14288
14469
  resolver(resp);
14289
14470
  }
14290
14471
  });
14291
- proc.on("exit", () => {
14292
- for (const [, resolver] of this.pending) {
14293
- resolver({ id: -1, ok: false, error: "Bridge process exited" });
14294
- }
14295
- this.pending.clear();
14472
+ proc.on("error", (err) => {
14473
+ if (this.proc !== proc)
14474
+ return;
14475
+ this.failPending(`Bridge process failed to start: ${err.message}`);
14476
+ this.proc = null;
14477
+ this.rl = null;
14478
+ });
14479
+ proc.on("exit", (code) => {
14480
+ if (this.proc !== proc)
14481
+ return;
14482
+ const stderrTail = this.lastStderr.join("").trim();
14483
+ this.failPending(stderrTail ? `Bridge process exited (code ${code}): ${stderrTail.slice(0, 300)}` : `Bridge process exited (code ${code})`);
14296
14484
  this.proc = null;
14297
14485
  this.rl = null;
14298
14486
  });
14299
14487
  }
14488
+ failPending(error) {
14489
+ for (const [, resolver] of this.pending) {
14490
+ resolver({ id: -1, ok: false, error });
14491
+ }
14492
+ this.pending.clear();
14493
+ }
14300
14494
  async goto(url, timeoutMs) {
14301
14495
  await this.send("goto", { url, timeoutMs });
14302
14496
  }
@@ -15940,8 +16134,8 @@ class PlanTracker {
15940
16134
  var init_tracker = () => {};
15941
16135
 
15942
16136
  // src/modules/execution/audit-runners.ts
15943
- import { readdirSync as readdirSync8 } from "fs";
15944
- import { join as join23 } from "path";
16137
+ import { existsSync as existsSync28, readdirSync as readdirSync8 } from "fs";
16138
+ import { dirname as dirname10, join as join23, resolve as resolve17 } from "path";
15945
16139
  function findTestFile(dir, depth = 0) {
15946
16140
  if (depth > 5)
15947
16141
  return null;
@@ -16000,7 +16194,7 @@ async function runTests(baseDir) {
16000
16194
  return {
16001
16195
  checked: true,
16002
16196
  passed: entry.exitCode === 0,
16003
- failed: entry.exitCode === 0 ? 0 : -1,
16197
+ failed: entry.exitCode === 0 ? 0 : 1,
16004
16198
  passedCount: 0,
16005
16199
  detail: output.slice(0, 200).trim(),
16006
16200
  command: "bun test"
@@ -16021,6 +16215,25 @@ function parseTypecheckErrors(output) {
16021
16215
  `).find((l) => /error TS\d+/.test(l));
16022
16216
  return line ? line.trim().slice(0, 300) : null;
16023
16217
  }
16218
+ function findTypecheckRoot(baseDir, existingFiles = []) {
16219
+ const candidates = [baseDir, ...existingFiles];
16220
+ let best = null;
16221
+ for (const start of candidates) {
16222
+ let dir = resolve17(start);
16223
+ for (let depth = 0;depth <= 10; depth++) {
16224
+ if (existsSync28(join23(dir, "tsconfig.json"))) {
16225
+ if (!best || depth < best.depth)
16226
+ best = { depth, root: dir };
16227
+ break;
16228
+ }
16229
+ const parent = dirname10(dir);
16230
+ if (parent === dir)
16231
+ break;
16232
+ dir = parent;
16233
+ }
16234
+ }
16235
+ return best?.root ?? null;
16236
+ }
16024
16237
  async function runTypecheck(baseDir) {
16025
16238
  const entry = processRegistry.start("npx --no-install tsc --noEmit --skipLibCheck", baseDir);
16026
16239
  const exited = await processRegistry.waitForExit(entry.id, 90000);
@@ -16051,11 +16264,11 @@ var init_audit_runners = __esm(() => {
16051
16264
  });
16052
16265
 
16053
16266
  // src/modules/execution/auditor.ts
16054
- import { existsSync as existsSync28, readdirSync as readdirSync9 } from "fs";
16055
- import { resolve as resolve17, join as join24, basename as basename2 } from "path";
16267
+ import { existsSync as existsSync29, readdirSync as readdirSync9 } from "fs";
16268
+ import { resolve as resolve18, join as join24, basename as basename2 } from "path";
16056
16269
  function findExistingFile(baseDir, filePath) {
16057
- const direct = resolve17(baseDir, filePath);
16058
- if (existsSync28(direct))
16270
+ const direct = resolve18(baseDir, filePath);
16271
+ if (existsSync29(direct))
16059
16272
  return direct;
16060
16273
  const name = basename2(filePath).toLowerCase();
16061
16274
  const suffix = filePath.replace(/\\/g, "/").toLowerCase();
@@ -16103,8 +16316,9 @@ class Auditor {
16103
16316
  const existingFiles = [];
16104
16317
  const leftoverFiles = [];
16105
16318
  for (const filePath of createFiles) {
16106
- if (findExistingFile(this.baseDir, filePath)) {
16107
- existingFiles.push(filePath);
16319
+ const resolved = findExistingFile(this.baseDir, filePath);
16320
+ if (resolved) {
16321
+ existingFiles.push(resolved);
16108
16322
  } else {
16109
16323
  missingFiles.push(filePath);
16110
16324
  }
@@ -16123,11 +16337,14 @@ class Auditor {
16123
16337
  }
16124
16338
  }
16125
16339
  let typecheckError = null;
16126
- if (missingFiles.length === 0 && leftoverFiles.length === 0 && existsSync28(join24(this.baseDir, "tsconfig.json"))) {
16127
- try {
16128
- typecheckError = await runTypecheck(this.baseDir);
16129
- } catch {
16130
- typecheckError = null;
16340
+ if (missingFiles.length === 0 && leftoverFiles.length === 0) {
16341
+ const root = findTypecheckRoot(this.baseDir, existingFiles);
16342
+ if (root) {
16343
+ try {
16344
+ typecheckError = await runTypecheck(root);
16345
+ } catch {
16346
+ typecheckError = null;
16347
+ }
16131
16348
  }
16132
16349
  }
16133
16350
  const doneSteps = plan.steps.filter((s) => s.status === "done").length;
@@ -16140,7 +16357,7 @@ class Auditor {
16140
16357
  count: String(auditedFiles.length)
16141
16358
  });
16142
16359
  }
16143
- const testsFailing = testRun !== null && testRun.failed > 0;
16360
+ const testsFailing = testRun !== null && !testRun.passed;
16144
16361
  const typecheckFailing = typecheckError !== null;
16145
16362
  const passed = missingFiles.length === 0 && leftoverFiles.length === 0 && !testsFailing && !typecheckFailing;
16146
16363
  const stepsPending = terminalSteps < totalSteps;
@@ -16206,7 +16423,7 @@ var init_auditor = __esm(() => {
16206
16423
  });
16207
16424
 
16208
16425
  // src/modules/execution/plan-store.ts
16209
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync14, existsSync as existsSync29, readdirSync as readdirSync10, rmSync } from "fs";
16426
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync14, existsSync as existsSync30, readdirSync as readdirSync10, rmSync } from "fs";
16210
16427
  import { join as join25 } from "path";
16211
16428
  function readPlanFile(path, fallbackBaseDir) {
16212
16429
  try {
@@ -16232,7 +16449,7 @@ function writePlanFile(path, plan) {
16232
16449
  writeFileSync10(path, JSON.stringify(plan, null, 2), "utf-8");
16233
16450
  }
16234
16451
  function listDir(dir, baseDir) {
16235
- if (!existsSync29(dir))
16452
+ if (!existsSync30(dir))
16236
16453
  return [];
16237
16454
  const files = readdirSync10(dir).filter((f) => f.endsWith(".json"));
16238
16455
  return files.map((f) => readPlanFile(join25(dir, f), baseDir)).filter((p) => p !== null);
@@ -16257,7 +16474,7 @@ class PlanStore {
16257
16474
  legacyPath;
16258
16475
  constructor(baseDir) {
16259
16476
  const mmaDir = join25(baseDir, ".mma");
16260
- if (!existsSync29(mmaDir))
16477
+ if (!existsSync30(mmaDir))
16261
16478
  mkdirSync14(mmaDir, { recursive: true });
16262
16479
  this.baseDir = baseDir;
16263
16480
  this.plansDir = join25(mmaDir, "plans");
@@ -16265,7 +16482,7 @@ class PlanStore {
16265
16482
  this.archiveDir = join25(this.plansDir, "archive");
16266
16483
  this.legacyPath = join25(mmaDir, LEGACY_FILE);
16267
16484
  for (const dir of [this.plansDir, this.draftsDir, this.archiveDir]) {
16268
- if (!existsSync29(dir))
16485
+ if (!existsSync30(dir))
16269
16486
  mkdirSync14(dir, { recursive: true });
16270
16487
  }
16271
16488
  }
@@ -16277,12 +16494,12 @@ class PlanStore {
16277
16494
  }
16278
16495
  loadActive() {
16279
16496
  const activePath = this.activePath();
16280
- if (existsSync29(activePath)) {
16497
+ if (existsSync30(activePath)) {
16281
16498
  const plan = readPlanFile(activePath, this.baseDir);
16282
16499
  if (plan)
16283
16500
  return plan;
16284
16501
  }
16285
- if (existsSync29(this.legacyPath)) {
16502
+ if (existsSync30(this.legacyPath)) {
16286
16503
  const legacy = readPlanFile(this.legacyPath, this.baseDir);
16287
16504
  if (legacy) {
16288
16505
  this.saveActive(legacy);
@@ -16296,7 +16513,7 @@ class PlanStore {
16296
16513
  }
16297
16514
  clearActive() {
16298
16515
  const p = this.activePath();
16299
- if (existsSync29(p))
16516
+ if (existsSync30(p))
16300
16517
  rmSync(p, { force: true });
16301
16518
  }
16302
16519
  saveDraft(plan) {
@@ -16304,11 +16521,11 @@ class PlanStore {
16304
16521
  }
16305
16522
  loadDraft(id) {
16306
16523
  const p = join25(this.draftsDir, `${id}.json`);
16307
- return existsSync29(p) ? readPlanFile(p, this.baseDir) : null;
16524
+ return existsSync30(p) ? readPlanFile(p, this.baseDir) : null;
16308
16525
  }
16309
16526
  removeDraft(id) {
16310
16527
  const p = join25(this.draftsDir, `${id}.json`);
16311
- if (existsSync29(p))
16528
+ if (existsSync30(p))
16312
16529
  rmSync(p, { force: true });
16313
16530
  }
16314
16531
  listDrafts() {
@@ -16327,7 +16544,7 @@ class PlanStore {
16327
16544
  }
16328
16545
  removeArchived(id) {
16329
16546
  const p = join25(this.archiveDir, `${id}.json`);
16330
- if (existsSync29(p))
16547
+ if (existsSync30(p))
16331
16548
  rmSync(p, { force: true });
16332
16549
  }
16333
16550
  listAll() {
@@ -17104,8 +17321,8 @@ var init_execution_plugin = __esm(() => {
17104
17321
  });
17105
17322
 
17106
17323
  // src/modules/execution/module.ts
17107
- import { existsSync as existsSync30, readFileSync as readFileSync17 } from "fs";
17108
- import { resolve as resolve18 } from "path";
17324
+ import { existsSync as existsSync31, readFileSync as readFileSync17 } from "fs";
17325
+ import { resolve as resolve19 } from "path";
17109
17326
 
17110
17327
  class ExecutionModule {
17111
17328
  name = "execution";
@@ -17299,7 +17516,7 @@ class ExecutionModule {
17299
17516
  "poetry.lock",
17300
17517
  "requirements.txt"
17301
17518
  ];
17302
- const hasLockFile = lockFiles.some((f) => existsSync30(resolve18(this.baseDir, f)));
17519
+ const hasLockFile = lockFiles.some((f) => existsSync31(resolve19(this.baseDir, f)));
17303
17520
  if (!hasLockFile) {
17304
17521
  if (contextManager) {
17305
17522
  const hints = this.state.depsGateHints.get(step.id) || 0;
@@ -17319,8 +17536,8 @@ class ExecutionModule {
17319
17536
  }
17320
17537
  if (stepPaths.length === 0)
17321
17538
  return;
17322
- const allExist = stepPaths.every((p) => existsSync30(resolve18(this.baseDir, p)));
17323
- const allGone = stepPaths.every((p) => !existsSync30(resolve18(this.baseDir, p)));
17539
+ const allExist = stepPaths.every((p) => existsSync31(resolve19(this.baseDir, p)));
17540
+ const allGone = stepPaths.every((p) => !existsSync31(resolve19(this.baseDir, p)));
17324
17541
  const satisfied = step.kind === "delete" ? allGone && !allExist : allExist;
17325
17542
  if (!satisfied)
17326
17543
  return;
@@ -17328,7 +17545,7 @@ class ExecutionModule {
17328
17545
  const emptyFiles = [];
17329
17546
  for (const p of stepPaths) {
17330
17547
  try {
17331
- const content = readFileSync17(resolve18(this.baseDir, p), "utf-8");
17548
+ const content = readFileSync17(resolve19(this.baseDir, p), "utf-8");
17332
17549
  if (content.trim().length < 10) {
17333
17550
  emptyFiles.push(p);
17334
17551
  }
@@ -17426,7 +17643,7 @@ var init_module = __esm(() => {
17426
17643
  import {
17427
17644
  readFileSync as readFileSync18,
17428
17645
  writeFileSync as writeFileSync11,
17429
- existsSync as existsSync31,
17646
+ existsSync as existsSync32,
17430
17647
  readdirSync as readdirSync11,
17431
17648
  unlinkSync as unlinkSync4
17432
17649
  } from "fs";
@@ -17526,7 +17743,7 @@ class SessionFileEncryptor {
17526
17743
  const files = readdirSync11(sessionDir);
17527
17744
  for (const file of files) {
17528
17745
  const filePath = join26(sessionDir, file);
17529
- if (existsSync31(filePath) && !file.endsWith(".enc")) {
17746
+ if (existsSync32(filePath) && !file.endsWith(".enc")) {
17530
17747
  try {
17531
17748
  const content = readFileSync18(filePath, "utf8");
17532
17749
  const encrypted = this.encryptFileContent(content);
@@ -17567,7 +17784,7 @@ var init_session_encryption = __esm(() => {
17567
17784
 
17568
17785
  // src/modules/session/store.ts
17569
17786
  import {
17570
- existsSync as existsSync32,
17787
+ existsSync as existsSync33,
17571
17788
  mkdirSync as mkdirSync15,
17572
17789
  readdirSync as readdirSync12,
17573
17790
  readFileSync as readFileSync19,
@@ -17617,7 +17834,7 @@ class SessionStore {
17617
17834
  return join27(this.sessionDir(id), "session.jsonl");
17618
17835
  }
17619
17836
  sessionExists(id) {
17620
- return existsSync32(this.metaPath(id));
17837
+ return existsSync33(this.metaPath(id));
17621
17838
  }
17622
17839
  saveMeta(id, meta) {
17623
17840
  this._metaCache.set(id, meta);
@@ -17635,7 +17852,7 @@ class SessionStore {
17635
17852
  if (cached)
17636
17853
  return cached;
17637
17854
  const path = this.metaPath(id);
17638
- if (!existsSync32(path))
17855
+ if (!existsSync33(path))
17639
17856
  return null;
17640
17857
  try {
17641
17858
  const raw = readFileSync19(path, "utf-8");
@@ -17667,7 +17884,7 @@ class SessionStore {
17667
17884
  }
17668
17885
  loadHistory(id) {
17669
17886
  const path = this.historyPath(id);
17670
- if (!existsSync32(path))
17887
+ if (!existsSync33(path))
17671
17888
  return [];
17672
17889
  try {
17673
17890
  const raw = readFileSync19(path, "utf-8");
@@ -17708,7 +17925,7 @@ class SessionStore {
17708
17925
  }
17709
17926
  loadSessionLog(id) {
17710
17927
  const path = this.sessionLogPath(id);
17711
- if (!existsSync32(path))
17928
+ if (!existsSync33(path))
17712
17929
  return [];
17713
17930
  try {
17714
17931
  const raw = readFileSync19(path, "utf-8");
@@ -17736,7 +17953,7 @@ class SessionStore {
17736
17953
  }
17737
17954
  }
17738
17955
  listSessions() {
17739
- if (!existsSync32(this.baseDir))
17956
+ if (!existsSync33(this.baseDir))
17740
17957
  return [];
17741
17958
  const entries = readdirSync12(this.baseDir, { withFileTypes: true });
17742
17959
  const sessions = [];
@@ -17753,7 +17970,7 @@ class SessionStore {
17753
17970
  deleteSession(id) {
17754
17971
  this._metaCache.delete(id);
17755
17972
  const dir = this.sessionDir(id);
17756
- if (existsSync32(dir)) {
17973
+ if (existsSync33(dir)) {
17757
17974
  rmSync2(dir, { recursive: true, force: true });
17758
17975
  }
17759
17976
  }
@@ -17765,7 +17982,7 @@ class SessionStore {
17765
17982
  const updatedAt = new Date(session2.updatedAt);
17766
17983
  if (updatedAt < thirtyDaysAgo) {
17767
17984
  const historyPath = this.historyPath(session2.id);
17768
- if (existsSync32(historyPath)) {
17985
+ if (existsSync33(historyPath)) {
17769
17986
  const content = readFileSync19(historyPath, "utf-8");
17770
17987
  const compressed = gzipSync(content);
17771
17988
  const gzPath = join27(this.baseDir, `${session2.id}.jsonl.gz`);
@@ -17978,7 +18195,7 @@ class ProfileCompressor {
17978
18195
  }
17979
18196
 
17980
18197
  // src/modules/user-profile/profile.ts
17981
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync13, existsSync as existsSync33, mkdirSync as mkdirSync16 } from "fs";
18198
+ import { readFileSync as readFileSync20, writeFileSync as writeFileSync13, existsSync as existsSync34, mkdirSync as mkdirSync16 } from "fs";
17982
18199
  import { join as join28 } from "path";
17983
18200
  import { homedir as homedir9, hostname, platform as platform5, type } from "os";
17984
18201
  import { env } from "process";
@@ -18003,14 +18220,14 @@ class UserProfile {
18003
18220
  return this.info;
18004
18221
  }
18005
18222
  save() {
18006
- if (!existsSync33(this.profileDir)) {
18223
+ if (!existsSync34(this.profileDir)) {
18007
18224
  mkdirSync16(this.profileDir, { recursive: true });
18008
18225
  }
18009
18226
  writeFileSync13(join28(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
18010
18227
  }
18011
18228
  load() {
18012
18229
  const path = join28(this.profileDir, "profile.json");
18013
- if (!existsSync33(path))
18230
+ if (!existsSync34(path))
18014
18231
  return null;
18015
18232
  try {
18016
18233
  const data = JSON.parse(readFileSync20(path, "utf-8"));
@@ -18048,12 +18265,12 @@ class UserProfile {
18048
18265
  var init_profile = () => {};
18049
18266
 
18050
18267
  // src/modules/skills/loader.ts
18051
- import { readdirSync as readdirSync13, readFileSync as readFileSync21, existsSync as existsSync34, statSync as statSync6 } from "fs";
18268
+ import { readdirSync as readdirSync13, readFileSync as readFileSync21, existsSync as existsSync35, statSync as statSync6 } from "fs";
18052
18269
  import { join as join29 } from "path";
18053
18270
 
18054
18271
  class SkillsLoader {
18055
18272
  loadFromDir(dirPath) {
18056
- if (!existsSync34(dirPath))
18273
+ if (!existsSync35(dirPath))
18057
18274
  return [];
18058
18275
  const skills = [];
18059
18276
  this.scanDir(dirPath, skills);
@@ -18307,7 +18524,7 @@ var init_browser2 = __esm(() => {
18307
18524
 
18308
18525
  // src/modules/lsp/command.ts
18309
18526
  import { delimiter, join as join30 } from "path";
18310
- import { existsSync as existsSync35 } from "fs";
18527
+ import { existsSync as existsSync36 } from "fs";
18311
18528
  import { platform as platform6 } from "os";
18312
18529
  function resolveSpawnCommand(command, platformName = platform6(), pathEnv = process.env.PATH ?? "") {
18313
18530
  if (platformName !== "win32")
@@ -18319,7 +18536,7 @@ function resolveSpawnCommand(command, platformName = platform6(), pathEnv = proc
18319
18536
  for (const dir of dirs) {
18320
18537
  for (const ext of WIN_EXTS) {
18321
18538
  const candidate = join30(dir, `${command}${ext}`);
18322
- if (existsSync35(candidate))
18539
+ if (existsSync36(candidate))
18323
18540
  return `${command}${ext}`;
18324
18541
  }
18325
18542
  }
@@ -18341,11 +18558,8 @@ var init_command = __esm(() => {
18341
18558
  });
18342
18559
 
18343
18560
  // src/modules/lsp/client.ts
18344
- import {
18345
- spawn as spawn6,
18346
- execSync as execSync2
18347
- } from "child_process";
18348
- import { resolve as resolve19 } from "path";
18561
+ import { spawn as spawn6, execSync as execSync2 } from "child_process";
18562
+ import { resolve as resolve20 } from "path";
18349
18563
  import { platform as platform7 } from "os";
18350
18564
 
18351
18565
  class LspClient {
@@ -18363,25 +18577,32 @@ class LspClient {
18363
18577
  try {
18364
18578
  await this.startServer(config, projectRoot);
18365
18579
  const rootUri = this.pathToUri(projectRoot);
18366
- const initResult = await this.sendRequest("initialize", {
18580
+ const initParams = {
18367
18581
  processId: process.pid,
18368
18582
  rootUri,
18369
18583
  workspaceFolders: [{ uri: rootUri, name: "workspace" }],
18370
18584
  capabilities: { textDocument: { publishDiagnostics: {} } }
18371
- }, timeout);
18585
+ };
18586
+ try {
18587
+ await this.sendRequest("initialize", initParams, timeout);
18588
+ } catch (e) {
18589
+ if (!(e instanceof Error) || !e.message.includes("initialize"))
18590
+ throw e;
18591
+ await this.shutdown();
18592
+ await this.startServer(config, projectRoot);
18593
+ await this.sendRequest("initialize", initParams, timeout);
18594
+ }
18372
18595
  this.initialized = true;
18373
18596
  this.sendNotification("initialized", {});
18374
- const uri = this.pathToUri(resolve19(filePath));
18597
+ const uri = this.pathToUri(resolve20(filePath));
18375
18598
  const fs2 = await import("fs");
18376
18599
  const content = fs2.readFileSync(filePath, "utf-8");
18377
- const diagPromise = new Promise((resolve20) => {
18378
- this.diagnosticsResolve = resolve20;
18600
+ const diagPromise = new Promise((resolve21) => {
18601
+ this.diagnosticsResolve = resolve21;
18379
18602
  this.diagnostics = [];
18380
18603
  this.diagnosticsTimer = setTimeout(() => {
18381
- if (this.diagnosticsResolve) {
18382
- this.diagnosticsResolve([]);
18383
- this.diagnosticsResolve = null;
18384
- }
18604
+ this.diagnosticsResolve?.([]);
18605
+ this.diagnosticsResolve = null;
18385
18606
  }, timeout);
18386
18607
  });
18387
18608
  this.sendNotification("textDocument/didOpen", {
@@ -18407,7 +18628,7 @@ class LspClient {
18407
18628
  throw new Error(`${config.command} not found in PATH`);
18408
18629
  }
18409
18630
  }
18410
- return new Promise((resolve20, reject) => {
18631
+ return new Promise((resolve21, reject) => {
18411
18632
  const args = config.args ?? [];
18412
18633
  const isWin = platform7() === "win32";
18413
18634
  let spawnCommand = resolveSpawnCommand(config.command);
@@ -18429,13 +18650,12 @@ class LspClient {
18429
18650
  });
18430
18651
  proc.stderr.on("data", () => {});
18431
18652
  proc.once("spawn", () => {
18432
- resolve20();
18653
+ resolve21();
18433
18654
  });
18434
18655
  this.process = proc;
18435
18656
  setTimeout(() => {
18436
- if (!this.initialized && this.process) {
18657
+ if (!this.initialized && this.process)
18437
18658
  reject(new Error("LSP server start timeout"));
18438
- }
18439
18659
  }, config.timeout ?? 1e4);
18440
18660
  });
18441
18661
  }
@@ -18486,10 +18706,8 @@ class LspClient {
18486
18706
  clearTimeout(this.diagnosticsTimer);
18487
18707
  this.diagnosticsTimer = null;
18488
18708
  }
18489
- if (this.diagnosticsResolve) {
18490
- this.diagnosticsResolve(this.diagnostics);
18491
- this.diagnosticsResolve = null;
18492
- }
18709
+ this.diagnosticsResolve?.(this.diagnostics);
18710
+ this.diagnosticsResolve = null;
18493
18711
  }
18494
18712
  return;
18495
18713
  }
@@ -18506,9 +18724,9 @@ class LspClient {
18506
18724
  }
18507
18725
  }
18508
18726
  sendRequest(method, params, timeout) {
18509
- return new Promise((resolve20, reject) => {
18727
+ return new Promise((resolve21, reject) => {
18510
18728
  const id = ++this.requestId;
18511
- this.pending.set(id, { resolve: resolve20, reject });
18729
+ this.pending.set(id, { resolve: resolve21, reject });
18512
18730
  const message = JSON.stringify({ jsonrpc: "2.0", id, method, params });
18513
18731
  this.write(message);
18514
18732
  setTimeout(() => {
@@ -18548,6 +18766,8 @@ class LspClient {
18548
18766
  this.process = null;
18549
18767
  }
18550
18768
  this.initialized = false;
18769
+ this.buffer = "";
18770
+ this.contentLength = -1;
18551
18771
  if (this.diagnosticsTimer) {
18552
18772
  clearTimeout(this.diagnosticsTimer);
18553
18773
  this.diagnosticsTimer = null;
@@ -18556,10 +18776,7 @@ class LspClient {
18556
18776
  }
18557
18777
  pathToUri(filePath) {
18558
18778
  const normalized = filePath.replace(/\\/g, "/");
18559
- if (/^[a-zA-Z]:/.test(normalized)) {
18560
- return `file:///${normalized}`;
18561
- }
18562
- return `file://${normalized}`;
18779
+ return /^[a-zA-Z]:/.test(normalized) ? `file:///${normalized}` : `file://${normalized}`;
18563
18780
  }
18564
18781
  languageFromPath(filePath) {
18565
18782
  const ext = filePath.split(".").pop()?.toLowerCase();
@@ -18588,20 +18805,26 @@ var init_client2 = __esm(() => {
18588
18805
  });
18589
18806
 
18590
18807
  // src/modules/lsp/module.ts
18591
- import { existsSync as existsSync36 } from "fs";
18592
- import { resolve as resolve20 } from "path";
18808
+ import { existsSync as existsSync37 } from "fs";
18809
+ import { resolve as resolve21 } from "path";
18593
18810
 
18594
18811
  class LspModule {
18595
18812
  name = "lsp";
18596
18813
  config;
18597
18814
  client = new LspClient;
18598
- consecutiveFailures = 0;
18599
- lspDisabled = false;
18815
+ failuresByServer = new Map;
18816
+ disabledServers = new Set;
18600
18817
  constructor(config) {
18601
18818
  this.config = { ...DEFAULT_LSP_CONFIG, ...config };
18602
18819
  }
18603
18820
  isLspDisabled() {
18604
- return this.lspDisabled;
18821
+ return this.disabledServers.size > 0;
18822
+ }
18823
+ isServerDisabled(key) {
18824
+ return this.disabledServers.has(key);
18825
+ }
18826
+ serverKey(server) {
18827
+ return `${server.command}:${(server.args ?? []).join(" ")}`;
18605
18828
  }
18606
18829
  getPlugin() {
18607
18830
  const self = this;
@@ -18611,8 +18834,6 @@ class LspModule {
18611
18834
  onAfterTool: async (_ctx, call, result) => {
18612
18835
  if (!self.config.enabled)
18613
18836
  return;
18614
- if (self.lspDisabled)
18615
- return;
18616
18837
  if (call.name !== "write_file" && call.name !== "edit_file")
18617
18838
  return;
18618
18839
  if (!result.success)
@@ -18620,16 +18841,19 @@ class LspModule {
18620
18841
  const filePath = String(call.arguments.path ?? "");
18621
18842
  if (!filePath)
18622
18843
  return;
18623
- const fullPath = resolve20(_ctx.baseDir, filePath);
18624
- if (!existsSync36(fullPath))
18844
+ const fullPath = resolve21(_ctx.baseDir, filePath);
18845
+ if (!existsSync37(fullPath))
18625
18846
  return;
18626
18847
  const serverConfig = getServerForFile(fullPath, self.config);
18627
18848
  if (!serverConfig)
18628
18849
  return;
18850
+ const key = self.serverKey(serverConfig);
18851
+ if (self.disabledServers.has(key))
18852
+ return;
18629
18853
  const projectRoot = findProjectRoot(fullPath, _ctx.baseDir, serverConfig.workspaceMarkers ?? []);
18630
18854
  try {
18631
18855
  const diagnostics = await self.client.checkFile(fullPath, _ctx.baseDir, serverConfig, projectRoot);
18632
- self.consecutiveFailures = 0;
18856
+ self.failuresByServer.set(key, 0);
18633
18857
  const errors = diagnostics.filter((d) => d.severity === 1);
18634
18858
  const warnings = diagnostics.filter((d) => d.severity === 2);
18635
18859
  if (errors.length > 0) {
@@ -18648,12 +18872,13 @@ ${items}`;
18648
18872
  }
18649
18873
  } catch (e) {
18650
18874
  const msg = e instanceof Error ? e.message : String(e);
18651
- self.consecutiveFailures++;
18875
+ const failures = (self.failuresByServer.get(key) ?? 0) + 1;
18876
+ self.failuresByServer.set(key, failures);
18652
18877
  try {
18653
18878
  _ctx.logger?.warn(`LSP check failed for ${filePath}: ${msg}`);
18654
18879
  } catch {}
18655
- if (self.consecutiveFailures >= MAX_CONSECUTIVE_LSP_FAILURES) {
18656
- self.lspDisabled = true;
18880
+ if (failures >= MAX_CONSECUTIVE_LSP_FAILURES) {
18881
+ self.disabledServers.add(key);
18657
18882
  result.output += `
18658
18883
 
18659
18884
  [LSP disabled: ${t("lsp.unavailable")}]`;
@@ -18699,7 +18924,7 @@ var init_lsp = __esm(() => {
18699
18924
  });
18700
18925
 
18701
18926
  // src/modules/indexer/walker.ts
18702
- import { readdirSync as readdirSync14, readFileSync as readFileSync22, statSync as statSync7, existsSync as existsSync37, watch } from "fs";
18927
+ import { readdirSync as readdirSync14, readFileSync as readFileSync22, statSync as statSync7, existsSync as existsSync38, watch } from "fs";
18703
18928
  import { join as join31, relative as relative2, extname as extname5 } from "path";
18704
18929
 
18705
18930
  class Indexer {
@@ -18727,7 +18952,7 @@ class Indexer {
18727
18952
  let totalSize = 0;
18728
18953
  let count = 0;
18729
18954
  const walkDir = (dir) => {
18730
- if (!existsSync37(dir))
18955
+ if (!existsSync38(dir))
18731
18956
  return;
18732
18957
  let entries;
18733
18958
  try {
@@ -18801,7 +19026,7 @@ var init_walker = __esm(() => {
18801
19026
  });
18802
19027
 
18803
19028
  // src/modules/indexer/cache.ts
18804
- import { readFileSync as readFileSync23, writeFileSync as writeFileSync14, existsSync as existsSync38, mkdirSync as mkdirSync17, rmSync as rmSync3 } from "fs";
19029
+ import { readFileSync as readFileSync23, writeFileSync as writeFileSync14, existsSync as existsSync39, mkdirSync as mkdirSync17, rmSync as rmSync3 } from "fs";
18805
19030
  import { join as join32 } from "path";
18806
19031
 
18807
19032
  class IndexCache {
@@ -18813,7 +19038,7 @@ class IndexCache {
18813
19038
  load() {
18814
19039
  if (this.cache)
18815
19040
  return this.cache;
18816
- if (!existsSync38(this.cachePath))
19041
+ if (!existsSync39(this.cachePath))
18817
19042
  return null;
18818
19043
  try {
18819
19044
  this.cache = JSON.parse(readFileSync23(this.cachePath, "utf-8"));
@@ -18825,13 +19050,13 @@ class IndexCache {
18825
19050
  save(result) {
18826
19051
  this.cache = result;
18827
19052
  const dir = join32(this.cachePath, "..");
18828
- if (!existsSync38(dir))
19053
+ if (!existsSync39(dir))
18829
19054
  mkdirSync17(dir, { recursive: true });
18830
19055
  writeFileSync14(this.cachePath, JSON.stringify(result), "utf-8");
18831
19056
  }
18832
19057
  invalidate() {
18833
19058
  this.cache = null;
18834
- if (existsSync38(this.cachePath)) {
19059
+ if (existsSync39(this.cachePath)) {
18835
19060
  try {
18836
19061
  rmSync3(this.cachePath);
18837
19062
  } catch {}
@@ -18841,11 +19066,11 @@ class IndexCache {
18841
19066
  var init_cache = () => {};
18842
19067
 
18843
19068
  // src/modules/indexer/project-profile.ts
18844
- import { readFileSync as readFileSync24, existsSync as existsSync39 } from "fs";
19069
+ import { readFileSync as readFileSync24, existsSync as existsSync40 } from "fs";
18845
19070
  import { join as join33 } from "path";
18846
19071
  function detectManifest(baseDir) {
18847
19072
  for (const manifest of MANIFEST_ORDER) {
18848
- if (existsSync39(join33(baseDir, manifest)))
19073
+ if (existsSync40(join33(baseDir, manifest)))
18849
19074
  return manifest;
18850
19075
  }
18851
19076
  return null;
@@ -19009,7 +19234,7 @@ var init_project_profile = __esm(() => {
19009
19234
  });
19010
19235
 
19011
19236
  // src/modules/indexer/module.ts
19012
- import { dirname as dirname11 } from "path";
19237
+ import { dirname as dirname12 } from "path";
19013
19238
 
19014
19239
  class IndexerModule {
19015
19240
  name = "indexer";
@@ -19160,7 +19385,7 @@ ${t("indexer.and_more", { count: result.files.length - 100 })}` : "";
19160
19385
  const counts = {};
19161
19386
  for (const f of result.files) {
19162
19387
  const normalized = f.path.replace(/\\/g, "/");
19163
- const dir = dirname11(normalized);
19388
+ const dir = dirname12(normalized);
19164
19389
  const key = dir === "." ? "(root)" : dir;
19165
19390
  counts[key] = (counts[key] || 0) + 1;
19166
19391
  }
@@ -19499,8 +19724,8 @@ __export(exports_bootstrap, {
19499
19724
  bootstrap: () => bootstrap
19500
19725
  });
19501
19726
  import { homedir as homedir11 } from "os";
19502
- import { join as join35, resolve as resolve21 } from "path";
19503
- import { existsSync as existsSync40, readFileSync as readFileSync25, writeFileSync as writeFileSync15 } from "fs";
19727
+ import { join as join35, resolve as resolve22 } from "path";
19728
+ import { existsSync as existsSync41, readFileSync as readFileSync25, writeFileSync as writeFileSync15 } from "fs";
19504
19729
  function buildSystemInfo(config, baseDir, profileCompressed) {
19505
19730
  const now = new Date().toISOString().replace("T", " ").slice(0, 19);
19506
19731
  const isWin = profileCompressed.toLowerCase().includes("win32");
@@ -19570,7 +19795,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19570
19795
  retry: config.retry,
19571
19796
  rateLimits: config.security?.rateLimits
19572
19797
  });
19573
- const baseDir = projectDir ? resolve21(projectDir) : process.cwd();
19798
+ const baseDir = projectDir ? resolve22(projectDir) : process.cwd();
19574
19799
  const projectMapCacheDir = join35(baseDir, ".mma");
19575
19800
  const indexerModule = new IndexerModule({
19576
19801
  baseDir,
@@ -19599,7 +19824,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19599
19824
  estimatedTokens: Math.ceil(systemInfoContent.length / 4)
19600
19825
  };
19601
19826
  const agentsMdGlobal = join35(dir, "AGENTS.md");
19602
- if (!existsSync40(agentsMdGlobal)) {
19827
+ if (!existsSync41(agentsMdGlobal)) {
19603
19828
  writeFileSync15(agentsMdGlobal, "", "utf-8");
19604
19829
  }
19605
19830
  const sessionDir = join35(dir, "sessions");
@@ -19744,7 +19969,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19744
19969
  join35(dir, "AGENTS.md")
19745
19970
  ];
19746
19971
  for (const p of agentsMdCandidates) {
19747
- if (existsSync40(p)) {
19972
+ if (existsSync41(p)) {
19748
19973
  const content = readFileSync25(p, "utf-8").trim();
19749
19974
  if (content) {
19750
19975
  agentsMdBlocks.push({
@@ -20377,10 +20602,10 @@ function menuList(items) {
20377
20602
  console.log(l);
20378
20603
  }
20379
20604
  function ask(rl, question, defaultValue) {
20380
- return new Promise((resolve22) => {
20605
+ return new Promise((resolve23) => {
20381
20606
  const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
20382
20607
  rl.question(prompt, (answer) => {
20383
- resolve22(answer.trim() || defaultValue || "");
20608
+ resolve23(answer.trim() || defaultValue || "");
20384
20609
  });
20385
20610
  });
20386
20611
  }
@@ -20599,12 +20824,12 @@ __export(exports_manifest, {
20599
20824
  getCertMark: () => getCertMark,
20600
20825
  MANIFEST_PATH: () => MANIFEST_PATH
20601
20826
  });
20602
- import { existsSync as existsSync41, readFileSync as readFileSync26, mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
20827
+ import { existsSync as existsSync42, readFileSync as readFileSync26, mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
20603
20828
  import { homedir as homedir13 } from "os";
20604
20829
  import { join as join37 } from "path";
20605
20830
  function readManifest(path = MANIFEST_PATH) {
20606
20831
  try {
20607
- if (existsSync41(path)) {
20832
+ if (existsSync42(path)) {
20608
20833
  const raw = JSON.parse(readFileSync26(path, "utf-8"));
20609
20834
  return { version: 1, certifications: raw.certifications ?? [] };
20610
20835
  }
@@ -27770,7 +27995,7 @@ var init_scenarios = __esm(() => {
27770
27995
  });
27771
27996
 
27772
27997
  // src/modules/certification/loader.ts
27773
- import { existsSync as existsSync42, readdirSync as readdirSync15, readFileSync as readFileSync27 } from "fs";
27998
+ import { existsSync as existsSync43, readdirSync as readdirSync15, readFileSync as readFileSync27 } from "fs";
27774
27999
  import { join as join38 } from "path";
27775
28000
  function validateScenario(s) {
27776
28001
  const errors2 = [];
@@ -27820,7 +28045,7 @@ function loadScenarios(userDir) {
27820
28045
  else
27821
28046
  scenarios.push(s);
27822
28047
  }
27823
- if (userDir && existsSync42(userDir)) {
28048
+ if (userDir && existsSync43(userDir)) {
27824
28049
  for (const file of readdirSync15(userDir)) {
27825
28050
  if (!file.endsWith(".yaml") && !file.endsWith(".yml"))
27826
28051
  continue;
@@ -27878,7 +28103,7 @@ var init_loader3 = __esm(() => {
27878
28103
  });
27879
28104
 
27880
28105
  // src/modules/certification/fact-checker.ts
27881
- import { existsSync as existsSync43, readFileSync as readFileSync28, statSync as statSync8 } from "fs";
28106
+ import { existsSync as existsSync44, readFileSync as readFileSync28, statSync as statSync8 } from "fs";
27882
28107
  import { join as join39 } from "path";
27883
28108
  function checkSandbox(sandboxDir, checks, exitCode, output) {
27884
28109
  const failures = [];
@@ -27898,7 +28123,7 @@ function runCheck(sandboxDir, check, exitCode, output) {
27898
28123
  case "fileExists":
27899
28124
  return isFile(join39(sandboxDir, check.path));
27900
28125
  case "fileNotExists":
27901
- return !existsSync43(join39(sandboxDir, check.path));
28126
+ return !existsSync44(join39(sandboxDir, check.path));
27902
28127
  case "dirExists":
27903
28128
  return isDir(join39(sandboxDir, check.path));
27904
28129
  case "fileContent": {
@@ -27924,14 +28149,14 @@ function runCheck(sandboxDir, check, exitCode, output) {
27924
28149
  }
27925
28150
  function isFile(p) {
27926
28151
  try {
27927
- return existsSync43(p) && statSync8(p).isFile();
28152
+ return existsSync44(p) && statSync8(p).isFile();
27928
28153
  } catch {
27929
28154
  return false;
27930
28155
  }
27931
28156
  }
27932
28157
  function isDir(p) {
27933
28158
  try {
27934
- return existsSync43(p) && statSync8(p).isDirectory();
28159
+ return existsSync44(p) && statSync8(p).isDirectory();
27935
28160
  } catch {
27936
28161
  return false;
27937
28162
  }
@@ -27962,9 +28187,9 @@ var init_fact_checker = () => {};
27962
28187
 
27963
28188
  // src/modules/certification/runner.ts
27964
28189
  import { spawn as spawn7 } from "child_process";
27965
- import { existsSync as existsSync44, mkdirSync as mkdirSync19, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
28190
+ import { existsSync as existsSync45, mkdirSync as mkdirSync19, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
27966
28191
  import { platform as platform8 } from "os";
27967
- import { join as join40, resolve as resolve22, dirname as dirname12 } from "path";
28192
+ import { join as join40, resolve as resolve23, dirname as dirname13 } from "path";
27968
28193
  async function runScenario(scenario, opts) {
27969
28194
  if (scenario.mode === "skip") {
27970
28195
  return {
@@ -28045,27 +28270,27 @@ function prepareSandbox(sandbox, scenario, mmaRoot) {
28045
28270
  mkdirSync19(sandbox, { recursive: true });
28046
28271
  for (const f of scenario.fixtures ?? []) {
28047
28272
  const src = join40(mmaRoot, f.source);
28048
- if (!existsSync44(src)) {
28273
+ if (!existsSync45(src)) {
28049
28274
  throw new Error(`fixture missing: ${f.source}`);
28050
28275
  }
28051
28276
  const dest = join40(sandbox, f.dest);
28052
- mkdirSync19(dirname12(dest), { recursive: true });
28277
+ mkdirSync19(dirname13(dest), { recursive: true });
28053
28278
  cpSync2(src, dest);
28054
28279
  }
28055
28280
  }
28056
28281
  function resolveMmaEntry(mmaRoot) {
28057
28282
  const dev = join40(mmaRoot, "src", "cli", "main.ts");
28058
- if (existsSync44(dev))
28283
+ if (existsSync45(dev))
28059
28284
  return dev;
28060
28285
  return join40(mmaRoot, "dist", "main.js");
28061
28286
  }
28062
28287
  function findMmaRoot(fromDir) {
28063
28288
  const candidates = [
28064
- resolve22(fromDir, "..", "..", ".."),
28065
- resolve22(fromDir, "..")
28289
+ resolve23(fromDir, "..", "..", ".."),
28290
+ resolve23(fromDir, "..")
28066
28291
  ];
28067
28292
  for (const c of candidates) {
28068
- if (existsSync44(join40(c, "package.json")))
28293
+ if (existsSync45(join40(c, "package.json")))
28069
28294
  return c;
28070
28295
  }
28071
28296
  return process.cwd();
@@ -28133,13 +28358,13 @@ __export(exports_cli, {
28133
28358
  });
28134
28359
  import { rmSync as rmSync5 } from "fs";
28135
28360
  import { homedir as homedir14 } from "os";
28136
- import { join as join41, dirname as dirname13 } from "path";
28361
+ import { join as join41, dirname as dirname14 } from "path";
28137
28362
  import { fileURLToPath as fileURLToPath2 } from "url";
28138
- import { existsSync as existsSync45, readFileSync as readFileSync29 } from "fs";
28363
+ import { existsSync as existsSync46, readFileSync as readFileSync29 } from "fs";
28139
28364
  function readVersion() {
28140
28365
  const candidates = [join41(MMA_ROOT, "package.json")];
28141
28366
  for (const p of candidates) {
28142
- if (existsSync45(p)) {
28367
+ if (existsSync46(p)) {
28143
28368
  try {
28144
28369
  const raw = JSON.parse(readFileSync29(p, "utf-8"));
28145
28370
  if (raw.version)
@@ -28289,7 +28514,7 @@ var init_cli = __esm(() => {
28289
28514
  init_loader3();
28290
28515
  init_runner2();
28291
28516
  init_manifest();
28292
- HERE = dirname13(fileURLToPath2(import.meta.url));
28517
+ HERE = dirname14(fileURLToPath2(import.meta.url));
28293
28518
  MMA_ROOT = findMmaRoot(HERE);
28294
28519
  USER_SCENARIO_DIR = join41(homedir14(), ".mma", "certification", "scenarios");
28295
28520
  });
@@ -28300,18 +28525,18 @@ __export(exports_repl_commands, {
28300
28525
  registerAllCommands: () => registerAllCommands,
28301
28526
  COMMAND_GROUPS: () => COMMAND_GROUPS
28302
28527
  });
28303
- import { join as join43, dirname as dirname15 } from "path";
28528
+ import { join as join43, dirname as dirname16 } from "path";
28304
28529
  import { homedir as homedir16 } from "os";
28305
- import { existsSync as existsSync47, readFileSync as readFileSync31 } from "fs";
28530
+ import { existsSync as existsSync48, readFileSync as readFileSync31 } from "fs";
28306
28531
  import { fileURLToPath as fileURLToPath4 } from "url";
28307
28532
  function readVersion3() {
28308
- const here = dirname15(fileURLToPath4(import.meta.url));
28533
+ const here = dirname16(fileURLToPath4(import.meta.url));
28309
28534
  const candidates = [
28310
28535
  join43(here, "..", "..", "package.json"),
28311
28536
  join43(here, "..", "package.json")
28312
28537
  ];
28313
28538
  for (const p of candidates) {
28314
- if (existsSync47(p)) {
28539
+ if (existsSync48(p)) {
28315
28540
  try {
28316
28541
  const raw = JSON.parse(readFileSync31(p, "utf8"));
28317
28542
  if (raw.version)
@@ -28377,8 +28602,8 @@ function registerMmaCommands(ctx) {
28377
28602
  }
28378
28603
  try {
28379
28604
  const { loadFileAsDataUrl: loadFileAsDataUrl2, loadUrlAsDataUrl: loadUrlAsDataUrl2, readClipboardImage: readClipboardImage2 } = await Promise.resolve().then(() => (init_image_utils(), exports_image_utils));
28380
- const { existsSync: existsSync48 } = await import("fs");
28381
- const { resolve: resolve23 } = await import("path");
28605
+ const { existsSync: existsSync49 } = await import("fs");
28606
+ const { resolve: resolve24 } = await import("path");
28382
28607
  let dataUrl;
28383
28608
  let label;
28384
28609
  if (source.toLowerCase() === "clipboard") {
@@ -28396,8 +28621,8 @@ function registerMmaCommands(ctx) {
28396
28621
  dataUrl = result.dataUrl;
28397
28622
  label = source;
28398
28623
  } else {
28399
- const absPath = resolve23(process.cwd(), source);
28400
- if (!existsSync48(absPath)) {
28624
+ const absPath = resolve24(process.cwd(), source);
28625
+ if (!existsSync49(absPath)) {
28401
28626
  console.log(pc2.red(t("image.not_found", { path: source })));
28402
28627
  return;
28403
28628
  }
@@ -28933,9 +29158,9 @@ init_bootstrap();
28933
29158
  init_config2();
28934
29159
  init_setup();
28935
29160
  init_i18n();
28936
- import { join as join42, dirname as dirname14 } from "path";
29161
+ import { join as join42, dirname as dirname15 } from "path";
28937
29162
  import { homedir as homedir15 } from "os";
28938
- import { existsSync as existsSync46, readFileSync as readFileSync30 } from "fs";
29163
+ import { existsSync as existsSync47, readFileSync as readFileSync30 } from "fs";
28939
29164
 
28940
29165
  // src/cli/security-commands.ts
28941
29166
  init_bootstrap();
@@ -29557,13 +29782,13 @@ function createSecurityCommand(program2) {
29557
29782
  // src/cli/commands.ts
29558
29783
  import { fileURLToPath as fileURLToPath3 } from "url";
29559
29784
  function readVersion2() {
29560
- const here = dirname14(fileURLToPath3(import.meta.url));
29785
+ const here = dirname15(fileURLToPath3(import.meta.url));
29561
29786
  const candidates = [
29562
29787
  join42(here, "..", "..", "package.json"),
29563
29788
  join42(here, "..", "package.json")
29564
29789
  ];
29565
29790
  for (const p of candidates) {
29566
- if (existsSync46(p)) {
29791
+ if (existsSync47(p)) {
29567
29792
  try {
29568
29793
  const raw = JSON.parse(readFileSync30(p, "utf8"));
29569
29794
  if (raw.version)
@@ -30510,8 +30735,8 @@ class LineEditor {
30510
30735
  }
30511
30736
 
30512
30737
  // src/cli/repl.ts
30513
- import { existsSync as existsSync48, readFileSync as readFileSync32, writeFileSync as writeFileSync17 } from "fs";
30514
- import { join as join44, dirname as dirname16 } from "path";
30738
+ import { existsSync as existsSync49, readFileSync as readFileSync32, writeFileSync as writeFileSync17 } from "fs";
30739
+ import { join as join44, dirname as dirname17 } from "path";
30515
30740
  import { homedir as homedir17 } from "os";
30516
30741
  import { fileURLToPath as fileURLToPath5 } from "url";
30517
30742
 
@@ -31015,13 +31240,13 @@ init_box();
31015
31240
  init_i18n();
31016
31241
  init_repl_commands();
31017
31242
  function readVersion4() {
31018
- const here = dirname16(fileURLToPath5(import.meta.url));
31243
+ const here = dirname17(fileURLToPath5(import.meta.url));
31019
31244
  const candidates = [
31020
31245
  join44(here, "..", "..", "package.json"),
31021
31246
  join44(here, "..", "package.json")
31022
31247
  ];
31023
31248
  for (const p of candidates) {
31024
- if (existsSync48(p)) {
31249
+ if (existsSync49(p)) {
31025
31250
  try {
31026
31251
  const raw = JSON.parse(readFileSync32(p, "utf8"));
31027
31252
  if (raw.version)
@@ -31115,7 +31340,7 @@ class Repl {
31115
31340
  this.setupListeners();
31116
31341
  }
31117
31342
  loadHistory() {
31118
- if (existsSync48(this.historyPath)) {
31343
+ if (existsSync49(this.historyPath)) {
31119
31344
  try {
31120
31345
  const raw = readFileSync32(this.historyPath, "utf-8");
31121
31346
  this.history = raw.split(`
@@ -31478,7 +31703,7 @@ ${t("image.clipboard_empty")}`));
31478
31703
  join44(this.baseDir, ".mma", "AGENTS.md"),
31479
31704
  join44(this.configDir, "AGENTS.md")
31480
31705
  ];
31481
- const foundAgents = agentsMdCandidates.filter((p) => existsSync48(p));
31706
+ const foundAgents = agentsMdCandidates.filter((p) => existsSync49(p));
31482
31707
  if (foundAgents.length > 0) {
31483
31708
  for (const p of foundAgents) {
31484
31709
  row(t("repl.agents_label"), pc2.dim(p));
@@ -31530,18 +31755,29 @@ init_setup();
31530
31755
  init_config2();
31531
31756
  init_i18n();
31532
31757
  init_colors();
31533
- import { existsSync as existsSync49, readFileSync as readFileSync33 } from "fs";
31534
- import { join as join45, dirname as dirname17 } from "path";
31758
+ import { existsSync as existsSync50, readFileSync as readFileSync33 } from "fs";
31759
+ import { join as join45, dirname as dirname18 } from "path";
31535
31760
  import { homedir as homedir18 } from "os";
31536
31761
  import { fileURLToPath as fileURLToPath6 } from "url";
31537
31762
 
31538
31763
  // src/modules/updater/checker.ts
31539
31764
  init_command();
31540
31765
  import { platform as platform9 } from "os";
31766
+ function semverGt(a, b) {
31767
+ const pa = a.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
31768
+ const pb = b.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
31769
+ for (let i = 0;i < 3; i++) {
31770
+ const na = pa[i] ?? 0;
31771
+ const nb = pb[i] ?? 0;
31772
+ if (na !== nb)
31773
+ return na > nb;
31774
+ }
31775
+ return false;
31776
+ }
31541
31777
  var defaultRunner2 = async (command, args, options) => {
31542
31778
  const { execFile } = await import("child_process");
31543
- return new Promise((resolve23) => {
31544
- execFile(command, args, options, (err) => resolve23({ error: err?.message }));
31779
+ return new Promise((resolve24) => {
31780
+ execFile(command, args, options, (err) => resolve24({ error: err?.message }));
31545
31781
  });
31546
31782
  };
31547
31783
 
@@ -31574,7 +31810,7 @@ class Updater {
31574
31810
  if (!latest) {
31575
31811
  return { updateAvailable: false, current: this.currentVersion, error: "No latest tag" };
31576
31812
  }
31577
- const updateAvailable = latest !== this.currentVersion;
31813
+ const updateAvailable = semverGt(latest, this.currentVersion);
31578
31814
  return { updateAvailable, current: this.currentVersion, latest };
31579
31815
  } catch (e) {
31580
31816
  return { updateAvailable: false, current: this.currentVersion, error: e.message };
@@ -31616,6 +31852,7 @@ class UpdaterModule {
31616
31852
  logger;
31617
31853
  started = false;
31618
31854
  timer = null;
31855
+ idlePromise = null;
31619
31856
  installEnabled = true;
31620
31857
  constructor(config, currentVersion, packageName, logger, installRunner) {
31621
31858
  this.config = config;
@@ -31639,7 +31876,7 @@ class UpdaterModule {
31639
31876
  if (this.started || !this.config.enabled)
31640
31877
  return;
31641
31878
  this.started = true;
31642
- this.runLoop();
31879
+ this.idlePromise = this.runLoop();
31643
31880
  }
31644
31881
  stop() {
31645
31882
  this.started = false;
@@ -31648,6 +31885,12 @@ class UpdaterModule {
31648
31885
  this.timer = null;
31649
31886
  }
31650
31887
  }
31888
+ async waitForIdle(timeoutMs = 130000) {
31889
+ const idle = this.idlePromise;
31890
+ if (!idle)
31891
+ return;
31892
+ await Promise.race([idle, new Promise((r) => setTimeout(r, timeoutMs))]);
31893
+ }
31651
31894
  async runLoop() {
31652
31895
  while (this.started) {
31653
31896
  await this.checkOnce();
@@ -31693,13 +31936,13 @@ class UpdaterModule {
31693
31936
  }
31694
31937
  // src/cli/main.ts
31695
31938
  function readVersion5() {
31696
- const here = dirname17(fileURLToPath6(import.meta.url));
31939
+ const here = dirname18(fileURLToPath6(import.meta.url));
31697
31940
  const candidates = [
31698
31941
  join45(here, "..", "..", "package.json"),
31699
31942
  join45(here, "..", "package.json")
31700
31943
  ];
31701
31944
  for (const p of candidates) {
31702
- if (existsSync49(p)) {
31945
+ if (existsSync50(p)) {
31703
31946
  try {
31704
31947
  const raw = JSON.parse(readFileSync33(p, "utf8"));
31705
31948
  if (raw.version)
@@ -31711,9 +31954,19 @@ function readVersion5() {
31711
31954
  }
31712
31955
  function startAutoUpdate(config) {
31713
31956
  try {
31714
- const module = new UpdaterModule(config.updater, readVersion5(), "micro-models-agent");
31957
+ const module = new UpdaterModule(config.updater, readVersion5(), "micro-models-agent", {
31958
+ info: (m) => process.stderr.write(pc2.dim(m) + `
31959
+ `),
31960
+ warn: (m) => process.stderr.write(pc2.yellow(m) + `
31961
+ `),
31962
+ error: (m) => process.stderr.write(pc2.red(m) + `
31963
+ `)
31964
+ });
31715
31965
  module.start();
31716
- } catch {}
31966
+ return module;
31967
+ } catch {
31968
+ return;
31969
+ }
31717
31970
  }
31718
31971
  async function main() {
31719
31972
  const program2 = createProgram();
@@ -31731,7 +31984,7 @@ async function main() {
31731
31984
  if (program2.args.length > 0) {
31732
31985
  const prompt = program2.args.join(" ");
31733
31986
  const { agent, config } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
31734
- startAutoUpdate(config);
31987
+ const updater = exitOnComplete ? undefined : startAutoUpdate(config);
31735
31988
  if (jsonMode) {
31736
31989
  const result2 = await agent.run(prompt);
31737
31990
  agent.shutdown();
@@ -31748,6 +32001,7 @@ async function main() {
31748
32001
  }, null, 2));
31749
32002
  process.stdout.write(`
31750
32003
  `);
32004
+ await updater?.waitForIdle();
31751
32005
  process.exit(result2.success ? 0 : 1);
31752
32006
  }
31753
32007
  const renderer = new Renderer({
@@ -31770,10 +32024,11 @@ async function main() {
31770
32024
  renderer.flush();
31771
32025
  const exitCode = printRunResult(result, () => renderer.flush());
31772
32026
  agent.shutdown();
32027
+ await updater?.waitForIdle();
31773
32028
  process.exit(exitCode);
31774
32029
  } else {
31775
32030
  const configPath = join45(homedir18(), ".mma", "config.json");
31776
- if (!existsSync49(configPath)) {
32031
+ if (!existsSync50(configPath)) {
31777
32032
  console.log(pc2.yellow(`
31778
32033
  ` + t("cli.first_run") + `
31779
32034
  `));