micro-models-agent 0.20.0 → 0.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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}",
@@ -10105,285 +10115,6 @@ var init_web_browse = __esm(() => {
10105
10115
  };
10106
10116
  });
10107
10117
 
10108
- // src/tools/user-input.ts
10109
- import * as readline from "readline";
10110
- function parseSelection(input, optionCount, multiple) {
10111
- const trimmed = input.trim();
10112
- if (!trimmed)
10113
- return null;
10114
- const parts = trimmed.split(",").map((p) => p.trim());
10115
- if (!multiple && parts.length > 1)
10116
- return null;
10117
- const indexes = [];
10118
- for (const part of parts) {
10119
- if (!/^\d+$/.test(part))
10120
- return null;
10121
- const idx = Number(part) - 1;
10122
- if (idx < 0 || idx >= optionCount)
10123
- return null;
10124
- if (indexes.includes(idx))
10125
- return null;
10126
- indexes.push(idx);
10127
- }
10128
- return indexes.length ? indexes : null;
10129
- }
10130
- function formatMenu(question, options, opts = {}) {
10131
- const lines = [question];
10132
- options.forEach((opt, i) => {
10133
- lines.push(` [${i + 1}] ${opt.label} — ${opt.description}`);
10134
- });
10135
- if (opts.allowCustom) {
10136
- lines.push(` [${options.length + 1}] ${t("tool.user_input.custom_option")}`);
10137
- }
10138
- return lines.join(`
10139
- `);
10140
- }
10141
- function choicePrompt(optionCount, multiple) {
10142
- return multiple ? t("tool.user_input.choice_multiple") : t("tool.user_input.choice_single", { max: optionCount });
10143
- }
10144
- function createRl() {
10145
- return readline.createInterface({
10146
- input: process.stdin,
10147
- output: process.stdout
10148
- });
10149
- }
10150
- function promptLine(rl, prompt) {
10151
- return new Promise((resolve11) => {
10152
- rl.question(prompt, (answer) => resolve11(answer));
10153
- });
10154
- }
10155
- async function askText(question) {
10156
- const rl = createRl();
10157
- try {
10158
- const answer = await promptLine(rl, `${question} `);
10159
- return answer.trim();
10160
- } finally {
10161
- rl.close();
10162
- }
10163
- }
10164
- async function askChoice(question, options, opts = {}) {
10165
- const multiple = opts.multiple ?? false;
10166
- const entryCount = options.length + (opts.allowCustom ? 1 : 0);
10167
- const customEntry = options.length;
10168
- const rl = createRl();
10169
- try {
10170
- console.log(formatMenu(question, options, opts));
10171
- for (;; ) {
10172
- const answer = await promptLine(rl, choicePrompt(entryCount, multiple));
10173
- const parsed = parseSelection(answer, entryCount, multiple);
10174
- if (parsed) {
10175
- return parsed.map((i) => opts.allowCustom && i === customEntry ? CUSTOM_INDEX : i);
10176
- }
10177
- console.log(t("tool.user_input.invalid"));
10178
- }
10179
- } finally {
10180
- rl.close();
10181
- }
10182
- }
10183
- async function askUser(question, opts = {}) {
10184
- const header = [opts.progress, opts.header].filter(Boolean).join(" — ");
10185
- const text = header ? `${header}
10186
- ${question}` : question;
10187
- if (!opts.options || opts.options.length === 0) {
10188
- const answer = await askText(text);
10189
- return answer ? [answer] : [];
10190
- }
10191
- const allowCustom = opts.custom ?? true;
10192
- const indexes = await askChoice(text, opts.options, {
10193
- multiple: opts.multiple,
10194
- allowCustom
10195
- });
10196
- const labels = [];
10197
- for (const idx of indexes) {
10198
- if (idx === CUSTOM_INDEX) {
10199
- const custom = await askText(t("tool.user_input.custom_prompt"));
10200
- if (custom)
10201
- labels.push(custom);
10202
- } else {
10203
- labels.push(opts.options[idx].label);
10204
- }
10205
- }
10206
- return labels;
10207
- }
10208
- var CUSTOM_INDEX = -1;
10209
- var init_user_input = __esm(() => {
10210
- init_i18n();
10211
- });
10212
-
10213
- // src/tools/question.ts
10214
- function isOption(val) {
10215
- if (!val || typeof val !== "object")
10216
- return false;
10217
- const opt = val;
10218
- return typeof opt.label === "string" && typeof opt.description === "string";
10219
- }
10220
- function normalizeQuestions(args) {
10221
- const raw = args.questions;
10222
- if (Array.isArray(raw)) {
10223
- const specs = [];
10224
- for (const item of raw) {
10225
- if (!item || typeof item !== "object")
10226
- continue;
10227
- const q = item;
10228
- if (typeof q.question !== "string" || !q.question.trim())
10229
- continue;
10230
- const options = Array.isArray(q.options) ? q.options.filter(isOption) : undefined;
10231
- if (Array.isArray(q.options) && options.length === 0)
10232
- continue;
10233
- specs.push({
10234
- question: q.question,
10235
- header: typeof q.header === "string" ? q.header : undefined,
10236
- options,
10237
- multiple: q.multiple === true,
10238
- custom: q.custom === false ? false : undefined
10239
- });
10240
- }
10241
- return specs;
10242
- }
10243
- if (typeof args.question === "string" && args.question.trim()) {
10244
- return [{ question: args.question }];
10245
- }
10246
- return [];
10247
- }
10248
- var DESCRIPTION = `Use this tool when you need to ask the user questions during execution. This allows you to:
10249
- 1. Gather user preferences or requirements
10250
- 2. Clarify ambiguous instructions
10251
- 3. Get decisions on implementation choices as you work
10252
- 4. Offer choices to the user about what direction to take.
10253
-
10254
- Usage notes:
10255
- - When "custom" is enabled (default), a "Type your own answer" option is added automatically; don't include "Other" or catch-all options
10256
- - Answers are returned as arrays of labels; set "multiple": true to allow selecting more than one
10257
- - If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
10258
- - Execution blocks until the user answers every question
10259
- - Omit "options" for a free-text question`, questionTool;
10260
- var init_question = __esm(() => {
10261
- init_i18n();
10262
- init_user_input();
10263
- questionTool = {
10264
- name: "question",
10265
- description: DESCRIPTION,
10266
- tags: ["core"],
10267
- interactive: true,
10268
- parameters: {
10269
- type: "object",
10270
- properties: {
10271
- questions: {
10272
- type: "array",
10273
- description: "Questions to ask",
10274
- items: {
10275
- type: "object",
10276
- properties: {
10277
- question: { type: "string", description: "Complete question" },
10278
- header: {
10279
- type: "string",
10280
- description: "Very short label (max 30 chars)"
10281
- },
10282
- options: {
10283
- type: "array",
10284
- description: "Available choices; omit for a free-text question",
10285
- items: {
10286
- type: "object",
10287
- properties: {
10288
- label: {
10289
- type: "string",
10290
- description: "Display text (1-5 words, concise)"
10291
- },
10292
- description: {
10293
- type: "string",
10294
- description: "Explanation of choice"
10295
- }
10296
- },
10297
- required: ["label", "description"]
10298
- }
10299
- },
10300
- multiple: {
10301
- type: "boolean",
10302
- description: "Allow selecting multiple choices"
10303
- },
10304
- custom: {
10305
- type: "boolean",
10306
- description: "Allow typing a custom answer (default: true)"
10307
- }
10308
- },
10309
- required: ["question"]
10310
- }
10311
- },
10312
- question: {
10313
- type: "string",
10314
- description: "Legacy single free-text question"
10315
- }
10316
- }
10317
- },
10318
- handler: async (ctx, args) => {
10319
- if (ctx.exitOnComplete) {
10320
- return { success: false, output: t("tool.interactive_disabled") };
10321
- }
10322
- const questions = normalizeQuestions(args);
10323
- if (questions.length === 0) {
10324
- return { success: false, output: t("tool.question.no_questions") };
10325
- }
10326
- const answers = [];
10327
- for (let i = 0;i < questions.length; i++) {
10328
- const q = questions[i];
10329
- const progress = questions.length > 1 ? t("tool.question.progress", {
10330
- current: i + 1,
10331
- total: questions.length
10332
- }) : undefined;
10333
- answers.push(await askUser(q.question, {
10334
- header: q.header,
10335
- options: q.options,
10336
- multiple: q.multiple,
10337
- custom: q.custom,
10338
- progress
10339
- }));
10340
- }
10341
- const formatted = questions.map((q, i) => `"${q.question}"="${answers[i].length ? answers[i].join(", ") : t("tool.question.unanswered")}"`).join(", ");
10342
- return {
10343
- success: true,
10344
- output: t("tool.question.answered", { formatted })
10345
- };
10346
- }
10347
- };
10348
- });
10349
-
10350
- // src/tools/approve.ts
10351
- var approveTool;
10352
- var init_approve = __esm(() => {
10353
- init_i18n();
10354
- init_user_input();
10355
- approveTool = {
10356
- name: "approve",
10357
- description: "Request user approval for an action. The user picks Yes or No from a menu.",
10358
- tags: ["core"],
10359
- interactive: true,
10360
- parameters: {
10361
- type: "object",
10362
- properties: {
10363
- action: {
10364
- type: "string",
10365
- description: "Description of the action requiring approval"
10366
- }
10367
- },
10368
- required: ["action"]
10369
- },
10370
- handler: async (ctx, args) => {
10371
- if (ctx.exitOnComplete) {
10372
- return { success: false, output: t("tool.interactive_disabled") };
10373
- }
10374
- const action = String(args.action || "");
10375
- const indexes = await askChoice(t("tool.approve_prompt", { action }), [
10376
- { label: t("tool.approve_yes"), description: t("tool.approve_yes_desc") },
10377
- { label: t("tool.approve_no"), description: t("tool.approve_no_desc") }
10378
- ]);
10379
- if (indexes[0] === 0) {
10380
- return { success: true, output: t("tool.approved") };
10381
- }
10382
- return { success: false, output: t("tool.rejected") };
10383
- }
10384
- };
10385
- });
10386
-
10387
10118
  // src/tools/load-skill.ts
