micro-models-agent 0.20.0 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -2548,14 +2548,19 @@ Available commands:`,
2548
2548
 
2549
2549
  Use this knowledge to answer the user's question.`,
2550
2550
  "exec.stuck": "Stuck on step {stepId}. Current: {description}. No progress for {iterations} iterations.",
2551
- "exec.stuck_recovery": "[Stuck detected: {iterations} iterations without progress. Stop repeating the same approach. Try a fundamentally different strategy: use a different tool (e.g., 'browser' tool for JavaScript-heavy websites instead of 'web_browse'), break the task into smaller parts, or explain to the user what went wrong and ask for guidance.]",
2551
+ "exec.stuck_recovery": '[STUCK: {iterations} iterations on step {stepId} — "{description}". You MUST change approach NOW. Concrete actions: (1) Check what this step needs are dependencies installed? (2) Use plan show to see the plan. (3) If commands fail create files directly via write_file. (4) After completing the step, call plan update step={stepId} status=done. Do NOT work on other steps until this one is finished.]',
2552
2552
  "exec.tool_errors": "Tool {tool} failed {count} times. Suggest alternative approach.",
2553
- "exec.tool_errors_recovery": "[Tool {tool} failed {count} times. Stop using this tool and try a different approach or explain the issue to the user.]",
2554
- "exec.repetitive_tool": "[You called {tool} {count} times in a row with the same arguments and result. Stop repeating try a different approach: create files, change arguments, or explain the problem to the user.]",
2555
- "exec.consecutive_failures_recovery": "[{count} consecutive tool calls failed. You are going in circles stop trying commands that keep failing. Explain the problem to the user, or try a completely different approach: write files directly, use a different tool, or break the task into smaller parts.]",
2556
- "exec.task_reminder": "[Remember the original task: {task}. Do not switch to unrelated tasks. If you cannot complete the original task, explain why to the user and ask for guidance.]",
2557
- "exec.plan_warning": '[⚠ Warning: you are working on step {step} - "{description}". Your {tool} call touches files not mentioned in the current step. Focus on the current step or update the plan via plan update.]',
2558
- "exec.off_track": `[ Outside plan: step {stepId} is "{description}", but you're using {tool} on a different path. Explain or return.]`,
2553
+ "exec.tool_errors_recovery": "[Tool {tool} failed {count} consecutive times. DO NOT use it again. Try a different approach: (1) create files directly via write_file, (2) use a different tool for the same task, (3) if nothing works — explain the problem and skip this step via plan update step=N status=skipped with a note why.]",
2554
+ "exec.repetitive_tool": "[REPETITION: you called {tool} {count} times with identical arguments and result. This is pointless. IMMEDIATELY change approach: create files directly, change arguments, or if it's a process check its status via process_log.]",
2555
+ "exec.consecutive_failures_recovery": "[{count} CONSECUTIVE TOOLS FAILED. Stop running commands. Here is what to do: (1) Create files directly via write_file instead of terminal commands. (2) Check if dependencies are installed (npm install). (3) Use list_dir to see what's in the project. (4) Do not attempt build/tests until all files are created.]",
2556
+ "exec.task_reminder": "[ORIGINAL TASK: {task}. You MUST work on this task only. Do not create files for other tasks. If you cannot complete it explain why and request help.]",
2557
+ "exec.plan_warning": '[⚠ PLAN: you are on step {step} "{description}", but calling {tool} with files outside this step. This is FORBIDDEN work STRICTLY on the current step. First complete it and call plan update step={step} status=done, only then move to the next.]',
2558
+ "exec.plan_blocked": "[ BLOCKED: {max} consecutive calls outside the current step. You are violating the plan. Return to step {step} and complete it. Other steps are blocked until this one is done.]",
2559
+ "exec.off_track": `[⚠ OFF PLAN: step {stepId} is "{description}", but you're using {tool} on a different path. Return to the current step immediately.]`,
2560
+ "exec.step_gate_deps": '[⚠ Step {step} "{description}": dependencies are not installed. First run the install command (npm install, pip install, etc.). Verify the lock file or dependency directory exists.]',
2561
+ "exec.step_gate_empty": "[⚠ Step {step}: files exist but appear empty: {files}. Add real code to these files before advancing to the next step.]",
2562
+ "exec.step_gate_ok": '[✓] Step {step} completed and verified. MOVING to step {nextStep}: "{nextDesc}". Work ONLY on this step.',
2563
+ "exec.step_gate_last": "[✓] Step {step} completed — that was the final step. Verify everything together and provide the final answer.]",
2559
2564
  "exec.audit_pass": "[✓] Task complete: {done}/{total} steps done, {files} files verified",
2560
2565
  "exec.audit_fail": "[✗] Task incomplete: {done}/{total} steps done, {files} files missing",
2561
2566
  "exec.audit_fail_typecheck": "[✗] Task incomplete: {done}/{total} steps done, {missing} files missing, typecheck error: {typeError}",
