micro-models-agent 0.37.0 → 0.39.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 +813 -426
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -2393,6 +2393,7 @@ var init_en = __esm(() => {
2393
2393
  "tool.friendly.search_history": "Searching history",
2394
2394
  "tool.friendly.pipeline_run": "Running pipeline",
2395
2395
  "tool.friendly.mcp_call": "MCP call",
2396
+ "tool.friendly.lsp_check": "Checking code",
2396
2397
  "tool.truncated": "[Truncated: {tokens} tokens removed]",
2397
2398
  "tool.subagent_queued": "Sub-agent task queued: {task}",
2398
2399
  "tool.subagent_artifact": `Sub-agent completed. Full result saved to artifact: {path}
@@ -2485,6 +2486,9 @@ Command: {command}`,
2485
2486
  "plan.switched": "Switched to plan {id}: {title}",
2486
2487
  "plan.replanned": "Plan re-planned: {kept} completed steps kept, {steps} new steps added",
2487
2488
  "plan.replan_no_steps": "Provide new steps for re-planning",
2489
+ "plan.active_in_progress": 'Cannot create a new plan: active plan {id} already has progress ({done}/{total} done, current: step {current}). Resume it — use "plan show" to view, then continue working. To replace it, call "plan abort" first, then "plan create" again.',
2490
+ "plan.id_ignored": 'note: plan ids are auto-generated; use "plan switch" to activate an existing plan by id.',
2491
+ "plan.existing_fresh": "note: the previous active plan had no progress and was preserved as a draft.",
2488
2492
  "todo.added": "Added {count} todo(s): {items}",
2489
2493
  "todo.marked_done": "Marked {count} item(s) as done",
2490
2494
  "todo.no_active": "No active todos",
@@ -2500,6 +2504,11 @@ Command: {command}`,
2500
2504
  "verify.script_failed": "Script '{script}' failed: {message}",
2501
2505
  "verify.syntax_error": "Syntax error in: {path}",
2502
2506
  "lsp.unavailable": "static checks are unavailable (server not found). Verify via the project's own build/test instead.",
2507
+ "lsp.check_disabled": "LSP checks are disabled in the config.",
2508
+ "lsp.check_no_path": "Provide a path (file or directory) to check.",
2509
+ "lsp.check_notfound": "Path not found: {path}",
2510
+ "lsp.check_unsupported": "No LSP server configured for: {path}",
2511
+ "lsp.check_clean": "No errors or warnings detected ({count} file(s) checked).",
2503
2512
  "cli.description": "Micro Models Agent — AI coding agent for small models",
2504
2513
  "cli.init": "Run interactive setup wizard",
2505
2514
  "cli.config_saved": "Configuration saved to ~/.mma/config.json",
@@ -2613,6 +2622,13 @@ Available commands:`,
2613
2622
  "cli.security.recommended_for": "Recommended for",
2614
2623
  "cli.yes": "Yes",
2615
2624
  "cli.no": "No",
2625
+ "cli.plugins.description": "Manage plugins",
2626
+ "cli.plugins.list": "List loaded plugins (name, version, source)",
2627
+ "cli.plugins.all": "Include builtin plugins",
2628
+ "cli.plugins.only_external": "(builtin plugins omitted — use --all to show them)",
2629
+ "cli.plugins.none": "No plugins loaded",
2630
+ "cli.plugins.header": "Plugins: {count} loaded (MMA v{mma})",
2631
+ "cli.plugins.builtin_mark": "●",
2616
2632
  "repl.help": "Show available commands",
2617
2633
  "repl.help_usage": "Usage: /help",
2618
2634
  "repl.exit": "Exit the REPL",
@@ -2822,6 +2838,8 @@ Use this knowledge to answer the user's question.`,
2822
2838
  "pipeline.circular": "Circular dependency: {stepId}",
2823
2839
  "plugin.loaded": "Loaded plugin: {name}",
2824
2840
  "plugin.skipped": "Skipped incompatible plugin: {entry} ({message})",
2841
+ "plugin.incompatible": "Skipped plugin {name}: requires MMA v{min}, current is v{current}",
2842
+ "plugin.dedup_older": "Plugin {name} v{version} skipped: a newer version is already loaded",
2825
2843
  "migration.detected": "[MMA] Detected old config. {summary}",
2826
2844
  "migration.summary": "[MMA] {summary}",
2827
2845
  "migration.config_bak": "- config → config.json.bak",
@@ -2992,6 +3010,7 @@ var init_ru = __esm(() => {
2992
3010
  "tool.friendly.search_history": "Поиск в истории",
2993
3011
  "tool.friendly.pipeline_run": "Запуск пайплайна",
2994
3012
  "tool.friendly.mcp_call": "MCP вызов",
3013
+ "tool.friendly.lsp_check": "Проверка кода",
2995
3014
  "tool.truncated": "[Обрезано: удалено {tokens} токенов]",
2996
3015
  "tool.subagent_queued": "Задача подагенту добавлена: {task}",
2997
3016
  "tool.subagent_artifact": `Суб-агент завершён. Полный результат сохранён в артефакт: {path}
@@ -3084,6 +3103,9 @@ var init_ru = __esm(() => {
3084
3103
  "plan.switched": "Переключено на план {id}: {title}",
3085
3104
  "plan.replanned": "План перепланирован: {kept} выполненных шагов сохранено, {steps} новых шагов добавлено",
3086
3105
  "plan.replan_no_steps": "Укажите новые шаги для перепланирования",
3106
+ "plan.active_in_progress": 'Нельзя создать новый план: активный план {id} уже имеет прогресс ({done}/{total} выполнено, текущий: шаг {current}). Продолжай его — вызови "plan show", чтобы увидеть, и продолжай работу. Чтобы заменить план, сначала вызови "plan abort", затем "plan create".',
3107
+ "plan.id_ignored": 'примечание: id плана генерируется автоматически; используй "plan switch", чтобы активировать существующий план по id.',
3108
+ "plan.existing_fresh": "примечание: предыдущий активный план не имел прогресса и сохранён как черновик.",
3087
3109
  "todo.added": "Добавлено {count} задач: {items}",
3088
3110
  "todo.marked_done": "Отмечено выполненными: {count}",
3089
3111
  "todo.no_active": "Нет активных задач",
@@ -3099,6 +3121,11 @@ var init_ru = __esm(() => {
3099
3121
  "verify.script_failed": "Скрипт '{script}' не пройден: {message}",
3100
3122
  "verify.syntax_error": "Синтаксическая ошибка в: {path}",
3101
3123
  "lsp.unavailable": "статические проверки недоступны (сервер не найден). Проверяйте через собственный build/test проекта.",
3124
+ "lsp.check_disabled": "LSP-проверки отключены в конфигурации.",
3125
+ "lsp.check_no_path": "Укажите путь (файл или каталог) для проверки.",
3126
+ "lsp.check_notfound": "Путь не найден: {path}",
3127
+ "lsp.check_unsupported": "Для файла не настроен LSP-сервер: {path}",
3128
+ "lsp.check_clean": "Ошибок и предупреждений не обнаружено (проверено файлов: {count}).",
3102
3129
  "cli.description": "Micro Models Agent — ИИ-агент для кодинга на малых моделях",
3103
3130
  "cli.init": "Запустить мастер настройки",
3104
3131
  "cli.config_saved": "Конфигурация сохранена в ~/.mma/config.json",
@@ -3208,6 +3235,13 @@ var init_ru = __esm(() => {
3208
3235
  "cli.security.recommended_for": "Рекомендуется для",
3209
3236
  "cli.yes": "Да",
3210
3237
  "cli.no": "Нет",
3238
+ "cli.plugins.description": "Управление плагинами",
3239
+ "cli.plugins.list": "Список загруженных плагинов (имя, версия, источник)",
3240
+ "cli.plugins.all": "Включить встроенные плагины",
3241
+ "cli.plugins.only_external": "(встроенные плагины скрыты — используйте --all)",
3242
+ "cli.plugins.none": "Плагины не загружены",
3243
+ "cli.plugins.header": "Плагины: {count} загружено (MMA v{mma})",
3244
+ "cli.plugins.builtin_mark": "●",
3211
3245
  "repl.help": "Показать доступные команды",
3212
3246
  "repl.help_usage": "Использование: /help",
3213
3247
  "repl.exit": "Выйти из REPL",
@@ -3421,6 +3455,8 @@ var init_ru = __esm(() => {
3421
3455
  "pipeline.circular": "Циклическая зависимость: {stepId}",
3422
3456
  "plugin.loaded": "Загружен плагин: {name}",
3423
3457
  "plugin.skipped": "Пропущен несовместимый плагин: {entry} ({message})",
3458
+ "plugin.incompatible": "Пропущен плагин {name}: требуется MMA v{min}, текущая v{current}",
3459
+ "plugin.dedup_older": "Плагин {name} v{version} пропущен: уже загружена более новая версия",
3424
3460
  "migration.detected": "[MMA] Обнаружен старый конфиг. {summary}",
3425
3461
  "migration.summary": "[MMA] {summary}",
3426
3462
  "migration.config_bak": "- config → config.json.bak",
@@ -11196,6 +11232,7 @@ class FactExtractor {
11196
11232
  deletedFacts = [];
11197
11233
  decisionFacts = [];
11198
11234
  errorFacts = [];
11235
+ readFacts = [];
11199
11236
  static MAX_FACTS = 20;
11200
11237
  get errorCount() {
11201
11238
  return this.errorFacts.length;
@@ -11211,13 +11248,19 @@ class FactExtractor {
11211
11248
  new RegExp(`Deleted ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
11212
11249
  new RegExp(`(?:Удалён|Файл удалён):? ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi")
11213
11250
  ];
11251
+ const readPatterns = [
11252
+ new RegExp(`── (${DRIVE}[\\w./\\\\-]+\\.[a-z]+) \\(`, "gi")
11253
+ ];
11214
11254
  const newFiles = [];
11215
11255
  const newDeleted = [];
11216
11256
  const newDecisions = [];
11217
11257
  const newErrors = [];
11258
+ const newReads = [];
11218
11259
  const lastEvent = new Map;
11219
11260
  for (const msg of turns) {
11220
11261
  const content = getMessageText(msg.content);
11262
+ if (msg.role === "system")
11263
+ continue;
11221
11264
  if (msg.role === "user" && (content.startsWith("<system-summary>") || content.includes("[Compressed:"))) {
11222
11265
  continue;
11223
11266
  }
@@ -11234,6 +11277,11 @@ class FactExtractor {
11234
11277
  lastEvent.set(match[1], "deleted");
11235
11278
  }
11236
11279
  }
11280
+ for (const pattern of readPatterns) {
11281
+ for (const match of content.matchAll(pattern)) {
11282
+ newReads.push(match[1]);
11283
+ }
11284
+ }
11237
11285
  if (content.includes("Plan:") && content.includes("[")) {
11238
11286
  const planLine = content.split(`
11239
11287
  `).find((line) => line.includes("Plan:"));
@@ -11249,11 +11297,13 @@ class FactExtractor {
11249
11297
  newDecisions.push(line.trim().slice(0, 200));
11250
11298
  }
11251
11299
  }
11252
- if (content.includes("Error:") || content.includes("failed") || content.includes("Ошибка:") || content.includes("не удалось")) {
11253
- const line = content.split(`
11300
+ if (msg.role === "tool" || msg.role === "assistant") {
11301
+ if (content.includes("Error:") || content.includes("failed") || content.includes("Ошибка:") || content.includes("не удалось")) {
11302
+ const line = content.split(`
11254
11303
  `).find((l) => l.includes("Error:") || l.includes("failed") || l.includes("Ошибка:") || l.includes("не удалось"));
11255
- if (line)
11256
- newErrors.push(line.trim().slice(0, 250));
11304
+ if (line)
11305
+ newErrors.push(line.trim().slice(0, 250));
11306
+ }
11257
11307
  }
11258
11308
  }
11259
11309
  const dedup = (arr) => [...new Set(arr)];
@@ -11263,6 +11313,7 @@ class FactExtractor {
11263
11313
  this.deletedFacts = dedup([...this.deletedFacts, ...finalDeleted].filter((p) => !finalFiles.includes(p))).slice(-FactExtractor.MAX_FACTS);
11264
11314
  this.decisionFacts = dedup([...this.decisionFacts, ...newDecisions]).slice(-FactExtractor.MAX_FACTS);
11265
11315
  this.errorFacts = dedup([...this.errorFacts, ...newErrors]).slice(-FactExtractor.MAX_FACTS);
11316
+ this.readFacts = dedup([...this.readFacts, ...newReads]).slice(-FactExtractor.MAX_FACTS);
11266
11317
  }
11267
11318
  filesLine() {
11268
11319
  const deletedSet = new Set(this.deletedFacts);
@@ -11286,6 +11337,11 @@ class FactExtractor {
11286
11337
  return null;
11287
11338
  return `[Errors: ${this.errorFacts.slice(-3).join("; ")}]`;
11288
11339
  }
11340
+ readLine() {
11341
+ if (this.readFacts.length === 0)
11342
+ return null;
11343
+ return `[Read: ${this.readFacts.slice(-12).join("; ")}]`;
11344
+ }
11289
11345
  }
11290
11346
  var init_fact_extractor = () => {};
11291
11347
 
@@ -11358,12 +11414,16 @@ class ContextManager {
11358
11414
  pendingImageParts = [];
11359
11415
  toolTokens = 0;
11360
11416
  onCompact = null;
11417
+ planSummaryProvider = null;
11361
11418
  constructor(contextWindow, contextBudget, tokenCounter) {
11362
11419
  this.contextWindow = contextWindow;
11363
11420
  this.compactionThreshold = contextBudget?.compactionThreshold ?? 0.75;
11364
11421
  this.budget = this.calculateBudget(contextWindow, contextBudget);
11365
11422
  this.tokenCounter = tokenCounter ?? null;
11366
11423
  }
11424
+ setPlanSummaryProvider(fn) {
11425
+ this.planSummaryProvider = fn;
11426
+ }
11367
11427
  calculateBudget(window, contextBudget) {
11368
11428
  if (contextBudget) {
11369
11429
  const systemPrompt = Math.floor(window * contextBudget.systemPrompt);
@@ -11516,12 +11576,18 @@ class ContextManager {
11516
11576
  if (this.lastUserFeedback && !task.startsWith(this.lastUserFeedback)) {
11517
11577
  parts.push(`[User feedback: ${this.lastUserFeedback}]`);
11518
11578
  }
11579
+ const planLine = this.planSummaryProvider?.();
11580
+ if (planLine)
11581
+ parts.push(`[Plan: ${planLine}]`);
11519
11582
  const filesLine = this.facts.filesLine();
11520
11583
  if (filesLine)
11521
11584
  parts.push(filesLine);
11522
11585
  const deletedLine = this.facts.deletedLine();
11523
11586
  if (deletedLine)
11524
11587
  parts.push(deletedLine);
11588
+ const readLine = this.facts.readLine();
11589
+ if (readLine)
11590
+ parts.push(readLine);
11525
11591
  const decisionsLine = this.facts.decisionsLine();
11526
11592
  if (decisionsLine)
11527
11593
  parts.push(decisionsLine);
@@ -12174,16 +12240,157 @@ var init_detector = __esm(() => {
12174
12240
  init_llm_judge();
12175
12241
  });
12176
12242
 
12243
+ // src/modules/lsp/command.ts
12244
+ import { delimiter, join as join13 } from "path";
12245
+ import { existsSync as existsSync22 } from "fs";
12246
+ import { platform as platform3 } from "os";
12247
+ function resolveSpawnCommand(command, platformName = platform3(), pathEnv = process.env.PATH ?? "") {
12248
+ if (platformName !== "win32")
12249
+ return command;
12250
+ if (command.includes("/") || command.includes("\\") || WIN_EXTS.some((ext) => command.toLowerCase().endsWith(ext))) {
12251
+ return command;
12252
+ }
12253
+ const dirs = pathEnv.split(delimiter).filter(Boolean);
12254
+ for (const dir of dirs) {
12255
+ for (const ext of WIN_EXTS) {
12256
+ const candidate = join13(dir, `${command}${ext}`);
12257
+ if (existsSync22(candidate))
12258
+ return `${command}${ext}`;
12259
+ }
12260
+ }
12261
+ return command;
12262
+ }
12263
+ function buildWinCommandLine(command, args) {
12264
+ const quote = (part) => {
12265
+ if (part === "")
12266
+ return '""';
12267
+ if (!/[\s"&|^<>()%!]/.test(part))
12268
+ return part;
12269
+ return `"${part.replace(/"/g, "\\\"")}"`;
12270
+ };
12271
+ return [command, ...args].map(quote).join(" ");
12272
+ }
12273
+ var WIN_EXTS;
12274
+ var init_command = __esm(() => {
12275
+ WIN_EXTS = [".cmd", ".bat", ".exe", ".com"];
12276
+ });
12277
+
12278
+ // src/modules/updater/checker.ts
12279
+ import { platform as platform4 } from "os";
12280
+ function semverGt(a, b) {
12281
+ const pa = a.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
12282
+ const pb = b.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
12283
+ for (let i = 0;i < 3; i++) {
12284
+ const na = pa[i] ?? 0;
12285
+ const nb = pb[i] ?? 0;
12286
+ if (na !== nb)
12287
+ return na > nb;
12288
+ }
12289
+ return false;
12290
+ }
12291
+
12292
+ class Updater {
12293
+ currentVersion;
12294
+ packageName;
12295
+ runner;
12296
+ constructor(currentVersion, packageName, runner) {
12297
+ this.currentVersion = currentVersion;
12298
+ this.packageName = packageName;
12299
+ this.runner = runner ?? defaultRunner;
12300
+ }
12301
+ getCurrentVersion() {
12302
+ return this.currentVersion;
12303
+ }
12304
+ getPackageName() {
12305
+ return this.packageName;
12306
+ }
12307
+ async check() {
12308
+ try {
12309
+ const response = await fetch(`https://registry.npmjs.org/${this.packageName}`, {
12310
+ headers: { Accept: "application/vnd.npm.install-v1+json" },
12311
+ signal: AbortSignal.timeout(5000)
12312
+ });
12313
+ if (!response.ok) {
12314
+ return { updateAvailable: false, current: this.currentVersion, error: `HTTP ${response.status}` };
12315
+ }
12316
+ const data = await response.json();
12317
+ const latest = data["dist-tags"]?.latest;
12318
+ if (!latest) {
12319
+ return { updateAvailable: false, current: this.currentVersion, error: "No latest tag" };
12320
+ }
12321
+ const updateAvailable = semverGt(latest, this.currentVersion);
12322
+ return { updateAvailable, current: this.currentVersion, latest };
12323
+ } catch (e) {
12324
+ return { updateAvailable: false, current: this.currentVersion, error: e.message };
12325
+ }
12326
+ }
12327
+ async install(latest) {
12328
+ try {
12329
+ const { command, args } = this.buildInstallCommand(latest);
12330
+ const res = await this.runner(command, args, {
12331
+ timeout: 120000,
12332
+ windowsHide: true
12333
+ });
12334
+ if (res.error)
12335
+ return { success: false, error: res.error };
12336
+ return { success: true, installed: latest };
12337
+ } catch (e) {
12338
+ const msg = e?.message ?? String(e);
12339
+ return { success: false, error: msg.split(`
12340
+ `)[0] };
12341
+ }
12342
+ }
12343
+ buildInstallCommand(latest) {
12344
+ const npmArgs = ["install", "-g", `${this.packageName}@${latest}`];
12345
+ const command = resolveSpawnCommand("npm");
12346
+ if (platform4() === "win32" && /\.(cmd|bat)$/i.test(command)) {
12347
+ return { command: "cmd.exe", args: ["/c", command, ...npmArgs] };
12348
+ }
12349
+ return { command, args: npmArgs };
12350
+ }
12351
+ }
12352
+ var defaultRunner = async (command, args, options) => {
12353
+ const { execFile } = await import("child_process");
12354
+ return new Promise((resolve13) => {
12355
+ execFile(command, args, options, (err) => resolve13({ error: err?.message }));
12356
+ });
12357
+ };
12358
+ var init_checker = __esm(() => {
12359
+ init_command();
12360
+ });
12361
+
12177
12362
  // src/modules/plugins/manager.ts
12178
12363
  class PluginManager {
12179
12364
  plugins = [];
12180
- register(plugin) {
12365
+ register(plugin, source) {
12366
+ if (source)
12367
+ plugin.source = source;
12368
+ const existing = this.plugins.find((p) => p.name === plugin.name);
12369
+ if (existing) {
12370
+ if (semverGt(plugin.version || "0.0.0", existing.version || "0.0.0")) {
12371
+ this.plugins = this.plugins.filter((p) => p.name !== plugin.name);
12372
+ this.plugins.push(plugin);
12373
+ this.sort();
12374
+ return { status: "registered" };
12375
+ }
12376
+ return { status: "skipped_older", existing };
12377
+ }
12181
12378
  this.plugins.push(plugin);
12379
+ this.sort();
12380
+ return { status: "registered" };
12381
+ }
12382
+ sort() {
12182
12383
  this.plugins.sort((a, b) => (b.priority || 0) - (a.priority || 0));
12183
12384
  }
12184
12385
  getAllPlugins() {
12185
12386
  return [...this.plugins];
12186
12387
  }
12388
+ getPluginInfos() {
12389
+ return [...this.plugins].map((plugin) => ({ plugin, source: plugin.source }));
12390
+ }
12391
+ getPlugin(name) {
12392
+ return this.plugins.find((p) => p.name === name) ?? null;
12393
+ }
12187
12394
  runOnBuildPrompt() {
12188
12395
  return this.plugins.map((p) => {
12189
12396
  try {
@@ -12320,6 +12527,9 @@ class PluginManager {
12320
12527
  return chunk;
12321
12528
  }
12322
12529
  }
12530
+ var init_manager2 = __esm(() => {
12531
+ init_checker();
12532
+ });
12323
12533
 
12324
12534
  // src/tools/subagent.ts
12325
12535
  function buildSubagentSystemPrompt(context, stable) {
@@ -12339,6 +12549,7 @@ var init_subagent = __esm(() => {
12339
12549
  init_agent();
12340
12550
  init_manager();
12341
12551
  init_detector();
12552
+ init_manager2();
12342
12553
  init_audit_log();
12343
12554
  init_session_isolation();
12344
12555
  init_store();
@@ -13393,6 +13604,7 @@ var init_pipeline_run = __esm(() => {
13393
13604
  init_parser();
13394
13605
  init_agent();
13395
13606
  init_manager();
13607
+ init_manager2();
13396
13608
  init_detector();
13397
13609
  init_audit_log();
13398
13610
  init_session_isolation();
@@ -13976,7 +14188,7 @@ ${JSON.stringify(result, null, 2)}`
13976
14188
 
13977
14189
  // src/tools/search-history.ts
13978
14190
  import * as fs from "fs";
13979
- import { join as join13 } from "path";
14191
+ import { join as join14 } from "path";
13980
14192
  import { homedir as homedir5 } from "os";
13981
14193
  function searchFile(filePath, query, maxResults, results) {
13982
14194
  if (!fs.existsSync(filePath))
@@ -14020,7 +14232,7 @@ var init_search_history = __esm(() => {
14020
14232
  const query = String(args.query || "").toLowerCase();
14021
14233
  const maxResults = Number(args.maxResults) || 5;
14022
14234
  const sessionId = args.sessionId ? String(args.sessionId) : null;
14023
- const sessionDir = join13(homedir5(), ".mma", "sessions");
14235
+ const sessionDir = join14(homedir5(), ".mma", "sessions");
14024
14236
  const results = [];
14025
14237
  try {
14026
14238
  if (!fs.existsSync(sessionDir)) {
@@ -14035,7 +14247,7 @@ var init_search_history = __esm(() => {
14035
14247
  continue;
14036
14248
  if (sessionId && entry.name !== sessionId)
14037
14249
  continue;
14038
- const historyFile = join13(sessionDir, entry.name, "history.jsonl");
14250
+ const historyFile = join14(sessionDir, entry.name, "history.jsonl");
14039
14251
  searchFile(historyFile, query, maxResults, results);
14040
14252
  if (results.length >= maxResults)
14041
14253
  break;
@@ -14062,8 +14274,8 @@ var init_search_history = __esm(() => {
14062
14274
  });
14063
14275
 
14064
14276
  // src/modules/memory/search.ts
14065
- import { readFileSync as readFileSync12, existsSync as existsSync23 } from "fs";
14066
- import { join as join14 } from "path";
14277
+ import { readFileSync as readFileSync12, existsSync as existsSync24 } from "fs";
14278
+ import { join as join15 } from "path";
14067
14279
 
14068
14280
  class MemorySearch {
14069
14281
  memoryDir;
@@ -14074,8 +14286,8 @@ class MemorySearch {
14074
14286
  const results = [];
14075
14287
  const lowerQuery = query.toLowerCase();
14076
14288
  for (const name of MEMORY_FILES) {
14077
- const path = join14(this.memoryDir, `${name}.md`);
14078
- if (!existsSync23(path))
14289
+ const path = join15(this.memoryDir, `${name}.md`);
14290
+ if (!existsSync24(path))
14079
14291
  continue;
14080
14292
  const content = readFileSync12(path, "utf-8");
14081
14293
  const lines = content.split(`
@@ -14086,8 +14298,8 @@ class MemorySearch {
14086
14298
  }
14087
14299
  }
14088
14300
  }
14089
- const prefsPath = join14(this.memoryDir, "preferences.json");
14090
- if (existsSync23(prefsPath)) {
14301
+ const prefsPath = join15(this.memoryDir, "preferences.json");
14302
+ if (existsSync24(prefsPath)) {
14091
14303
  try {
14092
14304
  const prefs = JSON.parse(readFileSync12(prefsPath, "utf-8"));
14093
14305
  for (const [key, value] of Object.entries(prefs)) {
@@ -14107,8 +14319,8 @@ var init_search = __esm(() => {
14107
14319
  });
14108
14320
 
14109
14321
  // src/modules/memory/store.ts
14110
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, appendFileSync as appendFileSync5, existsSync as existsSync24, mkdirSync as mkdirSync13 } from "fs";
14111
- import { join as join15 } from "path";
14322
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, appendFileSync as appendFileSync5, existsSync as existsSync25, mkdirSync as mkdirSync13 } from "fs";
14323
+ import { join as join16 } from "path";
14112
14324
 
14113
14325
  class MemoryStore {
14114
14326
  memoryDir;
@@ -14116,8 +14328,8 @@ class MemoryStore {
14116
14328
  this.memoryDir = memoryDir;
14117
14329
  this.ensureDir();
14118
14330
  for (const name of MEMORY_FILES2) {
14119
- const path = join15(this.memoryDir, `${name}.md`);
14120
- if (!existsSync24(path)) {
14331
+ const path = join16(this.memoryDir, `${name}.md`);
14332
+ if (!existsSync25(path)) {
14121
14333
  writeFileSync9(path, `# ${name.charAt(0).toUpperCase() + name.slice(1)}
14122
14334
 
14123
14335
  `, "utf-8");
@@ -14125,18 +14337,18 @@ class MemoryStore {
14125
14337
  }
14126
14338
  }
14127
14339
  ensureDir() {
14128
- if (!existsSync24(this.memoryDir)) {
14340
+ if (!existsSync25(this.memoryDir)) {
14129
14341
  mkdirSync13(this.memoryDir, { recursive: true });
14130
14342
  }
14131
14343
  }
14132
14344
  read(name) {
14133
- const path = join15(this.memoryDir, `${name}.md`);
14134
- if (!existsSync24(path))
14345
+ const path = join16(this.memoryDir, `${name}.md`);
14346
+ if (!existsSync25(path))
14135
14347
  return "";
14136
14348
  return readFileSync13(path, "utf-8");
14137
14349
  }
14138
14350
  append(name, entry) {
14139
- const path = join15(this.memoryDir, `${name}.md`);
14351
+ const path = join16(this.memoryDir, `${name}.md`);
14140
14352
  const timestamp = new Date().toISOString().replace("T", " ").slice(0, 19);
14141
14353
  const formatted = `- **${timestamp}** — ${entry}
14142
14354
  `;
@@ -14147,11 +14359,11 @@ class MemoryStore {
14147
14359
  return searchModule.query(query);
14148
14360
  }
14149
14361
  prefsPath() {
14150
- return join15(this.memoryDir, "preferences.json");
14362
+ return join16(this.memoryDir, "preferences.json");
14151
14363
  }
14152
14364
  getPreferences() {
14153
14365
  const path = this.prefsPath();
14154
- if (!existsSync24(path))
14366
+ if (!existsSync25(path))
14155
14367
  return {};
14156
14368
  try {
14157
14369
  return JSON.parse(readFileSync13(path, "utf-8"));
@@ -14187,7 +14399,7 @@ var init_store2 = __esm(() => {
14187
14399
 
14188
14400
  // src/tools/remember.ts
14189
14401
  import { homedir as homedir6 } from "os";
14190
- import { join as join16 } from "path";
14402
+ import { join as join17 } from "path";
14191
14403
  var CATEGORIES, rememberTool;
14192
14404
  var init_remember = __esm(() => {
14193
14405
  init_i18n();
@@ -14225,7 +14437,7 @@ var init_remember = __esm(() => {
14225
14437
  if (!CATEGORIES.includes(category)) {
14226
14438
  return { success: false, output: t("tool.invalid_params") };
14227
14439
  }
14228
- const memoryDir = join16(homedir6(), ".mma", "memory");
14440
+ const memoryDir = join17(homedir6(), ".mma", "memory");
14229
14441
  const store = new MemoryStore(memoryDir);
14230
14442
  try {
14231
14443
  if (category === "preferences") {
@@ -14258,7 +14470,7 @@ var init_remember = __esm(() => {
14258
14470
 
14259
14471
  // src/tools/recall.ts
14260
14472
  import { homedir as homedir7 } from "os";
14261
- import { join as join17 } from "path";
14473
+ import { join as join18 } from "path";
14262
14474
  function formatAll(store) {
14263
14475
  const parts = [];
14264
14476
  const prefs = store.getPreferences();
@@ -14341,7 +14553,7 @@ var init_recall = __esm(() => {
14341
14553
  handler: async (_ctx, args) => {
14342
14554
  const query = args.query ? String(args.query) : "";
14343
14555
  const category = args.category ? String(args.category) : "";
14344
- const memoryDir = join17(homedir7(), ".mma", "memory");
14556
+ const memoryDir = join18(homedir7(), ".mma", "memory");
14345
14557
  const store = new MemoryStore(memoryDir);
14346
14558
  try {
14347
14559
  if (!query && !category) {
@@ -14372,6 +14584,13 @@ var init_recall = __esm(() => {
14372
14584
  };
14373
14585
  });
14374
14586
 
14587
+ // src/modules/browser/bridge-path.ts
14588
+ import { existsSync as existsSync26 } from "fs";
14589
+ function pickExistingPath(candidates, fallback = candidates[0]) {
14590
+ return candidates.find((p) => existsSync26(p)) ?? fallback;
14591
+ }
14592
+ var init_bridge_path = () => {};
14593
+
14375
14594
  // src/modules/browser/bridge-client.ts
14376
14595
  var exports_bridge_client = {};
14377
14596
  __export(exports_bridge_client, {
@@ -14379,10 +14598,15 @@ __export(exports_bridge_client, {
14379
14598
  });
14380
14599
  import { spawn as spawn4 } from "child_process";
14381
14600
  import { createInterface } from "readline";
14382
- import { dirname as dirname9, join as join18 } from "path";
14601
+ import { dirname as dirname9, join as join19 } from "path";
14383
14602
  import { fileURLToPath } from "url";
14384
14603
  function bridgeScriptPath() {
14385
- return join18(dirname9(fileURLToPath(import.meta.url)), "bridge-server.mjs");
14604
+ const dir = dirname9(fileURLToPath(import.meta.url));
14605
+ const candidates = [
14606
+ join19(dir, "bridge-server.mjs"),
14607
+ join19(dir, "modules", "browser", "bridge-server.mjs")
14608
+ ];
14609
+ return pickExistingPath(candidates);
14386
14610
  }
14387
14611
 
14388
14612
  class BridgeDriver {
@@ -14560,6 +14784,7 @@ class BridgeDriver {
14560
14784
  var BRIDGE_REQUEST_TIMEOUT_MS = 30000;
14561
14785
  var init_bridge_client = __esm(() => {
14562
14786
  init_session();
14787
+ init_bridge_path();
14563
14788
  });
14564
14789
 
14565
14790
  // src/modules/browser/driver.ts
@@ -14939,15 +15164,15 @@ function buildTextExtractionScript() {
14939
15164
 
14940
15165
  // src/modules/browser/cookie-store.ts
14941
15166
  import { readFile, writeFile, mkdir } from "fs/promises";
14942
- import { join as join19 } from "path";
15167
+ import { join as join20 } from "path";
14943
15168
 
14944
15169
  class CookieStore {
14945
15170
  filePath;
14946
15171
  constructor(cookieDir) {
14947
- this.filePath = join19(cookieDir, "cookies.json");
15172
+ this.filePath = join20(cookieDir, "cookies.json");
14948
15173
  }
14949
15174
  async save(cookies) {
14950
- await mkdir(join19(this.filePath, ".."), { recursive: true });
15175
+ await mkdir(join20(this.filePath, ".."), { recursive: true });
14951
15176
  await writeFile(this.filePath, JSON.stringify(cookies, null, 2), "utf-8");
14952
15177
  }
14953
15178
  async load() {
@@ -15302,10 +15527,10 @@ var init_session = __esm(() => {
15302
15527
  });
15303
15528
 
15304
15529
  // src/tools/browser.ts
15305
- import { join as join20 } from "path";
15530
+ import { join as join21 } from "path";
15306
15531
  function getSession(ctx) {
15307
15532
  if (!session) {
15308
- const cookieDir = join20(ctx.baseDir, ".mma", "browser");
15533
+ const cookieDir = join21(ctx.baseDir, ".mma", "browser");
15309
15534
  session = new BrowserSession({
15310
15535
  ...DEFAULT_BROWSER_CONFIG,
15311
15536
  headless: ctx.config.browser?.headless ?? true,
@@ -15438,13 +15663,13 @@ async function readClipboardImage() {
15438
15663
  return readClipboardFallback();
15439
15664
  }
15440
15665
  async function readClipboardFallback() {
15441
- const { platform: platform3 } = await import("os");
15666
+ const { platform: platform5 } = await import("os");
15442
15667
  const { execSync } = await import("child_process");
15443
15668
  const { readFileSync: readFileSync15, unlinkSync: unlinkSync4 } = await import("fs");
15444
- const { join: join21 } = await import("path");
15445
- const tmpPath = join21(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
15669
+ const { join: join22 } = await import("path");
15670
+ const tmpPath = join22(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
15446
15671
  try {
15447
- if (platform3() === "linux") {
15672
+ if (platform5() === "linux") {
15448
15673
  execSync(`xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`, { timeout: 5000 });
15449
15674
  } else {
15450
15675
  return null;
@@ -15520,7 +15745,7 @@ var init_image_utils = __esm(() => {
15520
15745
  });
15521
15746
 
15522
15747
  // src/tools/attach-image.ts
15523
- import { existsSync as existsSync25 } from "fs";
15748
+ import { existsSync as existsSync27 } from "fs";
15524
15749
  import { resolve as resolve15 } from "path";
15525
15750
  var attachImageTool;
15526
15751
  var init_attach_image = __esm(() => {
@@ -15563,7 +15788,7 @@ var init_attach_image = __esm(() => {
15563
15788
  dataUrl = result.dataUrl;
15564
15789
  } else {
15565
15790
  const absPath = resolve15(ctx.baseDir, source);
15566
- if (!existsSync25(absPath)) {
15791
+ if (!existsSync27(absPath)) {
15567
15792
  return {
15568
15793
  success: false,
15569
15794
  output: t("image.not_found", { path: source })
@@ -15725,44 +15950,59 @@ class ModuleRegistry {
15725
15950
  }
15726
15951
 
15727
15952
  // src/modules/plugins/loader.ts
15728
- import { readdirSync as readdirSync7, existsSync as existsSync26, statSync as statSync5 } from "fs";
15729
- import { join as join21, basename as basename2 } from "path";
15953
+ import { readdirSync as readdirSync7, existsSync as existsSync28, statSync as statSync5 } from "fs";
15954
+ import { join as join22, basename as basename2 } from "path";
15730
15955
 
15731
15956
  class PluginLoader {
15732
- loadFromDir(dirPath, pluginManager, logger) {
15733
- if (!existsSync26(dirPath))
15957
+ loadFromDir(dirPath, pluginManager, logger, options) {
15958
+ if (!existsSync28(dirPath))
15734
15959
  return;
15735
15960
  const entries = readdirSync7(dirPath).sort();
15736
15961
  for (const entry of entries) {
15737
- const fullPath = join21(dirPath, entry);
15962
+ const fullPath = join22(dirPath, entry);
15738
15963
  const stat = statSync5(fullPath);
15739
15964
  if (stat.isFile()) {
15740
15965
  if (!entry.endsWith(".ts") && !entry.endsWith(".js"))
15741
15966
  continue;
15742
- this.tryLoad(fullPath, entry, pluginManager, logger);
15967
+ this.tryLoad(fullPath, entry, pluginManager, logger, options);
15743
15968
  } else if (stat.isDirectory()) {
15744
15969
  const entryFile = this.findEntryFile(fullPath);
15745
15970
  if (entryFile) {
15746
- this.tryLoad(entryFile, `${entry}/${basename2(entryFile)}`, pluginManager, logger);
15971
+ this.tryLoad(entryFile, `${entry}/${basename2(entryFile)}`, pluginManager, logger, options);
15747
15972
  }
15748
15973
  }
15749
15974
  }
15750
15975
  }
15751
15976
  findEntryFile(dir) {
15752
15977
  for (const name of FOLDER_ENTRY_NAMES) {
15753
- const candidate = join21(dir, name);
15754
- if (existsSync26(candidate))
15978
+ const candidate = join22(dir, name);
15979
+ if (existsSync28(candidate))
15755
15980
  return candidate;
15756
15981
  }
15757
15982
  return null;
15758
15983
  }
15759
- tryLoad(fullPath, label, pluginManager, logger) {
15984
+ tryLoad(fullPath, label, pluginManager, logger, options) {
15760
15985
  try {
15761
15986
  const mod = __require(fullPath);
15762
15987
  const plugin = mod.default || mod.plugin;
15763
15988
  if (plugin && plugin.name) {
15764
- pluginManager.register(plugin);
15765
- logger?.warn(t("plugin.loaded", { name: plugin.name }));
15989
+ if (plugin.minMmaVersion && options?.mmaVersion && semverGt(plugin.minMmaVersion, options.mmaVersion)) {
15990
+ logger?.warn(t("plugin.incompatible", {
15991
+ name: plugin.name,
15992
+ min: plugin.minMmaVersion,
15993
+ current: options.mmaVersion
15994
+ }));
15995
+ return;
15996
+ }
15997
+ const result = pluginManager.register(plugin, options?.source);
15998
+ if (result.status === "registered") {
15999
+ logger?.warn(t("plugin.loaded", { name: plugin.name }));
16000
+ } else if (result.status === "skipped_older") {
16001
+ logger?.warn(t("plugin.dedup_older", {
16002
+ name: plugin.name,
16003
+ version: plugin.version || "0.0.0"
16004
+ }));
16005
+ }
15766
16006
  }
15767
16007
  } catch (e) {
15768
16008
  logger?.warn(t("plugin.skipped", { entry: label, message: e.message }));
@@ -15772,14 +16012,15 @@ class PluginLoader {
15772
16012
  var FOLDER_ENTRY_NAMES;
15773
16013
  var init_loader = __esm(() => {
15774
16014
  init_i18n();
16015
+ init_checker();
15775
16016
  FOLDER_ENTRY_NAMES = ["plugin.ts", "plugin.js", "index.ts", "index.js"];
15776
16017
  });
15777
16018
 
15778
16019
  // src/modules/plugins/builtin/lint-on-write.ts
15779
16020
  import { spawn as spawn5, execSync } from "child_process";
15780
- import { existsSync as existsSync27, readFileSync as readFileSync15 } from "fs";
15781
- import { resolve as resolve16, extname as extname4, join as join22 } from "path";
15782
- import { platform as platform3 } from "os";
16021
+ import { existsSync as existsSync29, readFileSync as readFileSync15 } from "fs";
16022
+ import { resolve as resolve16, extname as extname4, join as join23 } from "path";
16023
+ import { platform as platform5 } from "os";
15783
16024
  function contentHash(content) {
15784
16025
  let h = 5381;
15785
16026
  for (let i = 0;i < content.length; i++) {
@@ -15789,7 +16030,7 @@ function contentHash(content) {
15789
16030
  }
15790
16031
  function getWinDecoder() {
15791
16032
  if (_winDecoder === undefined) {
15792
- if (platform3() !== "win32") {
16033
+ if (platform5() !== "win32") {
15793
16034
  _winDecoder = null;
15794
16035
  } else {
15795
16036
  try {
@@ -15811,6 +16052,7 @@ function getWinDecoder() {
15811
16052
  class LintOnWritePlugin {
15812
16053
  name = "lint-on-write";
15813
16054
  priority = 10;
16055
+ isBuiltin = true;
15814
16056
  _checkPromise = null;
15815
16057
  _checkTimestamp = 0;
15816
16058
  async onAfterTool(ctx, call, result) {
@@ -15824,7 +16066,7 @@ class LintOnWritePlugin {
15824
16066
  if (!path)
15825
16067
  return;
15826
16068
  const fullPath = resolve16(ctx.baseDir, path);
15827
- if (!existsSync27(fullPath))
16069
+ if (!existsSync29(fullPath))
15828
16070
  return;
15829
16071
  const ext = extname4(fullPath);
15830
16072
  const syntaxError = await this.checkSyntax(fullPath, ext, ctx.baseDir);
@@ -15880,8 +16122,8 @@ class LintOnWritePlugin {
15880
16122
  }
15881
16123
  async runProjectLint(ctx, result) {
15882
16124
  try {
15883
- const packageJsonPath = join22(ctx.baseDir, "package.json");
15884
- if (!existsSync27(packageJsonPath)) {
16125
+ const packageJsonPath = join23(ctx.baseDir, "package.json");
16126
+ if (!existsSync29(packageJsonPath)) {
15885
16127
  return;
15886
16128
  }
15887
16129
  const packageJson = JSON.parse(readFileSync15(packageJsonPath, "utf-8"));
@@ -15904,8 +16146,8 @@ class LintOnWritePlugin {
15904
16146
  "tsconfig.json",
15905
16147
  "package.json"
15906
16148
  ]);
15907
- const tsconfigPath = join22(projectRoot, "tsconfig.json");
15908
- if (!existsSync27(tsconfigPath)) {
16149
+ const tsconfigPath = join23(projectRoot, "tsconfig.json");
16150
+ if (!existsSync29(tsconfigPath)) {
15909
16151
  return;
15910
16152
  }
15911
16153
  const now = Date.now();
@@ -15997,6 +16239,7 @@ var init_lint_on_write = __esm(() => {
15997
16239
  class NotifyPlugin {
15998
16240
  name = "notify";
15999
16241
  priority = 5;
16242
+ isBuiltin = true;
16000
16243
  onSessionEnd(ctx) {
16001
16244
  ctx.logger.info("Session ended");
16002
16245
  }
@@ -16153,8 +16396,8 @@ class PlanTracker {
16153
16396
  var init_tracker = () => {};
16154
16397
 
16155
16398
  // src/modules/execution/audit-runners.ts
16156
- import { existsSync as existsSync28, readdirSync as readdirSync8 } from "fs";
16157
- import { dirname as dirname10, join as join23, resolve as resolve17 } from "path";
16399
+ import { existsSync as existsSync30, readdirSync as readdirSync8 } from "fs";
16400
+ import { dirname as dirname10, join as join24, resolve as resolve17 } from "path";
16158
16401
  function findTestFile(dir, depth = 0) {
16159
16402
  if (depth > 5)
16160
16403
  return null;
@@ -16165,7 +16408,7 @@ function findTestFile(dir, depth = 0) {
16165
16408
  return null;
16166
16409
  }
16167
16410
  for (const e of entries) {
16168
- const full = join23(dir, e.name);
16411
+ const full = join24(dir, e.name);
16169
16412
  if (e.isDirectory()) {
16170
16413
  if (SKIP_DIRS.has(e.name))
16171
16414
  continue;
@@ -16240,7 +16483,7 @@ function findTypecheckRoot(baseDir, existingFiles = []) {
16240
16483
  for (const start of candidates) {
16241
16484
  let dir = resolve17(start);
16242
16485
  for (let depth = 0;depth <= 10; depth++) {
16243
- if (existsSync28(join23(dir, "tsconfig.json"))) {
16486
+ if (existsSync30(join24(dir, "tsconfig.json"))) {
16244
16487
  if (!best || depth < best.depth)
16245
16488
  best = { depth, root: dir };
16246
16489
  break;
@@ -16283,11 +16526,11 @@ var init_audit_runners = __esm(() => {
16283
16526
  });
16284
16527
 
16285
16528
  // src/modules/execution/auditor.ts
16286
- import { existsSync as existsSync29, readdirSync as readdirSync9 } from "fs";
16287
- import { resolve as resolve18, join as join24, basename as basename3 } from "path";
16529
+ import { existsSync as existsSync31, readdirSync as readdirSync9 } from "fs";
16530
+ import { resolve as resolve18, join as join25, basename as basename3 } from "path";
16288
16531
  function findExistingFile(baseDir, filePath) {
16289
16532
  const direct = resolve18(baseDir, filePath);
16290
- if (existsSync29(direct))
16533
+ if (existsSync31(direct))
16291
16534
  return direct;
16292
16535
  const name = basename3(filePath).toLowerCase();
16293
16536
  const suffix = filePath.replace(/\\/g, "/").toLowerCase();
@@ -16304,7 +16547,7 @@ function findExistingFile(baseDir, filePath) {
16304
16547
  for (const e of entries) {
16305
16548
  if (found)
16306
16549
  return;
16307
- const full = join24(dir, e.name);
16550
+ const full = join25(dir, e.name);
16308
16551
  if (e.isDirectory()) {
16309
16552
  if (SKIP_DIRS.has(e.name))
16310
16553
  continue;
@@ -16442,8 +16685,8 @@ var init_auditor = __esm(() => {
16442
16685
  });
16443
16686
 
16444
16687
  // src/modules/execution/plan-store.ts
16445
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync14, existsSync as existsSync30, readdirSync as readdirSync10, rmSync } from "fs";
16446
- import { join as join25 } from "path";
16688
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync14, existsSync as existsSync32, readdirSync as readdirSync10, rmSync } from "fs";
16689
+ import { join as join26 } from "path";
16447
16690
  function readPlanFile(path, fallbackBaseDir) {
16448
16691
  try {
16449
16692
  const raw = readFileSync16(path, "utf-8");
@@ -16468,10 +16711,10 @@ function writePlanFile(path, plan) {
16468
16711
  writeFileSync10(path, JSON.stringify(plan, null, 2), "utf-8");
16469
16712
  }
16470
16713
  function listDir(dir, baseDir) {
16471
- if (!existsSync30(dir))
16714
+ if (!existsSync32(dir))
16472
16715
  return [];
16473
16716
  const files = readdirSync10(dir).filter((f) => f.endsWith(".json"));
16474
- return files.map((f) => readPlanFile(join25(dir, f), baseDir)).filter((p) => p !== null);
16717
+ return files.map((f) => readPlanFile(join26(dir, f), baseDir)).filter((p) => p !== null);
16475
16718
  }
16476
16719
  function toMeta(plan, status) {
16477
16720
  return {
@@ -16492,33 +16735,33 @@ class PlanStore {
16492
16735
  archiveDir;
16493
16736
  legacyPath;
16494
16737
  constructor(baseDir) {
16495
- const mmaDir = join25(baseDir, ".mma");
16496
- if (!existsSync30(mmaDir))
16738
+ const mmaDir = join26(baseDir, ".mma");
16739
+ if (!existsSync32(mmaDir))
16497
16740
  mkdirSync14(mmaDir, { recursive: true });
16498
16741
  this.baseDir = baseDir;
16499
- this.plansDir = join25(mmaDir, "plans");
16500
- this.draftsDir = join25(this.plansDir, "drafts");
16501
- this.archiveDir = join25(this.plansDir, "archive");
16502
- this.legacyPath = join25(mmaDir, LEGACY_FILE);
16742
+ this.plansDir = join26(mmaDir, "plans");
16743
+ this.draftsDir = join26(this.plansDir, "drafts");
16744
+ this.archiveDir = join26(this.plansDir, "archive");
16745
+ this.legacyPath = join26(mmaDir, LEGACY_FILE);
16503
16746
  for (const dir of [this.plansDir, this.draftsDir, this.archiveDir]) {
16504
- if (!existsSync30(dir))
16747
+ if (!existsSync32(dir))
16505
16748
  mkdirSync14(dir, { recursive: true });
16506
16749
  }
16507
16750
  }
16508
16751
  activePath() {
16509
- return join25(this.plansDir, "active.json");
16752
+ return join26(this.plansDir, "active.json");
16510
16753
  }
16511
16754
  saveActive(plan) {
16512
16755
  writePlanFile(this.activePath(), plan);
16513
16756
  }
16514
16757
  loadActive() {
16515
16758
  const activePath = this.activePath();
16516
- if (existsSync30(activePath)) {
16759
+ if (existsSync32(activePath)) {
16517
16760
  const plan = readPlanFile(activePath, this.baseDir);
16518
16761
  if (plan)
16519
16762
  return plan;
16520
16763
  }
16521
- if (existsSync30(this.legacyPath)) {
16764
+ if (existsSync32(this.legacyPath)) {
16522
16765
  const legacy = readPlanFile(this.legacyPath, this.baseDir);
16523
16766
  if (legacy) {
16524
16767
  this.saveActive(legacy);
@@ -16532,26 +16775,26 @@ class PlanStore {
16532
16775
  }
16533
16776
  clearActive() {
16534
16777
  const p = this.activePath();
16535
- if (existsSync30(p))
16778
+ if (existsSync32(p))
16536
16779
  rmSync(p, { force: true });
16537
16780
  }
16538
16781
  saveDraft(plan) {
16539
- writePlanFile(join25(this.draftsDir, `${plan.id}.json`), plan);
16782
+ writePlanFile(join26(this.draftsDir, `${plan.id}.json`), plan);
16540
16783
  }
16541
16784
  loadDraft(id) {
16542
- const p = join25(this.draftsDir, `${id}.json`);
16543
- return existsSync30(p) ? readPlanFile(p, this.baseDir) : null;
16785
+ const p = join26(this.draftsDir, `${id}.json`);
16786
+ return existsSync32(p) ? readPlanFile(p, this.baseDir) : null;
16544
16787
  }
16545
16788
  removeDraft(id) {
16546
- const p = join25(this.draftsDir, `${id}.json`);
16547
- if (existsSync30(p))
16789
+ const p = join26(this.draftsDir, `${id}.json`);
16790
+ if (existsSync32(p))
16548
16791
  rmSync(p, { force: true });
16549
16792
  }
16550
16793
  listDrafts() {
16551
16794
  return listDir(this.draftsDir, this.baseDir);
16552
16795
  }
16553
16796
  archivePlan(plan) {
16554
- writePlanFile(join25(this.archiveDir, `${plan.id}.json`), plan);
16797
+ writePlanFile(join26(this.archiveDir, `${plan.id}.json`), plan);
16555
16798
  this.removeDraft(plan.id);
16556
16799
  const active = this.loadActive();
16557
16800
  if (active && active.id === plan.id) {
@@ -16562,8 +16805,8 @@ class PlanStore {
16562
16805
  return listDir(this.archiveDir, this.baseDir);
16563
16806
  }
16564
16807
  removeArchived(id) {
16565
- const p = join25(this.archiveDir, `${id}.json`);
16566
- if (existsSync30(p))
16808
+ const p = join26(this.archiveDir, `${id}.json`);
16809
+ if (existsSync32(p))
16567
16810
  rmSync(p, { force: true });
16568
16811
  }
16569
16812
  listAll() {
@@ -16790,6 +17033,23 @@ ${lines.join(`
16790
17033
  if (parsed.error) {
16791
17034
  return { success: false, output: parsed.error };
16792
17035
  }
17036
+ const existing = deps.trackerRef.current;
17037
+ if (existing) {
17038
+ const activePlan = existing.getPlan();
17039
+ const progressed = activePlan.steps.some((s) => s.status === "done" || s.status === "failed" || s.status === "skipped");
17040
+ if (progressed) {
17041
+ const done = activePlan.steps.filter((s) => s.status === "done").length;
17042
+ return {
17043
+ success: false,
17044
+ output: t("plan.active_in_progress", {
17045
+ id: activePlan.id,
17046
+ done: String(done),
17047
+ total: String(activePlan.steps.length),
17048
+ current: String(existing.getCurrentStepIndex() + 1)
17049
+ })
17050
+ };
17051
+ }
17052
+ }
16793
17053
  deps.preserveActive();
16794
17054
  const plan = PlanCreator.createPlan(title, steps, deps.baseDir, parsed.kinds ?? []);
16795
17055
  deps.setPlan(plan);
@@ -16798,6 +17058,14 @@ ${lines.join(`
16798
17058
  title,
16799
17059
  steps: String(steps.length)
16800
17060
  });
17061
+ if (args.id) {
17062
+ output += `
17063
+ ${t("plan.id_ignored")}`;
17064
+ }
17065
+ if (existing) {
17066
+ output += `
17067
+ ${t("plan.existing_fresh")}`;
17068
+ }
16801
17069
  let displayOut = display;
16802
17070
  const taskText = deps.getTaskText(_ctx);
16803
17071
  if (taskText) {
@@ -17117,7 +17385,7 @@ var init_windows_commands = __esm(() => {
17117
17385
  });
17118
17386
 
17119
17387
  // src/modules/execution/execution-plugin.ts
17120
- import { platform as platform4 } from "os";
17388
+ import { platform as platform6 } from "os";
17121
17389
  function createExecutionPlugin(deps) {
17122
17390
  return {
17123
17391
  name: "execution",
@@ -17261,7 +17529,7 @@ Tool "${deps.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
17261
17529
  deps.stuckDetector.recordToolError(call.name, toolText.slice(0, 300));
17262
17530
  }
17263
17531
  if (!result.success) {
17264
- if (call.name === "bash" && platform4() === "win32") {
17532
+ if (call.name === "bash" && platform6() === "win32") {
17265
17533
  const cmd = String(call.arguments?.command ?? "");
17266
17534
  const forbidden = forbiddenWindowsCommand(cmd);
17267
17535
  if (forbidden) {
@@ -17340,7 +17608,7 @@ var init_execution_plugin = __esm(() => {
17340
17608
  });
17341
17609
 
17342
17610
  // src/modules/execution/module.ts
17343
- import { existsSync as existsSync31, readFileSync as readFileSync17 } from "fs";
17611
+ import { existsSync as existsSync33, readFileSync as readFileSync17 } from "fs";
17344
17612
  import { resolve as resolve19 } from "path";
17345
17613
 
17346
17614
  class ExecutionModule {
@@ -17433,6 +17701,14 @@ class ExecutionModule {
17433
17701
  estimatedTokens: 200
17434
17702
  };
17435
17703
  }
17704
+ getPlanSummary() {
17705
+ if (!this.tracker)
17706
+ return null;
17707
+ const plan = this.tracker.getPlan();
17708
+ const done = plan.steps.filter((s) => s.status === "done").length;
17709
+ const current = this.tracker.getCurrentStepIndex() + 1;
17710
+ return `[${plan.id}] "${plan.title}" ${done}/${plan.steps.length} done, current: step ${current} — continue it (plan show / re-plan), do NOT create a new plan`;
17711
+ }
17436
17712
  getToolDefinitions() {
17437
17713
  return createPlanToolDefinitions({
17438
17714
  store: this.store,
@@ -17535,7 +17811,7 @@ class ExecutionModule {
17535
17811
  "poetry.lock",
17536
17812
  "requirements.txt"
17537
17813
  ];
17538
- const hasLockFile = lockFiles.some((f) => existsSync31(resolve19(this.baseDir, f)));
17814
+ const hasLockFile = lockFiles.some((f) => existsSync33(resolve19(this.baseDir, f)));
17539
17815
  if (!hasLockFile) {
17540
17816
  if (contextManager) {
17541
17817
  const hints = this.state.depsGateHints.get(step.id) || 0;
@@ -17555,8 +17831,8 @@ class ExecutionModule {
17555
17831
  }
17556
17832
  if (stepPaths.length === 0)
17557
17833
  return;
17558
- const allExist = stepPaths.every((p) => existsSync31(resolve19(this.baseDir, p)));
17559
- const allGone = stepPaths.every((p) => !existsSync31(resolve19(this.baseDir, p)));
17834
+ const allExist = stepPaths.every((p) => existsSync33(resolve19(this.baseDir, p)));
17835
+ const allGone = stepPaths.every((p) => !existsSync33(resolve19(this.baseDir, p)));
17560
17836
  const satisfied = step.kind === "delete" ? allGone && !allExist : allExist;
17561
17837
  if (!satisfied)
17562
17838
  return;
@@ -17662,11 +17938,11 @@ var init_module = __esm(() => {
17662
17938
  import {
17663
17939
  readFileSync as readFileSync18,
17664
17940
  writeFileSync as writeFileSync11,
17665
- existsSync as existsSync32,
17941
+ existsSync as existsSync34,
17666
17942
  readdirSync as readdirSync11,
17667
17943
  unlinkSync as unlinkSync4
17668
17944
  } from "fs";
17669
- import { join as join26 } from "path";
17945
+ import { join as join27 } from "path";
17670
17946
  import { homedir as homedir8 } from "os";
17671
17947
 
17672
17948
  class SessionFileEncryptor {
@@ -17675,7 +17951,7 @@ class SessionFileEncryptor {
17675
17951
  constructor(config) {
17676
17952
  this.config = { ...DEFAULT_SESSION_ENCRYPTION, ...config };
17677
17953
  this.encryptor = new ConfigEncryptor({
17678
- keyPath: config?.keyPath || join26(homedir8(), ".mma", ".session-encryption-key")
17954
+ keyPath: config?.keyPath || join27(homedir8(), ".mma", ".session-encryption-key")
17679
17955
  });
17680
17956
  }
17681
17957
  isEnabled() {
@@ -17761,8 +18037,8 @@ class SessionFileEncryptor {
17761
18037
  return;
17762
18038
  const files = readdirSync11(sessionDir);
17763
18039
  for (const file of files) {
17764
- const filePath = join26(sessionDir, file);
17765
- if (existsSync32(filePath) && !file.endsWith(".enc")) {
18040
+ const filePath = join27(sessionDir, file);
18041
+ if (existsSync34(filePath) && !file.endsWith(".enc")) {
17766
18042
  try {
17767
18043
  const content = readFileSync18(filePath, "utf8");
17768
18044
  const encrypted = this.encryptFileContent(content);
@@ -17778,7 +18054,7 @@ class SessionFileEncryptor {
17778
18054
  const files = readdirSync11(sessionDir);
17779
18055
  for (const file of files) {
17780
18056
  if (file.endsWith(".enc")) {
17781
- const encFilePath = join26(sessionDir, file);
18057
+ const encFilePath = join27(sessionDir, file);
17782
18058
  const decFilePath = encFilePath.slice(0, -4);
17783
18059
  try {
17784
18060
  const content = readFileSync18(encFilePath, "utf8");
@@ -17803,7 +18079,7 @@ var init_session_encryption = __esm(() => {
17803
18079
 
17804
18080
  // src/modules/session/store.ts
17805
18081
  import {
17806
- existsSync as existsSync33,
18082
+ existsSync as existsSync35,
17807
18083
  mkdirSync as mkdirSync15,
17808
18084
  readdirSync as readdirSync12,
17809
18085
  readFileSync as readFileSync19,
@@ -17811,7 +18087,7 @@ import {
17811
18087
  writeFileSync as writeFileSync12,
17812
18088
  appendFileSync as appendFileSync6
17813
18089
  } from "fs";
17814
- import { join as join27 } from "path";
18090
+ import { join as join28 } from "path";
17815
18091
  import { gzipSync } from "zlib";
17816
18092
 
17817
18093
  class SessionStore {
@@ -17825,7 +18101,7 @@ class SessionStore {
17825
18101
  }
17826
18102
  }
17827
18103
  getSessionDir(id) {
17828
- return join27(this.baseDir, id);
18104
+ return join28(this.baseDir, id);
17829
18105
  }
17830
18106
  updateEncryption(config) {
17831
18107
  if (config?.enabled) {
@@ -17841,19 +18117,19 @@ class SessionStore {
17841
18117
  mkdirSync15(this.baseDir, { recursive: true });
17842
18118
  }
17843
18119
  sessionDir(id) {
17844
- return join27(this.baseDir, id);
18120
+ return join28(this.baseDir, id);
17845
18121
  }
17846
18122
  metaPath(id) {
17847
- return join27(this.sessionDir(id), "meta.json");
18123
+ return join28(this.sessionDir(id), "meta.json");
17848
18124
  }
17849
18125
  historyPath(id) {
17850
- return join27(this.sessionDir(id), "history.jsonl");
18126
+ return join28(this.sessionDir(id), "history.jsonl");
17851
18127
  }
17852
18128
  sessionLogPath(id) {
17853
- return join27(this.sessionDir(id), "session.jsonl");
18129
+ return join28(this.sessionDir(id), "session.jsonl");
17854
18130
  }
17855
18131
  sessionExists(id) {
17856
- return existsSync33(this.metaPath(id));
18132
+ return existsSync35(this.metaPath(id));
17857
18133
  }
17858
18134
  saveMeta(id, meta) {
17859
18135
  this._metaCache.set(id, meta);
@@ -17871,7 +18147,7 @@ class SessionStore {
17871
18147
  if (cached)
17872
18148
  return cached;
17873
18149
  const path = this.metaPath(id);
17874
- if (!existsSync33(path))
18150
+ if (!existsSync35(path))
17875
18151
  return null;
17876
18152
  try {
17877
18153
  const raw = readFileSync19(path, "utf-8");
@@ -17903,7 +18179,7 @@ class SessionStore {
17903
18179
  }
17904
18180
  loadHistory(id) {
17905
18181
  const path = this.historyPath(id);
17906
- if (!existsSync33(path))
18182
+ if (!existsSync35(path))
17907
18183
  return [];
17908
18184
  try {
17909
18185
  const raw = readFileSync19(path, "utf-8");
@@ -17944,7 +18220,7 @@ class SessionStore {
17944
18220
  }
17945
18221
  loadSessionLog(id) {
17946
18222
  const path = this.sessionLogPath(id);
17947
- if (!existsSync33(path))
18223
+ if (!existsSync35(path))
17948
18224
  return [];
17949
18225
  try {
17950
18226
  const raw = readFileSync19(path, "utf-8");
@@ -17972,7 +18248,7 @@ class SessionStore {
17972
18248
  }
17973
18249
  }
17974
18250
  listSessions() {
17975
- if (!existsSync33(this.baseDir))
18251
+ if (!existsSync35(this.baseDir))
17976
18252
  return [];
17977
18253
  const entries = readdirSync12(this.baseDir, { withFileTypes: true });
17978
18254
  const sessions = [];
@@ -17989,7 +18265,7 @@ class SessionStore {
17989
18265
  deleteSession(id) {
17990
18266
  this._metaCache.delete(id);
17991
18267
  const dir = this.sessionDir(id);
17992
- if (existsSync33(dir)) {
18268
+ if (existsSync35(dir)) {
17993
18269
  rmSync2(dir, { recursive: true, force: true });
17994
18270
  }
17995
18271
  }
@@ -18001,10 +18277,10 @@ class SessionStore {
18001
18277
  const updatedAt = new Date(session2.updatedAt);
18002
18278
  if (updatedAt < thirtyDaysAgo) {
18003
18279
  const historyPath = this.historyPath(session2.id);
18004
- if (existsSync33(historyPath)) {
18280
+ if (existsSync35(historyPath)) {
18005
18281
  const content = readFileSync19(historyPath, "utf-8");
18006
18282
  const compressed = gzipSync(content);
18007
- const gzPath = join27(this.baseDir, `${session2.id}.jsonl.gz`);
18283
+ const gzPath = join28(this.baseDir, `${session2.id}.jsonl.gz`);
18008
18284
  writeFileSync12(gzPath, compressed);
18009
18285
  rmSync2(historyPath);
18010
18286
  }
@@ -18157,7 +18433,7 @@ class SessionManager {
18157
18433
  }
18158
18434
  }
18159
18435
  }
18160
- var init_manager2 = __esm(() => {
18436
+ var init_manager3 = __esm(() => {
18161
18437
  init_i18n();
18162
18438
  init_session_isolation();
18163
18439
  });
@@ -18193,7 +18469,7 @@ var init_module2 = __esm(() => {
18193
18469
  // src/modules/session/index.ts
18194
18470
  var init_session2 = __esm(() => {
18195
18471
  init_store3();
18196
- init_manager2();
18472
+ init_manager3();
18197
18473
  init_module2();
18198
18474
  });
18199
18475
 
@@ -18214,9 +18490,9 @@ class ProfileCompressor {
18214
18490
  }
18215
18491
 
18216
18492
  // src/modules/user-profile/profile.ts
18217
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync13, existsSync as existsSync34, mkdirSync as mkdirSync16 } from "fs";
18218
- import { join as join28 } from "path";
18219
- import { homedir as homedir9, hostname, platform as platform5, type } from "os";
18493
+ import { readFileSync as readFileSync20, writeFileSync as writeFileSync13, existsSync as existsSync36, mkdirSync as mkdirSync16 } from "fs";
18494
+ import { join as join29 } from "path";
18495
+ import { homedir as homedir9, hostname, platform as platform7, type } from "os";
18220
18496
  import { env } from "process";
18221
18497
 
18222
18498
  class UserProfile {
@@ -18228,7 +18504,7 @@ class UserProfile {
18228
18504
  }
18229
18505
  collect() {
18230
18506
  this.info = {
18231
- platform: platform5(),
18507
+ platform: platform7(),
18232
18508
  os: `${type()} ${hostname()}`,
18233
18509
  hostname: hostname(),
18234
18510
  shell: env.SHELL || env.ComSpec || "unknown",
@@ -18239,14 +18515,14 @@ class UserProfile {
18239
18515
  return this.info;
18240
18516
  }
18241
18517
  save() {
18242
- if (!existsSync34(this.profileDir)) {
18518
+ if (!existsSync36(this.profileDir)) {
18243
18519
  mkdirSync16(this.profileDir, { recursive: true });
18244
18520
  }
18245
- writeFileSync13(join28(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
18521
+ writeFileSync13(join29(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
18246
18522
  }
18247
18523
  load() {
18248
- const path = join28(this.profileDir, "profile.json");
18249
- if (!existsSync34(path))
18524
+ const path = join29(this.profileDir, "profile.json");
18525
+ if (!existsSync36(path))
18250
18526
  return null;
18251
18527
  try {
18252
18528
  const data = JSON.parse(readFileSync20(path, "utf-8"));
@@ -18284,12 +18560,12 @@ class UserProfile {
18284
18560
  var init_profile = () => {};
18285
18561
 
18286
18562
  // src/modules/skills/loader.ts
18287
- import { readdirSync as readdirSync13, readFileSync as readFileSync21, existsSync as existsSync35, statSync as statSync6 } from "fs";
18288
- import { join as join29 } from "path";
18563
+ import { readdirSync as readdirSync13, readFileSync as readFileSync21, existsSync as existsSync37, statSync as statSync6 } from "fs";
18564
+ import { join as join30 } from "path";
18289
18565
 
18290
18566
  class SkillsLoader {
18291
18567
  loadFromDir(dirPath) {
18292
- if (!existsSync35(dirPath))
18568
+ if (!existsSync37(dirPath))
18293
18569
  return [];
18294
18570
  const skills = [];
18295
18571
  this.scanDir(dirPath, skills);
@@ -18298,7 +18574,7 @@ class SkillsLoader {
18298
18574
  scanDir(dirPath, skills) {
18299
18575
  const entries = readdirSync13(dirPath);
18300
18576
  for (const entry of entries) {
18301
- const fullPath = join29(dirPath, entry);
18577
+ const fullPath = join30(dirPath, entry);
18302
18578
  const stat = statSync6(fullPath);
18303
18579
  if (stat.isDirectory()) {
18304
18580
  this.scanDir(fullPath, skills);
@@ -18541,45 +18817,10 @@ var init_browser2 = __esm(() => {
18541
18817
  init_types();
18542
18818
  });
18543
18819
 
18544
- // src/modules/lsp/command.ts
18545
- import { delimiter, join as join30 } from "path";
18546
- import { existsSync as existsSync36 } from "fs";
18547
- import { platform as platform6 } from "os";
18548
- function resolveSpawnCommand(command, platformName = platform6(), pathEnv = process.env.PATH ?? "") {
18549
- if (platformName !== "win32")
18550
- return command;
18551
- if (command.includes("/") || command.includes("\\") || WIN_EXTS.some((ext) => command.toLowerCase().endsWith(ext))) {
18552
- return command;
18553
- }
18554
- const dirs = pathEnv.split(delimiter).filter(Boolean);
18555
- for (const dir of dirs) {
18556
- for (const ext of WIN_EXTS) {
18557
- const candidate = join30(dir, `${command}${ext}`);
18558
- if (existsSync36(candidate))
18559
- return `${command}${ext}`;
18560
- }
18561
- }
18562
- return command;
18563
- }
18564
- function buildWinCommandLine(command, args) {
18565
- const quote = (part) => {
18566
- if (part === "")
18567
- return '""';
18568
- if (!/[\s"&|^<>()%!]/.test(part))
18569
- return part;
18570
- return `"${part.replace(/"/g, "\\\"")}"`;
18571
- };
18572
- return [command, ...args].map(quote).join(" ");
18573
- }
18574
- var WIN_EXTS;
18575
- var init_command = __esm(() => {
18576
- WIN_EXTS = [".cmd", ".bat", ".exe", ".com"];
18577
- });
18578
-
18579
18820
  // src/modules/lsp/client.ts
18580
18821
  import { spawn as spawn6, execSync as execSync2 } from "child_process";
18581
18822
  import { resolve as resolve20 } from "path";
18582
- import { platform as platform7 } from "os";
18823
+ import { platform as platform8 } from "os";
18583
18824
 
18584
18825
  class LspClient {
18585
18826
  process = null;
@@ -18649,7 +18890,7 @@ class LspClient {
18649
18890
  }
18650
18891
  return new Promise((resolve21, reject) => {
18651
18892
  const args = config.args ?? [];
18652
- const isWin = platform7() === "win32";
18893
+ const isWin = platform8() === "win32";
18653
18894
  let spawnCommand = resolveSpawnCommand(config.command);
18654
18895
  let spawnArgs = args;
18655
18896
  const spawnOpts = {
@@ -18823,18 +19064,70 @@ var init_client2 = __esm(() => {
18823
19064
  init_command();
18824
19065
  });
18825
19066
 
19067
+ // src/modules/lsp/check-tool.ts
19068
+ import { readdir, stat } from "fs/promises";
19069
+ import { resolve as resolve21, relative as relative2 } from "path";
19070
+ async function collectCheckFiles(resolved, config) {
19071
+ const info = await stat(resolved).catch(() => null);
19072
+ if (!info)
19073
+ return [];
19074
+ if (info.isFile())
19075
+ return [resolved];
19076
+ const out = [];
19077
+ await walkDir(resolved, 0, out, config);
19078
+ return out;
19079
+ }
19080
+ async function walkDir(dir, depth, out, config) {
19081
+ if (depth > MAX_LSP_WALK_DEPTH || out.length >= MAX_LSP_CHECK_FILES)
19082
+ return;
19083
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
19084
+ for (const entry of entries) {
19085
+ if (out.length >= MAX_LSP_CHECK_FILES)
19086
+ break;
19087
+ if (entry.isDirectory()) {
19088
+ if (SKIP_DIRS2.has(entry.name))
19089
+ continue;
19090
+ await walkDir(resolve21(dir, entry.name), depth + 1, out, config);
19091
+ } else if (entry.isFile() && getServerForFile(resolve21(dir, entry.name), config)) {
19092
+ out.push(resolve21(dir, entry.name));
19093
+ }
19094
+ }
19095
+ }
19096
+ function formatCheckDiagnostics(items, baseDir) {
19097
+ return items.slice(0, 40).map(({ file, diag }) => {
19098
+ const rel = relative2(baseDir, file).replace(/\\/g, "/");
19099
+ const line = diag.range.start.line + 1;
19100
+ const col = diag.range.start.character + 1;
19101
+ const sev = severityLabels[diag.severity] ?? "unknown";
19102
+ return ` ${rel}(${line},${col}): ${sev}: ${diag.message}`;
19103
+ }).join(`
19104
+ `);
19105
+ }
19106
+ var MAX_LSP_CHECK_FILES = 15, MAX_LSP_WALK_DEPTH = 4, SKIP_DIRS2, severityLabels;
19107
+ var init_check_tool = __esm(() => {
19108
+ init_config();
19109
+ SKIP_DIRS2 = new Set(["node_modules", ".git", ".mma", "dist"]);
19110
+ severityLabels = {
19111
+ 1: "error",
19112
+ 2: "warning",
19113
+ 3: "info",
19114
+ 4: "hint"
19115
+ };
19116
+ });
19117
+
18826
19118
  // src/modules/lsp/module.ts
18827
- import { existsSync as existsSync37 } from "fs";
18828
- import { resolve as resolve21 } from "path";
19119
+ import { existsSync as existsSync38 } from "fs";
19120
+ import { resolve as resolve22 } from "path";
18829
19121
 
18830
19122
  class LspModule {
18831
19123
  name = "lsp";
18832
19124
  config;
18833
- client = new LspClient;
19125
+ client;
18834
19126
  failuresByServer = new Map;
18835
19127
  disabledServers = new Set;
18836
- constructor(config) {
19128
+ constructor(config, client) {
18837
19129
  this.config = { ...DEFAULT_LSP_CONFIG, ...config };
19130
+ this.client = client ?? new LspClient;
18838
19131
  }
18839
19132
  isLspDisabled() {
18840
19133
  return this.disabledServers.size > 0;
@@ -18860,8 +19153,8 @@ class LspModule {
18860
19153
  const filePath = String(call.arguments.path ?? "");
18861
19154
  if (!filePath)
18862
19155
  return;
18863
- const fullPath = resolve21(_ctx.baseDir, filePath);
18864
- if (!existsSync37(fullPath))
19156
+ const fullPath = resolve22(_ctx.baseDir, filePath);
19157
+ if (!existsSync38(fullPath))
18865
19158
  return;
18866
19159
  const serverConfig = getServerForFile(fullPath, self.config);
18867
19160
  if (!serverConfig)
@@ -18910,6 +19203,110 @@ ${items}`;
18910
19203
  }
18911
19204
  };
18912
19205
  }
19206
+ getToolDefinitions() {
19207
+ const self = this;
19208
+ return [
19209
+ {
19210
+ name: "lsp_check",
19211
+ description: 'Run static checks (LSP diagnostics) on a file or directory. For "check for errors" tasks: pass a file to check it, or a directory to check its source files (capped at 15). Returns [LSP errors] / [LSP warnings] for files with a configured LSP server.',
19212
+ tags: ["code", "check"],
19213
+ boundedOutput: true,
19214
+ timeoutMs: 90000,
19215
+ parameters: {
19216
+ type: "object",
19217
+ properties: {
19218
+ path: {
19219
+ type: "string",
19220
+ description: "File or directory to check"
19221
+ }
19222
+ },
19223
+ required: ["path"]
19224
+ },
19225
+ handler: async (ctx, args) => self.runCheck(ctx, args)
19226
+ }
19227
+ ];
19228
+ }
19229
+ async runCheck(ctx, args) {
19230
+ if (!this.config.enabled) {
19231
+ return { success: false, output: t("lsp.check_disabled") };
19232
+ }
19233
+ const path = String(args.path ?? "");
19234
+ if (!path)
19235
+ return { success: false, output: t("lsp.check_no_path") };
19236
+ const resolved = safeResolvePath(ctx.baseDir, path);
19237
+ const securityPaths = ctx.config?.security?.paths || DEFAULT_SECURITY_CONFIG.paths;
19238
+ const scopeCheck = isPathInScope(ctx.baseDir, resolved, ctx.scope, securityPaths);
19239
+ if (!scopeCheck.allowed) {
19240
+ return {
19241
+ success: false,
19242
+ output: t("file.path_not_allowed", {
19243
+ path: `${path} — ${scopeCheck.reason}`
19244
+ })
19245
+ };
19246
+ }
19247
+ if (!existsSync38(resolved)) {
19248
+ return { success: false, output: t("lsp.check_notfound", { path }) };
19249
+ }
19250
+ const files = await collectCheckFiles(resolved, this.config);
19251
+ const errors = [];
19252
+ const warnings = [];
19253
+ const notes = [];
19254
+ let checked = 0;
19255
+ for (const file of files) {
19256
+ const serverConfig = getServerForFile(file, this.config);
19257
+ if (!serverConfig)
19258
+ continue;
19259
+ const key = this.serverKey(serverConfig);
19260
+ if (this.disabledServers.has(key))
19261
+ continue;
19262
+ const projectRoot = findProjectRoot(file, ctx.baseDir, serverConfig.workspaceMarkers ?? []);
19263
+ try {
19264
+ const diags = await this.client.checkFile(file, ctx.baseDir, serverConfig, projectRoot);
19265
+ this.failuresByServer.set(key, 0);
19266
+ checked++;
19267
+ for (const d of diags) {
19268
+ if (d.severity === 1)
19269
+ errors.push({ file, diag: d });
19270
+ else if (d.severity === 2)
19271
+ warnings.push({ file, diag: d });
19272
+ }
19273
+ } catch (e) {
19274
+ const msg = e instanceof Error ? e.message : String(e);
19275
+ const failures = (this.failuresByServer.get(key) ?? 0) + 1;
19276
+ this.failuresByServer.set(key, failures);
19277
+ try {
19278
+ ctx.logger?.warn(`LSP check failed for ${file}: ${msg}`);
19279
+ } catch {}
19280
+ if (failures >= MAX_CONSECUTIVE_LSP_FAILURES) {
19281
+ this.disabledServers.add(key);
19282
+ notes.push(`[LSP disabled: ${t("lsp.unavailable")}]`);
19283
+ } else {
19284
+ notes.push(`[LSP check failed for ${file}: ${msg}]`);
19285
+ }
19286
+ }
19287
+ }
19288
+ const lines = [];
19289
+ if (errors.length > 0) {
19290
+ lines.push(`[LSP errors] (${errors.length}):`);
19291
+ lines.push(formatCheckDiagnostics(errors, ctx.baseDir));
19292
+ }
19293
+ if (warnings.length > 0) {
19294
+ lines.push(`[LSP warnings] (${warnings.length}):`);
19295
+ lines.push(formatCheckDiagnostics(warnings, ctx.baseDir));
19296
+ }
19297
+ if (checked > 0 && errors.length === 0 && warnings.length === 0) {
19298
+ lines.push(t("lsp.check_clean", { count: String(checked) }));
19299
+ }
19300
+ lines.push(...notes);
19301
+ if (lines.length === 0) {
19302
+ return {
19303
+ success: true,
19304
+ output: t("lsp.check_clean", { count: String(checked) })
19305
+ };
19306
+ }
19307
+ return { success: errors.length === 0, output: lines.join(`
19308
+ `) };
19309
+ }
18913
19310
  }
18914
19311
  function formatDiagnostics(diagnostics, filePath) {
18915
19312
  const filename = filePath.replace(/\\/g, "/").split("/").pop() ?? filePath;
@@ -18921,18 +19318,16 @@ function formatDiagnostics(diagnostics, filePath) {
18921
19318
  }).join(`
18922
19319
  `);
18923
19320
  }
18924
- var severityLabels, MAX_CONSECUTIVE_LSP_FAILURES = 3;
19321
+ var MAX_CONSECUTIVE_LSP_FAILURES = 3;
18925
19322
  var init_module5 = __esm(() => {
18926
19323
  init_client2();
18927
19324
  init_config();
18928
19325
  init_project_root();
19326
+ init_path_validator();
19327
+ init_security();
19328
+ init_path_utils();
18929
19329
  init_i18n();
18930
- severityLabels = {
18931
- 1: "error",
18932
- 2: "warning",
18933
- 3: "info",
18934
- 4: "hint"
18935
- };
19330
+ init_check_tool();
18936
19331
  });
18937
19332
 
18938
19333
  // src/modules/lsp/index.ts
@@ -18943,8 +19338,8 @@ var init_lsp = __esm(() => {
18943
19338
  });
18944
19339
 
18945
19340
  // src/modules/indexer/walker.ts
18946
- import { readdirSync as readdirSync14, readFileSync as readFileSync22, statSync as statSync7, existsSync as existsSync38, watch } from "fs";
18947
- import { join as join31, relative as relative2, extname as extname5 } from "path";
19341
+ import { readdirSync as readdirSync14, readFileSync as readFileSync22, statSync as statSync7, existsSync as existsSync39, watch } from "fs";
19342
+ import { join as join31, relative as relative3, extname as extname5 } from "path";
18948
19343
 
18949
19344
  class Indexer {
18950
19345
  baseDir;
@@ -18970,8 +19365,8 @@ class Indexer {
18970
19365
  const files = [];
18971
19366
  let totalSize = 0;
18972
19367
  let count = 0;
18973
- const walkDir = (dir) => {
18974
- if (!existsSync38(dir))
19368
+ const walkDir2 = (dir) => {
19369
+ if (!existsSync39(dir))
18975
19370
  return;
18976
19371
  let entries;
18977
19372
  try {
@@ -18983,26 +19378,26 @@ class Indexer {
18983
19378
  if (count >= this.MAX_FILES)
18984
19379
  return;
18985
19380
  const fullPath = join31(dir, entry);
18986
- const relPath = relative2(this.baseDir, fullPath);
18987
- const stat = statSync7(fullPath);
18988
- if (stat.isDirectory()) {
19381
+ const relPath = relative3(this.baseDir, fullPath);
19382
+ const stat2 = statSync7(fullPath);
19383
+ if (stat2.isDirectory()) {
18989
19384
  if (!IGNORE_DIRS.has(entry)) {
18990
- walkDir(fullPath);
19385
+ walkDir2(fullPath);
18991
19386
  }
18992
- } else if (stat.isFile()) {
19387
+ } else if (stat2.isFile()) {
18993
19388
  const ext = extname5(entry).toLowerCase();
18994
19389
  const language = LANGUAGES[ext];
18995
19390
  if (language) {
18996
19391
  const content = readFileSync22(fullPath, "utf-8");
18997
19392
  const exports = this.extractExports(content, language);
18998
- files.push({ path: relPath, language, exports, size: stat.size });
18999
- totalSize += stat.size;
19393
+ files.push({ path: relPath, language, exports, size: stat2.size });
19394
+ totalSize += stat2.size;
19000
19395
  count++;
19001
19396
  }
19002
19397
  }
19003
19398
  }
19004
19399
  };
19005
- walkDir(this.baseDir);
19400
+ walkDir2(this.baseDir);
19006
19401
  return { files, totalSize };
19007
19402
  }
19008
19403
  extractExports(content, language) {
@@ -19045,7 +19440,7 @@ var init_walker = __esm(() => {
19045
19440
  });
19046
19441
 
19047
19442
  // src/modules/indexer/cache.ts
19048
- import { readFileSync as readFileSync23, writeFileSync as writeFileSync14, existsSync as existsSync39, mkdirSync as mkdirSync17, rmSync as rmSync3 } from "fs";
19443
+ import { readFileSync as readFileSync23, writeFileSync as writeFileSync14, existsSync as existsSync40, mkdirSync as mkdirSync17, rmSync as rmSync3 } from "fs";
19049
19444
  import { join as join32 } from "path";
19050
19445
 
19051
19446
  class IndexCache {
@@ -19057,7 +19452,7 @@ class IndexCache {
19057
19452
  load() {
19058
19453
  if (this.cache)
19059
19454
  return this.cache;
19060
- if (!existsSync39(this.cachePath))
19455
+ if (!existsSync40(this.cachePath))
19061
19456
  return null;
19062
19457
  try {
19063
19458
  this.cache = JSON.parse(readFileSync23(this.cachePath, "utf-8"));
@@ -19069,13 +19464,13 @@ class IndexCache {
19069
19464
  save(result) {
19070
19465
  this.cache = result;
19071
19466
  const dir = join32(this.cachePath, "..");
19072
- if (!existsSync39(dir))
19467
+ if (!existsSync40(dir))
19073
19468
  mkdirSync17(dir, { recursive: true });
19074
19469
  writeFileSync14(this.cachePath, JSON.stringify(result), "utf-8");
19075
19470
  }
19076
19471
  invalidate() {
19077
19472
  this.cache = null;
19078
- if (existsSync39(this.cachePath)) {
19473
+ if (existsSync40(this.cachePath)) {
19079
19474
  try {
19080
19475
  rmSync3(this.cachePath);
19081
19476
  } catch {}
@@ -19085,11 +19480,11 @@ class IndexCache {
19085
19480
  var init_cache = () => {};
19086
19481
 
19087
19482
  // src/modules/indexer/project-profile.ts
19088
- import { readFileSync as readFileSync24, existsSync as existsSync40 } from "fs";
19483
+ import { readFileSync as readFileSync24, existsSync as existsSync41 } from "fs";
19089
19484
  import { join as join33 } from "path";
19090
19485
  function detectManifest(baseDir) {
19091
19486
  for (const manifest of MANIFEST_ORDER) {
19092
- if (existsSync40(join33(baseDir, manifest)))
19487
+ if (existsSync41(join33(baseDir, manifest)))
19093
19488
  return manifest;
19094
19489
  }
19095
19490
  return null;
@@ -19736,6 +20131,29 @@ var init_module8 = __esm(() => {
19736
20131
  MEMORY_MD_FILES = ["errors", "conventions", "decisions", "facts"];
19737
20132
  });
19738
20133
 
20134
+ // src/core/version.ts
20135
+ import { existsSync as existsSync42, readFileSync as readFileSync25 } from "fs";
20136
+ import { join as join35, dirname as dirname13 } from "path";
20137
+ import { fileURLToPath as fileURLToPath2 } from "url";
20138
+ function readMmaVersion() {
20139
+ const here = dirname13(fileURLToPath2(import.meta.url));
20140
+ const candidates = [
20141
+ join35(here, "..", "..", "package.json"),
20142
+ join35(here, "..", "package.json")
20143
+ ];
20144
+ for (const p of candidates) {
20145
+ if (existsSync42(p)) {
20146
+ try {
20147
+ const raw = JSON.parse(readFileSync25(p, "utf8"));
20148
+ if (raw.version)
20149
+ return raw.version;
20150
+ } catch {}
20151
+ }
20152
+ }
20153
+ return "0.0.0";
20154
+ }
20155
+ var init_version = () => {};
20156
+
19739
20157
  // src/core/bootstrap.ts
19740
20158
  var exports_bootstrap = {};
19741
20159
  __export(exports_bootstrap, {
@@ -19743,8 +20161,8 @@ __export(exports_bootstrap, {
19743
20161
  bootstrap: () => bootstrap
19744
20162
  });
19745
20163
  import { homedir as homedir11 } from "os";
19746
- import { join as join35, resolve as resolve22 } from "path";
19747
- import { existsSync as existsSync41, readFileSync as readFileSync25, writeFileSync as writeFileSync15 } from "fs";
20164
+ import { join as join36, resolve as resolve23 } from "path";
20165
+ import { existsSync as existsSync43, readFileSync as readFileSync26, writeFileSync as writeFileSync15 } from "fs";
19748
20166
  function buildSystemInfo(config, baseDir, profileCompressed) {
19749
20167
  const now = new Date().toISOString().replace("T", " ").slice(0, 19);
19750
20168
  const isWin = profileCompressed.toLowerCase().includes("win32");
@@ -19757,7 +20175,7 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
19757
20175
  lines.push(`Windows (PowerShell): use list_dir/read_file/delete_file/create_dir tools instead of dir/type/del/mkdir. No PowerShell cmdlets (Get-Content, Select-Object, Write-Output), no head/tail/grep/cat. Use forward slashes in paths. CWD: ${baseDir}`);
19758
20176
  }
19759
20177
  lines.push(`Runtime: Bun is available — run TypeScript directly with "bun <file.ts>" and tests with "bun test" (no tsx/ts-node/npm install needed for that).`);
19760
- lines.push(`Bash: use "workdir" param instead of "cd dir && cmd". One command per call. Long-running processes: pass "background: true" (id immediately), otherwise auto-backgrounded after a few seconds — check with process_log.`, `DEVELOPMENT RULES (strict): 1) install deps BEFORE writing source (npm/pip/cargo, verify lockfile exists; never import uninstalled packages); 2) framework/toolkit init BEFORE app code; 3) follow plan sequentially, after each step verify deliverables exist with real content, then "plan update step=N status=done"; 4) verify work on disk + command output — don't assume success; 5) no premature work (no files/imports for future steps); 6) stuck after 2+ failures: STOP, try a different approach, write files directly, ask the user.`, `PLAN QUALITY: each step = CONCRETE deliverables (exact file paths with extensions, exact packages, exact commands). Vague steps ("Setup the project") forbidden. Cover init → deps → framework → code → verification. 5-8 steps.`);
20178
+ lines.push(`Bash: use "workdir" param instead of "cd dir && cmd". One command per call. Long-running processes: pass "background: true" (id immediately), otherwise auto-backgrounded after a few seconds — check with process_log.`, `DEVELOPMENT RULES (strict): 1) install deps BEFORE writing source (npm/pip/cargo, verify lockfile exists; never import uninstalled packages); 2) framework/toolkit init BEFORE app code; 3) follow plan sequentially, after each step verify deliverables exist with real content, then "plan update step=N status=done"; 4) verify work on disk + command output — don't assume success; 5) no premature work (no files/imports for future steps); 6) stuck after 2+ failures: STOP, try a different approach, write files directly, ask the user.`, `PLAN QUALITY: each step = CONCRETE deliverables (exact file paths with extensions, exact packages, exact commands). Vague steps ("Setup the project") forbidden. Cover init → deps → framework → code → verification. 5-8 steps.`, `For "check/fix errors" tasks, each verification step must name the exact command that proves the errors are gone — "bun run build", "bun test", or the "lsp_check" tool — and the step is done only after that check reports clean.`);
19761
20179
  if (config.autoPlan) {
19762
20180
  lines.push(`Plan rule (MANDATORY): any task creating files, installing packages, or requiring multiple actions MUST create a plan with the "plan" tool BEFORE starting. Each step = a concrete deliverable.`);
19763
20181
  }
@@ -19770,8 +20188,8 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
19770
20188
  `);
19771
20189
  }
19772
20190
  async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19773
- const dir = configDir || join35(homedir11(), ".mma");
19774
- const projectConfigPath = projectDir ? join35(projectDir, ".mmrc") : join35(process.cwd(), ".mmrc");
20191
+ const dir = configDir || join36(homedir11(), ".mma");
20192
+ const projectConfigPath = projectDir ? join36(projectDir, ".mmrc") : join36(process.cwd(), ".mmrc");
19775
20193
  const config = loadConfig({ configDir: dir, projectConfigPath });
19776
20194
  setLocale(config.locale);
19777
20195
  try {
@@ -19781,7 +20199,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19781
20199
  }
19782
20200
  } catch {}
19783
20201
  const logger = new Logger(config.logLevel);
19784
- logger.setLogDir(join35(dir, "logs"));
20202
+ logger.setLogDir(join36(dir, "logs"));
19785
20203
  logger.debug("MMA bootstrap", {
19786
20204
  version: config.version,
19787
20205
  model: config.model
@@ -19803,7 +20221,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19803
20221
  logger.info(`Model ${config.model} loaded in ${loadResult.loadTime}s`);
19804
20222
  }
19805
20223
  }
19806
- const profile = new UserProfile(join35(dir));
20224
+ const profile = new UserProfile(join36(dir));
19807
20225
  profile.load() || profile.collect();
19808
20226
  profile.save();
19809
20227
  const llmProvider = new OpenAICompatProvider({
@@ -19814,8 +20232,8 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19814
20232
  retry: config.retry,
19815
20233
  rateLimits: config.security?.rateLimits
19816
20234
  });
19817
- const baseDir = projectDir ? resolve22(projectDir) : process.cwd();
19818
- const projectMapCacheDir = join35(baseDir, ".mma");
20235
+ const baseDir = projectDir ? resolve23(projectDir) : process.cwd();
20236
+ const projectMapCacheDir = join36(baseDir, ".mma");
19819
20237
  const indexerModule = new IndexerModule({
19820
20238
  baseDir,
19821
20239
  cacheDir: projectMapCacheDir
@@ -19826,9 +20244,9 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19826
20244
  logger.warn(`Project indexing failed: ${err.message}`);
19827
20245
  }
19828
20246
  const skillsLoader = new SkillsLoader;
19829
- const builtinDir = join35(import.meta.dirname, "skills", "builtin");
19830
- const globalDir = join35(homedir11(), ".agents", "skills");
19831
- const projectSkillsDir = join35(baseDir, ".mma", "skills");
20247
+ const builtinDir = join36(import.meta.dirname, "skills", "builtin");
20248
+ const globalDir = join36(homedir11(), ".agents", "skills");
20249
+ const projectSkillsDir = join36(baseDir, ".mma", "skills");
19832
20250
  const availableSkills = skillsLoader.loadFromAllSources(builtinDir, globalDir, projectSkillsDir);
19833
20251
  const skillsBudget = Math.floor(config.contextWindow * config.skills.budget);
19834
20252
  const skillsModule = new SkillsModule(availableSkills, skillsBudget);
@@ -19842,11 +20260,11 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19842
20260
  essential: true,
19843
20261
  estimatedTokens: Math.ceil(systemInfoContent.length / 4)
19844
20262
  };
19845
- const agentsMdGlobal = join35(dir, "AGENTS.md");
19846
- if (!existsSync41(agentsMdGlobal)) {
20263
+ const agentsMdGlobal = join36(dir, "AGENTS.md");
20264
+ if (!existsSync43(agentsMdGlobal)) {
19847
20265
  writeFileSync15(agentsMdGlobal, "", "utf-8");
19848
20266
  }
19849
- const sessionDir = join35(dir, "sessions");
20267
+ const sessionDir = join36(dir, "sessions");
19850
20268
  const sessionStore = new SessionStore(sessionDir);
19851
20269
  sessionStore.init();
19852
20270
  const sessionManager = new SessionManager(sessionStore, {
@@ -19913,7 +20331,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19913
20331
  const mcpModule = new MCPModule(config);
19914
20332
  await mcpModule.initialize();
19915
20333
  moduleRegistry.register(mcpModule);
19916
- const memoryStore = new MemoryStore(join35(dir, "memory"));
20334
+ const memoryStore = new MemoryStore(join36(dir, "memory"));
19917
20335
  const memoryModule = new MemoryModule(memoryStore);
19918
20336
  moduleRegistry.register(memoryModule);
19919
20337
  if (config.browser.enabled) {
@@ -19964,10 +20382,17 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19964
20382
  pluginManager.register(plugin);
19965
20383
  pluginManager.register(plugin2);
19966
20384
  const pluginLoader = new PluginLoader;
19967
- const globalPluginsDir = join35(homedir11(), ".mma", "plugins");
19968
- const projectPluginsDir = join35(baseDir, ".mma", "plugins");
19969
- pluginLoader.loadFromDir(globalPluginsDir, pluginManager, logger);
19970
- pluginLoader.loadFromDir(projectPluginsDir, pluginManager, logger);
20385
+ const globalPluginsDir = join36(homedir11(), ".mma", "plugins");
20386
+ const projectPluginsDir = join36(baseDir, ".mma", "plugins");
20387
+ const mmaVersion = readMmaVersion();
20388
+ pluginLoader.loadFromDir(globalPluginsDir, pluginManager, logger, {
20389
+ source: "global",
20390
+ mmaVersion
20391
+ });
20392
+ pluginLoader.loadFromDir(projectPluginsDir, pluginManager, logger, {
20393
+ source: "project",
20394
+ mmaVersion
20395
+ });
19971
20396
  contextManager.onCompact = (summary) => {
19972
20397
  const meta = sessionManager.getActiveMeta();
19973
20398
  if (!meta || !config.session.autoSave)
@@ -19979,17 +20404,18 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19979
20404
  timestamp: new Date().toISOString()
19980
20405
  });
19981
20406
  };
20407
+ contextManager.setPlanSummaryProvider(() => execModule.getPlanSummary());
19982
20408
  const agentsMdBlocks = [];
19983
20409
  const skipAgentsMd = noAgentsMd === true;
19984
20410
  if (!skipAgentsMd) {
19985
20411
  const agentsMdCandidates = [
19986
- join35(baseDir, "AGENTS.md"),
19987
- join35(baseDir, ".mma", "AGENTS.md"),
19988
- join35(dir, "AGENTS.md")
20412
+ join36(baseDir, "AGENTS.md"),
20413
+ join36(baseDir, ".mma", "AGENTS.md"),
20414
+ join36(dir, "AGENTS.md")
19989
20415
  ];
19990
20416
  for (const p of agentsMdCandidates) {
19991
- if (existsSync41(p)) {
19992
- const content = readFileSync25(p, "utf-8").trim();
20417
+ if (existsSync43(p)) {
20418
+ const content = readFileSync26(p, "utf-8").trim();
19993
20419
  if (content) {
19994
20420
  agentsMdBlocks.push({
19995
20421
  content,
@@ -20053,6 +20479,7 @@ var init_bootstrap = __esm(() => {
20053
20479
  init_openai_compat();
20054
20480
  init_tools();
20055
20481
  init_executor();
20482
+ init_manager2();
20056
20483
  init_loader();
20057
20484
  init_lint_on_write();
20058
20485
  init_notify();
@@ -20071,6 +20498,7 @@ var init_bootstrap = __esm(() => {
20071
20498
  init_store2();
20072
20499
  init_i18n();
20073
20500
  init_agent();
20501
+ init_version();
20074
20502
  });
20075
20503
 
20076
20504
  // node_modules/ansi-regex/index.js
@@ -20621,10 +21049,10 @@ function menuList(items) {
20621
21049
  console.log(l);
20622
21050
  }
20623
21051
  function ask(rl, question, defaultValue) {
20624
- return new Promise((resolve23) => {
21052
+ return new Promise((resolve24) => {
20625
21053
  const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
20626
21054
  rl.question(prompt, (answer) => {
20627
- resolve23(answer.trim() || defaultValue || "");
21055
+ resolve24(answer.trim() || defaultValue || "");
20628
21056
  });
20629
21057
  });
20630
21058
  }
@@ -20843,20 +21271,20 @@ __export(exports_manifest, {
20843
21271
  getCertMark: () => getCertMark,
20844
21272
  MANIFEST_PATH: () => MANIFEST_PATH
20845
21273
  });
20846
- import { existsSync as existsSync42, readFileSync as readFileSync26, mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
21274
+ import { existsSync as existsSync44, readFileSync as readFileSync27, mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
20847
21275
  import { homedir as homedir13 } from "os";
20848
- import { join as join37 } from "path";
21276
+ import { join as join38 } from "path";
20849
21277
  function readManifest(path = MANIFEST_PATH) {
20850
21278
  try {
20851
- if (existsSync42(path)) {
20852
- const raw = JSON.parse(readFileSync26(path, "utf-8"));
21279
+ if (existsSync44(path)) {
21280
+ const raw = JSON.parse(readFileSync27(path, "utf-8"));
20853
21281
  return { version: 1, certifications: raw.certifications ?? [] };
20854
21282
  }
20855
21283
  } catch {}
20856
21284
  return { version: 1, certifications: [] };
20857
21285
  }
20858
21286
  function saveManifest(m, path = MANIFEST_PATH) {
20859
- mkdirSync18(join37(homedir13(), ".mma"), { recursive: true });
21287
+ mkdirSync18(join38(homedir13(), ".mma"), { recursive: true });
20860
21288
  writeFileSync16(path, JSON.stringify(m, null, 2), "utf-8");
20861
21289
  }
20862
21290
  function upsertCertification(entry, path = MANIFEST_PATH) {
@@ -20891,7 +21319,7 @@ function getCertMark(model, providerUrl, currentVersion, path = MANIFEST_PATH) {
20891
21319
  }
20892
21320
  var MANIFEST_PATH;
20893
21321
  var init_manifest = __esm(() => {
20894
- MANIFEST_PATH = join37(homedir13(), ".mma", "certifications.json");
21322
+ MANIFEST_PATH = join38(homedir13(), ".mma", "certifications.json");
20895
21323
  });
20896
21324
 
20897
21325
  // node_modules/yaml/dist/nodes/identity.js
@@ -28014,8 +28442,8 @@ var init_scenarios = __esm(() => {
28014
28442
  });
28015
28443
 
28016
28444
  // src/modules/certification/loader.ts
28017
- import { existsSync as existsSync43, readdirSync as readdirSync15, readFileSync as readFileSync27 } from "fs";
28018
- import { join as join38 } from "path";
28445
+ import { existsSync as existsSync45, readdirSync as readdirSync15, readFileSync as readFileSync28 } from "fs";
28446
+ import { join as join39 } from "path";
28019
28447
  function validateScenario(s) {
28020
28448
  const errors2 = [];
28021
28449
  const isSkip = s.mode === "skip";
@@ -28064,12 +28492,12 @@ function loadScenarios(userDir) {
28064
28492
  else
28065
28493
  scenarios.push(s);
28066
28494
  }
28067
- if (userDir && existsSync43(userDir)) {
28495
+ if (userDir && existsSync45(userDir)) {
28068
28496
  for (const file of readdirSync15(userDir)) {
28069
28497
  if (!file.endsWith(".yaml") && !file.endsWith(".yml"))
28070
28498
  continue;
28071
28499
  try {
28072
- const raw = readFileSync27(join38(userDir, file), "utf-8");
28500
+ const raw = readFileSync28(join39(userDir, file), "utf-8");
28073
28501
  const data = $parse(raw);
28074
28502
  const parsed = normalizeScenario(data, file);
28075
28503
  const errs = validateScenario(parsed);
@@ -28122,8 +28550,8 @@ var init_loader3 = __esm(() => {
28122
28550
  });
28123
28551
 
28124
28552
  // src/modules/certification/fact-checker.ts
28125
- import { existsSync as existsSync44, readFileSync as readFileSync28, statSync as statSync8 } from "fs";
28126
- import { join as join39 } from "path";
28553
+ import { existsSync as existsSync46, readFileSync as readFileSync29, statSync as statSync8 } from "fs";
28554
+ import { join as join40 } from "path";
28127
28555
  function checkSandbox(sandboxDir, checks, exitCode, output) {
28128
28556
  const failures = [];
28129
28557
  for (const check of checks) {
@@ -28140,16 +28568,16 @@ function runCheck(sandboxDir, check, exitCode, output) {
28140
28568
  case "outputContains":
28141
28569
  return output.includes(check.text);
28142
28570
  case "fileExists":
28143
- return isFile(join39(sandboxDir, check.path));
28571
+ return isFile(join40(sandboxDir, check.path));
28144
28572
  case "fileNotExists":
28145
- return !existsSync44(join39(sandboxDir, check.path));
28573
+ return !existsSync46(join40(sandboxDir, check.path));
28146
28574
  case "dirExists":
28147
- return isDir(join39(sandboxDir, check.path));
28575
+ return isDir(join40(sandboxDir, check.path));
28148
28576
  case "fileContent": {
28149
- const abs = join39(sandboxDir, check.path);
28577
+ const abs = join40(sandboxDir, check.path);
28150
28578
  if (!isFile(abs))
28151
28579
  return false;
28152
- const content = readFileSync28(abs, "utf-8");
28580
+ const content = readFileSync29(abs, "utf-8");
28153
28581
  if (check.contains !== undefined)
28154
28582
  return content.includes(check.contains);
28155
28583
  if (check.equals !== undefined)
@@ -28157,10 +28585,10 @@ function runCheck(sandboxDir, check, exitCode, output) {
28157
28585
  return false;
28158
28586
  }
28159
28587
  case "fileRegex": {
28160
- const abs = join39(sandboxDir, check.path);
28588
+ const abs = join40(sandboxDir, check.path);
28161
28589
  if (!isFile(abs))
28162
28590
  return false;
28163
- return new RegExp(check.pattern).test(readFileSync28(abs, "utf-8"));
28591
+ return new RegExp(check.pattern).test(readFileSync29(abs, "utf-8"));
28164
28592
  }
28165
28593
  default:
28166
28594
  return false;
@@ -28168,14 +28596,14 @@ function runCheck(sandboxDir, check, exitCode, output) {
28168
28596
  }
28169
28597
  function isFile(p) {
28170
28598
  try {
28171
- return existsSync44(p) && statSync8(p).isFile();
28599
+ return existsSync46(p) && statSync8(p).isFile();
28172
28600
  } catch {
28173
28601
  return false;
28174
28602
  }
28175
28603
  }
28176
28604
  function isDir(p) {
28177
28605
  try {
28178
- return existsSync44(p) && statSync8(p).isDirectory();
28606
+ return existsSync46(p) && statSync8(p).isDirectory();
28179
28607
  } catch {
28180
28608
  return false;
28181
28609
  }
@@ -28206,9 +28634,9 @@ var init_fact_checker = () => {};
28206
28634
 
28207
28635
  // src/modules/certification/runner.ts
28208
28636
  import { spawn as spawn7 } from "child_process";
28209
- import { existsSync as existsSync45, mkdirSync as mkdirSync19, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
28210
- import { platform as platform8 } from "os";
28211
- import { join as join40, resolve as resolve23, dirname as dirname13 } from "path";
28637
+ import { existsSync as existsSync47, mkdirSync as mkdirSync19, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
28638
+ import { platform as platform9 } from "os";
28639
+ import { join as join41, resolve as resolve24, dirname as dirname14 } from "path";
28212
28640
  async function runScenario(scenario, opts) {
28213
28641
  if (scenario.mode === "skip") {
28214
28642
  return {
@@ -28221,13 +28649,13 @@ async function runScenario(scenario, opts) {
28221
28649
  }
28222
28650
  const reps = scenario.reps ?? opts.defaultReps;
28223
28651
  const threshold = Math.min(scenario.passThreshold ?? opts.defaultThreshold, reps);
28224
- const runner = opts.runner ?? defaultRunner;
28652
+ const runner = opts.runner ?? defaultRunner2;
28225
28653
  const timeoutMs = opts.timeoutMs ?? 120000;
28226
28654
  const entryPoint = resolveMmaEntry(opts.mmaRoot);
28227
28655
  let passed = 0;
28228
28656
  let firstError;
28229
28657
  for (let i = 1;i <= reps; i++) {
28230
- const sandbox = join40(opts.sandboxBase, `run-${scenario.id}-${i}`);
28658
+ const sandbox = join41(opts.sandboxBase, `run-${scenario.id}-${i}`);
28231
28659
  let failures = [];
28232
28660
  let exitCode = -1;
28233
28661
  let output = "";
@@ -28288,28 +28716,28 @@ function prepareSandbox(sandbox, scenario, mmaRoot) {
28288
28716
  rmSync4(sandbox, { recursive: true, force: true });
28289
28717
  mkdirSync19(sandbox, { recursive: true });
28290
28718
  for (const f of scenario.fixtures ?? []) {
28291
- const src = join40(mmaRoot, f.source);
28292
- if (!existsSync45(src)) {
28719
+ const src = join41(mmaRoot, f.source);
28720
+ if (!existsSync47(src)) {
28293
28721
  throw new Error(`fixture missing: ${f.source}`);
28294
28722
  }
28295
- const dest = join40(sandbox, f.dest);
28296
- mkdirSync19(dirname13(dest), { recursive: true });
28723
+ const dest = join41(sandbox, f.dest);
28724
+ mkdirSync19(dirname14(dest), { recursive: true });
28297
28725
  cpSync2(src, dest);
28298
28726
  }
28299
28727
  }
28300
28728
  function resolveMmaEntry(mmaRoot) {
28301
- const dev = join40(mmaRoot, "src", "cli", "main.ts");
28302
- if (existsSync45(dev))
28729
+ const dev = join41(mmaRoot, "src", "cli", "main.ts");
28730
+ if (existsSync47(dev))
28303
28731
  return dev;
28304
- return join40(mmaRoot, "dist", "main.js");
28732
+ return join41(mmaRoot, "dist", "main.js");
28305
28733
  }
28306
28734
  function findMmaRoot(fromDir) {
28307
28735
  const candidates = [
28308
- resolve23(fromDir, "..", "..", ".."),
28309
- resolve23(fromDir, "..")
28736
+ resolve24(fromDir, "..", "..", ".."),
28737
+ resolve24(fromDir, "..")
28310
28738
  ];
28311
28739
  for (const c of candidates) {
28312
- if (existsSync45(join40(c, "package.json")))
28740
+ if (existsSync47(join41(c, "package.json")))
28313
28741
  return c;
28314
28742
  }
28315
28743
  return process.cwd();
@@ -28318,7 +28746,7 @@ function killTree2(child) {
28318
28746
  const pid = child.pid;
28319
28747
  if (!pid)
28320
28748
  return;
28321
- if (platform8() === "win32") {
28749
+ if (platform9() === "win32") {
28322
28750
  spawn7("taskkill", ["/pid", String(pid), "/T", "/F"], {
28323
28751
  windowsHide: true,
28324
28752
  stdio: "ignore"
@@ -28333,7 +28761,7 @@ function killTree2(child) {
28333
28761
  } catch {}
28334
28762
  }
28335
28763
  }
28336
- var defaultRunner = (env2, cwd, args, timeoutMs) => new Promise((resolvePromise) => {
28764
+ var defaultRunner2 = (env2, cwd, args, timeoutMs) => new Promise((resolvePromise) => {
28337
28765
  const child = spawn7(process.execPath, args, {
28338
28766
  cwd,
28339
28767
  env: env2,
@@ -28377,15 +28805,15 @@ __export(exports_cli, {
28377
28805
  });
28378
28806
  import { rmSync as rmSync5 } from "fs";
28379
28807
  import { homedir as homedir14 } from "os";
28380
- import { join as join41, dirname as dirname14 } from "path";
28381
- import { fileURLToPath as fileURLToPath2 } from "url";
28382
- import { existsSync as existsSync46, readFileSync as readFileSync29 } from "fs";
28808
+ import { join as join42, dirname as dirname15 } from "path";
28809
+ import { fileURLToPath as fileURLToPath3 } from "url";
28810
+ import { existsSync as existsSync48, readFileSync as readFileSync30 } from "fs";
28383
28811
  function readVersion() {
28384
- const candidates = [join41(MMA_ROOT, "package.json")];
28812
+ const candidates = [join42(MMA_ROOT, "package.json")];
28385
28813
  for (const p of candidates) {
28386
- if (existsSync46(p)) {
28814
+ if (existsSync48(p)) {
28387
28815
  try {
28388
- const raw = JSON.parse(readFileSync29(p, "utf-8"));
28816
+ const raw = JSON.parse(readFileSync30(p, "utf-8"));
28389
28817
  if (raw.version)
28390
28818
  return raw.version;
28391
28819
  } catch {}
@@ -28421,7 +28849,7 @@ async function certify(opts) {
28421
28849
  return;
28422
28850
  }
28423
28851
  console.log(t("cli.cert_started", { model: opts.name, provider: providerUrl }));
28424
- const sandboxBase = join41(process.cwd(), ".mma", "certification");
28852
+ const sandboxBase = join42(process.cwd(), ".mma", "certification");
28425
28853
  const results = [];
28426
28854
  const total = selected.length;
28427
28855
  let idx = 0;
@@ -28533,9 +28961,9 @@ var init_cli = __esm(() => {
28533
28961
  init_loader3();
28534
28962
  init_runner2();
28535
28963
  init_manifest();
28536
- HERE = dirname14(fileURLToPath2(import.meta.url));
28964
+ HERE = dirname15(fileURLToPath3(import.meta.url));
28537
28965
  MMA_ROOT = findMmaRoot(HERE);
28538
- USER_SCENARIO_DIR = join41(homedir14(), ".mma", "certification", "scenarios");
28966
+ USER_SCENARIO_DIR = join42(homedir14(), ".mma", "certification", "scenarios");
28539
28967
  });
28540
28968
 
28541
28969
  // src/cli/repl-commands.ts
@@ -28544,20 +28972,20 @@ __export(exports_repl_commands, {
28544
28972
  registerAllCommands: () => registerAllCommands,
28545
28973
  COMMAND_GROUPS: () => COMMAND_GROUPS
28546
28974
  });
28547
- import { join as join43, dirname as dirname16 } from "path";
28975
+ import { join as join44, dirname as dirname17 } from "path";
28548
28976
  import { homedir as homedir16 } from "os";
28549
- import { existsSync as existsSync48, readFileSync as readFileSync31 } from "fs";
28550
- import { fileURLToPath as fileURLToPath4 } from "url";
28977
+ import { existsSync as existsSync50, readFileSync as readFileSync32 } from "fs";
28978
+ import { fileURLToPath as fileURLToPath5 } from "url";
28551
28979
  function readVersion3() {
28552
- const here = dirname16(fileURLToPath4(import.meta.url));
28980
+ const here = dirname17(fileURLToPath5(import.meta.url));
28553
28981
  const candidates = [
28554
- join43(here, "..", "..", "package.json"),
28555
- join43(here, "..", "package.json")
28982
+ join44(here, "..", "..", "package.json"),
28983
+ join44(here, "..", "package.json")
28556
28984
  ];
28557
28985
  for (const p of candidates) {
28558
- if (existsSync48(p)) {
28986
+ if (existsSync50(p)) {
28559
28987
  try {
28560
- const raw = JSON.parse(readFileSync31(p, "utf8"));
28988
+ const raw = JSON.parse(readFileSync32(p, "utf8"));
28561
28989
  if (raw.version)
28562
28990
  return raw.version;
28563
28991
  } catch {}
@@ -28621,8 +29049,8 @@ function registerMmaCommands(ctx) {
28621
29049
  }
28622
29050
  try {
28623
29051
  const { loadFileAsDataUrl: loadFileAsDataUrl2, loadUrlAsDataUrl: loadUrlAsDataUrl2, readClipboardImage: readClipboardImage2 } = await Promise.resolve().then(() => (init_image_utils(), exports_image_utils));
28624
- const { existsSync: existsSync49 } = await import("fs");
28625
- const { resolve: resolve24 } = await import("path");
29052
+ const { existsSync: existsSync51 } = await import("fs");
29053
+ const { resolve: resolve25 } = await import("path");
28626
29054
  let dataUrl;
28627
29055
  let label;
28628
29056
  if (source.toLowerCase() === "clipboard") {
@@ -28640,8 +29068,8 @@ function registerMmaCommands(ctx) {
28640
29068
  dataUrl = result.dataUrl;
28641
29069
  label = source;
28642
29070
  } else {
28643
- const absPath = resolve24(process.cwd(), source);
28644
- if (!existsSync49(absPath)) {
29071
+ const absPath = resolve25(process.cwd(), source);
29072
+ if (!existsSync51(absPath)) {
28645
29073
  console.log(pc2.red(t("image.not_found", { path: source })));
28646
29074
  return;
28647
29075
  }
@@ -28720,7 +29148,7 @@ function registerMmaCommands(ctx) {
28720
29148
  console.log(pc2.yellow(t("repl.wizard_running")));
28721
29149
  await ctx.withExclusiveInput(async () => {
28722
29150
  const answers = await runSetup(ctx.rl);
28723
- const configPath = join43(homedir16(), ".mma", "config.json");
29151
+ const configPath = join44(homedir16(), ".mma", "config.json");
28724
29152
  ctx.config.provider.type = answers.provider;
28725
29153
  ctx.config.provider.baseUrl = answers.apiBase;
28726
29154
  ctx.config.provider.apiKey = answers.apiKey;
@@ -28774,7 +29202,7 @@ Excluded blocks: ${info.excluded.length}`));
28774
29202
  return;
28775
29203
  }
28776
29204
  ctx.config.provider.type = name;
28777
- const configPath = join43(homedir16(), ".mma", "config.json");
29205
+ const configPath = join44(homedir16(), ".mma", "config.json");
28778
29206
  saveConfig(ctx.config, configPath);
28779
29207
  await ctx.agent.reconfigure(ctx.config);
28780
29208
  console.log(pc2.green(t("repl.provider_set", { name })));
@@ -28830,7 +29258,7 @@ Excluded blocks: ${info.excluded.length}`));
28830
29258
  return;
28831
29259
  }
28832
29260
  ctx.config.model = name;
28833
- const configPath = join43(homedir16(), ".mma", "config.json");
29261
+ const configPath = join44(homedir16(), ".mma", "config.json");
28834
29262
  saveConfig(ctx.config, configPath);
28835
29263
  await ctx.agent.reconfigure(ctx.config);
28836
29264
  console.log(pc2.green(t("repl.model_set", { name })));
@@ -28855,7 +29283,7 @@ Excluded blocks: ${info.excluded.length}`));
28855
29283
  return;
28856
29284
  }
28857
29285
  ctx.config.contextWindow = size;
28858
- const configPath = join43(homedir16(), ".mma", "config.json");
29286
+ const configPath = join44(homedir16(), ".mma", "config.json");
28859
29287
  saveConfig(ctx.config, configPath);
28860
29288
  await ctx.agent.reconfigure(ctx.config);
28861
29289
  console.log(pc2.green(t("cli.context_set", { size })));
@@ -28874,10 +29302,10 @@ Excluded blocks: ${info.excluded.length}`));
28874
29302
  ctx.agent.shutdown();
28875
29303
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
28876
29304
  const { homedir: homedir17 } = await import("os");
28877
- const { join: join44 } = await import("path");
29305
+ const { join: join45 } = await import("path");
28878
29306
  const configDir = ctx.configDir;
28879
29307
  const baseDir = ctx.baseDir;
28880
- const projectConfigPath = join44(baseDir, ".mmrc");
29308
+ const projectConfigPath = join45(baseDir, ".mmrc");
28881
29309
  const freshConfig = loadConfig2({ configDir, projectConfigPath });
28882
29310
  Object.assign(ctx.config, freshConfig);
28883
29311
  const { bootstrap: bootstrap2 } = await Promise.resolve().then(() => (init_bootstrap(), exports_bootstrap));
@@ -29177,14 +29605,14 @@ init_bootstrap();
29177
29605
  init_config2();
29178
29606
  init_setup();
29179
29607
  init_i18n();
29180
- import { join as join42, dirname as dirname15 } from "path";
29608
+ import { join as join43, dirname as dirname16 } from "path";
29181
29609
  import { homedir as homedir15 } from "os";
29182
- import { existsSync as existsSync47, readFileSync as readFileSync30 } from "fs";
29610
+ import { existsSync as existsSync49, readFileSync as readFileSync31 } from "fs";
29183
29611
 
29184
29612
  // src/cli/security-commands.ts
29185
29613
  init_bootstrap();
29186
29614
  init_config2();
29187
- import { join as join36 } from "path";
29615
+ import { join as join37 } from "path";
29188
29616
  import { homedir as homedir12 } from "os";
29189
29617
 
29190
29618
  // src/modules/security/security-policies.ts
@@ -29714,7 +30142,7 @@ function createSecurityCommand(program2) {
29714
30142
  }
29715
30143
  });
29716
30144
  securityCmd.command("set-policy").argument("<preset>", t("cli.security.preset")).description(t("cli.security.set_policy")).action(async (preset) => {
29717
- const configPath = join36(homedir12(), ".mma", "config.json");
30145
+ const configPath = join37(homedir12(), ".mma", "config.json");
29718
30146
  const { config: appConfig } = await bootstrap();
29719
30147
  const validPresets = ["strict", "balanced", "permissive"];
29720
30148
  if (!validPresets.includes(preset)) {
@@ -29729,7 +30157,7 @@ function createSecurityCommand(program2) {
29729
30157
  console.log(t("cli.security.policy_description", { description: policy.description }));
29730
30158
  });
29731
30159
  securityCmd.command("enable-encryption").description(t("cli.security.enable_encryption")).action(async () => {
29732
- const configPath = join36(homedir12(), ".mma", "config.json");
30160
+ const configPath = join37(homedir12(), ".mma", "config.json");
29733
30161
  const { config: appConfig } = await bootstrap();
29734
30162
  appConfig.security = appConfig.security || {};
29735
30163
  appConfig.security.sessionEncryption = {
@@ -29741,7 +30169,7 @@ function createSecurityCommand(program2) {
29741
30169
  console.log(t("cli.security.encryption_enabled"));
29742
30170
  });
29743
30171
  securityCmd.command("disable-encryption").description(t("cli.security.disable_encryption")).action(async () => {
29744
- const configPath = join36(homedir12(), ".mma", "config.json");
30172
+ const configPath = join37(homedir12(), ".mma", "config.json");
29745
30173
  const { config: appConfig } = await bootstrap();
29746
30174
  appConfig.security = appConfig.security || {};
29747
30175
  appConfig.security.sessionEncryption = {
@@ -29753,7 +30181,7 @@ function createSecurityCommand(program2) {
29753
30181
  console.log(t("cli.security.encryption_disabled"));
29754
30182
  });
29755
30183
  securityCmd.command("enable-audit").description(t("cli.security.enable_audit")).action(async () => {
29756
- const configPath = join36(homedir12(), ".mma", "config.json");
30184
+ const configPath = join37(homedir12(), ".mma", "config.json");
29757
30185
  const { config: appConfig } = await bootstrap();
29758
30186
  appConfig.security = appConfig.security || {};
29759
30187
  appConfig.security.auditNotifier = {
@@ -29767,7 +30195,7 @@ function createSecurityCommand(program2) {
29767
30195
  console.log(t("cli.security.audit_enabled"));
29768
30196
  });
29769
30197
  securityCmd.command("disable-audit").description(t("cli.security.disable_audit")).action(async () => {
29770
- const configPath = join36(homedir12(), ".mma", "config.json");
30198
+ const configPath = join37(homedir12(), ".mma", "config.json");
29771
30199
  const { config: appConfig } = await bootstrap();
29772
30200
  appConfig.security = appConfig.security || {};
29773
30201
  appConfig.security.auditNotifier = {
@@ -29798,18 +30226,49 @@ function createSecurityCommand(program2) {
29798
30226
  });
29799
30227
  }
29800
30228
 
30229
+ // src/cli/plugin-commands.ts
30230
+ init_bootstrap();
30231
+ init_version();
30232
+ init_i18n();
30233
+ function createPluginCommand(program2) {
30234
+ const pluginsCmd = program2.command("plugins").description(t("cli.plugins.description"));
30235
+ pluginsCmd.command("list").description(t("cli.plugins.list")).option("-a, --all", t("cli.plugins.all")).action(async (cmdOpts) => {
30236
+ const { pluginManager } = await bootstrap();
30237
+ const all = cmdOpts.all === true;
30238
+ let infos = pluginManager.getPluginInfos();
30239
+ if (!all) {
30240
+ infos = infos.filter(({ plugin: plugin3 }) => !plugin3.isBuiltin);
30241
+ }
30242
+ if (infos.length === 0) {
30243
+ console.log(t("cli.plugins.none"));
30244
+ return;
30245
+ }
30246
+ const mmaVersion = readMmaVersion();
30247
+ console.log(t("cli.plugins.header", { count: String(infos.length), mma: mmaVersion }));
30248
+ for (const { plugin: plugin3, source } of infos) {
30249
+ const version = plugin3.version || "-";
30250
+ const origin = source || "builtin";
30251
+ const builtin = plugin3.isBuiltin ? t("cli.plugins.builtin_mark") : " ";
30252
+ console.log(` ${builtin} ${plugin3.name} v${version} [${origin}]`);
30253
+ }
30254
+ if (!all) {
30255
+ console.log(t("cli.plugins.only_external"));
30256
+ }
30257
+ });
30258
+ }
30259
+
29801
30260
  // src/cli/commands.ts
29802
- import { fileURLToPath as fileURLToPath3 } from "url";
30261
+ import { fileURLToPath as fileURLToPath4 } from "url";
29803
30262
  function readVersion2() {
29804
- const here = dirname15(fileURLToPath3(import.meta.url));
30263
+ const here = dirname16(fileURLToPath4(import.meta.url));
29805
30264
  const candidates = [
29806
- join42(here, "..", "..", "package.json"),
29807
- join42(here, "..", "package.json")
30265
+ join43(here, "..", "..", "package.json"),
30266
+ join43(here, "..", "package.json")
29808
30267
  ];
29809
30268
  for (const p of candidates) {
29810
- if (existsSync47(p)) {
30269
+ if (existsSync49(p)) {
29811
30270
  try {
29812
- const raw = JSON.parse(readFileSync30(p, "utf8"));
30271
+ const raw = JSON.parse(readFileSync31(p, "utf8"));
29813
30272
  if (raw.version)
29814
30273
  return raw.version;
29815
30274
  } catch {}
@@ -29822,7 +30281,7 @@ function createProgram() {
29822
30281
  const program2 = new Command().name("mma").description(t("cli.description")).version(version).option("--no-agents-md", t("cli.no_agents_md")).option("-d, --dir <path>", t("cli.dir")).option("-e, --exit-on-complete", t("cli.exit_on_complete")).option("-j, --json", t("cli.json"));
29823
30282
  program2.command("init").description(t("cli.init")).action(async () => {
29824
30283
  const answers = await runSetup();
29825
- const configPath = join42(homedir15(), ".mma", "config.json");
30284
+ const configPath = join43(homedir15(), ".mma", "config.json");
29826
30285
  const { config } = await bootstrap();
29827
30286
  config.provider.type = answers.provider;
29828
30287
  config.provider.baseUrl = answers.apiBase;
@@ -29867,7 +30326,7 @@ function createProgram() {
29867
30326
  });
29868
30327
  const configCmd = program2.command("config").description(t("cli.manage_config"));
29869
30328
  configCmd.command("set").argument("<key>", t("cli.config_key")).argument("<value>", "Config value").description(t("cli.set_value")).action(async (key, value) => {
29870
- const configPath = join42(homedir15(), ".mma", "config.json");
30329
+ const configPath = join43(homedir15(), ".mma", "config.json");
29871
30330
  const { config } = await bootstrap();
29872
30331
  const keys = key.split(".");
29873
30332
  let obj = config;
@@ -29930,7 +30389,7 @@ function createProgram() {
29930
30389
  console.log(t("cli.model_hint"));
29931
30390
  });
29932
30391
  model.command("use").argument("<name>", "Model name").description(t("cli.set_model")).action(async (name) => {
29933
- const configPath = join42(homedir15(), ".mma", "config.json");
30392
+ const configPath = join43(homedir15(), ".mma", "config.json");
29934
30393
  const { config } = await bootstrap();
29935
30394
  config.model = name;
29936
30395
  saveConfig(config, configPath);
@@ -29966,7 +30425,7 @@ function createProgram() {
29966
30425
  await uncertify2(name, config);
29967
30426
  });
29968
30427
  program2.command("context").description(t("cli.manage_context")).argument("<size>", "Context window size in tokens").action(async (size) => {
29969
- const configPath = join42(homedir15(), ".mma", "config.json");
30428
+ const configPath = join43(homedir15(), ".mma", "config.json");
29970
30429
  const { config } = await bootstrap();
29971
30430
  const contextWindow = parseInt(size, 10);
29972
30431
  if (isNaN(contextWindow) || contextWindow < 1024) {
@@ -29984,7 +30443,7 @@ function createProgram() {
29984
30443
  console.log(t("cli.base_url"), config.provider.baseUrl);
29985
30444
  });
29986
30445
  provider.command("use").argument("<name>", "Provider name").description(t("cli.set_provider")).action(async (name) => {
29987
- const configPath = join42(homedir15(), ".mma", "config.json");
30446
+ const configPath = join43(homedir15(), ".mma", "config.json");
29988
30447
  const { config } = await bootstrap();
29989
30448
  config.provider.type = name;
29990
30449
  saveConfig(config, configPath);
@@ -30026,6 +30485,7 @@ function createProgram() {
30026
30485
  console.log(t("session.deleted", { id }));
30027
30486
  });
30028
30487
  createSecurityCommand(program2);
30488
+ createPluginCommand(program2);
30029
30489
  program2.argument("[prompt...]", "Prompt to execute").description("Run a single prompt").action((prompt) => {});
30030
30490
  return program2;
30031
30491
  }
@@ -30791,10 +31251,10 @@ class LineEditor {
30791
31251
  }
30792
31252
 
30793
31253
  // src/cli/repl.ts
30794
- import { existsSync as existsSync49, readFileSync as readFileSync32, writeFileSync as writeFileSync17 } from "fs";
30795
- import { join as join44, dirname as dirname17 } from "path";
31254
+ import { existsSync as existsSync51, readFileSync as readFileSync33, writeFileSync as writeFileSync17 } from "fs";
31255
+ import { join as join45, dirname as dirname18 } from "path";
30796
31256
  import { homedir as homedir17 } from "os";
30797
- import { fileURLToPath as fileURLToPath5 } from "url";
31257
+ import { fileURLToPath as fileURLToPath6 } from "url";
30798
31258
 
30799
31259
  // src/cli/completer.ts
30800
31260
  class SlashCommandProvider {
@@ -31296,15 +31756,15 @@ init_box();
31296
31756
  init_i18n();
31297
31757
  init_repl_commands();
31298
31758
  function readVersion4() {
31299
- const here = dirname17(fileURLToPath5(import.meta.url));
31759
+ const here = dirname18(fileURLToPath6(import.meta.url));
31300
31760
  const candidates = [
31301
- join44(here, "..", "..", "package.json"),
31302
- join44(here, "..", "package.json")
31761
+ join45(here, "..", "..", "package.json"),
31762
+ join45(here, "..", "package.json")
31303
31763
  ];
31304
31764
  for (const p of candidates) {
31305
- if (existsSync49(p)) {
31765
+ if (existsSync51(p)) {
31306
31766
  try {
31307
- const raw = JSON.parse(readFileSync32(p, "utf8"));
31767
+ const raw = JSON.parse(readFileSync33(p, "utf8"));
31308
31768
  if (raw.version)
31309
31769
  return raw.version;
31310
31770
  } catch {}
@@ -31360,10 +31820,10 @@ class Repl {
31360
31820
  this.skillsModule = skillsModule;
31361
31821
  this.pluginManager = pluginManager;
31362
31822
  this.logger = logger;
31363
- this.configDir = configDir || join44(homedir17(), ".mma");
31823
+ this.configDir = configDir || join45(homedir17(), ".mma");
31364
31824
  this.baseDir = baseDir || process.cwd();
31365
31825
  this.noAgentsMd = noAgentsMd === true;
31366
- this.historyPath = join44(homedir17(), ".mma", "repl-history");
31826
+ this.historyPath = join45(homedir17(), ".mma", "repl-history");
31367
31827
  this.loadHistory();
31368
31828
  this.rl = process.stdin.isTTY ? new LineEditor({
31369
31829
  input: process.stdin,
@@ -31396,9 +31856,9 @@ class Repl {
31396
31856
  this.setupListeners();
31397
31857
  }
31398
31858
  loadHistory() {
31399
- if (existsSync49(this.historyPath)) {
31859
+ if (existsSync51(this.historyPath)) {
31400
31860
  try {
31401
- const raw = readFileSync32(this.historyPath, "utf-8");
31861
+ const raw = readFileSync33(this.historyPath, "utf-8");
31402
31862
  this.history = raw.split(`
31403
31863
  `).filter(Boolean).slice(-this.maxHistory);
31404
31864
  } catch {
@@ -31737,10 +32197,14 @@ ${t("image.clipboard_empty")}`));
31737
32197
  row(t("repl.skills_label"), `${pc2.white(String(this.skillsModule.getAvailable().length))} available, ${pc2.dim(`budget: ${budget.total} tokens`)}`);
31738
32198
  }
31739
32199
  if (this.pluginManager) {
31740
- const plugins = this.pluginManager.getAllPlugins().filter((p) => !p.isBuiltin);
31741
- if (plugins.length > 0) {
31742
- const pluginNames = plugins.map((p) => p.name).join(", ");
31743
- row(t("repl.plugins_label"), `${pc2.white(String(plugins.length))} active ${pc2.dim(`(${pluginNames})`)}`);
32200
+ const infos = this.pluginManager.getPluginInfos().filter(({ plugin: plugin3 }) => !plugin3.isBuiltin);
32201
+ if (infos.length > 0) {
32202
+ const pluginNames = infos.map(({ plugin: plugin3, source }) => {
32203
+ const version4 = plugin3.version ? `${pc2.dim(plugin3.version)}` : "";
32204
+ const origin = source ? pc2.dim(` [${source}]`) : "";
32205
+ return `${plugin3.name}${version4}${origin}`;
32206
+ }).join(", ");
32207
+ row(t("repl.plugins_label"), `${pc2.white(String(infos.length))} active ${pc2.dim(`(${pluginNames})`)}`);
31744
32208
  }
31745
32209
  }
31746
32210
  const mcpServers = this.config.mcpServers || {};
@@ -31755,11 +32219,11 @@ ${t("image.clipboard_empty")}`));
31755
32219
  row(t("repl.agents_label"), pc2.red(t("repl.disabled")));
31756
32220
  } else {
31757
32221
  const agentsMdCandidates = [
31758
- join44(this.baseDir, "AGENTS.md"),
31759
- join44(this.baseDir, ".mma", "AGENTS.md"),
31760
- join44(this.configDir, "AGENTS.md")
32222
+ join45(this.baseDir, "AGENTS.md"),
32223
+ join45(this.baseDir, ".mma", "AGENTS.md"),
32224
+ join45(this.configDir, "AGENTS.md")
31761
32225
  ];
31762
- const foundAgents = agentsMdCandidates.filter((p) => existsSync49(p));
32226
+ const foundAgents = agentsMdCandidates.filter((p) => existsSync51(p));
31763
32227
  if (foundAgents.length > 0) {
31764
32228
  for (const p of foundAgents) {
31765
32229
  row(t("repl.agents_label"), pc2.dim(p));
@@ -31770,7 +32234,7 @@ ${t("image.clipboard_empty")}`));
31770
32234
  }
31771
32235
  const meta = this.sessionManager?.getActiveMeta();
31772
32236
  if (meta) {
31773
- const sessionPath = join44(this.configDir, "sessions", meta.id);
32237
+ const sessionPath = join45(this.configDir, "sessions", meta.id);
31774
32238
  row(t("repl.session_label"), `${pc2.cyan(meta.name)} ${pc2.dim(`(${meta.id.slice(0, 12)})`)} — ${meta.messageCount} msgs ${pc2.dim(sessionPath)}`);
31775
32239
  }
31776
32240
  const headerWidth = Math.max(50, Math.min(96, process.stdout.columns || 96));
@@ -31811,93 +32275,16 @@ init_setup();
31811
32275
  init_config2();
31812
32276
  init_i18n();
31813
32277
  init_colors();
31814
- import { existsSync as existsSync50, readFileSync as readFileSync33 } from "fs";
31815
- import { join as join45, dirname as dirname18 } from "path";
32278
+ import { existsSync as existsSync52, readFileSync as readFileSync34 } from "fs";
32279
+ import { join as join46, dirname as dirname19 } from "path";
31816
32280
  import { homedir as homedir18 } from "os";
31817
- import { fileURLToPath as fileURLToPath6 } from "url";
32281
+ import { fileURLToPath as fileURLToPath7 } from "url";
31818
32282
 
31819
- // src/modules/updater/checker.ts
31820
- init_command();
31821
- import { platform as platform9 } from "os";
31822
- function semverGt(a, b) {
31823
- const pa = a.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
31824
- const pb = b.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
31825
- for (let i = 0;i < 3; i++) {
31826
- const na = pa[i] ?? 0;
31827
- const nb = pb[i] ?? 0;
31828
- if (na !== nb)
31829
- return na > nb;
31830
- }
31831
- return false;
31832
- }
31833
- var defaultRunner2 = async (command, args, options) => {
31834
- const { execFile } = await import("child_process");
31835
- return new Promise((resolve24) => {
31836
- execFile(command, args, options, (err) => resolve24({ error: err?.message }));
31837
- });
31838
- };
32283
+ // src/modules/updater/index.ts
32284
+ init_checker();
31839
32285
 
31840
- class Updater {
31841
- currentVersion;
31842
- packageName;
31843
- runner;
31844
- constructor(currentVersion, packageName, runner) {
31845
- this.currentVersion = currentVersion;
31846
- this.packageName = packageName;
31847
- this.runner = runner ?? defaultRunner2;
31848
- }
31849
- getCurrentVersion() {
31850
- return this.currentVersion;
31851
- }
31852
- getPackageName() {
31853
- return this.packageName;
31854
- }
31855
- async check() {
31856
- try {
31857
- const response = await fetch(`https://registry.npmjs.org/${this.packageName}`, {
31858
- headers: { Accept: "application/vnd.npm.install-v1+json" },
31859
- signal: AbortSignal.timeout(5000)
31860
- });
31861
- if (!response.ok) {
31862
- return { updateAvailable: false, current: this.currentVersion, error: `HTTP ${response.status}` };
31863
- }
31864
- const data = await response.json();
31865
- const latest = data["dist-tags"]?.latest;
31866
- if (!latest) {
31867
- return { updateAvailable: false, current: this.currentVersion, error: "No latest tag" };
31868
- }
31869
- const updateAvailable = semverGt(latest, this.currentVersion);
31870
- return { updateAvailable, current: this.currentVersion, latest };
31871
- } catch (e) {
31872
- return { updateAvailable: false, current: this.currentVersion, error: e.message };
31873
- }
31874
- }
31875
- async install(latest) {
31876
- try {
31877
- const { command, args } = this.buildInstallCommand(latest);
31878
- const res = await this.runner(command, args, {
31879
- timeout: 120000,
31880
- windowsHide: true
31881
- });
31882
- if (res.error)
31883
- return { success: false, error: res.error };
31884
- return { success: true, installed: latest };
31885
- } catch (e) {
31886
- const msg = e?.message ?? String(e);
31887
- return { success: false, error: msg.split(`
31888
- `)[0] };
31889
- }
31890
- }
31891
- buildInstallCommand(latest) {
31892
- const npmArgs = ["install", "-g", `${this.packageName}@${latest}`];
31893
- const command = resolveSpawnCommand("npm");
31894
- if (platform9() === "win32" && /\.(cmd|bat)$/i.test(command)) {
31895
- return { command: "cmd.exe", args: ["/c", command, ...npmArgs] };
31896
- }
31897
- return { command, args: npmArgs };
31898
- }
31899
- }
31900
32286
  // src/modules/updater/module.ts
32287
+ init_checker();
31901
32288
  init_i18n();
31902
32289
  init_colors();
31903
32290
 
@@ -31992,15 +32379,15 @@ class UpdaterModule {
31992
32379
  }
31993
32380
  // src/cli/main.ts
31994
32381
  function readVersion5() {
31995
- const here = dirname18(fileURLToPath6(import.meta.url));
32382
+ const here = dirname19(fileURLToPath7(import.meta.url));
31996
32383
  const candidates = [
31997
- join45(here, "..", "..", "package.json"),
31998
- join45(here, "..", "package.json")
32384
+ join46(here, "..", "..", "package.json"),
32385
+ join46(here, "..", "package.json")
31999
32386
  ];
32000
32387
  for (const p of candidates) {
32001
- if (existsSync50(p)) {
32388
+ if (existsSync52(p)) {
32002
32389
  try {
32003
- const raw = JSON.parse(readFileSync33(p, "utf8"));
32390
+ const raw = JSON.parse(readFileSync34(p, "utf8"));
32004
32391
  if (raw.version)
32005
32392
  return raw.version;
32006
32393
  } catch {}
@@ -32083,15 +32470,15 @@ async function main() {
32083
32470
  await updater?.waitForIdle();
32084
32471
  process.exit(exitCode);
32085
32472
  } else {
32086
- const configPath = join45(homedir18(), ".mma", "config.json");
32087
- if (!existsSync50(configPath)) {
32473
+ const configPath = join46(homedir18(), ".mma", "config.json");
32474
+ if (!existsSync52(configPath)) {
32088
32475
  console.log(pc2.yellow(`
32089
32476
  ` + t("cli.first_run") + `
32090
32477
  `));
32091
32478
  const answers = await runSetup();
32092
32479
  const config2 = loadConfig({
32093
- configDir: join45(homedir18(), ".mma"),
32094
- projectConfigPath: projectDir ? join45(projectDir, ".mmrc") : join45(process.cwd(), ".mmrc")
32480
+ configDir: join46(homedir18(), ".mma"),
32481
+ projectConfigPath: projectDir ? join46(projectDir, ".mmrc") : join46(process.cwd(), ".mmrc")
32095
32482
  });
32096
32483
  config2.provider.type = answers.provider;
32097
32484
  config2.provider.baseUrl = answers.apiBase;