micro-models-agent 0.40.0 → 0.41.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.
Files changed (2) hide show
  1. package/dist/main.js +322 -62
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -2072,11 +2072,14 @@ var init_security = __esm(() => {
2072
2072
  });
2073
2073
 
2074
2074
  // src/modules/lsp/config.ts
2075
- function getServerForFile(filePath, config) {
2075
+ function getLanguageForFile(filePath) {
2076
2076
  const ext = filePath.split(".").pop()?.toLowerCase();
2077
2077
  if (!ext)
2078
2078
  return null;
2079
- const lang = LANGUAGE_MAP[ext];
2079
+ return LANGUAGE_MAP[ext] ?? null;
2080
+ }
2081
+ function getServerForFile(filePath, config) {
2082
+ const lang = getLanguageForFile(filePath);
2080
2083
  if (!lang)
2081
2084
  return null;
2082
2085
  return config.servers[lang] ?? null;
@@ -2122,22 +2125,40 @@ var init_config = __esm(() => {
2122
2125
  },
2123
2126
  json: {
2124
2127
  command: "npx",
2125
- args: ["vscode-json-languageserver", "--stdio"],
2126
- timeout: 1e4,
2128
+ args: [
2129
+ "--yes",
2130
+ "--package",
2131
+ "vscode-langservers-extracted",
2132
+ "vscode-json-language-server",
2133
+ "--stdio"
2134
+ ],
2135
+ timeout: 30000,
2127
2136
  autoInstall: true,
2128
2137
  workspaceMarkers: ["package.json"]
2129
2138
  },
2130
2139
  html: {
2131
2140
  command: "npx",
2132
- args: ["vscode-html-languageserver", "--stdio"],
2133
- timeout: 1e4,
2141
+ args: [
2142
+ "--yes",
2143
+ "--package",
2144
+ "vscode-langservers-extracted",
2145
+ "vscode-html-language-server",
2146
+ "--stdio"
2147
+ ],
2148
+ timeout: 60000,
2134
2149
  autoInstall: true,
2135
2150
  workspaceMarkers: ["package.json"]
2136
2151
  },
2137
2152
  css: {
2138
2153
  command: "npx",
2139
- args: ["vscode-css-languageserver", "--stdio"],
2140
- timeout: 20000,
2154
+ args: [
2155
+ "--yes",
2156
+ "--package",
2157
+ "vscode-langservers-extracted",
2158
+ "vscode-css-language-server",
2159
+ "--stdio"
2160
+ ],
2161
+ timeout: 60000,
2141
2162
  autoInstall: true,
2142
2163
  workspaceMarkers: ["package.json"]
2143
2164
  }
@@ -2449,6 +2470,8 @@ Summary:
2449
2470
  Command: {command}`,
2450
2471
  "proc.promoted_hint": "Command still running after {ms} ms — moved to the background",
2451
2472
  "proc.manage_hint": "Check output: process_log id={id}. Stop it: process_kill id={id}. List all: process_list.",
2473
+ "proc.output_preview": `First output:
2474
+ {lines}`,
2452
2475
  "proc.none": "No background processes running.",
2453
2476
  "proc.not_found": "Process not found: {id}",
2454
2477
  "proc.killed": "Process {id} (PID {pid}) killed.",
@@ -2697,6 +2720,10 @@ Available commands:`,
2697
2720
  "repl.skills_label": "Skills:",
2698
2721
  "repl.plugins_label": "Plugins:",
2699
2722
  "repl.mcp_label": "MCP:",
2723
+ "repl.lsp_label": "LSP:",
2724
+ "repl.lsp_timeout": "timeout",
2725
+ "repl.lsp_failed": "did not start",
2726
+ "repl.lsp_unknown": "unknown",
2700
2727
  "repl.work_dir": "Dir:",
2701
2728
  "repl.agents_label": "Instructions:",
2702
2729
  "repl.not_found": "not found",
@@ -2860,6 +2887,7 @@ Use this knowledge to answer the user's question.`,
2860
2887
  "ui.success_prefix": "✓ ",
2861
2888
  "ui.warning_prefix": "⚠ ",
2862
2889
  "ui.thinking": "Thinking…",
2890
+ "ui.step_context": "step {id}: {desc}",
2863
2891
  "indexer.map_header": "Project map",
2864
2892
  "indexer.top_directories": "Top directories",
2865
2893
  "indexer.files": "Files",
@@ -3075,6 +3103,8 @@ var init_ru = __esm(() => {
3075
3103
  Команда: {command}`,
3076
3104
  "proc.promoted_hint": "Команда всё ещё выполняется через {ms} мс — переведена в фоновый режим",
3077
3105
  "proc.manage_hint": "Проверить вывод: process_log id={id}. Остановить: process_kill id={id}. Список всех: process_list.",
3106
+ "proc.output_preview": `Первые строки вывода:
3107
+ {lines}`,
3078
3108
  "proc.none": "Фоновых процессов нет.",
3079
3109
  "proc.not_found": "Процесс не найден: {id}",
3080
3110
  "proc.killed": "Процесс {id} (PID {pid}) остановлен.",
@@ -3319,6 +3349,10 @@ var init_ru = __esm(() => {
3319
3349
  "repl.skills_label": "Скиллы:",
3320
3350
  "repl.plugins_label": "Плагины:",
3321
3351
  "repl.mcp_label": "MCP:",
3352
+ "repl.lsp_label": "LSP:",
3353
+ "repl.lsp_timeout": "таймаут",
3354
+ "repl.lsp_failed": "не стартовал",
3355
+ "repl.lsp_unknown": "неизвестно",
3322
3356
  "repl.work_dir": "Директория:",
3323
3357
  "repl.agents_label": "Инструкции:",
3324
3358
  "repl.not_found": "не найден",
@@ -3486,6 +3520,7 @@ var init_ru = __esm(() => {
3486
3520
  "ui.success_prefix": "✓ ",
3487
3521
  "ui.warning_prefix": "⚠ ",
3488
3522
  "ui.thinking": "Думаю…",
3523
+ "ui.step_context": "шаг {id}: {desc}",
3489
3524
  "indexer.map_header": "Карта проекта",
3490
3525
  "indexer.top_directories": "Основные директории",
3491
3526
  "indexer.files": "Файлы",
@@ -7628,7 +7663,7 @@ Hint: ${t("exec.npm_exec_hint")}`;
7628
7663
  }
7629
7664
  return output;
7630
7665
  }
7631
- 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;
7666
+ var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, BG_OUTPUT_PREVIEW_LINES = 15, 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;
7632
7667
  var init_bash = __esm(() => {
7633
7668
  init_command_validator();
7634
7669
  init_audit_log();
@@ -7829,15 +7864,21 @@ Hint: "${originalFirstWord}" may not work on Windows. ${UNIX_TO_WIN_HINTS[origin
7829
7864
  return { success: code === 0, output: output2 };
7830
7865
  }
7831
7866
  const explicit = args.background === true;
7832
- const output = `${t("proc.started", {
7867
+ const preview = entry.log.filter((l) => l.trim() !== "").slice(0, BG_OUTPUT_PREVIEW_LINES).join(`
7868
+ `);
7869
+ let output = `${t("proc.started", {
7833
7870
  id: entry.id,
7834
7871
  pid: entry.pid,
7835
7872
  command
7836
7873
  })}${explicit ? "" : `
7837
- ${t("proc.promoted_hint", { ms: settleMs })}`}
7838
- ${t("proc.manage_hint", {
7839
- id: entry.id
7840
- })}`;
7874
+ ${t("proc.promoted_hint", { ms: settleMs })}`}`;
7875
+ if (preview) {
7876
+ output += `
7877
+
7878
+ ${t("proc.output_preview", { lines: preview })}`;
7879
+ }
7880
+ output += `
7881
+ ${t("proc.manage_hint", { id: entry.id })}`;
7841
7882
  if (securityConfig?.logCommands) {
7842
7883
  logBashCommand(ctx.sessionId, sanitizeCommandForLog(command), true, `Started in background: ${entry.id} (PID ${entry.pid})`);
7843
7884
  }
@@ -18946,24 +18987,7 @@ class LspClient {
18946
18987
  async checkFile(filePath, baseDir, config, projectRoot = baseDir) {
18947
18988
  const timeout = config.timeout ?? DEFAULT_TIMEOUT;
18948
18989
  try {
18949
- await this.startServer(config, projectRoot);
18950
- const rootUri = this.pathToUri(projectRoot);
18951
- const initParams = {
18952
- processId: process.pid,
18953
- rootUri,
18954
- workspaceFolders: [{ uri: rootUri, name: "workspace" }],
18955
- capabilities: { textDocument: { publishDiagnostics: {} } }
18956
- };
18957
- try {
18958
- await this.sendRequest("initialize", initParams, timeout);
18959
- } catch (e) {
18960
- if (!(e instanceof Error) || !e.message.includes("initialize"))
18961
- throw e;
18962
- await this.shutdown();
18963
- await this.startServer(config, projectRoot);
18964
- await this.sendRequest("initialize", initParams, timeout);
18965
- }
18966
- this.initialized = true;
18990
+ await this.initialize(config, projectRoot, timeout);
18967
18991
  this.sendNotification("initialized", {});
18968
18992
  const uri = this.pathToUri(resolve20(filePath));
18969
18993
  const fs2 = await import("fs");
@@ -18991,6 +19015,33 @@ class LspClient {
18991
19015
  await this.shutdown();
18992
19016
  }
18993
19017
  }
19018
+ async probe(config, projectRoot, timeout) {
19019
+ try {
19020
+ await this.initialize(config, projectRoot, timeout);
19021
+ } finally {
19022
+ await this.shutdown();
19023
+ }
19024
+ }
19025
+ async initialize(config, projectRoot, timeout) {
19026
+ await this.startServer(config, projectRoot);
19027
+ const rootUri = this.pathToUri(projectRoot);
19028
+ const initParams = {
19029
+ processId: process.pid,
19030
+ rootUri,
19031
+ workspaceFolders: [{ uri: rootUri, name: "workspace" }],
19032
+ capabilities: { textDocument: { publishDiagnostics: {} } }
19033
+ };
19034
+ try {
19035
+ await this.sendRequest("initialize", initParams, timeout);
19036
+ } catch (e) {
19037
+ if (!(e instanceof Error) || !e.message.includes("initialize"))
19038
+ throw e;
19039
+ await this.shutdown();
19040
+ await this.startServer(config, projectRoot);
19041
+ await this.sendRequest("initialize", initParams, timeout);
19042
+ }
19043
+ this.initialized = true;
19044
+ }
18994
19045
  async startServer(config, projectRoot) {
18995
19046
  if (config.autoInstall === false) {
18996
19047
  try {
@@ -31516,8 +31567,8 @@ class LineEditor {
31516
31567
  }
31517
31568
 
31518
31569
  // src/cli/repl.ts
31519
- import { existsSync as existsSync52, readFileSync as readFileSync33, writeFileSync as writeFileSync17 } from "fs";
31520
- import { join as join46, dirname as dirname18 } from "path";
31570
+ import { existsSync as existsSync53, readFileSync as readFileSync34, writeFileSync as writeFileSync17 } from "fs";
31571
+ import { join as join47, dirname as dirname18 } from "path";
31521
31572
  import { homedir as homedir17 } from "os";
31522
31573
  import { fileURLToPath as fileURLToPath6 } from "url";
31523
31574
 
@@ -31926,13 +31977,14 @@ class Renderer {
31926
31977
  thinkingEnd() {
31927
31978
  this.spinner.stop();
31928
31979
  }
31929
- toolStart(tool, args) {
31980
+ toolStart(tool, args, stepContext) {
31930
31981
  this.endCard();
31931
31982
  this.spinner.stop();
31932
31983
  const summary = summarizeArgs2(args);
31984
+ const step = stepContext ? ` ${pc2.cyan(`← ${stepContext}`)}` : "";
31933
31985
  if (!this.rich) {
31934
31986
  this.out.write(`
31935
- ${pc2.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
31987
+ ${pc2.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}${step}
31936
31988
  `);
31937
31989
  return;
31938
31990
  }
@@ -31940,11 +31992,19 @@ ${pc2.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
31940
31992
  if (this.toolStyle === "inline") {
31941
31993
  const marker = toolMarker(tool);
31942
31994
  this.out.write(`
31943
- ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
31995
+ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}${step}
31944
31996
  `);
31945
31997
  return;
31946
31998
  }
31947
- this.spinner.start(`${pc2.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}`);
31999
+ this.spinner.start(`${pc2.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}${step}`);
32000
+ }
32001
+ planBlock(lines) {
32002
+ this.endCard();
32003
+ this.spinner.stop();
32004
+ for (const line of lines) {
32005
+ this.out.write(`${line}
32006
+ `);
32007
+ }
31948
32008
  }
31949
32009
  toolEnd(_tool, duration, error, ctxDelta) {
31950
32010
  this.spinner.stop();
@@ -32020,13 +32080,170 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
32020
32080
  init_box();
32021
32081
  init_i18n();
32022
32082
  init_repl_commands();
32083
+
32084
+ // src/modules/lsp/probe.ts
32085
+ init_client2();
32086
+ init_config();
32087
+ init_check_tool();
32088
+ init_project_root();
32089
+ var DEFAULT_PROBE_TIMEOUT_MS = 20000;
32090
+ async function probeLspServers(config, baseDir, deps = {}) {
32091
+ if (!config.enabled)
32092
+ return { ok: [], failed: [], noneApplicable: true };
32093
+ const client = deps.client ?? new LspClient;
32094
+ const timeoutMs = deps.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
32095
+ const collect = deps.collectFiles ?? collectCheckFiles;
32096
+ const files = await collect(baseDir, config);
32097
+ const seen = new Set;
32098
+ const servers = [];
32099
+ for (const file of files) {
32100
+ const language = getLanguageForFile(file);
32101
+ if (!language)
32102
+ continue;
32103
+ const server = config.servers[language];
32104
+ if (!server || seen.has(language))
32105
+ continue;
32106
+ seen.add(language);
32107
+ servers.push({ language, config: server });
32108
+ }
32109
+ if (servers.length === 0)
32110
+ return { ok: [], failed: [], noneApplicable: true };
32111
+ const results = await Promise.all(servers.map(async ({ language, config: server }) => {
32112
+ const projectRoot = findProjectRoot(baseDir, baseDir, server.workspaceMarkers ?? []);
32113
+ const started = Date.now();
32114
+ try {
32115
+ await client.probe(server, projectRoot, timeoutMs);
32116
+ return { language, ok: true, durationMs: Date.now() - started };
32117
+ } catch (e) {
32118
+ return {
32119
+ language,
32120
+ ok: false,
32121
+ error: e instanceof Error ? e.message : String(e),
32122
+ durationMs: Date.now() - started
32123
+ };
32124
+ }
32125
+ }));
32126
+ return {
32127
+ ok: results.filter((r) => r.ok),
32128
+ failed: results.filter((r) => !r.ok),
32129
+ noneApplicable: false
32130
+ };
32131
+ }
32132
+
32133
+ // src/cli/repl.ts
32134
+ init_config();
32135
+
32136
+ // src/ui/plan-view.ts
32137
+ init_colors();
32138
+ init_js_identifiers();
32139
+ init_i18n();
32140
+ import { existsSync as existsSync52, readFileSync as readFileSync33 } from "fs";
32141
+ import { join as join46 } from "path";
32142
+ function readActivePlan(baseDir) {
32143
+ const p = join46(baseDir, ".mma", "plans", "active.json");
32144
+ if (!existsSync52(p))
32145
+ return null;
32146
+ try {
32147
+ const raw = readFileSync33(p, "utf-8");
32148
+ if (!raw.trim())
32149
+ return null;
32150
+ const parsed = JSON.parse(raw);
32151
+ if (!parsed || !Array.isArray(parsed.steps))
32152
+ return null;
32153
+ return {
32154
+ id: parsed.id || "plan",
32155
+ title: parsed.title || "Plan",
32156
+ steps: parsed.steps,
32157
+ createdAt: parsed.createdAt || new Date().toISOString(),
32158
+ baseDir: parsed.baseDir || baseDir,
32159
+ name: parsed.name
32160
+ };
32161
+ } catch {
32162
+ return null;
32163
+ }
32164
+ }
32165
+ function currentStepIndex(plan) {
32166
+ const idx = plan.steps.findIndex((s) => s.status !== "done" && s.status !== "skipped");
32167
+ return idx === -1 ? 0 : idx;
32168
+ }
32169
+ function stepMark(step) {
32170
+ switch (step.status) {
32171
+ case "done":
32172
+ return pc2.green("[x]");
32173
+ case "in_progress":
32174
+ return pc2.yellow("[*]");
32175
+ case "failed":
32176
+ return pc2.red("[!]");
32177
+ case "skipped":
32178
+ return pc2.dim("[-]");
32179
+ default:
32180
+ return pc2.dim("[ ]");
32181
+ }
32182
+ }
32183
+ function formatPlanChecklist(plan) {
32184
+ const done = plan.steps.filter((s) => s.status === "done" || s.status === "skipped").length;
32185
+ const total = plan.steps.length;
32186
+ const pct = total === 0 ? 0 : Math.round(done / total * 100);
32187
+ const cur = currentStepIndex(plan);
32188
+ const barLen = 15;
32189
+ const filled = Math.round(pct / 100 * barLen);
32190
+ const bar = pc2.green("█".repeat(filled)) + pc2.dim("░".repeat(barLen - filled));
32191
+ const title = plan.title.replace(/^\[\d+[^]]*\]\s*/, "");
32192
+ const lines = [
32193
+ `${pc2.cyan(`[${plan.id}]`)} ${pc2.bold(title)} ${bar} ${pc2.dim(`${done}/${total} ${pct}%`)}`
32194
+ ];
32195
+ for (let i = 0;i < plan.steps.length; i++) {
32196
+ const step = plan.steps[i];
32197
+ const active = i === cur;
32198
+ const desc = active ? pc2.yellow(step.description) : pc2.dim(step.description);
32199
+ lines.push(` ${stepMark(step)} ${step.id}. ${desc}`);
32200
+ for (const sub of step.subtasks ?? []) {
32201
+ lines.push(` ${sub.done ? pc2.green("[x]") : pc2.dim("[ ]")} ${sub.text}`);
32202
+ }
32203
+ }
32204
+ return lines;
32205
+ }
32206
+ var STRUCTURAL_TOOLS = new Set([
32207
+ "plan",
32208
+ "todo",
32209
+ "verify",
32210
+ "project_map",
32211
+ "lsp_check",
32212
+ "enable_tools"
32213
+ ]);
32214
+ function truncate3(s, max = 48) {
32215
+ return s.length > max ? `${s.slice(0, max - 1)}…` : s;
32216
+ }
32217
+ function stepContextForTool(plan, tool, args) {
32218
+ if (!plan || STRUCTURAL_TOOLS.has(tool))
32219
+ return null;
32220
+ const argStr = JSON.stringify(args);
32221
+ const callPaths = extractFileLikeTokens(stripUrls(argStr)).map((p) => p.toLowerCase());
32222
+ if (callPaths.length > 0) {
32223
+ for (const step2 of plan.steps) {
32224
+ if (step2.status === "done" || step2.status === "skipped")
32225
+ continue;
32226
+ const stepPaths = extractFileLikeTokens(stripUrls(step2.description)).map((p) => p.toLowerCase());
32227
+ if (stepPaths.length > 0 && stepPaths.some((s) => callPaths.some((c) => c.includes(s) || s.includes(c)))) {
32228
+ return t("ui.step_context", { id: step2.id, desc: truncate3(step2.description) });
32229
+ }
32230
+ }
32231
+ }
32232
+ const cur = currentStepIndex(plan);
32233
+ const step = plan.steps[cur];
32234
+ if (!step)
32235
+ return null;
32236
+ return t("ui.step_context", { id: step.id, desc: truncate3(step.description) });
32237
+ }
32238
+
32239
+ // src/cli/repl.ts
32023
32240
  function readVersion4() {
32024
32241
  const here = dirname18(fileURLToPath6(import.meta.url));
32025
- const candidates = [join46(here, "..", "..", "package.json"), join46(here, "..", "package.json")];
32242
+ const candidates = [join47(here, "..", "..", "package.json"), join47(here, "..", "package.json")];
32026
32243
  for (const p of candidates) {
32027
- if (existsSync52(p)) {
32244
+ if (existsSync53(p)) {
32028
32245
  try {
32029
- const raw = JSON.parse(readFileSync33(p, "utf8"));
32246
+ const raw = JSON.parse(readFileSync34(p, "utf8"));
32030
32247
  if (raw.version)
32031
32248
  return raw.version;
32032
32249
  } catch {}
@@ -32067,6 +32284,8 @@ class Repl {
32067
32284
  noAgentsMd;
32068
32285
  pendingClipboardImage = null;
32069
32286
  exitOnClose = false;
32287
+ activePlan = null;
32288
+ lastPlanSig = "";
32070
32289
  rl;
32071
32290
  agent;
32072
32291
  config;
@@ -32082,10 +32301,10 @@ class Repl {
32082
32301
  this.skillsModule = skillsModule;
32083
32302
  this.pluginManager = pluginManager;
32084
32303
  this.logger = logger;
32085
- this.configDir = configDir || join46(homedir17(), ".mma");
32304
+ this.configDir = configDir || join47(homedir17(), ".mma");
32086
32305
  this.baseDir = baseDir || process.cwd();
32087
32306
  this.noAgentsMd = noAgentsMd === true;
32088
- this.historyPath = join46(homedir17(), ".mma", "repl-history");
32307
+ this.historyPath = join47(homedir17(), ".mma", "repl-history");
32089
32308
  this.loadHistory();
32090
32309
  this.rl = process.stdin.isTTY ? new LineEditor({
32091
32310
  input: process.stdin,
@@ -32118,9 +32337,9 @@ class Repl {
32118
32337
  this.setupListeners();
32119
32338
  }
32120
32339
  loadHistory() {
32121
- if (existsSync52(this.historyPath)) {
32340
+ if (existsSync53(this.historyPath)) {
32122
32341
  try {
32123
- const raw = readFileSync33(this.historyPath, "utf-8");
32342
+ const raw = readFileSync34(this.historyPath, "utf-8");
32124
32343
  this.history = raw.split(`
32125
32344
  `).filter(Boolean).slice(-this.maxHistory);
32126
32345
  } catch {
@@ -32319,11 +32538,15 @@ ${t("image.clipboard_empty")}`));
32319
32538
  spinner: this.config.ui?.spinner ?? true,
32320
32539
  toolStyle: this.config.ui?.toolStyle ?? "inline"
32321
32540
  });
32541
+ this.refreshActivePlan(renderer);
32322
32542
  const result = await this.agent.run(input, (c) => renderer.text(c), (m) => renderer.meta(m), (ev) => {
32323
32543
  if (ev.type === "start") {
32324
- renderer.toolStart(ev.tool, ev.args);
32544
+ renderer.toolStart(ev.tool, ev.args, stepContextForTool(this.activePlan, ev.tool, ev.args));
32325
32545
  } else {
32326
32546
  renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta);
32547
+ if (ev.tool === "plan" || ev.tool === "todo") {
32548
+ this.refreshActivePlan(renderer);
32549
+ }
32327
32550
  }
32328
32551
  }, (phase) => {
32329
32552
  if (phase === "thinking") {
@@ -32347,6 +32570,20 @@ ${t("image.clipboard_empty")}`));
32347
32570
  this.agentRunning = false;
32348
32571
  }
32349
32572
  }
32573
+ refreshActivePlan(renderer) {
32574
+ const plan = readActivePlan(this.baseDir);
32575
+ const sig = plan ? JSON.stringify(plan.steps.map((s) => `${s.id}:${s.status}`)) : "";
32576
+ if (!plan) {
32577
+ this.activePlan = null;
32578
+ this.lastPlanSig = "";
32579
+ return;
32580
+ }
32581
+ this.activePlan = plan;
32582
+ if (sig !== this.lastPlanSig) {
32583
+ this.lastPlanSig = sig;
32584
+ renderer.planBlock(formatPlanChecklist(plan));
32585
+ }
32586
+ }
32350
32587
  commands = new Map;
32351
32588
  registerCommand(cmd) {
32352
32589
  this.commands.set(cmd.name, cmd);
@@ -32433,7 +32670,7 @@ ${t("image.clipboard_empty")}`));
32433
32670
  }