@@ -3040,14 +3045,19 @@ var init_ru = __esm(() => {
3040
3045
 
3041
3046
  Используйте эти знания для ответа на вопрос пользователя.`,
3042
3047
  "exec.stuck": "Застряли на шаге {stepId}. Текущий: {description}. Нет прогресса {iterations} итераций.",
3043
- "exec.stuck_recovery": "[Обнаружено застревание: {iterations} итераций без прогресса. Прекратите повторять тот же подход. Попробуйте принципиально другую стратегию: используйте другой инструмент (например, 'browser' для сайтов с JavaScript вместо 'web_browse'), разбейте задачу на подзадачи или объясните пользователю что пошло не так и спросите совета.]",
3048
+ "exec.stuck_recovery": '[ЗАСТРЕВАНИЕ: {iterations} итераций на шаге {stepId} "{description}". ВЫ ДОЛЖНЫ сменить подход прямо сейчас. Конкретные действия: (1) Проверьте что нужно для этого шага установлены ли зависимости? (2) Используйте plan show чтобы увидеть план. (3) Если команды не работают создайте файлы напрямую через write_file. (4) После завершения шага вызовите plan update step={stepId} status=done. НЕ переходите к другим шагам пока этот не завершён.]',
3044
3049
  "exec.tool_errors": "Инструмент {tool} сбоил {count} раз. Предложите другой подход.",
3045
- "exec.tool_errors_recovery": "[Инструмент {tool} сбоил {count} раз. Прекратите использовать этот инструмент и попробуйте другой подход или объясните проблему пользователю.]",
3046
- "exec.repetitive_tool": "[Вы вызвали {tool} {count} раз подряд с одинаковыми аргументами и результатом. Прекратите повторять попробуйте другой подход: создайте файлы, измените аргументы или объясните пользователю проблему.]",
3047
- "exec.consecutive_failures_recovery": "[{count} последовательных вызовов тулов завершились ошибкой. Вы ходите по кругу прекратите пытаться выполнить команды, которые постоянно падают. Объясните проблему пользователю или попробуйте совершенно другой подход: напишите файлы напрямую, используйте другой инструмент или разбейте задачу на части.]",
3048
- "exec.task_reminder": "[Напоминание о задаче: {task}. Не переключайтесь на посторонние задачи. Если не можете выполнить задачу — объясните почему и спросите совета.]",
3049
- "exec.plan_warning": '[⚠ Внимание: вы работаете над шагом {step} — "{description}". Ваш вызов {tool} касается файлов, не упомянутых в текущем шаге. Сосредоточьтесь на текущем шаге или обновите план через plan update.]',
3050
- "exec.off_track": '[ Вне плана: шаг {stepId} "{description}", но вы используете {tool} для другого пути. Объясните или вернитесь.]',
3050
+ "exec.tool_errors_recovery": "[Инструмент {tool} сбоил {count} раз подряд. НЕ ИСПОЛЬЗУЙТЕ его снова. Используйте другой подход: (1) создайте нужные файлы напрямую через write_file, (2) используйте другой инструмент для той же задачи, (3) если ничего не работает — объясните проблему и переходите к следующему шагу плана через plan update step=N status=skipped с пометкой почему.]",
3051
+ "exec.repetitive_tool": "[ПОВТОРЕНИЕ: вы вызвали {tool} {count} раз с одинаковыми аргументами и результатом. Это бессмысленно. НЕМЕДЛЕННО смените подход: создайте файлы напрямую, измените аргументы или, если это процесс — проверьте его статус через process_log.]",
3052
+ "exec.consecutive_failures_recovery": "[{count} ИНСТРУМЕНТОВ ПОДРЯД УПАЛИ. Хватит пытаться запускать команды. Вот что делать: (1) Создавайте файлы напрямую через write_file вместо команд терминала. (2) Проверьте, установлены ли зависимости (npm install). (3) Используйте list_dir чтобы увидеть что есть в проекте. (4) Не пытайтесь запустить сборку/тесты пока не созданы все файлы.]",
3053
+ "exec.task_reminder": "[ИСХОДНАЯ ЗАДАЧА: {task}. ВЫ ДОЛЖНЫ работать только над этой задачей. Не создавайте файлы для других задач. Если не можете выполнить — объясните почему и запросите помощь.]",
3054
+ "exec.plan_warning": '[⚠ ПЛАН: вы на шаге {step} — "{description}", но вызываете {tool} для файлов вне этого шага. Это ЗАПРЕЩЕНО работайте СТРОГО над текущим шагом. Сначала завершите его и вызовите plan update step={step} status=done, только потом переходите к следующему.]',
3055
+ "exec.plan_blocked": "[ ЗАБЛОКИРОВАНО: {max} вызова подряд вне текущего шага. Вы нарушаете план. Вернитесь к шагу {step} и завершите его. Другие шаги будут заблокированы пока текущий не выполнен.]",
3056
+ "exec.off_track": '[⚠ ВНЕ ПЛАНА: шаг {stepId} — "{description}", но вы используете {tool} для другого пути. Вернитесь к текущему шагу немедленно.]',
3057
+ "exec.step_gate_deps": '[⚠ Шаг {step} "{description}": зависимости не установлены. Сначала выполните команду установки (npm install, pip install и т.д.). Проверьте что lock-файл или директория зависимостей существует.]',
3058
+ "exec.step_gate_empty": "[⚠ Шаг {step}: файлы существуют, но выглядят пустыми: {files}. Добавьте реальный код в эти файлы перед тем как переходить к следующему шагу.]",
3059
+ "exec.step_gate_ok": '[✓] Шаг {step} завершён и проверен. ПЕРЕХОДИМ к шагу {nextStep}: "{nextDesc}". Работайте ТОЛЬКО над этим шагом.',
3060
+ "exec.step_gate_last": "[✓] Шаг {step} завершён — это был последний шаг. Проверьте всё вместе и предоставьте финальный ответ.]",
3051
3061
  "exec.audit_pass": "[✓] Задача выполнена: {done}/{total} шагов, {files} файлов проверено",
3052
3062
  "exec.audit_fail": "[✗] Задача не выполнена: {done}/{total} шагов, {files} файлов отсутствует",
3053
3063
  "exec.audit_fail_typecheck": "[✗] Задача не выполнена: {done}/{total} шагов, {missing} файлов отсутствует, ошибка typecheck: {typeError}",
@@ -12823,11 +12833,18 @@ class StuckDetector {
12823
12833
  if (!this.hasRepetitiveToolCalls())
12824
12834
  return "";
12825
12835
  const last = this.recentToolCalls[this.recentToolCalls.length - 1];
12826
- return t("exec.repetitive_tool", { tool: last.name, count: this.repetitionThreshold });
12836
+ return t("exec.repetitive_tool", {
12837
+ tool: last.name,
12838
+ count: this.repetitionThreshold
12839
+ });
12827
12840
  }
12828
12841
  getStuckReason() {
12829
12842
  if (this.isStuck()) {
12830
- return t("exec.stuck", { stepId: String(this.currentStepId), description: this.currentStepDescription, iterations: this.iterationsOnCurrentStep });
12843
+ return t("exec.stuck", {
12844
+ stepId: String(this.currentStepId ?? "?"),
12845
+ description: this.currentStepDescription,
12846
+ iterations: this.iterationsOnCurrentStep
12847
+ });
12831
12848
  }
12832
12849
  const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
12833
12850
  if (errorTool) {
@@ -12837,14 +12854,23 @@ class StuckDetector {
12837
12854
  }
12838
12855
  getRecoveryMessage() {
12839
12856
  if (this.isStuck()) {
12840
- return t("exec.stuck_recovery", { iterations: this.iterationsOnCurrentStep });
12857
+ return t("exec.stuck_recovery", {
12858
+ iterations: this.iterationsOnCurrentStep,
12859
+ stepId: String(this.currentStepId ?? "?"),
12860
+ description: this.currentStepDescription
12861
+ });
12841
12862
  }
12842
12863
  const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
12843
12864
  if (errorTool) {
12844
- return t("exec.tool_errors_recovery", { tool: errorTool[0], count: errorTool[1] });
12865
+ return t("exec.tool_errors_recovery", {
12866
+ tool: errorTool[0],
12867
+ count: errorTool[1]
12868
+ });
12845
12869
  }
12846
12870
  if (this.hasConsecutiveFailures()) {
12847
- return t("exec.consecutive_failures_recovery", { count: this.consecutiveFailures });
12871
+ return t("exec.consecutive_failures_recovery", {
12872
+ count: this.consecutiveFailures
12873
+ });
12848
12874
  }
12849
12875
  if (this.hasRepetitiveToolCalls()) {
12850
12876
  return this.getRepetitiveToolMessage();
@@ -12859,7 +12885,11 @@ class StuckDetector {
12859
12885
  const pathInCall = (toolArgStr.match(/\b[\w./-]+\.[a-z]+/gi) || []).map((p) => p.toLowerCase());
12860
12886
  const overlaps = pathInCall.some((p) => pathInStep.some((s) => p.includes(s) || s.includes(p)));
12861
12887
  if (!overlaps && pathInStep.length > 0 && pathInCall.length > 0) {
12862
- return t("exec.off_track", { stepId: String(this.currentStepId), description: this.currentStepDescription, tool: toolName });
12888
+ return t("exec.off_track", {
12889
+ stepId: String(this.currentStepId),
12890
+ description: this.currentStepDescription,
12891
+ tool: toolName
12892
+ });
12863
12893
  }
12864
12894
  return null;
12865
12895
  }
@@ -12994,7 +13024,7 @@ class PlanPersister {
12994
13024
  var init_plan_persister = () => {};
12995
13025
 
12996
13026
  // src/modules/execution/module.ts
12997
- import { existsSync as existsSync26 } from "fs";
13027
+ import { existsSync as existsSync26, readFileSync as readFileSync15 } from "fs";
12998
13028
  import { resolve as resolve14 } from "path";
12999
13029
 
13000
13030
  class ExecutionModule {
@@ -13006,6 +13036,8 @@ class ExecutionModule {
13006
13036
  persister;
13007
13037
  baseDir;
13008
13038
  lastRecoveryIteration = -STUCK_RECOVERY_COOLDOWN;
13039
+ consecutivePlanWarnings = 0;
13040
+ lastStepId = -1;
13009
13041
  constructor(baseDir, stuckThreshold = 8) {
13010
13042
  this.baseDir = baseDir;
13011
13043
  this.verifier = new StepVerifier(baseDir);
@@ -13221,10 +13253,16 @@ Sub-tasks: ${note}`
13221
13253
  onBeforeThink: (ctx) => {
13222
13254
  const step = this.tracker?.getCurrentStep();
13223
13255
  if (this.tracker && step) {
13256
+ if (step.id !== this.lastStepId) {
13257
+ this.consecutivePlanWarnings = 0;
13258
+ this.lastStepId = step.id;
13259
+ }
13224
13260
  this.stuckDetector.setCurrentStep(step.id, step.description);
13225
13261
  this.stuckDetector.recordIteration(step.id);
13226
13262
  } else {
13227
13263
  this.stuckDetector.reset();
13264
+ this.consecutivePlanWarnings = 0;
13265
+ this.lastStepId = -1;
13228
13266
  }
13229
13267
  const stuckReason = this.stuckDetector.getStuckReason();
13230
13268
  if (stuckReason) {
@@ -13251,6 +13289,16 @@ Sub-tasks: ${note}`
13251
13289
  role: "user",
13252
13290
  content: `<system-summary>${warning}</system-summary>`
13253
13291
  });
13292
+ this.consecutivePlanWarnings++;
13293
+ if (this.consecutivePlanWarnings >= MAX_PLAN_WARNINGS_BEFORE_BLOCK) {
13294
+ ctx.contextManager.addMessage({
13295
+ role: "user",
13296
+ content: `<system-summary>${t("exec.plan_blocked", { step: String(this.tracker?.getCurrentStep()?.id ?? "?"), max: MAX_PLAN_WARNINGS_BEFORE_BLOCK })}</system-summary>`
13297
+ });
13298
+ return false;
13299
+ }
13300
+ } else {
13301
+ this.consecutivePlanWarnings = 0;
13254
13302
  }
13255
13303
  return true;
13256
13304
  },
@@ -13268,7 +13316,7 @@ Sub-tasks: ${note}`
13268
13316
  this.stuckDetector.recordToolSuccess();
13269
13317
  }
13270
13318
  if (this.tracker && result.success && (call.name === "write_file" || call.name === "edit_file")) {
13271
- this.advancePlanIfStepComplete();
13319
+ this.advancePlanIfStepComplete(ctx.contextManager);
13272
13320
  }
13273
13321
  }
13274
13322
  };
@@ -13310,24 +13358,78 @@ Sub-tasks: ${note}`
13310
13358
  tool: call.name
13311
13359
  });
13312
13360
  }
13313
- advancePlanIfStepComplete() {
13361
+ advancePlanIfStepComplete(contextManager) {
13314
13362
  const step = this.tracker?.getCurrentStep();
13315
13363
  if (!step)
13316
13364
  return;
13365
+ const stepText = step.description.toLowerCase();
13317
13366
  const stepPaths = step.description.match(/\b[\w./\\-]+\.[a-z]+/gi) || [];
13367
+ const isDepsStep = stepText.includes("install") || stepText.includes("зависим") || stepText.includes("init") || stepText.includes("инициализац");
13368
+ if (isDepsStep) {
13369
+ const lockFiles = [
13370
+ "package-lock.json",
13371
+ "bun.lockb",
13372
+ "yarn.lock",
13373
+ "pnpm-lock.yaml",
13374
+ "node_modules",
13375
+ "go.sum",
13376
+ "Cargo.lock",
13377
+ "Pipfile.lock",
13378
+ "poetry.lock",
13379
+ "requirements.txt"
13380
+ ];
13381
+ const hasLockFile = lockFiles.some((f) => existsSync26(resolve14(this.baseDir, f)));
13382
+ if (!hasLockFile) {
13383
+ if (contextManager) {
13384
+ contextManager.addMessage({
13385
+ role: "user",
13386
+ content: `<system-summary>${t("exec.step_gate_deps", { step: String(step.id), description: step.description })}</system-summary>`
13387
+ });
13388
+ }
13389
+ return;
13390
+ }
13391
+ }
13318
13392
  if (stepPaths.length === 0)
13319
13393
  return;
13320
13394
  const allExist = stepPaths.every((p) => existsSync26(resolve14(this.baseDir, p)));
13321
- if (allExist) {
13322
- this.tracker?.updateStepStatus(step.id, "done");
13323
- this.tracker?.advance();
13324
- if (this.tracker) {
13325
- this.persister.save(this.tracker.getPlan());
13326
- }
13395
+ if (!allExist)
13396
+ return;
13397
+ const emptyFiles = [];
13398
+ for (const p of stepPaths) {
13399
+ try {
13400
+ const content = readFileSync15(resolve14(this.baseDir, p), "utf-8");
13401
+ if (content.trim().length < 10) {
13402
+ emptyFiles.push(p);
13403
+ }
13404
+ } catch {}
13405
+ }
13406
+ if (emptyFiles.length > 0 && contextManager) {
13407
+ contextManager.addMessage({
13408
+ role: "user",
13409
+ content: `<system-summary>${t("exec.step_gate_empty", { step: String(step.id), files: emptyFiles.join(", ") })}</system-summary>`
13410
+ });
13411
+ return;
13412
+ }
13413
+ this.tracker?.updateStepStatus(step.id, "done");
13414
+ const hadNext = this.tracker?.advance() ?? false;
13415
+ if (this.tracker) {
13416
+ this.persister.save(this.tracker.getPlan());
13417
+ }
13418
+ if (contextManager) {
13419
+ const nextStep = hadNext ? this.tracker?.getCurrentStep() : null;
13420
+ const nextMsg = nextStep ? t("exec.step_gate_ok", {
13421
+ step: String(step.id),
13422
+ nextStep: String(nextStep.id),
13423
+ nextDesc: nextStep.description
13424
+ }) : t("exec.step_gate_last", { step: String(step.id) });
13425
+ contextManager.addMessage({
13426
+ role: "user",
13427
+ content: `<system-summary>${nextMsg}</system-summary>`
13428
+ });
13327
13429
  }
13328
13430
  }
13329
13431
  }
13330
- var STUCK_RECOVERY_COOLDOWN = 5;
13432
+ var STUCK_RECOVERY_COOLDOWN = 5, MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3;
13331
13433
  var init_module = __esm(() => {
13332
13434
  init_i18n();
13333
13435
  init_tracker();
@@ -13338,7 +13440,7 @@ var init_module = __esm(() => {
13338
13440
  });
13339
13441
 
13340
13442
  // src/modules/security/session-encryption.ts
13341
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync10, existsSync as existsSync27, readdirSync as readdirSync6, unlinkSync as unlinkSync3 } from "fs";
13443
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, existsSync as existsSync27, readdirSync as readdirSync6, unlinkSync as unlinkSync3 } from "fs";
13342
13444
  import { join as join19 } from "path";
13343
13445
  import { homedir as homedir8 } from "os";
13344
13446
 
@@ -13388,7 +13490,7 @@ class SessionFileEncryptor {
13388
13490
  return lines.map((line) => this.decryptFileContent(line));
13389
13491
  }
13390
13492
  readSessionFile(filePath) {
13391
- const content = readFileSync15(filePath, "utf8");
13493
+ const content = readFileSync16(filePath, "utf8");
13392
13494
  return this.decryptFileContent(content);
13393
13495
  }
13394
13496
  writeSessionFile(filePath, content) {
@@ -13396,7 +13498,7 @@ class SessionFileEncryptor {
13396
13498
  writeFileSync10(filePath, encrypted, "utf8");
13397
13499
  }
13398
13500
  readSessionJSON(filePath) {
13399
- const content = readFileSync15(filePath, "utf8");
13501
+ const content = readFileSync16(filePath, "utf8");
13400
13502
  return this.decryptJSON(content);
13401
13503
  }
13402
13504
  writeSessionJSON(filePath, obj) {
@@ -13404,7 +13506,7 @@ class SessionFileEncryptor {
13404
13506
  writeFileSync10(filePath, content, "utf8");
13405
13507
  }
13406
13508
  readSessionJSONL(filePath) {
13407
- const content = readFileSync15(filePath, "utf8");
13509
+ const content = readFileSync16(filePath, "utf8");
13408
13510
  const lines = content.split(`
13409
13511
  `).filter((line) => line.trim());
13410
13512
  const decryptedLines = this.decryptJSONL(lines);
@@ -13423,7 +13525,7 @@ class SessionFileEncryptor {
13423
13525
  const filePath = join19(sessionDir, file);
13424
13526
  if (existsSync27(filePath) && !file.endsWith(".enc")) {
13425
13527
  try {
13426
- const content = readFileSync15(filePath, "utf8");
13528
+ const content = readFileSync16(filePath, "utf8");
13427
13529
  const encrypted = this.encryptFileContent(content);
13428
13530
  writeFileSync10(filePath + ".enc", encrypted, "utf8");
13429
13531
  unlinkSync3(filePath);
@@ -13440,7 +13542,7 @@ class SessionFileEncryptor {
13440
13542
  const encFilePath = join19(sessionDir, file);
13441
13543
  const decFilePath = encFilePath.slice(0, -4);
13442
13544
  try {
13443
- const content = readFileSync15(encFilePath, "utf8");
13545
+ const content = readFileSync16(encFilePath, "utf8");
13444
13546
  const decrypted = this.decryptFileContent(content);
13445
13547
  writeFileSync10(decFilePath, decrypted, "utf8");
13446
13548
  unlinkSync3(encFilePath);
@@ -13465,7 +13567,7 @@ import {
13465
13567
  existsSync as existsSync28,
13466
13568
  mkdirSync as mkdirSync12,
13467
13569
  readdirSync as readdirSync7,
13468
- readFileSync as readFileSync16,
13570
+ readFileSync as readFileSync17,
13469
13571
  rmSync,
13470
13572
  writeFileSync as writeFileSync11,
13471
13573
  appendFileSync as appendFileSync5
@@ -13525,7 +13627,7 @@ class SessionStore {
13525
13627
  if (!existsSync28(path))
13526
13628
  return null;
13527
13629
  try {
13528
- const raw = readFileSync16(path, "utf-8");
13630
+ const raw = readFileSync17(path, "utf-8");
13529
13631
  const content = this.encryptor ? this.encryptor.decryptFileContent(raw) : raw;
13530
13632
  return JSON.parse(content);
13531
13633
  } catch {
@@ -13555,7 +13657,7 @@ class SessionStore {
13555
13657
  if (!existsSync28(path))
13556
13658
  return [];
13557
13659
  try {
13558
- const raw = readFileSync16(path, "utf-8");
13660
+ const raw = readFileSync17(path, "utf-8");
13559
13661
  const lines = raw.split(`
13560
13662
  `).filter(Boolean);
13561
13663
  if (this.encryptor?.isEnabled()) {
@@ -13584,7 +13686,7 @@ class SessionStore {
13584
13686
  if (!existsSync28(path))
13585
13687
  return [];
13586
13688
  try {
13587
- const raw = readFileSync16(path, "utf-8");
13689
+ const raw = readFileSync17(path, "utf-8");
13588
13690
  const lines = raw.split(`
13589
13691
  `).filter(Boolean);
13590
13692
  if (this.encryptor?.isEnabled()) {
@@ -13626,7 +13728,7 @@ class SessionStore {
13626
13728
  if (updatedAt < thirtyDaysAgo) {
13627
13729
  const historyPath = this.historyPath(session2.id);
13628
13730
  if (existsSync28(historyPath)) {
13629
- const content = readFileSync16(historyPath, "utf-8");
13731
+ const content = readFileSync17(historyPath, "utf-8");
13630
13732
  const compressed = gzipSync(content);
13631
13733
  const gzPath = join20(this.baseDir, `${session2.id}.jsonl.gz`);
13632
13734
  writeFileSync11(gzPath, compressed);
@@ -13835,7 +13937,7 @@ class ProfileCompressor {
13835
13937
  }
13836
13938
 
13837
13939
  // src/modules/user-profile/profile.ts
13838
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync12, existsSync as existsSync29, mkdirSync as mkdirSync13 } from "fs";
13940
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, existsSync as existsSync29, mkdirSync as mkdirSync13 } from "fs";
13839
13941
  import { join as join21 } from "path";
13840
13942
  import { homedir as homedir9, hostname, platform as platform4, type } from "os";
13841
13943
  import { env } from "process";
@@ -13870,7 +13972,7 @@ class UserProfile {
13870
13972
  if (!existsSync29(path))
13871
13973
  return null;
13872
13974
  try {
13873
- const data = JSON.parse(readFileSync17(path, "utf-8"));
13975
+ const data = JSON.parse(readFileSync18(path, "utf-8"));
13874
13976
  this.info = {
13875
13977
  platform: data.platform,
13876
13978
  os: data.os,
@@ -13905,7 +14007,7 @@ class UserProfile {
13905
14007
  var init_profile = () => {};
13906
14008
 
13907
14009
  // src/modules/skills/loader.ts
13908
- import { readdirSync as readdirSync8, readFileSync as readFileSync18, existsSync as existsSync30, statSync as statSync5 } from "fs";
14010
+ import { readdirSync as readdirSync8, readFileSync as readFileSync19, existsSync as existsSync30, statSync as statSync5 } from "fs";
13909
14011
  import { join as join22 } from "path";
13910
14012
 
13911
14013
  class SkillsLoader {
@@ -13927,7 +14029,7 @@ class SkillsLoader {
13927
14029
  }
13928
14030
  if (!entry.endsWith(".md") && !entry.endsWith(".skill.md"))
13929
14031
  continue;
13930
- const content = readFileSync18(fullPath, "utf-8");
14032
+ const content = readFileSync19(fullPath, "utf-8");
13931
14033
  const parsed = this.parseSkillFile(content, fullPath);
13932
14034
  if (parsed)
13933
14035
  skills.push(parsed);
@@ -14199,7 +14301,7 @@ var init_browser2 = __esm(() => {
14199
14301
  });
14200
14302
 
14201
14303
  // src/modules/indexer/walker.ts
14202
- import { readdirSync as readdirSync9, readFileSync as readFileSync19, statSync as statSync6, existsSync as existsSync31, watch } from "fs";
14304
+ import { readdirSync as readdirSync9, readFileSync as readFileSync20, statSync as statSync6, existsSync as existsSync31, watch } from "fs";
14203
14305
  import { join as join23, relative, extname as extname5 } from "path";
14204
14306
 
14205
14307
  class Indexer {
@@ -14249,7 +14351,7 @@ class Indexer {
14249
14351
  const ext = extname5(entry).toLowerCase();
14250
14352
  const language = LANGUAGES[ext];
14251
14353
  if (language) {
14252
- const content = readFileSync19(fullPath, "utf-8");
14354
+ const content = readFileSync20(fullPath, "utf-8");
14253
14355
  const exports = this.extractExports(content, language);
14254
14356
  files.push({ path: relPath, language, exports, size: stat.size });
14255
14357
  totalSize += stat.size;
@@ -14301,7 +14403,7 @@ var init_walker = __esm(() => {
14301
14403
  });
14302
14404
 
14303
14405
  // src/modules/indexer/cache.ts
14304
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync13, existsSync as existsSync32, mkdirSync as mkdirSync14, rmSync as rmSync2 } from "fs";
14406
+ import { readFileSync as readFileSync21, writeFileSync as writeFileSync13, existsSync as existsSync32, mkdirSync as mkdirSync14, rmSync as rmSync2 } from "fs";
14305
14407
  import { join as join24 } from "path";
14306
14408
 
14307
14409
  class IndexCache {
@@ -14316,7 +14418,7 @@ class IndexCache {
14316
14418
  if (!existsSync32(this.cachePath))
14317
14419
  return null;
14318
14420
  try {
14319
- this.cache = JSON.parse(readFileSync20(this.cachePath, "utf-8"));
14421
+ this.cache = JSON.parse(readFileSync21(this.cachePath, "utf-8"));
14320
14422
  return this.cache;
14321
14423
  } catch {
14322
14424
  return null;
@@ -14743,7 +14845,7 @@ __export(exports_bootstrap, {
14743
14845
  });
14744
14846
  import { homedir as homedir11 } from "os";
14745
14847
  import { join as join26, resolve as resolve15 } from "path";
14746
- import { existsSync as existsSync33, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "fs";
14848
+ import { existsSync as existsSync33, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
14747
14849
  function buildSystemInfo(config, baseDir, profileCompressed) {
14748
14850
  const now = new Date().toISOString().replace("T", " ").slice(0, 19);
14749
14851
  const isWin = profileCompressed.toLowerCase().includes("win32");
@@ -14765,9 +14867,10 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
14765
14867
  if (isWin) {
14766
14868
  lines.push(``, `Windows environment — use Windows-compatible commands:`, `- Use "dir" instead of "ls". Use "dir /b" for bare listing.`, `- Use "type" or "Get-Content" instead of "cat".`, `- Use "cd" instead of "pwd". Use "echo %cd%" to print working directory.`, `- Use "copy" instead of "cp", "move" instead of "mv", "del" instead of "rm".`, `- Do not use "mkdir -p" — Windows mkdir creates intermediate dirs by default. Use the create_dir tool instead.`, `- Use forward slashes (/) or escaped backslashes (\\\\) in file paths.`, `- Your working directory is: ${baseDir}`);
14767
14869
  }
14768
- lines.push(``, `Bash tool rules:`, `- Use the "workdir" parameter to run commands in a specific directory. Do NOT chain "cd dir && cmd" — the security module blocks the "&&" operator.`, `- Run one command per tool call. Split multi-step shell operations into separate bash calls.`);
14870
+ lines.push(``, `Bash tool rules:`, `- Use the "workdir" parameter to run commands in a specific directory. Do NOT chain "cd dir && cmd" — the security module blocks the "&&" operator.`, `- Run one command per tool call. Split multi-step shell operations into separate bash calls.`, `- Background processes (dev servers, watchers, long-running npm install): use the "background: true" parameter or the tool will auto-detect and background them. Check output with process_log.`);
14871
+ lines.push(``, `=== DEVELOPMENT RULES — follow these strictly ===`, ``, `1. DEPENDENCIES FIRST: Before writing any source code, ALWAYS install project dependencies (e.g., "npm install", "pip install -r requirements.txt", "cargo build", "go mod tidy"). Verify the package manager's lock file or dependency directory exists. Never write code that imports/uses packages that aren't installed yet.`, `2. TOOLKIT/FWK FIRST: If the task specifies a framework or UI library, initialize and configure it BEFORE writing application code. Run its project init command first, then add components/modules. Never write your own version of what the framework already provides.`, `3. ONE STEP AT A TIME: Follow the plan sequentially. Complete step N before starting step N+1. When a step is done: verify the deliverables exist and have real content (not empty), then call "plan update step=N status=done". Do not redo completed work.`, `4. VERIFY YOUR WORK: After creating/modifying files, verify they exist on disk. After installing dependencies, verify the package manager completed successfully. After any command, check its output for errors. Don't assume operations succeeded.`, `5. NO PREMATURE WORK: Do not create files for future steps. Do not add imports/references to packages or modules that haven't been installed yet. Do not reference files or components that don't exist yet. Build incrementally — one layer at a time.`, `6. WHEN STUCK: If a command fails 2+ times, STOP and try a different approach. Write files directly instead of using commands. Ask the user for help. Never repeat the same failing command more than twice.`);
14769
14872
  if (config.autoPlan) {
14770
- lines.push(``, `Plan rule: For complex tasks with 3+ steps, you may create a plan using the "plan" tool. For simple questions, just answer directly.`);
14873
+ lines.push(``, `Plan rule (MANDATORY): For ANY task that requires creating files, installing packages, or multiple actions — you MUST create a plan using the "plan" tool BEFORE starting work. Each step must describe a concrete deliverable (specific files to create, packages to install, commands to run). Do not combine unrelated work into one step.`);
14771
14874
  }
14772
14875
  const hasMCP = config.mcpServers && Object.values(config.mcpServers).some((s) => s.enabled !== false);
14773
14876
  if (hasMCP) {
@@ -14975,7 +15078,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
14975
15078
  ];
14976
15079
  for (const p of agentsMdCandidates) {
14977
15080
  if (existsSync33(p)) {
14978
- const content = readFileSync21(p, "utf-8").trim();
15081
+ const content = readFileSync22(p, "utf-8").trim();
14979
15082
  if (content) {
14980
15083
  agentsMdBlocks.push({
14981
15084
  content,
@@ -15810,7 +15913,7 @@ async function runSetup() {
15810
15913
  init_i18n();
15811
15914
  import { join as join28, dirname as dirname9 } from "path";
15812
15915
  import { homedir as homedir13 } from "os";
15813
- import { existsSync as existsSync34, readFileSync as readFileSync22 } from "fs";
15916
+ import { existsSync as existsSync34, readFileSync as readFileSync23 } from "fs";
15814
15917
 
15815
15918
  // src/cli/security-commands.ts
15816
15919
  init_bootstrap();
@@ -16411,7 +16514,7 @@ function readVersion() {
16411
16514
  ];
16412
16515
  for (const p of candidates) {
16413
16516
  if (existsSync34(p)) {
16414
- const raw = JSON.parse(readFileSync22(p, "utf8"));
16517
+ const raw = JSON.parse(readFileSync23(p, "utf8"));
16415
16518
  if (raw.version)
16416
16519
  return raw.version;
16417
16520
  }
@@ -16574,7 +16677,7 @@ init_bootstrap();
16574
16677
  // src/cli/repl.ts
16575
16678
  init_colors();
16576
16679
  import * as readline3 from "readline";
16577
- import { existsSync as existsSync35, readFileSync as readFileSync23, writeFileSync as writeFileSync15 } from "fs";
16680
+ import { existsSync as existsSync35, readFileSync as readFileSync24, writeFileSync as writeFileSync15 } from "fs";
16578
16681
  import { join as join29, dirname as dirname10 } from "path";
16579
16682
  import { homedir as homedir14 } from "os";
16580
16683
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -17030,7 +17133,7 @@ function readVersion2() {
17030
17133
  ];
17031
17134
  for (const p of candidates) {
17032
17135
  if (existsSync35(p)) {
17033
- const raw = JSON.parse(readFileSync23(p, "utf8"));
17136
+ const raw = JSON.parse(readFileSync24(p, "utf8"));
17034
17137
  if (raw.version)
17035
17138
  return raw.version;
17036
17139
  }
@@ -17123,7 +17226,7 @@ class Repl {
17123
17226
  loadHistory() {
17124
17227
  if (existsSync35(this.historyPath)) {
17125
17228
  try {
17126
- const raw = readFileSync23(this.historyPath, "utf-8");
17229
+ const raw = readFileSync24(this.historyPath, "utf-8");
17127
17230
  this.history = raw.split(`
17128
17231
  `).filter(Boolean).slice(-this.maxHistory);
17129
17232
  } catch {
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: git-expert
3
+ description: Git branching, merge conflicts, rebasing, workflow strategies
4
+ keywords: [git, branch, merge, rebase, commit, conflict, workflow]
5
+ mma_version: 2
6
+ ---
7
+
8
+ # Git Expert
9
+
10
+ ## Branching Strategies
11
+
12
+ - **Feature branches**: `feature/xxx` from `main`, merge via PR
13
+ - **Hotfix**: `hotfix/xxx` from `main`, merge to both `main` and `develop`
14
+ - **Release**: `release/xxx` from `develop`, merge to `main` + `develop`
15
+
16
+ ## Conflict Resolution
17
+
18
+ 1. `git fetch origin` — get latest
19
+ 2. `git checkout your-branch`
20
+ 3. `git rebase origin/main` — apply your commits on top
21
+ 4. Resolve conflicts file by file
22
+ 5. `git add <files>` then `git rebase --continue`
23
+
24
+ ## Useful Commands
25
+
26
+ - `git log --oneline --graph --all` — visualize history
27
+ - `git diff --stat` — summary of changes
28
+ - `git stash pop` — restore stashed changes
29
+ - `git cherry-pick <commit>` — apply specific commit
30
+
31
+ ## Best Practices
32
+
33
+ - Commit often with clear messages
34
+ - Keep commits atomic (one logical change)
35
+ - Never force push to shared branches
36
+ - Use `git rebase -i` to clean up before PR
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: typescript-expert
3
+ description: TypeScript type system, generics, advanced patterns, compiler API
4
+ keywords: [typescript, types, generics, tsconfig, compiler, interface, type]
5
+ mma_version: 2
6
+ ---
7
+
8
+ # TypeScript Expert
9
+
10
+ ## Key Patterns
11
+
12
+ - Use `readonly` for immutable properties
13
+ - Prefer `interface` over `type` for object shapes
14
+ - Use `as const` for literal types
15
+ - Prefer `unknown` over `any` for type safety
16
+
17
+ ## Common Pitfalls
18
+
19
+ - Avoid `any` — use `unknown` + type guards
20
+ - Don't use `enum` — use `const` objects or union types
21
+ - Avoid `namespace` — use ES modules
22
+
23
+ ## Type Guards
24
+
25
+ ```ts
26
+ function isString(val: unknown): val is string {
27
+ return typeof val === 'string'
28
+ }
29
+ ```
30
+
31
+ ## Generics
32
+
33
+ - Use `extends` for constraints
34
+ - Use `infer` for conditional type extraction
35
+ - Prefer explicit over implicit for complex cases
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "micro-models-agent",
3
- "version": "0.20.0",
3
+ "version": "0.20.1",
4
4
  "description": "Micro Models Agent (MMA) — LLM agent harness for small models (Qwen3.5-9B, 32K-64K context)",
5
5
  "type": "module",
6
6
  "bin": {