10388
10119
  function createLoadSkillTool(skillsModule) {
10389
10120
  return {
@@ -12384,8 +12115,6 @@ function registerAllTools(registry2, skillsModule) {
12384
12115
  webSearchTool,
12385
12116
  webFetchTool,
12386
12117
  webBrowseTool,
12387
- questionTool,
12388
- approveTool,
12389
12118
  pipelineRunTool,
12390
12119
  mcpCallTool,
12391
12120
  searchHistoryTool,
@@ -12418,8 +12147,6 @@ var init_tools = __esm(() => {
12418
12147
  init_web_search();
12419
12148
  init_web_fetch();
12420
12149
  init_web_browse();
12421
- init_question();
12422
- init_approve();
12423
12150
  init_load_skill();
12424
12151
  init_pipeline_run();
12425
12152
  init_mcp_call();
@@ -12823,11 +12550,18 @@ class StuckDetector {
12823
12550
  if (!this.hasRepetitiveToolCalls())
12824
12551
  return "";
12825
12552
  const last = this.recentToolCalls[this.recentToolCalls.length - 1];
12826
- return t("exec.repetitive_tool", { tool: last.name, count: this.repetitionThreshold });
12553
+ return t("exec.repetitive_tool", {
12554
+ tool: last.name,
12555
+ count: this.repetitionThreshold
12556
+ });
12827
12557
  }
12828
12558
  getStuckReason() {
12829
12559
  if (this.isStuck()) {
12830
- return t("exec.stuck", { stepId: String(this.currentStepId), description: this.currentStepDescription, iterations: this.iterationsOnCurrentStep });
12560
+ return t("exec.stuck", {
12561
+ stepId: String(this.currentStepId ?? "?"),
12562
+ description: this.currentStepDescription,
12563
+ iterations: this.iterationsOnCurrentStep
12564
+ });
12831
12565
  }
12832
12566
  const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
12833
12567
  if (errorTool) {
@@ -12837,14 +12571,23 @@ class StuckDetector {
12837
12571
  }
12838
12572
  getRecoveryMessage() {
12839
12573
  if (this.isStuck()) {
12840
- return t("exec.stuck_recovery", { iterations: this.iterationsOnCurrentStep });
12574
+ return t("exec.stuck_recovery", {
12575
+ iterations: this.iterationsOnCurrentStep,
12576
+ stepId: String(this.currentStepId ?? "?"),
12577
+ description: this.currentStepDescription
12578
+ });
12841
12579
  }
12842
12580
  const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
12843
12581
  if (errorTool) {
12844
- return t("exec.tool_errors_recovery", { tool: errorTool[0], count: errorTool[1] });
12582
+ return t("exec.tool_errors_recovery", {
12583
+ tool: errorTool[0],
12584
+ count: errorTool[1]
12585
+ });
12845
12586
  }
12846
12587
  if (this.hasConsecutiveFailures()) {
12847
- return t("exec.consecutive_failures_recovery", { count: this.consecutiveFailures });
12588
+ return t("exec.consecutive_failures_recovery", {
12589
+ count: this.consecutiveFailures
12590
+ });
12848
12591
  }
12849
12592
  if (this.hasRepetitiveToolCalls()) {
12850
12593
  return this.getRepetitiveToolMessage();
@@ -12859,7 +12602,11 @@ class StuckDetector {
12859
12602
  const pathInCall = (toolArgStr.match(/\b[\w./-]+\.[a-z]+/gi) || []).map((p) => p.toLowerCase());
12860
12603
  const overlaps = pathInCall.some((p) => pathInStep.some((s) => p.includes(s) || s.includes(p)));
12861
12604
  if (!overlaps && pathInStep.length > 0 && pathInCall.length > 0) {
12862
- return t("exec.off_track", { stepId: String(this.currentStepId), description: this.currentStepDescription, tool: toolName });
12605
+ return t("exec.off_track", {
12606
+ stepId: String(this.currentStepId),
12607
+ description: this.currentStepDescription,
12608
+ tool: toolName
12609
+ });
12863
12610
  }
12864
12611
  return null;
12865
12612
  }
@@ -12994,7 +12741,7 @@ class PlanPersister {
12994
12741
  var init_plan_persister = () => {};
12995
12742
 
12996
12743
  // src/modules/execution/module.ts
12997
- import { existsSync as existsSync26 } from "fs";
12744
+ import { existsSync as existsSync26, readFileSync as readFileSync15 } from "fs";
12998
12745
  import { resolve as resolve14 } from "path";
12999
12746
 
13000
12747
  class ExecutionModule {
@@ -13006,6 +12753,8 @@ class ExecutionModule {
13006
12753
  persister;
13007
12754
  baseDir;
13008
12755
  lastRecoveryIteration = -STUCK_RECOVERY_COOLDOWN;
12756
+ consecutivePlanWarnings = 0;
12757
+ lastStepId = -1;
13009
12758
  constructor(baseDir, stuckThreshold = 8) {
13010
12759
  this.baseDir = baseDir;
13011
12760
  this.verifier = new StepVerifier(baseDir);
@@ -13221,10 +12970,16 @@ Sub-tasks: ${note}`
13221
12970
  onBeforeThink: (ctx) => {
13222
12971
  const step = this.tracker?.getCurrentStep();
13223
12972
  if (this.tracker && step) {
12973
+ if (step.id !== this.lastStepId) {
12974
+ this.consecutivePlanWarnings = 0;
12975
+ this.lastStepId = step.id;
12976
+ }
13224
12977
  this.stuckDetector.setCurrentStep(step.id, step.description);
13225
12978
  this.stuckDetector.recordIteration(step.id);
13226
12979
  } else {
13227
12980
  this.stuckDetector.reset();
12981
+ this.consecutivePlanWarnings = 0;
12982
+ this.lastStepId = -1;
13228
12983
  }
13229
12984
  const stuckReason = this.stuckDetector.getStuckReason();
13230
12985
  if (stuckReason) {
@@ -13251,6 +13006,16 @@ Sub-tasks: ${note}`
13251
13006
  role: "user",
13252
13007
  content: `<system-summary>${warning}</system-summary>`
13253
13008
  });
13009
+ this.consecutivePlanWarnings++;
13010
+ if (this.consecutivePlanWarnings >= MAX_PLAN_WARNINGS_BEFORE_BLOCK) {
13011
+ ctx.contextManager.addMessage({
13012
+ role: "user",
13013
+ content: `<system-summary>${t("exec.plan_blocked", { step: String(this.tracker?.getCurrentStep()?.id ?? "?"), max: MAX_PLAN_WARNINGS_BEFORE_BLOCK })}</system-summary>`
13014
+ });
13015
+ return false;
13016
+ }
13017
+ } else {
13018
+ this.consecutivePlanWarnings = 0;
13254
13019
  }
13255
13020
  return true;
13256
13021
  },
@@ -13268,7 +13033,7 @@ Sub-tasks: ${note}`
13268
13033
  this.stuckDetector.recordToolSuccess();
13269
13034
  }
13270
13035
  if (this.tracker && result.success && (call.name === "write_file" || call.name === "edit_file")) {
13271
- this.advancePlanIfStepComplete();
13036
+ this.advancePlanIfStepComplete(ctx.contextManager);
13272
13037
  }
13273
13038
  }
13274
13039
  };
@@ -13288,9 +13053,7 @@ Sub-tasks: ${note}`
13288
13053
  "glob",
13289
13054
  "grep",
13290
13055
  "file_info",
13291
- "load_skill",
13292
- "question",
13293
- "approve"
13056
+ "load_skill"
13294
13057
  ];
13295
13058
  if (allowedAlways.includes(call.name))
13296
13059
  return null;
@@ -13310,24 +13073,78 @@ Sub-tasks: ${note}`
13310
13073
  tool: call.name
13311
13074
  });
13312
13075
  }
13313
- advancePlanIfStepComplete() {
13076
+ advancePlanIfStepComplete(contextManager) {
13314
13077
  const step = this.tracker?.getCurrentStep();
13315
13078
  if (!step)
13316
13079
  return;
13080
+ const stepText = step.description.toLowerCase();
13317
13081
  const stepPaths = step.description.match(/\b[\w./\\-]+\.[a-z]+/gi) || [];
13082
+ const isDepsStep = stepText.includes("install") || stepText.includes("зависим") || stepText.includes("init") || stepText.includes("инициализац");
13083
+ if (isDepsStep) {
13084
+ const lockFiles = [
13085
+ "package-lock.json",
13086
+ "bun.lockb",
13087
+ "yarn.lock",
13088
+ "pnpm-lock.yaml",
13089
+ "node_modules",
13090
+ "go.sum",
13091
+ "Cargo.lock",
13092
+ "Pipfile.lock",
13093
+ "poetry.lock",
13094
+ "requirements.txt"
13095
+ ];
13096
+ const hasLockFile = lockFiles.some((f) => existsSync26(resolve14(this.baseDir, f)));
13097
+ if (!hasLockFile) {
13098
+ if (contextManager) {
13099
+ contextManager.addMessage({
13100
+ role: "user",
13101
+ content: `<system-summary>${t("exec.step_gate_deps", { step: String(step.id), description: step.description })}</system-summary>`
13102
+ });
13103
+ }
13104
+ return;
13105
+ }
13106
+ }
13318
13107
  if (stepPaths.length === 0)
13319
13108
  return;
13320
13109
  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
- }
13110
+ if (!allExist)
13111
+ return;
13112
+ const emptyFiles = [];
13113
+ for (const p of stepPaths) {
13114
+ try {
13115
+ const content = readFileSync15(resolve14(this.baseDir, p), "utf-8");
13116
+ if (content.trim().length < 10) {
13117
+ emptyFiles.push(p);
13118
+ }
13119
+ } catch {}
13120
+ }
13121
+ if (emptyFiles.length > 0 && contextManager) {
13122
+ contextManager.addMessage({
13123
+ role: "user",
13124
+ content: `<system-summary>${t("exec.step_gate_empty", { step: String(step.id), files: emptyFiles.join(", ") })}</system-summary>`
13125
+ });
13126
+ return;
13127
+ }
13128
+ this.tracker?.updateStepStatus(step.id, "done");
13129
+ const hadNext = this.tracker?.advance() ?? false;
13130
+ if (this.tracker) {
13131
+ this.persister.save(this.tracker.getPlan());
13132
+ }
13133
+ if (contextManager) {
13134
+ const nextStep = hadNext ? this.tracker?.getCurrentStep() : null;
13135
+ const nextMsg = nextStep ? t("exec.step_gate_ok", {
13136
+ step: String(step.id),
13137
+ nextStep: String(nextStep.id),
13138
+ nextDesc: nextStep.description
13139
+ }) : t("exec.step_gate_last", { step: String(step.id) });
13140
+ contextManager.addMessage({
13141
+ role: "user",
13142
+ content: `<system-summary>${nextMsg}</system-summary>`
13143
+ });
13327
13144
  }
13328
13145
  }
13329
13146
  }
13330
- var STUCK_RECOVERY_COOLDOWN = 5;
13147
+ var STUCK_RECOVERY_COOLDOWN = 5, MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3;
13331
13148
  var init_module = __esm(() => {
13332
13149
  init_i18n();
13333
13150
  init_tracker();
@@ -13338,7 +13155,7 @@ var init_module = __esm(() => {
13338
13155
  });
13339
13156
 
13340
13157
  // 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";
13158
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, existsSync as existsSync27, readdirSync as readdirSync6, unlinkSync as unlinkSync3 } from "fs";
13342
13159
  import { join as join19 } from "path";
13343
13160
  import { homedir as homedir8 } from "os";
13344
13161
 
@@ -13388,7 +13205,7 @@ class SessionFileEncryptor {
13388
13205
  return lines.map((line) => this.decryptFileContent(line));
13389
13206
  }
13390
13207
  readSessionFile(filePath) {
13391
- const content = readFileSync15(filePath, "utf8");
13208
+ const content = readFileSync16(filePath, "utf8");
13392
13209
  return this.decryptFileContent(content);
13393
13210
  }
13394
13211
  writeSessionFile(filePath, content) {
@@ -13396,7 +13213,7 @@ class SessionFileEncryptor {
13396
13213
  writeFileSync10(filePath, encrypted, "utf8");
13397
13214
  }
13398
13215
  readSessionJSON(filePath) {
13399
- const content = readFileSync15(filePath, "utf8");
13216
+ const content = readFileSync16(filePath, "utf8");
13400
13217
  return this.decryptJSON(content);
13401
13218
  }
13402
13219
  writeSessionJSON(filePath, obj) {
@@ -13404,7 +13221,7 @@ class SessionFileEncryptor {
13404
13221
  writeFileSync10(filePath, content, "utf8");
13405
13222
  }
13406
13223
  readSessionJSONL(filePath) {
13407
- const content = readFileSync15(filePath, "utf8");
13224
+ const content = readFileSync16(filePath, "utf8");
13408
13225
  const lines = content.split(`
13409
13226
  `).filter((line) => line.trim());
13410
13227
  const decryptedLines = this.decryptJSONL(lines);
@@ -13423,7 +13240,7 @@ class SessionFileEncryptor {
13423
13240
  const filePath = join19(sessionDir, file);
13424
13241
  if (existsSync27(filePath) && !file.endsWith(".enc")) {
13425
13242
  try {
13426
- const content = readFileSync15(filePath, "utf8");
13243
+ const content = readFileSync16(filePath, "utf8");
13427
13244
  const encrypted = this.encryptFileContent(content);
13428
13245
  writeFileSync10(filePath + ".enc", encrypted, "utf8");
13429
13246
  unlinkSync3(filePath);
@@ -13440,7 +13257,7 @@ class SessionFileEncryptor {
13440
13257
  const encFilePath = join19(sessionDir, file);
13441
13258
  const decFilePath = encFilePath.slice(0, -4);
13442
13259
  try {
13443
- const content = readFileSync15(encFilePath, "utf8");
13260
+ const content = readFileSync16(encFilePath, "utf8");
13444
13261
  const decrypted = this.decryptFileContent(content);
13445
13262
  writeFileSync10(decFilePath, decrypted, "utf8");
13446
13263
  unlinkSync3(encFilePath);
@@ -13465,7 +13282,7 @@ import {
13465
13282
  existsSync as existsSync28,
13466
13283
  mkdirSync as mkdirSync12,
13467
13284
  readdirSync as readdirSync7,
13468
- readFileSync as readFileSync16,
13285
+ readFileSync as readFileSync17,
13469
13286
  rmSync,
13470
13287
  writeFileSync as writeFileSync11,
13471
13288
  appendFileSync as appendFileSync5
@@ -13525,7 +13342,7 @@ class SessionStore {
13525
13342
  if (!existsSync28(path))
13526
13343
  return null;
13527
13344
  try {
13528
- const raw = readFileSync16(path, "utf-8");
13345
+ const raw = readFileSync17(path, "utf-8");
13529
13346
  const content = this.encryptor ? this.encryptor.decryptFileContent(raw) : raw;
13530
13347
  return JSON.parse(content);
13531
13348
  } catch {
@@ -13555,7 +13372,7 @@ class SessionStore {
13555
13372
  if (!existsSync28(path))
13556
13373
  return [];
13557
13374
  try {
13558
- const raw = readFileSync16(path, "utf-8");
13375
+ const raw = readFileSync17(path, "utf-8");
13559
13376
  const lines = raw.split(`
13560
13377
  `).filter(Boolean);
13561
13378
  if (this.encryptor?.isEnabled()) {
@@ -13584,7 +13401,7 @@ class SessionStore {
13584
13401
  if (!existsSync28(path))
13585
13402
  return [];
13586
13403
  try {
13587
- const raw = readFileSync16(path, "utf-8");
13404
+ const raw = readFileSync17(path, "utf-8");
13588
13405
  const lines = raw.split(`
13589
13406
  `).filter(Boolean);
13590
13407
  if (this.encryptor?.isEnabled()) {
@@ -13626,7 +13443,7 @@ class SessionStore {
13626
13443
  if (updatedAt < thirtyDaysAgo) {
13627
13444
  const historyPath = this.historyPath(session2.id);
13628
13445
  if (existsSync28(historyPath)) {
13629
- const content = readFileSync16(historyPath, "utf-8");
13446
+ const content = readFileSync17(historyPath, "utf-8");
13630
13447
  const compressed = gzipSync(content);
13631
13448
  const gzPath = join20(this.baseDir, `${session2.id}.jsonl.gz`);
13632
13449
  writeFileSync11(gzPath, compressed);
@@ -13835,7 +13652,7 @@ class ProfileCompressor {
13835
13652
  }
13836
13653
 
13837
13654
  // src/modules/user-profile/profile.ts
13838
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync12, existsSync as existsSync29, mkdirSync as mkdirSync13 } from "fs";
13655
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, existsSync as existsSync29, mkdirSync as mkdirSync13 } from "fs";
13839
13656
  import { join as join21 } from "path";
13840
13657
  import { homedir as homedir9, hostname, platform as platform4, type } from "os";
13841
13658
  import { env } from "process";
@@ -13870,7 +13687,7 @@ class UserProfile {
13870
13687
  if (!existsSync29(path))
13871
13688
  return null;
13872
13689
  try {
13873
- const data = JSON.parse(readFileSync17(path, "utf-8"));
13690
+ const data = JSON.parse(readFileSync18(path, "utf-8"));
13874
13691
  this.info = {
13875
13692
  platform: data.platform,
13876
13693
  os: data.os,
@@ -13905,7 +13722,7 @@ class UserProfile {
13905
13722
  var init_profile = () => {};
13906
13723
 
13907
13724
  // src/modules/skills/loader.ts
13908
- import { readdirSync as readdirSync8, readFileSync as readFileSync18, existsSync as existsSync30, statSync as statSync5 } from "fs";
13725
+ import { readdirSync as readdirSync8, readFileSync as readFileSync19, existsSync as existsSync30, statSync as statSync5 } from "fs";
13909
13726
  import { join as join22 } from "path";
13910
13727
 
13911
13728
  class SkillsLoader {
@@ -13927,7 +13744,7 @@ class SkillsLoader {
13927
13744
  }
13928
13745
  if (!entry.endsWith(".md") && !entry.endsWith(".skill.md"))
13929
13746
  continue;
13930
- const content = readFileSync18(fullPath, "utf-8");
13747
+ const content = readFileSync19(fullPath, "utf-8");
13931
13748
  const parsed = this.parseSkillFile(content, fullPath);
13932
13749
  if (parsed)
13933
13750
  skills.push(parsed);
@@ -14199,7 +14016,7 @@ var init_browser2 = __esm(() => {
14199
14016
  });
14200
14017
 
14201
14018
  // src/modules/indexer/walker.ts
14202
- import { readdirSync as readdirSync9, readFileSync as readFileSync19, statSync as statSync6, existsSync as existsSync31, watch } from "fs";
14019
+ import { readdirSync as readdirSync9, readFileSync as readFileSync20, statSync as statSync6, existsSync as existsSync31, watch } from "fs";
14203
14020
  import { join as join23, relative, extname as extname5 } from "path";
14204
14021
 
14205
14022
  class Indexer {
@@ -14249,7 +14066,7 @@ class Indexer {
14249
14066
  const ext = extname5(entry).toLowerCase();
14250
14067
  const language = LANGUAGES[ext];
14251
14068
  if (language) {
14252
- const content = readFileSync19(fullPath, "utf-8");
14069
+ const content = readFileSync20(fullPath, "utf-8");
14253
14070
  const exports = this.extractExports(content, language);
14254
14071
  files.push({ path: relPath, language, exports, size: stat.size });
14255
14072
  totalSize += stat.size;
@@ -14301,7 +14118,7 @@ var init_walker = __esm(() => {
14301
14118
  });
14302
14119
 
14303
14120
  // src/modules/indexer/cache.ts
14304
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync13, existsSync as existsSync32, mkdirSync as mkdirSync14, rmSync as rmSync2 } from "fs";
14121
+ import { readFileSync as readFileSync21, writeFileSync as writeFileSync13, existsSync as existsSync32, mkdirSync as mkdirSync14, rmSync as rmSync2 } from "fs";
14305
14122
  import { join as join24 } from "path";
14306
14123
 
14307
14124
  class IndexCache {
@@ -14316,7 +14133,7 @@ class IndexCache {
14316
14133
  if (!existsSync32(this.cachePath))
14317
14134
  return null;
14318
14135
  try {
14319
- this.cache = JSON.parse(readFileSync20(this.cachePath, "utf-8"));
14136
+ this.cache = JSON.parse(readFileSync21(this.cachePath, "utf-8"));
14320
14137
  return this.cache;
14321
14138
  } catch {
14322
14139
  return null;
@@ -14743,7 +14560,7 @@ __export(exports_bootstrap, {
14743
14560
  });
14744
14561
  import { homedir as homedir11 } from "os";
14745
14562
  import { join as join26, resolve as resolve15 } from "path";
14746
- import { existsSync as existsSync33, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "fs";
14563
+ import { existsSync as existsSync33, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
14747
14564
  function buildSystemInfo(config, baseDir, profileCompressed) {
14748
14565
  const now = new Date().toISOString().replace("T", " ").slice(0, 19);
14749
14566
  const isWin = profileCompressed.toLowerCase().includes("win32");
@@ -14765,9 +14582,10 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
14765
14582
  if (isWin) {
14766
14583
  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
14584
  }
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.`);
14585
+ 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.`);
14586
+ 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
14587
  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.`);
14588
+ 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
14589
  }
14772
14590
  const hasMCP = config.mcpServers && Object.values(config.mcpServers).some((s) => s.enabled !== false);
14773
14591
  if (hasMCP) {
@@ -14975,7 +14793,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
14975
14793
  ];
14976
14794
  for (const p of agentsMdCandidates) {
14977
14795
  if (existsSync33(p)) {
14978
- const content = readFileSync21(p, "utf-8").trim();
14796
+ const content = readFileSync22(p, "utf-8").trim();
14979
14797
  if (content) {
14980
14798
  agentsMdBlocks.push({
14981
14799
  content,
@@ -15526,7 +15344,7 @@ init_config();
15526
15344
  init_colors();
15527
15345
  init_i18n();
15528
15346
  init_spinner();
15529
- import * as readline2 from "readline";
15347
+ import * as readline from "readline";
15530
15348
 
15531
15349
  // src/ui/box.ts
15532
15350
  init_string_width();
@@ -15706,7 +15524,7 @@ async function testChat(apiBase, apiKey, model) {
15706
15524
  }
15707
15525
  async function runSetup() {
15708
15526
  console.log(t("setup.title"));
15709
- const rl = readline2.createInterface({
15527
+ const rl = readline.createInterface({
15710
15528
  input: process.stdin,
15711
15529
  output: process.stdout
15712
15530
  });
@@ -15810,7 +15628,7 @@ async function runSetup() {
15810
15628
  init_i18n();
15811
15629
  import { join as join28, dirname as dirname9 } from "path";
15812
15630
  import { homedir as homedir13 } from "os";
15813
- import { existsSync as existsSync34, readFileSync as readFileSync22 } from "fs";
15631
+ import { existsSync as existsSync34, readFileSync as readFileSync23 } from "fs";
15814
15632
 
15815
15633
  // src/cli/security-commands.ts
15816
15634
  init_bootstrap();
@@ -16411,7 +16229,7 @@ function readVersion() {
16411
16229
  ];
16412
16230
  for (const p of candidates) {
16413
16231
  if (existsSync34(p)) {
16414
- const raw = JSON.parse(readFileSync22(p, "utf8"));
16232
+ const raw = JSON.parse(readFileSync23(p, "utf8"));
16415
16233
  if (raw.version)
16416
16234
  return raw.version;
16417
16235
  }
@@ -16573,8 +16391,8 @@ init_bootstrap();
16573
16391
 
16574
16392
  // src/cli/repl.ts
16575
16393
  init_colors();
16576
- import * as readline3 from "readline";
16577
- import { existsSync as existsSync35, readFileSync as readFileSync23, writeFileSync as writeFileSync15 } from "fs";
16394
+ import * as readline2 from "readline";
16395
+ import { existsSync as existsSync35, readFileSync as readFileSync24, writeFileSync as writeFileSync15 } from "fs";
16578
16396
  import { join as join29, dirname as dirname10 } from "path";
16579
16397
  import { homedir as homedir14 } from "os";
16580
16398
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -17030,7 +16848,7 @@ function readVersion2() {
17030
16848
  ];
17031
16849
  for (const p of candidates) {
17032
16850
  if (existsSync35(p)) {
17033
- const raw = JSON.parse(readFileSync23(p, "utf8"));
16851
+ const raw = JSON.parse(readFileSync24(p, "utf8"));
17034
16852
  if (raw.version)
17035
16853
  return raw.version;
17036
16854
  }
@@ -17104,7 +16922,7 @@ class Repl {
17104
16922
  this.registerSessionCommands();
17105
16923
  this.registerSkillCommands();
17106
16924
  this.setupCompleter();
17107
- this.rl = readline3.createInterface({
16925
+ this.rl = readline2.createInterface({
17108
16926
  input: process.stdin,
17109
16927
  output: process.stdout,
17110
16928
  prompt: pc.cyan("> "),
@@ -17123,7 +16941,7 @@ class Repl {
17123
16941
  loadHistory() {
17124
16942
  if (existsSync35(this.historyPath)) {
17125
16943
  try {
17126
- const raw = readFileSync23(this.historyPath, "utf-8");
16944
+ const raw = readFileSync24(this.historyPath, "utf-8");
17127
16945
  this.history = raw.split(`
17128
16946
  `).filter(Boolean).slice(-this.maxHistory);
17129
16947
  } catch {
@@ -17741,7 +17559,7 @@ class Repl {
17741
17559
  this.running = false;
17742
17560
  });
17743
17561
  if (process.stdin.isTTY) {
17744
- readline3.emitKeypressEvents(process.stdin);
17562
+ readline2.emitKeypressEvents(process.stdin);
17745
17563
  process.stdin.on("keypress", async (str, key) => {
17746
17564
  if (key.name === "escape") {
17747
17565
  const now = Date.now();
@@ -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.2",
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": {