32434
32671
  }
32435
32672
  }
32436
- start() {
32673
+ async start() {
32437
32674
  this.running = true;
32438
32675
  const info = [];
32439
32676
  const row = (label, value) => {
@@ -32469,17 +32706,21 @@ ${t("image.clipboard_empty")}`));
32469
32706
  const names = enabledServers.map(([name]) => name).join(", ");
32470
32707
  row(t("repl.mcp_label"), `${pc2.white(String(enabledServers.length))} ${pc2.dim(`(${names})`)}`);
32471
32708
  }
32709
+ const lspSummary = await this.probeLspBanner();
32710
+ if (lspSummary) {
32711
+ row(t("repl.lsp_label"), lspSummary);
32712
+ }
32472
32713
  const cwd = process.cwd();
32473
32714
  row(t("repl.work_dir"), pc2.dim(cwd));
32474
32715
  if (this.noAgentsMd) {
32475
32716
  row(t("repl.agents_label"), pc2.red(t("repl.disabled")));
32476
32717
  } else {
32477
32718
  const agentsMdCandidates = [
32478
- join46(this.baseDir, "AGENTS.md"),
32479
- join46(this.baseDir, ".mma", "AGENTS.md"),
32480
- join46(this.configDir, "AGENTS.md")
32719
+ join47(this.baseDir, "AGENTS.md"),
32720
+ join47(this.baseDir, ".mma", "AGENTS.md"),
32721
+ join47(this.configDir, "AGENTS.md")
32481
32722
  ];
32482
- const foundAgents = agentsMdCandidates.filter((p) => existsSync52(p));
32723
+ const foundAgents = agentsMdCandidates.filter((p) => existsSync53(p));
32483
32724
  if (foundAgents.length > 0) {
32484
32725
  for (const p of foundAgents) {
32485
32726
  row(t("repl.agents_label"), pc2.dim(p));
@@ -32490,7 +32731,7 @@ ${t("image.clipboard_empty")}`));
32490
32731
  }
32491
32732
  const meta = this.sessionManager?.getActiveMeta();
32492
32733
  if (meta) {
32493
- const sessionPath = join46(this.configDir, "sessions", meta.id);
32734
+ const sessionPath = join47(this.configDir, "sessions", meta.id);
32494
32735
  row(t("repl.session_label"), `${pc2.cyan(meta.name)} ${pc2.dim(`(${meta.id.slice(0, 12)})`)} — ${meta.messageCount} msgs ${pc2.dim(sessionPath)}`);
32495
32736
  }
32496
32737
  const headerWidth = Math.max(50, Math.min(96, process.stdout.columns || 96));
@@ -32503,6 +32744,25 @@ ${t("image.clipboard_empty")}`));
32503
32744
  console.log();
32504
32745
  this.rl.prompt();
32505
32746
  }
32747
+ async probeLspBanner() {
32748
+ const config = this.config.lsp ?? DEFAULT_LSP_CONFIG;
32749
+ try {
32750
+ const summary = await probeLspServers(config, this.baseDir);
32751
+ if (summary.noneApplicable)
32752
+ return null;
32753
+ const parts = [];
32754
+ for (const r of summary.ok) {
32755
+ parts.push(pc2.green(`✓ ${r.language}${pc2.dim(` (${(r.durationMs / 1000).toFixed(1)}s)`)}`));
32756
+ }
32757
+ for (const r of summary.failed) {
32758
+ const reason = r.error?.includes("timeout") ? t("repl.lsp_timeout") : t("repl.lsp_failed");
32759
+ parts.push(pc2.red(`✗ ${r.language}${pc2.dim(` — ${reason}`)}`));
32760
+ }
32761
+ return parts.join(" ");
32762
+ } catch {
32763
+ return pc2.yellow(t("repl.lsp_unknown"));
32764
+ }
32765
+ }
32506
32766
  stop() {
32507
32767
  this.running = false;
32508
32768
  this.saveHistory();
@@ -32531,8 +32791,8 @@ init_setup();
32531
32791
  init_config2();
32532
32792
  init_i18n();
32533
32793
  init_colors();
32534
- import { existsSync as existsSync53, readFileSync as readFileSync34 } from "fs";
32535
- import { join as join47, dirname as dirname19 } from "path";
32794
+ import { existsSync as existsSync54, readFileSync as readFileSync35 } from "fs";
32795
+ import { join as join48, dirname as dirname19 } from "path";
32536
32796
  import { homedir as homedir18 } from "os";
32537
32797
  import { fileURLToPath as fileURLToPath7 } from "url";
32538
32798
 
@@ -32636,11 +32896,11 @@ class UpdaterModule {
32636
32896
  // src/cli/main.ts
32637
32897
  function readVersion5() {
32638
32898
  const here = dirname19(fileURLToPath7(import.meta.url));
32639
- const candidates = [join47(here, "..", "..", "package.json"), join47(here, "..", "package.json")];
32899
+ const candidates = [join48(here, "..", "..", "package.json"), join48(here, "..", "package.json")];
32640
32900
  for (const p of candidates) {
32641
- if (existsSync53(p)) {
32901
+ if (existsSync54(p)) {
32642
32902
  try {
32643
- const raw = JSON.parse(readFileSync34(p, "utf8"));
32903
+ const raw = JSON.parse(readFileSync35(p, "utf8"));
32644
32904
  if (raw.version)
32645
32905
  return raw.version;
32646
32906
  } catch {}
@@ -32723,15 +32983,15 @@ async function main() {
32723
32983
  await updater?.waitForIdle();
32724
32984
  process.exit(exitCode);
32725
32985
  } else {
32726
- const configPath = join47(homedir18(), ".mma", "config.json");
32727
- if (!existsSync53(configPath)) {
32986
+ const configPath = join48(homedir18(), ".mma", "config.json");
32987
+ if (!existsSync54(configPath)) {
32728
32988
  console.log(pc2.yellow(`
32729
32989
  ` + t("cli.first_run") + `
32730
32990
  `));
32731
32991
  const answers = await runSetup();
32732
32992
  const config2 = loadConfig({
32733
- configDir: join47(homedir18(), ".mma"),
32734
- projectConfigPath: projectDir ? join47(projectDir, ".mmrc") : join47(process.cwd(), ".mmrc")
32993
+ configDir: join48(homedir18(), ".mma"),
32994
+ projectConfigPath: projectDir ? join48(projectDir, ".mmrc") : join48(process.cwd(), ".mmrc")
32735
32995
  });
32736
32996
  config2.provider.type = answers.provider;
32737
32997
  config2.provider.baseUrl = answers.apiBase;
@@ -32785,7 +33045,7 @@ async function main() {
32785
33045
  } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
32786
33046
  startAutoUpdate(config);
32787
33047
  const repl = new Repl(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger, true);
32788
- repl.start();
33048
+ await repl.start();
32789
33049
  }
32790
33050
  }
32791
33051
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "micro-models-agent",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
4
4
  "description": "Micro Models Agent (MMA) — LLM agent harness for small models (Qwen3.5-9B, 32K-64K context)",
5
5
  "type": "module",
6
6
  "bin": {