micro-models-agent 0.35.4 → 0.36.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 +1263 -923
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -2472,6 +2472,10 @@ Command: {command}`,
2472
2472
  "plan.step_already_done": "Step {step} is already done — nothing to do. Plan progress:",
2473
2473
  "plan.order_blocked": 'Cannot mark step {step} done: step {first} ("{desc}") is not finished yet. Complete earlier steps first, or mark step {first} status=skipped if it is not needed.',
2474
2474
  "plan.deliverables_missing": "Cannot mark step {step} done: the files it names do not exist yet: {files}. Create these files first (or mark the step status=skipped if they are not actually needed).",
2475
+ "plan.kinds_mismatch": "kinds array length must match steps",
2476
+ "plan.kinds_invalid": 'Invalid step kind(s): {kinds}. Use "create" or "delete".',
2477
+ "plan.kinds_not_array": 'kinds must be an array of "create" or "delete"',
2478
+ "plan.deliverables_remain": "Cannot mark step {step} done: the files it names still exist: {files}. This step is kind=delete — delete these files first (or set status=skipped if they should stay).",
2475
2479
  "plan.show_header": "Plan status:",
2476
2480
  "plan.show_empty": "(plan has no steps)",
2477
2481
  "plan.list_header": "Plans:",
@@ -2768,6 +2772,7 @@ Use this knowledge to answer the user's question.`,
2768
2772
  "exec.audit_pass": "[✓] Task complete: {done}/{total} steps done, {files} files verified",
2769
2773
  "exec.audit_pending": "[✗] Task incomplete: {done}/{total} steps done — remaining steps are not marked done",
2770
2774
  "exec.audit_fail": "[✗] Task incomplete: {done}/{total} steps done, {files} files missing",
2775
+ "exec.audit_leftovers": "[✗] Task incomplete: {done}/{total} steps done, {files} files from delete-steps still exist",
2771
2776
  "exec.audit_fail_tests": "[✗] Task incomplete: {done}/{total} steps done, tests FAILING: {failed} failed / {passed} passed — {detail}",
2772
2777
  "exec.audit_fail_typecheck": "[✗] Task incomplete: {done}/{total} steps done, {missing} files missing, typecheck error: {typeError}",
2773
2778
  "exec.audit_incomplete": "[⚠ Final audit incomplete: {summary}. Task is NOT finished — continue working. Remaining steps: {steps}]",
@@ -2779,6 +2784,7 @@ Use this knowledge to answer the user's question.`,
2779
2784
  [Hints]
2780
2785
  {hints}`,
2781
2786
  "exec.file_rewrite_warning": "⚠️ File {file} has been rewritten {count} times. Consider a different approach — the current fix strategy is not working.",
2787
+ "exec.forbidden_cmd": 'STOP using "{cmd}" via the bash tool — it is not a native Windows cmd.exe command and has failed repeatedly this session. Use the dedicated tool instead: grep → the grep tool, ls/dir → list_dir, find → glob, rm → delete_file, sed → edit_file, touch → write_file, which → `where`, cp/mv → move_file, diff → read_file. Do NOT call bash for this purpose again.',
2782
2788
  "hall.max_retries_exhausted": "Model returned empty or insufficient responses after multiple retries",
2783
2789
  "hall.short_response": "Response too short or empty",
2784
2790
  "hall.repetitive": "Response too repetitive ({pct}% overlap)",
@@ -3064,6 +3070,10 @@ var init_ru = __esm(() => {
3064
3070
  "plan.step_already_done": "Шаг {step} уже выполнен — ничего делать не нужно. Прогресс плана:",
3065
3071
  "plan.order_blocked": "Нельзя отметить шаг {step} выполненным: шаг {first} («{desc}») ещё не завершён. Сначала завершите предыдущие шаги или отметьте шаг {first} status=skipped, если он не нужен.",
3066
3072
  "plan.deliverables_missing": "Нельзя отметить шаг {step} выполненным: указанные в нём файлы ещё не существуют: {files}. Сначала создайте эти файлы (или отметьте шаг status=skipped, если они на самом деле не нужны).",
3073
+ "plan.kinds_mismatch": "Длина kinds должна совпадать с числом шагов",
3074
+ "plan.kinds_invalid": 'Недопустимый kind шага: {kinds}. Используй "create" или "delete".',
3075
+ "plan.kinds_not_array": 'kinds должен быть массивом значений "create" или "delete"',
3076
+ "plan.deliverables_remain": "Нельзя пометить шаг {step} как выполненный: указанные файлы всё ещё существуют: {files}. Шаг имеет kind=delete — сначала удали их (или поставь status=skipped, если они должны остаться).",
3067
3077
  "plan.show_header": "Статус плана:",
3068
3078
  "plan.show_empty": "(в плане нет шагов)",
3069
3079
  "plan.list_header": "Планы:",
@@ -3360,6 +3370,7 @@ var init_ru = __esm(() => {
3360
3370
  "exec.audit_pass": "[✓] Задача выполнена: {done}/{total} шагов, {files} файлов проверено",
3361
3371
  "exec.audit_pending": "[✗] Задача не выполнена: {done}/{total} шагов — оставшиеся шаги не отмечены выполненными",
3362
3372
  "exec.audit_fail": "[✗] Задача не выполнена: {done}/{total} шагов, {files} файлов отсутствует",
3373
+ "exec.audit_leftovers": "[✗] Задача не выполнена: {done}/{total} шагов, {files} файлов из delete-шагов всё ещё существуют",
3363
3374
  "exec.audit_fail_tests": "[✗] Задача не выполнена: {done}/{total} шагов, тесты ПАДАЮТ: {failed} failed / {passed} passed — {detail}",
3364
3375
  "exec.audit_fail_typecheck": "[✗] Задача не выполнена: {done}/{total} шагов, {missing} файлов отсутствует, ошибка typecheck: {typeError}",
3365
3376
  "exec.audit_incomplete": "[⚠ Финальная проверка не пройдена: {summary}. Задача НЕ завершена — продолжайте работу. Оставшиеся шаги: {steps}]",
@@ -3371,6 +3382,7 @@ var init_ru = __esm(() => {
3371
3382
  [Подсказки]
3372
3383
  {hints}`,
3373
3384
  "exec.file_rewrite_warning": "⚠️ Файл {file} был перезаписан {count} раз. Попробуйте другой подход — текущая стратегия исправлений не работает.",
3385
+ "exec.forbidden_cmd": 'ПРЕКРАТИ использовать "{cmd}" через bash — это не команда Windows cmd.exe, и она уже неоднократно падала в этой сессии. Используй предназначенный тул: grep → тул grep, ls/dir → list_dir, find → glob, rm → delete_file, sed → edit_file, touch → write_file, which → `where`, cp/mv → move_file, diff → read_file. Больше не вызывай bash для этого.',
3374
3386
  "hall.max_retries_exhausted": "Модель вернула пустой или недостаточный ответ после нескольких попыток",
3375
3387
  "hall.short_response": "Слишком короткий или пустой ответ",
3376
3388
  "hall.repetitive": "Слишком повторяющийся ответ ({pct}% совпадение)",
@@ -4902,6 +4914,30 @@ var exports_openai_compat = {};
4902
4914
  __export(exports_openai_compat, {
4903
4915
  OpenAICompatProvider: () => OpenAICompatProvider
4904
4916
  });
4917
+ function buildRequestBody(opts) {
4918
+ const body = {
4919
+ model: opts.model,
4920
+ messages: opts.messages,
4921
+ stream: opts.stream
4922
+ };
4923
+ if (opts.maxTokens !== undefined)
4924
+ body.max_tokens = opts.maxTokens;
4925
+ if (opts.reasoningEffort) {
4926
+ body.reasoning_effort = opts.reasoningEffort;
4927
+ }
4928
+ if (opts.tools && opts.tools.length > 0) {
4929
+ body.tools = opts.tools.map((t2) => ({
4930
+ type: "function",
4931
+ function: {
4932
+ name: t2.name,
4933
+ description: t2.description,
4934
+ parameters: t2.parameters
4935
+ }
4936
+ }));
4937
+ body.tool_choice = "auto";
4938
+ }
4939
+ return body;
4940
+ }
4905
4941
 
4906
4942
  class OpenAICompatProvider {
4907
4943
  model;
@@ -4949,26 +4985,14 @@ class OpenAICompatProvider {
4949
4985
  }
4950
4986
  }
4951
4987
  async* doStream(messages, tools, signal, options) {
4952
- const body = {
4988
+ const body = buildRequestBody({
4953
4989
  model: this.model,
4954
4990
  messages,
4991
+ tools,
4955
4992
  stream: true,
4956
- max_tokens: options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096
4957
- };
4958
- if (options?.reasoningEffort) {
4959
- body.reasoning_effort = options.reasoningEffort;
4960
- }
4961
- if (tools && tools.length > 0) {
4962
- body.tools = tools.map((t2) => ({
4963
- type: "function",
4964
- function: {
4965
- name: t2.name,
4966
- description: t2.description,
4967
- parameters: t2.parameters
4968
- }
4969
- }));
4970
- body.tool_choice = "auto";
4971
- }
4993
+ maxTokens: options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096,
4994
+ reasoningEffort: options?.reasoningEffort
4995
+ });
4972
4996
  const headers = {
4973
4997
  "Content-Type": "application/json"
4974
4998
  };
@@ -5094,26 +5118,14 @@ class OpenAICompatProvider {
5094
5118
  }
5095
5119
  }
5096
5120
  async doNonStreaming(messages, tools, signal, options) {
5097
- const body = {
5121
+ const body = buildRequestBody({
5098
5122
  model: this.model,
5099
5123
  messages,
5124
+ tools,
5100
5125
  stream: false,
5101
- max_tokens: options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096
5102
- };
5103
- if (options?.reasoningEffort) {
5104
- body.reasoning_effort = options.reasoningEffort;
5105
- }
5106
- if (tools && tools.length > 0) {
5107
- body.tools = tools.map((t2) => ({
5108
- type: "function",
5109
- function: {
5110
- name: t2.name,
5111
- description: t2.description,
5112
- parameters: t2.parameters
5113
- }
5114
- }));
5115
- body.tool_choice = "auto";
5116
- }
5126
+ maxTokens: options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096,
5127
+ reasoningEffort: options?.reasoningEffort
5128
+ });
5117
5129
  const headers = {
5118
5130
  "Content-Type": "application/json"
5119
5131
  };
@@ -9374,13 +9386,14 @@ class StuckDetector {
9374
9386
  if (desc.includes("deploy") || desc.includes("publish")) {
9375
9387
  hints.push("Verify credentials and network access before deploying.");
9376
9388
  }
9377
- if (this.hasRepetitiveToolCalls()) {
9389
+ const primary = this.getPrimaryReason();
9390
+ if (this.hasRepetitiveToolCalls() && primary !== "repetitive") {
9378
9391
  hints.push("You are calling the same tool repeatedly with the same arguments. Try a different approach.");
9379
9392
  }
9380
- if (this.hasConsecutiveFailures()) {
9393
+ if (this.hasConsecutiveFailures() && primary !== "consecutive") {
9381
9394
  hints.push("Multiple different tools are failing. Check if the environment is set up correctly.");
9382
9395
  }
9383
- if (this.hasReadOnlyLoop()) {
9396
+ if (this.hasReadOnlyLoop() && primary !== "read-only") {
9384
9397
  hints.push("You have made many read-only tool calls (read_file/glob/grep/browser) without writing anything. Stop exploring — make the edit/write the task needs, or use the plan tool to decide next steps.");
9385
9398
  }
9386
9399
  return hints;
@@ -9506,35 +9519,50 @@ class StuckDetector {
9506
9519
  return "";
9507
9520
  }
9508
9521
  getRecoveryMessage() {
9509
- if (this.isStuck()) {
9522
+ const reason = this.getPrimaryReason();
9523
+ if (reason === "stuck") {
9510
9524
  return t("exec.stuck_recovery", {
9511
9525
  iterations: this.iterationsOnCurrentStep,
9512
9526
  stepId: String(this.currentStepId ?? "?"),
9513
9527
  description: this.currentStepDescription
9514
9528
  });
9515
9529
  }
9516
- const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
9517
- if (errorTool) {
9530
+ if (reason === "tool-errors") {
9531
+ const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
9518
9532
  return t("exec.tool_errors_recovery", {
9519
9533
  tool: errorTool[0],
9520
9534
  count: errorTool[1]
9521
9535
  });
9522
9536
  }
9523
- if (this.hasConsecutiveFailures()) {
9537
+ if (reason === "consecutive") {
9524
9538
  return t("exec.consecutive_failures_recovery", {
9525
9539
  count: this.consecutiveFailures
9526
9540
  });
9527
9541
  }
9528
- if (this.hasRepetitiveToolCalls()) {
9542
+ if (reason === "repetitive") {
9529
9543
  return this.getRepetitiveToolMessage();
9530
9544
  }
9531
- if (this.hasReadOnlyLoop()) {
9545
+ if (reason === "read-only") {
9532
9546
  return t("exec.read_only_loop_recovery", {
9533
9547
  count: String(this.readOnlyStreak)
9534
9548
  });
9535
9549
  }
9536
9550
  return "";
9537
9551
  }
9552
+ getPrimaryReason() {
9553
+ if (this.isStuck())
9554
+ return "stuck";
9555
+ const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
9556
+ if (errorTool)
9557
+ return "tool-errors";
9558
+ if (this.hasConsecutiveFailures())
9559
+ return "consecutive";
9560
+ if (this.hasRepetitiveToolCalls())
9561
+ return "repetitive";
9562
+ if (this.hasReadOnlyLoop())
9563
+ return "read-only";
9564
+ return null;
9565
+ }
9538
9566
  reset() {
9539
9567
  this.currentStepId = null;
9540
9568
  this.iterationsOnCurrentStep = 0;
@@ -10382,7 +10410,11 @@ class Agent {
10382
10410
  ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10383
10411
  }
10384
10412
  emitPhase(iteration, phase, onPhase) {
10385
- this.deps.pluginManager.runOnPhase?.({ iteration, logger: this.deps.logger }, phase);
10413
+ this.deps.pluginManager.runOnPhase?.({
10414
+ iteration,
10415
+ logger: this.deps.logger,
10416
+ contextManager: this.deps.contextManager
10417
+ }, phase);
10386
10418
  onPhase?.(phase);
10387
10419
  }
10388
10420
  async run(input, onChunk, onMeta, onTool, onPhase) {
@@ -10412,7 +10444,8 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10412
10444
  }
10413
10445
  pluginManager.runOnSessionStart({
10414
10446
  logger,
10415
- sessionManager: sessionManager?.getActiveMeta()
10447
+ sessionManager: sessionManager?.getActiveMeta(),
10448
+ contextManager
10416
10449
  });
10417
10450
  if (config.session?.baselineCheck !== false) {
10418
10451
  const verifier = new StepVerifier(baseDir);
@@ -10545,7 +10578,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10545
10578
  if (chunk.type === "reasoning" && chunk.content) {
10546
10579
  reasoningContent += chunk.content;
10547
10580
  if (config.showReasoning) {
10548
- const metaOut = pluginManager.runOnMeta({ iteration, logger }, chunk.content);
10581
+ const metaOut = pluginManager.runOnMeta({ iteration, logger, contextManager }, chunk.content);
10549
10582
  if (metaOut) {
10550
10583
  onMeta?.(pc2.dim(metaOut));
10551
10584
  }
@@ -10579,7 +10612,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10579
10612
  logger.logLLMResponse(config.model, textContent.length, Date.now() - llmStart, err.message, "agent");
10580
10613
  logger.error(`LLM call failed: ${err.message}`);
10581
10614
  slog.logError(err.message);
10582
- pluginManager.runOnError({ iteration, logger }, err);
10615
+ pluginManager.runOnError({ iteration, logger, contextManager }, err);
10583
10616
  return {
10584
10617
  success: false,
10585
10618
  text: lastText,
@@ -10598,7 +10631,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10598
10631
  const showText = textChunks.length > 0 && (!sawToolCall || toolComments && !isToolCallJson(textContent));
10599
10632
  if (showText) {
10600
10633
  for (const chunk of textChunks) {
10601
- const textOut = pluginManager.runOnText({ iteration, logger }, chunk);
10634
+ const textOut = pluginManager.runOnText({ iteration, logger, contextManager }, chunk);
10602
10635
  onChunk?.(textOut);
10603
10636
  }
10604
10637
  }
@@ -10610,7 +10643,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10610
10643
  } else if (reasoningContent) {
10611
10644
  llmResponse = { type: "reasoning", content: reasoningContent };
10612
10645
  }
10613
- pluginManager.runOnAfterThink({ iteration, logger }, llmResponse);
10646
+ pluginManager.runOnAfterThink({ iteration, logger, contextManager }, llmResponse);
10614
10647
  if (this.deps.exitOnComplete && sawToolCall) {
10615
10648
  const signature = toolCalls.map((tc) => `${tc.name}:${JSON.stringify(tc.arguments)}`).join("|");
10616
10649
  if (signature && signature === lastToolSignature) {
@@ -10651,7 +10684,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10651
10684
  toolName: call.name,
10652
10685
  args: call.arguments
10653
10686
  });
10654
- pluginManager.runOnToolStart({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments });
10687
+ pluginManager.runOnToolStart({ iteration, logger, contextManager }, { id: call.id, name: call.name, arguments: call.arguments });
10655
10688
  onTool?.({ type: "start", tool: call.name, args: call.arguments });
10656
10689
  slog.logToolCall(call, iteration);
10657
10690
  const tokensBeforeTool = contextManager.getEstimatedTokens();
@@ -10667,13 +10700,13 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10667
10700
  hallucinationDetector.getConsistencyCheck().trackDeletedFile(filePath);
10668
10701
  }
10669
10702
  }
10670
- pluginManager.runOnToolEnd({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments }, result, duration);
10703
+ pluginManager.runOnToolEnd({ iteration, logger, contextManager }, { id: call.id, name: call.name, arguments: call.arguments }, result, duration);
10671
10704
  if (result.display) {
10672
10705
  onMeta?.(`
10673
10706
  ` + result.display + `
10674
10707
  `);
10675
10708
  } else {
10676
- const metaOut = pluginManager.runOnMeta({ iteration, logger }, result.output);
10709
+ const metaOut = pluginManager.runOnMeta({ iteration, logger, contextManager }, result.output);
10677
10710
  onMeta?.(`
10678
10711
  ` + pc2.dim(metaOut) + `
10679
10712
  `);
@@ -10994,7 +11027,8 @@ ${warnLine}
10994
11027
  logger.closeSessionLog();
10995
11028
  pluginManager.runOnSessionEnd({
10996
11029
  logger,
10997
- sessionManager: sessionManager?.getActiveMeta()
11030
+ sessionManager: sessionManager?.getActiveMeta(),
11031
+ contextManager
10998
11032
  });
10999
11033
  }
11000
11034
  }
@@ -11009,6 +11043,105 @@ var init_agent = __esm(() => {
11009
11043
  init_verifier();
11010
11044
  });
11011
11045
 
11046
+ // src/modules/context/fact-extractor.ts
11047
+ class FactExtractor {
11048
+ fileFacts = [];
11049
+ deletedFacts = [];
11050
+ decisionFacts = [];
11051
+ errorFacts = [];
11052
+ static MAX_FACTS = 20;
11053
+ get errorCount() {
11054
+ return this.errorFacts.length;
11055
+ }
11056
+ extract(turns) {
11057
+ const DRIVE = "(?:[a-zA-Z]:[\\\\/])?";
11058
+ const createdPatterns = [
11059
+ new RegExp(`(?:Created|Updated|Written|Moved|Replaced in) ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
11060
+ new RegExp(`Файл (?:создан|обновлён|записан|перемещён):? ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
11061
+ new RegExp(`file (?:created|updated|written|moved):? ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi")
11062
+ ];
11063
+ const deletedPatterns = [
11064
+ new RegExp(`Deleted ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
11065
+ new RegExp(`(?:Удалён|Файл удалён):? ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi")
11066
+ ];
11067
+ const newFiles = [];
11068
+ const newDeleted = [];
11069
+ const newDecisions = [];
11070
+ const newErrors = [];
11071
+ const lastEvent = new Map;
11072
+ for (const msg of turns) {
11073
+ const content = getMessageText(msg.content);
11074
+ if (msg.role === "user" && (content.startsWith("<system-summary>") || content.includes("[Compressed:"))) {
11075
+ continue;
11076
+ }
11077
+ if (msg.role === "tool") {
11078
+ for (const pattern of createdPatterns) {
11079
+ for (const match of content.matchAll(pattern)) {
11080
+ newFiles.push(match[1]);
11081
+ lastEvent.set(match[1], "file");
11082
+ }
11083
+ }
11084
+ for (const pattern of deletedPatterns) {
11085
+ for (const match of content.matchAll(pattern)) {
11086
+ newDeleted.push(match[1]);
11087
+ lastEvent.set(match[1], "deleted");
11088
+ }
11089
+ }
11090
+ if (content.includes("Plan:") && content.includes("[")) {
11091
+ const planLine = content.split(`
11092
+ `).find((line) => line.includes("Plan:"));
11093
+ if (planLine)
11094
+ newDecisions.push(planLine.trim());
11095
+ }
11096
+ }
11097
+ if (msg.role === "assistant") {
11098
+ if (content.includes("decided:") || content.includes("decision:")) {
11099
+ const line = content.split(`
11100
+ `).find((l) => l.includes("decided:") || l.includes("decision:"));
11101
+ if (line)
11102
+ newDecisions.push(line.trim().slice(0, 200));
11103
+ }
11104
+ }
11105
+ if (content.includes("Error:") || content.includes("failed") || content.includes("Ошибка:") || content.includes("не удалось")) {
11106
+ const line = content.split(`
11107
+ `).find((l) => l.includes("Error:") || l.includes("failed") || l.includes("Ошибка:") || l.includes("не удалось"));
11108
+ if (line)
11109
+ newErrors.push(line.trim().slice(0, 250));
11110
+ }
11111
+ }
11112
+ const dedup = (arr) => [...new Set(arr)];
11113
+ const finalFiles = dedup(newFiles.filter((p) => lastEvent.get(p) === "file"));
11114
+ const finalDeleted = dedup(newDeleted.filter((p) => lastEvent.get(p) === "deleted"));
11115
+ this.fileFacts = dedup([...this.fileFacts, ...finalFiles].filter((p) => !finalDeleted.includes(p))).slice(-FactExtractor.MAX_FACTS);
11116
+ this.deletedFacts = dedup([...this.deletedFacts, ...finalDeleted].filter((p) => !finalFiles.includes(p))).slice(-FactExtractor.MAX_FACTS);
11117
+ this.decisionFacts = dedup([...this.decisionFacts, ...newDecisions]).slice(-FactExtractor.MAX_FACTS);
11118
+ this.errorFacts = dedup([...this.errorFacts, ...newErrors]).slice(-FactExtractor.MAX_FACTS);
11119
+ }
11120
+ filesLine() {
11121
+ const deletedSet = new Set(this.deletedFacts);
11122
+ const liveFiles = this.fileFacts.filter((p) => !deletedSet.has(p));
11123
+ if (liveFiles.length === 0)
11124
+ return null;
11125
+ return `[Files: ${liveFiles.slice(-15).join("; ")}]`;
11126
+ }
11127
+ deletedLine() {
11128
+ if (this.deletedFacts.length === 0)
11129
+ return null;
11130
+ return `[Deleted: ${this.deletedFacts.slice(-10).join("; ")}]`;
11131
+ }
11132
+ decisionsLine() {
11133
+ if (this.decisionFacts.length === 0)
11134
+ return null;
11135
+ return `[Decisions: ${this.decisionFacts.slice(-5).join("; ")}]`;
11136
+ }
11137
+ errorsLine() {
11138
+ if (this.errorFacts.length === 0)
11139
+ return null;
11140
+ return `[Errors: ${this.errorFacts.slice(-3).join("; ")}]`;
11141
+ }
11142
+ }
11143
+ var init_fact_extractor = () => {};
11144
+
11012
11145
  // src/modules/context/manager.ts
11013
11146
  function summarizeArgs(args) {
11014
11147
  if (!args)
@@ -11027,6 +11160,18 @@ function truncate(s, max) {
11027
11160
  return s;
11028
11161
  return s.slice(0, max - 3) + "...";
11029
11162
  }
11163
+ function looksLikeErrorPaste(text) {
11164
+ if (/(?:^|\s)ERROR|Error:|error TS\d+|Transform failed|\[plugin:|SyntaxError|Cannot find|Uncaught|exception/i.test(text)) {
11165
+ return true;
11166
+ }
11167
+ if (/(?:^|\n)\s*\d+\s*\|/.test(text))
11168
+ return true;
11169
+ if (/[\w./\\-]+\.[a-z]{1,6}:\d+:\d+/.test(text))
11170
+ return true;
11171
+ if (/(?:^|\n)\s*at\s+\S+/.test(text))
11172
+ return true;
11173
+ return false;
11174
+ }
11030
11175
  function extractTriedAndFailed(messages) {
11031
11176
  const failures = new Map;
11032
11177
  for (const msg of messages) {
@@ -11058,11 +11203,10 @@ class ContextManager {
11058
11203
  peakTokens = 0;
11059
11204
  budget;
11060
11205
  compactionThreshold;
11061
- fileFacts = [];
11062
- decisionFacts = [];
11063
- errorFacts = [];
11206
+ facts = new FactExtractor;
11064
11207
  lastUserTask = "";
11065
- static MAX_FACTS = 20;
11208
+ sessionMission = "";
11209
+ lastUserFeedback = "";
11066
11210
  tokenCounter;
11067
11211
  pendingImageParts = [];
11068
11212
  toolTokens = 0;
@@ -11101,6 +11245,7 @@ class ContextManager {
11101
11245
  resetUserTurn() {
11102
11246
  this.iterationsSinceCompaction = 0;
11103
11247
  this.lastUserTask = "";
11248
+ this.lastUserFeedback = "";
11104
11249
  }
11105
11250
  getQuality() {
11106
11251
  const usedTokens = this.getEstimatedTokens();
@@ -11108,16 +11253,22 @@ class ContextManager {
11108
11253
  const COMPACTION_PENALTY_FLOOR = 0.5;
11109
11254
  const compactionLoss = Math.max(COMPACTION_PENALTY_FLOOR, 1 - this.compactionCount * 0.15);
11110
11255
  const msgCount = this.messages.length || 1;
11111
- const errorDensity = Math.max(0, 1 - Math.min(1, this.errorFacts.length / msgCount));
11256
+ const errorDensity = Math.max(0, 1 - Math.min(1, this.facts.errorCount / msgCount));
11112
11257
  const freshness = Math.max(0, 1 - this.iterationsSinceCompaction / COMPACTION_INTERVAL);
11113
11258
  const score = tokenLoad * 0.4 + compactionLoss * 0.25 + errorDensity * 0.2 + freshness * 0.15;
11114
11259
  return Math.round(Math.min(100, Math.max(0, score * 100)));
11115
11260
  }
11116
11261
  addMessage(msg) {
11117
- if (msg.role === "user" && !this.lastUserTask) {
11262
+ if (msg.role === "user") {
11118
11263
  const text = typeof msg.content === "string" ? msg.content : getMessageText(msg.content);
11119
11264
  if (text.trim() && !text.startsWith("<system-summary>") && !text.includes("[Compressed:")) {
11120
- this.lastUserTask = text.trim();
11265
+ if (looksLikeErrorPaste(text) && (this.lastUserTask || this.sessionMission)) {
11266
+ this.lastUserFeedback = text.trim().slice(0, 400);
11267
+ } else if (!this.lastUserTask && !this.lastUserFeedback) {
11268
+ this.lastUserTask = text.trim();
11269
+ if (!this.sessionMission)
11270
+ this.sessionMission = text.trim();
11271
+ }
11121
11272
  }
11122
11273
  }
11123
11274
  if (msg.role === "user" && this.pendingImageParts.length > 0) {
@@ -11206,22 +11357,27 @@ class ContextManager {
11206
11357
  const cutoff = this.messages.length - KEEP_LAST_N * 2;
11207
11358
  const oldTurns = this.messages.slice(0, cutoff);
11208
11359
  const recentTurns = this.messages.slice(cutoff);
11209
- this.extractFacts(oldTurns);
11360
+ this.facts.extract(oldTurns);
11210
11361
  const parts = [];
11211
11362
  parts.push(`[Compressed: ${oldTurns.length} old turns removed]`);
11212
- if (this.lastUserTask) {
11213
- const task = this.lastUserTask.slice(0, 400);
11363
+ const task = (this.lastUserTask || this.sessionMission).slice(0, 400);
11364
+ if (task)
11214
11365
  parts.push(`[Task: ${task}]`);
11215
- }
11216
- if (this.fileFacts.length > 0) {
11217
- parts.push(`[Files: ${this.fileFacts.slice(-15).join("; ")}]`);
11218
- }
11219
- if (this.decisionFacts.length > 0) {
11220
- parts.push(`[Decisions: ${this.decisionFacts.slice(-5).join("; ")}]`);
11221
- }
11222
- if (this.errorFacts.length > 0) {
11223
- parts.push(`[Errors: ${this.errorFacts.slice(-3).join("; ")}]`);
11224
- }
11366
+ if (this.lastUserFeedback && !task.startsWith(this.lastUserFeedback)) {
11367
+ parts.push(`[User feedback: ${this.lastUserFeedback}]`);
11368
+ }
11369
+ const filesLine = this.facts.filesLine();
11370
+ if (filesLine)
11371
+ parts.push(filesLine);
11372
+ const deletedLine = this.facts.deletedLine();
11373
+ if (deletedLine)
11374
+ parts.push(deletedLine);
11375
+ const decisionsLine = this.facts.decisionsLine();
11376
+ if (decisionsLine)
11377
+ parts.push(decisionsLine);
11378
+ const errorsLine = this.facts.errorsLine();
11379
+ if (errorsLine)
11380
+ parts.push(errorsLine);
11225
11381
  const triedAndFailed = extractTriedAndFailed(oldTurns);
11226
11382
  if (triedAndFailed.length > 0) {
11227
11383
  const lines = triedAndFailed.map((t2) => `- ${t2.tool}(${t2.args}): ${t2.error} (failed ${t2.count}x)`);
@@ -11251,55 +11407,6 @@ ${lines.join(`
11251
11407
  this.onCompact(summary);
11252
11408
  }
11253
11409
  }
11254
- extractFacts(turns) {
11255
- const DRIVE = "(?:[a-zA-Z]:[\\\\/])?";
11256
- const filePatterns = [
11257
- new RegExp(`(?:Created|Updated|Written|Deleted|Moved) ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
11258
- new RegExp(`Replaced in ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
11259
- new RegExp(`Файл (?:создан|обновлён|записан|удалён|перемещён):? ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
11260
- new RegExp(`file (?:created|updated|written|deleted|moved):? ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi")
11261
- ];
11262
- const newFiles = [];
11263
- const newDecisions = [];
11264
- const newErrors = [];
11265
- for (const msg of turns) {
11266
- const content = getMessageText(msg.content);
11267
- if (msg.role === "user" && (content.startsWith("<system-summary>") || content.includes("[Compressed:"))) {
11268
- continue;
11269
- }
11270
- if (msg.role === "tool") {
11271
- for (const pattern of filePatterns) {
11272
- for (const match of content.matchAll(pattern)) {
11273
- newFiles.push(match[1]);
11274
- }
11275
- }
11276
- if (content.includes("Plan:") && content.includes("[")) {
11277
- const planLine = content.split(`
11278
- `).find((line) => line.includes("Plan:"));
11279
- if (planLine)
11280
- newDecisions.push(planLine.trim());
11281
- }
11282
- }
11283
- if (msg.role === "assistant") {
11284
- if (content.includes("decided:") || content.includes("decision:")) {
11285
- const line = content.split(`
11286
- `).find((l) => l.includes("decided:") || l.includes("decision:"));
11287
- if (line)
11288
- newDecisions.push(line.trim().slice(0, 200));
11289
- }
11290
- }
11291
- if (content.includes("Error:") || content.includes("failed") || content.includes("Ошибка:") || content.includes("не удалось")) {
11292
- const line = content.split(`
11293
- `).find((l) => l.includes("Error:") || l.includes("failed") || l.includes("Ошибка:") || l.includes("не удалось"));
11294
- if (line)
11295
- newErrors.push(line.trim().slice(0, 250));
11296
- }
11297
- }
11298
- const dedup = (arr) => [...new Set(arr)];
11299
- this.fileFacts = dedup([...this.fileFacts, ...newFiles]).slice(-ContextManager.MAX_FACTS);
11300
- this.decisionFacts = dedup([...this.decisionFacts, ...newDecisions]).slice(-ContextManager.MAX_FACTS);
11301
- this.errorFacts = dedup([...this.errorFacts, ...newErrors]).slice(-ContextManager.MAX_FACTS);
11302
- }
11303
11410
  updateSystemPrompt(content) {
11304
11411
  const idx = this.messages.findIndex((m) => m.role === "system");
11305
11412
  if (idx >= 0) {
@@ -11318,6 +11425,8 @@ ${lines.join(`
11318
11425
  this.compactionCount = 0;
11319
11426
  this.peakTokens = 0;
11320
11427
  this.lastUserTask = "";
11428
+ this.lastUserFeedback = "";
11429
+ this.sessionMission = "";
11321
11430
  }
11322
11431
  getEstimatedTokens() {
11323
11432
  return this.messages.reduce((sum, m) => sum + this.estimateMessageTokens(m), 0) + this.toolTokens;
@@ -11334,7 +11443,9 @@ ${lines.join(`
11334
11443
  }
11335
11444
  }
11336
11445
  var COMPACTION_INTERVAL = 15, KEEP_LAST_N = 6;
11337
- var init_manager = () => {};
11446
+ var init_manager = __esm(() => {
11447
+ init_fact_extractor();
11448
+ });
11338
11449
 
11339
11450
  // src/modules/hallucination/consistency.ts
11340
11451
  class ConsistencyCheck {
@@ -11445,6 +11556,10 @@ function isFilePathLike(s) {
11445
11556
  return false;
11446
11557
  return FILE_EXTENSIONS.has(ext);
11447
11558
  }
11559
+ const last = s.split(/[\\/]/).pop() ?? "";
11560
+ const lastDot = last.lastIndexOf(".");
11561
+ if (lastDot <= 0)
11562
+ return false;
11448
11563
  return true;
11449
11564
  }
11450
11565
  function extractFileLikeTokens(text) {
@@ -15708,7 +15823,7 @@ class PlanCreator {
15708
15823
  const hasActionWord = actionWords.some((w) => task.toLowerCase().includes(w));
15709
15824
  return hasActionWord && words.length > 8;
15710
15825
  }
15711
- static createPlan(title, stepDescriptions, baseDir) {
15826
+ static createPlan(title, stepDescriptions, baseDir, kinds) {
15712
15827
  const stepCount = stepDescriptions.length;
15713
15828
  return {
15714
15829
  id: generatePlanId(),
@@ -15716,7 +15831,8 @@ class PlanCreator {
15716
15831
  steps: stepDescriptions.map((desc, i) => ({
15717
15832
  id: i + 1,
15718
15833
  description: desc,
15719
- status: "pending"
15834
+ status: "pending",
15835
+ kind: kinds?.[i] ?? "create"
15720
15836
  })),
15721
15837
  createdAt: new Date().toISOString(),
15722
15838
  baseDir
@@ -15730,7 +15846,8 @@ class PlanCreator {
15730
15846
  const added = newSteps.map((desc, i) => ({
15731
15847
  id: kept.length + i + 1,
15732
15848
  description: desc,
15733
- status: "pending"
15849
+ status: "pending",
15850
+ kind: "create"
15734
15851
  }));
15735
15852
  return {
15736
15853
  id: plan.id,
@@ -15822,42 +15939,9 @@ class PlanTracker {
15822
15939
  }
15823
15940
  var init_tracker = () => {};
15824
15941
 
15825
- // src/modules/execution/auditor.ts
15826
- import { existsSync as existsSync28, readdirSync as readdirSync8 } from "fs";
15827
- import { resolve as resolve17, join as join23, basename as basename2 } from "path";
15828
- function findExistingFile(baseDir, filePath) {
15829
- const direct = resolve17(baseDir, filePath);
15830
- if (existsSync28(direct))
15831
- return direct;
15832
- const name = basename2(filePath).toLowerCase();
15833
- const suffix = filePath.replace(/\\/g, "/").toLowerCase();
15834
- let found = null;
15835
- const walk = (dir, depth) => {
15836
- if (found || depth > RESOLVE_MAX_DEPTH)
15837
- return;
15838
- let entries;
15839
- try {
15840
- entries = readdirSync8(dir, { withFileTypes: true });
15841
- } catch {
15842
- return;
15843
- }
15844
- for (const e of entries) {
15845
- if (found)
15846
- return;
15847
- const full = join23(dir, e.name);
15848
- if (e.isDirectory()) {
15849
- if (SKIP_DIRS.has(e.name))
15850
- continue;
15851
- walk(full, depth + 1);
15852
- } else if (e.name.toLowerCase() === name || full.replace(/\\/g, "/").toLowerCase().endsWith("/" + suffix)) {
15853
- found = full;
15854
- return;
15855
- }
15856
- }
15857
- };
15858
- walk(baseDir, 0);
15859
- return found;
15860
- }
15942
+ // src/modules/execution/audit-runners.ts
15943
+ import { readdirSync as readdirSync8 } from "fs";
15944
+ import { join as join23 } from "path";
15861
15945
  function findTestFile(dir, depth = 0) {
15862
15946
  if (depth > 5)
15863
15947
  return null;
@@ -15932,6 +16016,76 @@ async function runTests(baseDir) {
15932
16016
  command: "bun test"
15933
16017
  };
15934
16018
  }
16019
+ function parseTypecheckErrors(output) {
16020
+ const line = output.split(`
16021
+ `).find((l) => /error TS\d+/.test(l));
16022
+ return line ? line.trim().slice(0, 300) : null;
16023
+ }
16024
+ async function runTypecheck(baseDir) {
16025
+ const entry = processRegistry.start("npx --no-install tsc --noEmit --skipLibCheck", baseDir);
16026
+ const exited = await processRegistry.waitForExit(entry.id, 90000);
16027
+ const output = entry.log.join(`
16028
+ `);
16029
+ processRegistry.remove(entry.id);
16030
+ if (!exited)
16031
+ return null;
16032
+ return parseTypecheckErrors(output);
16033
+ }
16034
+ var SKIP_DIRS, TEST_EXT_RE, TEST_STEP_RE;
16035
+ var init_audit_runners = __esm(() => {
16036
+ init_bash();
16037
+ init_processes();
16038
+ SKIP_DIRS = new Set([
16039
+ "node_modules",
16040
+ ".git",
16041
+ ".mma",
16042
+ "dist",
16043
+ "build",
16044
+ "coverage",
16045
+ ".next",
16046
+ ".nuxt",
16047
+ "vendor"
16048
+ ]);
16049
+ TEST_EXT_RE = /\.(test|spec)\.[jt]sx?$/i;
16050
+ TEST_STEP_RE = /\b(test(ing|s)?|тест(ы|ирование|ировать)?|провер\w*\s+тест|запустить\s+тест)\b|bun test|npm test|vitest|pytest|go test|jest|mocha/i;
16051
+ });
16052
+
16053
+ // src/modules/execution/auditor.ts
16054
+ import { existsSync as existsSync28, readdirSync as readdirSync9 } from "fs";
16055
+ import { resolve as resolve17, join as join24, basename as basename2 } from "path";
16056
+ function findExistingFile(baseDir, filePath) {
16057
+ const direct = resolve17(baseDir, filePath);
16058
+ if (existsSync28(direct))
16059
+ return direct;
16060
+ const name = basename2(filePath).toLowerCase();
16061
+ const suffix = filePath.replace(/\\/g, "/").toLowerCase();
16062
+ let found = null;
16063
+ const walk = (dir, depth) => {
16064
+ if (found || depth > RESOLVE_MAX_DEPTH)
16065
+ return;
16066
+ let entries;
16067
+ try {
16068
+ entries = readdirSync9(dir, { withFileTypes: true });
16069
+ } catch {
16070
+ return;
16071
+ }
16072
+ for (const e of entries) {
16073
+ if (found)
16074
+ return;
16075
+ const full = join24(dir, e.name);
16076
+ if (e.isDirectory()) {
16077
+ if (SKIP_DIRS.has(e.name))
16078
+ continue;
16079
+ walk(full, depth + 1);
16080
+ } else if (e.name.toLowerCase() === name || full.replace(/\\/g, "/").toLowerCase().endsWith("/" + suffix)) {
16081
+ found = full;
16082
+ return;
16083
+ }
16084
+ }
16085
+ };
16086
+ walk(baseDir, 0);
16087
+ return found;
16088
+ }
15935
16089
 
15936
16090
  class Auditor {
15937
16091
  baseDir;
@@ -15939,18 +16093,27 @@ class Auditor {
15939
16093
  this.baseDir = baseDir;
15940
16094
  }
15941
16095
  async audit(plan) {
15942
- const allStepText = plan.steps.filter((s) => s.status === "done").map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
15943
- const uniqueFiles = extractFileLikeTokens(stripUrls(allStepText));
16096
+ const doneCreate = plan.steps.filter((s) => s.status === "done" && s.kind !== "delete");
16097
+ const doneDelete = plan.steps.filter((s) => s.status === "done" && s.kind === "delete");
16098
+ const createText = doneCreate.map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
16099
+ const deleteText = doneDelete.map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
16100
+ const createFiles = extractFileLikeTokens(stripUrls(createText));
16101
+ const deleteFiles = extractFileLikeTokens(stripUrls(deleteText));
15944
16102
  const missingFiles = [];
15945
16103
  const existingFiles = [];
15946
- for (const filePath of uniqueFiles) {
15947
- const resolved = findExistingFile(this.baseDir, filePath);
15948
- if (resolved) {
16104
+ const leftoverFiles = [];
16105
+ for (const filePath of createFiles) {
16106
+ if (findExistingFile(this.baseDir, filePath)) {
15949
16107
  existingFiles.push(filePath);
15950
16108
  } else {
15951
16109
  missingFiles.push(filePath);
15952
16110
  }
15953
16111
  }
16112
+ for (const filePath of deleteFiles) {
16113
+ if (findExistingFile(this.baseDir, filePath)) {
16114
+ leftoverFiles.push(filePath);
16115
+ }
16116
+ }
15954
16117
  let testRun = null;
15955
16118
  if (hasTestStep(plan) && findTestFile(this.baseDir)) {
15956
16119
  try {
@@ -15959,17 +16122,27 @@ class Auditor {
15959
16122
  testRun = null;
15960
16123
  }
15961
16124
  }
16125
+ let typecheckError = null;
16126
+ if (missingFiles.length === 0 && leftoverFiles.length === 0 && existsSync28(join24(this.baseDir, "tsconfig.json"))) {
16127
+ try {
16128
+ typecheckError = await runTypecheck(this.baseDir);
16129
+ } catch {
16130
+ typecheckError = null;
16131
+ }
16132
+ }
15962
16133
  const doneSteps = plan.steps.filter((s) => s.status === "done").length;
15963
16134
  const terminalSteps = plan.steps.filter((s) => s.status === "done" || s.status === "skipped").length;
15964
16135
  const totalSteps = plan.steps.length;
16136
+ const auditedFiles = [...new Set([...createFiles, ...deleteFiles])];
15965
16137
  let massEditWarning = null;
15966
- if (uniqueFiles.length > MASS_EDIT_THRESHOLD) {
16138
+ if (auditedFiles.length > MASS_EDIT_THRESHOLD) {
15967
16139
  massEditWarning = t("exec.mass_edit_warning", {
15968
- count: String(uniqueFiles.length)
16140
+ count: String(auditedFiles.length)
15969
16141
  });
15970
16142
  }
15971
16143
  const testsFailing = testRun !== null && testRun.failed > 0;
15972
- const passed = missingFiles.length === 0 && !testsFailing;
16144
+ const typecheckFailing = typecheckError !== null;
16145
+ const passed = missingFiles.length === 0 && leftoverFiles.length === 0 && !testsFailing && !typecheckFailing;
15973
16146
  const stepsPending = terminalSteps < totalSteps;
15974
16147
  let summary;
15975
16148
  if (missingFiles.length > 0) {
@@ -15978,6 +16151,12 @@ class Auditor {
15978
16151
  total: totalSteps,
15979
16152
  files: missingFiles.length
15980
16153
  });
16154
+ } else if (leftoverFiles.length > 0) {
16155
+ summary = t("exec.audit_leftovers", {
16156
+ done: doneSteps,
16157
+ total: totalSteps,
16158
+ files: leftoverFiles.length
16159
+ });
15981
16160
  } else if (testsFailing) {
15982
16161
  summary = t("exec.audit_fail_tests", {
15983
16162
  done: doneSteps,
@@ -15986,6 +16165,13 @@ class Auditor {
15986
16165
  passed: String(testRun.passedCount),
15987
16166
  detail: testRun.detail
15988
16167
  });
16168
+ } else if (typecheckFailing) {
16169
+ summary = t("exec.audit_fail_typecheck", {
16170
+ done: doneSteps,
16171
+ total: totalSteps,
16172
+ missing: String(missingFiles.length),
16173
+ typeError: typecheckError
16174
+ });
15989
16175
  } else if (stepsPending) {
15990
16176
  summary = t("exec.audit_pending", {
15991
16177
  done: doneSteps,
@@ -16001,38 +16187,27 @@ class Auditor {
16001
16187
  return {
16002
16188
  passed,
16003
16189
  missingFiles,
16190
+ leftoverFiles,
16004
16191
  createdFiles: existingFiles,
16005
16192
  modifiedFiles: [],
16006
16193
  summary,
16007
16194
  massEditWarning,
16008
- testRun
16195
+ testRun,
16196
+ typecheckError
16009
16197
  };
16010
16198
  }
16011
16199
  }
16012
- var MASS_EDIT_THRESHOLD = 10, SKIP_DIRS, TEST_EXT_RE, TEST_STEP_RE, RESOLVE_MAX_DEPTH = 8;
16200
+ var MASS_EDIT_THRESHOLD = 10, RESOLVE_MAX_DEPTH = 8;
16013
16201
  var init_auditor = __esm(() => {
16014
16202
  init_i18n();
16015
16203
  init_js_identifiers();
16016
- init_bash();
16017
- init_processes();
16018
- SKIP_DIRS = new Set([
16019
- "node_modules",
16020
- ".git",
16021
- ".mma",
16022
- "dist",
16023
- "build",
16024
- "coverage",
16025
- ".next",
16026
- ".nuxt",
16027
- "vendor"
16028
- ]);
16029
- TEST_EXT_RE = /\.(test|spec)\.[jt]sx?$/i;
16030
- TEST_STEP_RE = /\b(test(ing|s)?|тест(ы|ирование|ировать)?|провер\w*\s+тест|запустить\s+тест)\b|bun test|npm test|vitest|pytest|go test|jest|mocha/i;
16204
+ init_audit_runners();
16205
+ init_audit_runners();
16031
16206
  });
16032
16207
 
16033
16208
  // src/modules/execution/plan-store.ts
16034
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync14, existsSync as existsSync29, readdirSync as readdirSync9, rmSync } from "fs";
16035
- import { join as join24 } from "path";
16209
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync14, existsSync as existsSync29, readdirSync as readdirSync10, rmSync } from "fs";
16210
+ import { join as join25 } from "path";
16036
16211
  function readPlanFile(path, fallbackBaseDir) {
16037
16212
  try {
16038
16213
  const raw = readFileSync16(path, "utf-8");
@@ -16059,8 +16234,8 @@ function writePlanFile(path, plan) {
16059
16234
  function listDir(dir, baseDir) {
16060
16235
  if (!existsSync29(dir))
16061
16236
  return [];
16062
- const files = readdirSync9(dir).filter((f) => f.endsWith(".json"));
16063
- return files.map((f) => readPlanFile(join24(dir, f), baseDir)).filter((p) => p !== null);
16237
+ const files = readdirSync10(dir).filter((f) => f.endsWith(".json"));
16238
+ return files.map((f) => readPlanFile(join25(dir, f), baseDir)).filter((p) => p !== null);
16064
16239
  }
16065
16240
  function toMeta(plan, status) {
16066
16241
  return {
@@ -16081,21 +16256,21 @@ class PlanStore {
16081
16256
  archiveDir;
16082
16257
  legacyPath;
16083
16258
  constructor(baseDir) {
16084
- const mmaDir = join24(baseDir, ".mma");
16259
+ const mmaDir = join25(baseDir, ".mma");
16085
16260
  if (!existsSync29(mmaDir))
16086
16261
  mkdirSync14(mmaDir, { recursive: true });
16087
16262
  this.baseDir = baseDir;
16088
- this.plansDir = join24(mmaDir, "plans");
16089
- this.draftsDir = join24(this.plansDir, "drafts");
16090
- this.archiveDir = join24(this.plansDir, "archive");
16091
- this.legacyPath = join24(mmaDir, LEGACY_FILE);
16263
+ this.plansDir = join25(mmaDir, "plans");
16264
+ this.draftsDir = join25(this.plansDir, "drafts");
16265
+ this.archiveDir = join25(this.plansDir, "archive");
16266
+ this.legacyPath = join25(mmaDir, LEGACY_FILE);
16092
16267
  for (const dir of [this.plansDir, this.draftsDir, this.archiveDir]) {
16093
16268
  if (!existsSync29(dir))
16094
16269
  mkdirSync14(dir, { recursive: true });
16095
16270
  }
16096
16271
  }
16097
16272
  activePath() {
16098
- return join24(this.plansDir, "active.json");
16273
+ return join25(this.plansDir, "active.json");
16099
16274
  }
16100
16275
  saveActive(plan) {
16101
16276
  writePlanFile(this.activePath(), plan);
@@ -16125,14 +16300,14 @@ class PlanStore {
16125
16300
  rmSync(p, { force: true });
16126
16301
  }
16127
16302
  saveDraft(plan) {
16128
- writePlanFile(join24(this.draftsDir, `${plan.id}.json`), plan);
16303
+ writePlanFile(join25(this.draftsDir, `${plan.id}.json`), plan);
16129
16304
  }
16130
16305
  loadDraft(id) {
16131
- const p = join24(this.draftsDir, `${id}.json`);
16306
+ const p = join25(this.draftsDir, `${id}.json`);
16132
16307
  return existsSync29(p) ? readPlanFile(p, this.baseDir) : null;
16133
16308
  }
16134
16309
  removeDraft(id) {
16135
- const p = join24(this.draftsDir, `${id}.json`);
16310
+ const p = join25(this.draftsDir, `${id}.json`);
16136
16311
  if (existsSync29(p))
16137
16312
  rmSync(p, { force: true });
16138
16313
  }
@@ -16140,7 +16315,7 @@ class PlanStore {
16140
16315
  return listDir(this.draftsDir, this.baseDir);
16141
16316
  }
16142
16317
  archivePlan(plan) {
16143
- writePlanFile(join24(this.archiveDir, `${plan.id}.json`), plan);
16318
+ writePlanFile(join25(this.archiveDir, `${plan.id}.json`), plan);
16144
16319
  this.removeDraft(plan.id);
16145
16320
  const active = this.loadActive();
16146
16321
  if (active && active.id === plan.id) {
@@ -16151,7 +16326,7 @@ class PlanStore {
16151
16326
  return listDir(this.archiveDir, this.baseDir);
16152
16327
  }
16153
16328
  removeArchived(id) {
16154
- const p = join24(this.archiveDir, `${id}.json`);
16329
+ const p = join25(this.archiveDir, `${id}.json`);
16155
16330
  if (existsSync29(p))
16156
16331
  rmSync(p, { force: true });
16157
16332
  }
@@ -16245,91 +16420,12 @@ var init_plan_coverage = __esm(() => {
16245
16420
  ]);
16246
16421
  });
16247
16422
 
16248
- // src/modules/execution/module.ts
16249
- import { existsSync as existsSync30, readFileSync as readFileSync17 } from "fs";
16250
- import { resolve as resolve18 } from "path";
16251
-
16252
- class ExecutionModule {
16253
- name = "execution";
16254
- tracker = null;
16255
- verifier;
16256
- stuckDetector;
16257
- auditor;
16258
- store;
16259
- baseDir;
16260
- lastRecoveryIteration = -STUCK_RECOVERY_COOLDOWN;
16261
- consecutivePlanWarnings = 0;
16262
- lastStepId = -1;
16263
- depsGateHints = new Map;
16264
- _auditSkipsRemaining = 0;
16265
- stuckNotified = false;
16266
- pendingMessages = [];
16267
- constructor(baseDir, stuckThreshold = 6) {
16268
- this.baseDir = baseDir;
16269
- this.verifier = new StepVerifier(baseDir);
16270
- this.stuckDetector = new StuckDetector(stuckThreshold);
16271
- this.auditor = new Auditor(baseDir);
16272
- this.store = new PlanStore(baseDir);
16273
- }
16274
- setPlan(plan) {
16275
- this.tracker = new PlanTracker(plan);
16276
- this.store.saveActive(plan);
16277
- this._auditSkipsRemaining = 0;
16278
- }
16279
- restorePlan() {
16280
- const plan = this.store.loadActive();
16281
- if (!plan)
16282
- return false;
16283
- this.tracker = new PlanTracker(plan);
16284
- this.tracker.syncCurrentStep();
16285
- this._auditSkipsRemaining = 1;
16286
- return true;
16287
- }
16288
- getTracker() {
16289
- return this.tracker;
16290
- }
16291
- getStore() {
16292
- return this.store;
16293
- }
16294
- getStuckDetector() {
16295
- return this.stuckDetector;
16296
- }
16297
- async runFinalAudit() {
16298
- if (!this.tracker)
16299
- return null;
16300
- if (this._auditSkipsRemaining > 0) {
16301
- this._auditSkipsRemaining--;
16302
- return null;
16303
- }
16304
- const plan = this.tracker.getPlan();
16305
- const audit = await this.auditor.audit(plan);
16306
- const pendingSteps = plan.steps.flatMap((s) => s.status !== "done" && s.status !== "skipped" ? [`${s.id}. ${s.description}`] : []);
16307
- const done = plan.steps.filter((s) => s.status === "done").length;
16308
- const passed = audit.passed && pendingSteps.length === 0;
16309
- return {
16310
- passed,
16311
- done,
16312
- total: plan.steps.length,
16313
- pendingSteps,
16314
- missingFiles: audit.missingFiles,
16315
- summary: audit.summary
16316
- };
16317
- }
16318
- getSystemPromptBlock() {
16319
- if (!this.tracker)
16320
- return null;
16321
- return {
16322
- content: this.tracker.toPromptBlock(),
16323
- priority: "critical",
16324
- essential: true,
16325
- estimatedTokens: 200
16326
- };
16327
- }
16328
- getToolDefinitions() {
16329
- return [
16330
- {
16331
- name: "plan",
16332
- description: `Create, update, show, abort, list, switch, or re-plan multi-step plans.
16423
+ // src/modules/execution/plan-tool.ts
16424
+ function createPlanToolDefinitions(deps) {
16425
+ return [
16426
+ {
16427
+ name: "plan",
16428
+ description: `Create, update, show, abort, list, switch, or re-plan multi-step plans.
16333
16429
 
16334
16430
  Actions:
16335
16431
  - create: Start a new plan. Previous active plan is auto-preserved: incomplete → draft, complete → archive.
@@ -16346,198 +16442,222 @@ Write CONCRETE steps with exact file paths and commands:
16346
16442
  - Specify WHICH CLI commands to run with exact arguments
16347
16443
  - Each step should include at least one file extension (.ts, .tsx, .json, etc.) or a command verb (install, create, build, run, add, init)
16348
16444
  - Good: "Создать src/components/Header.tsx с навигацией и логотипом"
16349
- - Bad: "Настройка проекта" (too vague — what exactly needs to be configured?)`,
16350
- parameters: {
16351
- type: "object",
16352
- properties: {
16353
- action: {
16354
- type: "string",
16355
- enum: [
16356
- "create",
16357
- "update",
16358
- "show",
16359
- "abort",
16360
- "list",
16361
- "switch",
16362
- "re-plan"
16363
- ]
16364
- },
16365
- title: { type: "string" },
16366
- steps: { type: "array", items: { type: "string" } },
16367
- step: { type: "number" },
16368
- status: { type: "string", enum: ["done", "failed", "skipped"] },
16369
- note: { type: "string" },
16370
- id: { type: "string", description: "Plan id (for switch action)" }
16445
+ - Bad: "Настройка проекта" (too vague — what exactly needs to be configured?)
16446
+ - For steps that REMOVE files, pass kinds: ["delete"] aligned with steps (the done-gate verifies those files are gone instead of demanding they exist).`,
16447
+ parameters: {
16448
+ type: "object",
16449
+ properties: {
16450
+ action: {
16451
+ type: "string",
16452
+ enum: [
16453
+ "create",
16454
+ "update",
16455
+ "show",
16456
+ "abort",
16457
+ "list",
16458
+ "switch",
16459
+ "re-plan"
16460
+ ]
16461
+ },
16462
+ title: { type: "string" },
16463
+ steps: { type: "array", items: { type: "string" } },
16464
+ kinds: {
16465
+ type: "array",
16466
+ items: { type: "string", enum: ["create", "delete"] },
16467
+ description: 'Optional per-step intent, aligned 1:1 with steps. Pass "delete" for steps whose purpose is REMOVING files — the done-gate then requires those files to be GONE (not present). Defaults to "create".'
16371
16468
  },
16372
- required: ["action"]
16469
+ step: { type: "number" },
16470
+ status: { type: "string", enum: ["done", "failed", "skipped"] },
16471
+ note: { type: "string" },
16472
+ id: { type: "string", description: "Plan id (for switch action)" }
16373
16473
  },
16374
- handler: async (_ctx, args) => {
16375
- const action = String(args.action);
16376
- if (action === "list") {
16377
- const metas = this.store.listAll();
16378
- if (metas.length === 0) {
16379
- return { success: true, output: t("plan.list_empty") };
16380
- }
16381
- const lines = metas.map((m) => {
16382
- const icon = m.status === "active" ? "[*]" : m.status === "draft" ? "[ ]" : "[-]";
16383
- const namePart = m.name ? ` (${m.name})` : "";
16384
- return `${icon} ${m.id}${namePart} ${m.title} ${m.doneCount}/${m.stepCount}`;
16385
- });
16386
- return {
16387
- success: true,
16388
- output: `${t("plan.list_header")}
16474
+ required: ["action"]
16475
+ },
16476
+ handler: async (_ctx, args) => {
16477
+ const action = String(args.action);
16478
+ if (action === "list") {
16479
+ const metas = deps.store.listAll();
16480
+ if (metas.length === 0) {
16481
+ return { success: true, output: t("plan.list_empty") };
16482
+ }
16483
+ const lines = metas.map((m) => {
16484
+ const icon = m.status === "active" ? "[*]" : m.status === "draft" ? "[ ]" : "[-]";
16485
+ const namePart = m.name ? ` (${m.name})` : "";
16486
+ return `${icon} ${m.id}${namePart} — ${m.title} ${m.doneCount}/${m.stepCount}`;
16487
+ });
16488
+ return {
16489
+ success: true,
16490
+ output: `${t("plan.list_header")}
16389
16491
  ${lines.join(`
16390
16492
  `)}`
16493
+ };
16494
+ }
16495
+ if (action === "switch") {
16496
+ const planId = String(args.id || "");
16497
+ if (!planId) {
16498
+ return { success: false, output: t("plan.switch_no_id") };
16499
+ }
16500
+ const found = deps.store.find(planId);
16501
+ if (!found) {
16502
+ return {
16503
+ success: false,
16504
+ output: t("plan.not_found", { id: planId })
16391
16505
  };
16392
16506
  }
16393
- if (action === "switch") {
16394
- const planId = String(args.id || "");
16395
- if (!planId) {
16396
- return { success: false, output: t("plan.switch_no_id") };
16507
+ deps.preserveActive();
16508
+ if (found.status === "draft") {
16509
+ deps.store.removeDraft(found.plan.id);
16510
+ } else if (found.status === "archived") {
16511
+ deps.store.removeArchived(found.plan.id);
16512
+ }
16513
+ deps.setPlan(found.plan);
16514
+ const display = PlanCreator.toPromptBlock(found.plan, 0);
16515
+ return {
16516
+ success: true,
16517
+ output: t("plan.switched", {
16518
+ id: found.plan.id,
16519
+ title: found.plan.title
16520
+ }),
16521
+ display
16522
+ };
16523
+ }
16524
+ if (action === "re-plan") {
16525
+ if (!deps.trackerRef.current) {
16526
+ return { success: false, output: t("plan.no_active") };
16527
+ }
16528
+ const newSteps = Array.isArray(args.steps) ? args.steps.map(String) : [];
16529
+ if (newSteps.length === 0) {
16530
+ return { success: false, output: t("plan.replan_no_steps") };
16531
+ }
16532
+ const tracker = deps.trackerRef.current;
16533
+ const oldPlan = tracker.getPlan();
16534
+ const replanned = PlanCreator.replan(oldPlan, newSteps, args.title ? String(args.title) : undefined);
16535
+ const keptCount = replanned.steps.length - newSteps.length;
16536
+ deps.setPlan(replanned);
16537
+ const display = PlanCreator.toPromptBlock(replanned, keptCount);
16538
+ return {
16539
+ success: true,
16540
+ output: t("plan.replanned", {
16541
+ kept: String(keptCount),
16542
+ steps: String(newSteps.length)
16543
+ }),
16544
+ display
16545
+ };
16546
+ }
16547
+ if (action === "create") {
16548
+ const title = String(args.title || "Task Plan");
16549
+ const steps = Array.isArray(args.steps) ? args.steps.map(String) : [];
16550
+ if (steps.length === 0) {
16551
+ return { success: false, output: t("plan.no_steps") };
16552
+ }
16553
+ const parsed = deps.parseKinds(args, steps);
16554
+ if (parsed.error) {
16555
+ return { success: false, output: parsed.error };
16556
+ }
16557
+ deps.preserveActive();
16558
+ const plan = PlanCreator.createPlan(title, steps, deps.baseDir, parsed.kinds ?? []);
16559
+ deps.setPlan(plan);
16560
+ const display = PlanCreator.toPromptBlock(plan, 0);
16561
+ let output = t("plan.created", {
16562
+ title,
16563
+ steps: String(steps.length)
16564
+ });
16565
+ let displayOut = display;
16566
+ const taskText = deps.getTaskText(_ctx);
16567
+ if (taskText) {
16568
+ const { missing } = checkPlanCoverage(taskText, steps);
16569
+ if (missing.length > 0) {
16570
+ const warn = t("plan.coverage_warning", {
16571
+ missing: missing.join(", ")
16572
+ });
16573
+ output += `
16574
+ ⚠️ ${warn}`;
16575
+ displayOut = `${display}
16576
+ ⚠️ ${warn}`;
16397
16577
  }
16398
- const found = this.store.find(planId);
16578
+ }
16579
+ return {
16580
+ success: true,
16581
+ output,
16582
+ display: displayOut
16583
+ };
16584
+ }
16585
+ if (action === "show") {
16586
+ const planId = args.id ? String(args.id) : "";
16587
+ if (planId && planId !== deps.trackerRef.current?.getPlan()?.id) {
16588
+ const found = deps.store.find(planId);
16399
16589
  if (!found) {
16400
16590
  return {
16401
16591
  success: false,
16402
16592
  output: t("plan.not_found", { id: planId })
16403
16593
  };
16404
16594
  }
16405
- this.preserveActive();
16406
- if (found.status === "draft") {
16407
- this.store.removeDraft(found.plan.id);
16408
- } else if (found.status === "archived") {
16409
- this.store.removeArchived(found.plan.id);
16410
- }
16411
- this.setPlan(found.plan);
16412
- const display = PlanCreator.toPromptBlock(found.plan, 0);
16595
+ const display2 = PlanCreator.toPromptBlock(found.plan, 0);
16413
16596
  return {
16414
16597
  success: true,
16415
- output: t("plan.switched", {
16416
- id: found.plan.id,
16417
- title: found.plan.title
16418
- }),
16419
- display
16598
+ output: `${t("plan.show_header")}
16599
+ ${display2}`,
16600
+ display: display2
16420
16601
  };
16421
16602
  }
16422
- if (action === "re-plan") {
16423
- if (!this.tracker) {
16424
- return { success: false, output: t("plan.no_active") };
16425
- }
16426
- const newSteps = Array.isArray(args.steps) ? args.steps.map(String) : [];
16427
- if (newSteps.length === 0) {
16428
- return { success: false, output: t("plan.replan_no_steps") };
16429
- }
16430
- const oldPlan = this.tracker.getPlan();
16431
- const replanned = PlanCreator.replan(oldPlan, newSteps, args.title ? String(args.title) : undefined);
16432
- const keptCount = replanned.steps.length - newSteps.length;
16433
- this.setPlan(replanned);
16434
- const display = PlanCreator.toPromptBlock(replanned, keptCount);
16435
- return {
16436
- success: true,
16437
- output: t("plan.replanned", {
16438
- kept: String(keptCount),
16439
- steps: String(newSteps.length)
16440
- }),
16441
- display
16442
- };
16603
+ if (!deps.trackerRef.current) {
16604
+ return { success: false, output: t("plan.no_active") };
16443
16605
  }
16444
- if (action === "create") {
16445
- const title = String(args.title || "Task Plan");
16446
- const steps = Array.isArray(args.steps) ? args.steps.map(String) : [];
16447
- if (steps.length === 0) {
16448
- return { success: false, output: t("plan.no_steps") };
16449
- }
16450
- this.preserveActive();
16451
- const plan = PlanCreator.createPlan(title, steps, this.baseDir);
16452
- this.setPlan(plan);
16453
- const display = PlanCreator.toPromptBlock(plan, 0);
16454
- let output = t("plan.created", {
16455
- title,
16456
- steps: String(steps.length)
16457
- });
16458
- let displayOut = display;
16459
- const taskText = this.getTaskText(_ctx);
16460
- if (taskText) {
16461
- const { missing } = checkPlanCoverage(taskText, steps);
16462
- if (missing.length > 0) {
16463
- const warn = t("plan.coverage_warning", {
16464
- missing: missing.join(", ")
16465
- });
16466
- output += `
16467
- ⚠️ ${warn}`;
16468
- displayOut = `${display}
16469
- ⚠️ ${warn}`;
16470
- }
16471
- }
16472
- return {
16473
- success: true,
16474
- output,
16475
- display: displayOut
16476
- };
16606
+ const display = deps.trackerRef.current.toPromptBlock();
16607
+ return {
16608
+ success: true,
16609
+ output: `${t("plan.show_header")}
16610
+ ${display}`,
16611
+ display
16612
+ };
16613
+ }
16614
+ if (action === "update" && args.step && deps.trackerRef.current) {
16615
+ const tracker = deps.trackerRef.current;
16616
+ const stepId = Number(args.step);
16617
+ const target = tracker.getStep(stepId);
16618
+ if (!target) {
16619
+ return { success: false, output: t("plan.step_not_found") };
16477
16620
  }
16478
- if (action === "show") {
16479
- const planId = args.id ? String(args.id) : "";
16480
- if (planId && planId !== this.tracker?.getPlan()?.id) {
16481
- const found = this.store.find(planId);
16482
- if (!found) {
16483
- return {
16484
- success: false,
16485
- output: t("plan.not_found", { id: planId })
16486
- };
16487
- }
16488
- const display2 = PlanCreator.toPromptBlock(found.plan, 0);
16489
- return {
16490
- success: true,
16491
- output: `${t("plan.show_header")}
16492
- ${display2}`,
16493
- display: display2
16494
- };
16495
- }
16496
- if (!this.tracker) {
16497
- return { success: false, output: t("plan.no_active") };
16498
- }
16499
- const display = this.tracker.toPromptBlock();
16621
+ const status = String(args.status || "done");
16622
+ if (status === "done" && target.status === "done") {
16623
+ const progress2 = tracker.getProgressString();
16624
+ const display2 = tracker.toPromptBlock();
16500
16625
  return {
16501
16626
  success: true,
16502
- output: `${t("plan.show_header")}
16503
- ${display}`,
16504
- display
16627
+ output: `${t("plan.step_already_done", {
16628
+ step: String(stepId)
16629
+ })}
16630
+ ${progress2}`,
16631
+ display: display2
16505
16632
  };
16506
16633
  }
16507
- if (action === "update" && args.step && this.tracker) {
16508
- const stepId = Number(args.step);
16509
- const target = this.tracker.getStep(stepId);
16510
- if (!target) {
16511
- return { success: false, output: t("plan.step_not_found") };
16512
- }
16513
- const status = String(args.status || "done");
16514
- if (status === "done" && target.status === "done") {
16515
- const progress2 = this.tracker.getProgressString();
16516
- const display2 = this.tracker.toPromptBlock();
16634
+ if (status === "done") {
16635
+ const blockers = tracker.getPlan().steps.filter((s) => s.id < stepId && s.status !== "done" && s.status !== "skipped");
16636
+ if (blockers.length > 0) {
16517
16637
  return {
16518
- success: true,
16519
- output: `${t("plan.step_already_done", {
16520
- step: String(stepId)
16521
- })}
16522
- ${progress2}`,
16523
- display: display2
16638
+ success: false,
16639
+ output: t("plan.order_blocked", {
16640
+ step: String(stepId),
16641
+ first: String(blockers[0].id),
16642
+ desc: blockers[0].description
16643
+ })
16524
16644
  };
16525
16645
  }
16526
- if (status === "done") {
16527
- const blockers = this.tracker.getPlan().steps.filter((s) => s.id < stepId && s.status !== "done" && s.status !== "skipped");
16528
- if (blockers.length > 0) {
16646
+ }
16647
+ if (status === "done") {
16648
+ if (target.kind === "delete") {
16649
+ const leftovers = deps.stillExistingDeliverables(target);
16650
+ if (leftovers.length > 0) {
16529
16651
  return {
16530
16652
  success: false,
16531
- output: t("plan.order_blocked", {
16653
+ output: t("plan.deliverables_remain", {
16532
16654
  step: String(stepId),
16533
- first: String(blockers[0].id),
16534
- desc: blockers[0].description
16655
+ files: leftovers.join(", ")
16535
16656
  })
16536
16657
  };
16537
16658
  }
16538
- }
16539
- if (status === "done") {
16540
- const missing = this.missingStepDeliverables(target);
16659
+ } else {
16660
+ const missing = deps.missingStepDeliverables(target);
16541
16661
  if (missing.length > 0) {
16542
16662
  return {
16543
16663
  success: false,
@@ -16548,373 +16668,559 @@ ${progress2}`,
16548
16668
  };
16549
16669
  }
16550
16670
  }
16551
- this.tracker.updateStepStatus(stepId, status);
16552
- if (args.note)
16553
- this.tracker.addNote(Number(args.step), String(args.note));
16554
- this.tracker.syncCurrentStep();
16555
- this.store.saveActive(this.tracker.getPlan());
16556
- const progress = this.tracker.getProgressString();
16557
- const display = this.tracker.toPromptBlock();
16558
- _ctx.sessionLog?.plan("step-update", `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })} | ${progress} | current: step ${this.tracker.getCurrentStepIndex() + 1}`);
16559
- return {
16560
- success: true,
16561
- output: `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })}
16562
- ${progress}`,
16563
- display
16564
- };
16565
16671
  }
16566
- if (action === "update" && Array.isArray(args.steps) && args.steps.length > 0 && this.tracker) {
16567
- const title = String(args.title || this.tracker.getPlan().title);
16568
- const steps = args.steps.map(String);
16569
- const plan = PlanCreator.createPlan(title, steps, this.baseDir);
16570
- this.setPlan(plan);
16571
- const display = PlanCreator.toPromptBlock(plan, 0);
16572
- const output = t("plan.updated", {
16573
- title,
16574
- steps: String(steps.length)
16575
- });
16576
- return {
16577
- success: true,
16578
- output,
16579
- display
16580
- };
16581
- }
16582
- if (action === "abort") {
16583
- if (this.tracker) {
16584
- this.store.archivePlan(this.tracker.getPlan());
16585
- }
16586
- this.tracker = null;
16587
- this.store.clearActive();
16588
- return { success: true, output: t("plan.aborted") };
16589
- }
16590
- if (!this.tracker) {
16591
- return { success: false, output: t("plan.no_active") };
16672
+ tracker.updateStepStatus(stepId, status);
16673
+ if (args.note)
16674
+ tracker.addNote(Number(args.step), String(args.note));
16675
+ tracker.syncCurrentStep();
16676
+ deps.store.saveActive(tracker.getPlan());
16677
+ const progress = tracker.getProgressString();
16678
+ const display = tracker.toPromptBlock();
16679
+ _ctx.sessionLog?.plan("step-update", `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })} | ${progress} | current: step ${tracker.getCurrentStepIndex() + 1}`);
16680
+ return {
16681
+ success: true,
16682
+ output: `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })}
16683
+ ${progress}`,
16684
+ display
16685
+ };
16686
+ }
16687
+ if (action === "update" && Array.isArray(args.steps) && args.steps.length > 0 && deps.trackerRef.current) {
16688
+ const tracker = deps.trackerRef.current;
16689
+ const title = String(args.title || tracker.getPlan().title);
16690
+ const steps = args.steps.map(String);
16691
+ const parsed = deps.parseKinds(args, steps);
16692
+ if (parsed.error) {
16693
+ return { success: false, output: parsed.error };
16592
16694
  }
16695
+ const plan = PlanCreator.createPlan(title, steps, deps.baseDir, parsed.kinds ?? []);
16696
+ deps.setPlan(plan);
16697
+ const display = PlanCreator.toPromptBlock(plan, 0);
16698
+ const output = t("plan.updated", {
16699
+ title,
16700
+ steps: String(steps.length)
16701
+ });
16593
16702
  return {
16594
- success: false,
16595
- output: t("plan.unknown_action", { action })
16703
+ success: true,
16704
+ output,
16705
+ display
16596
16706
  };
16597
16707
  }
16598
- },
16599
- {
16600
- name: "todo",
16601
- description: "Manage sub-tasks within current plan step. Use to break down complex steps into smaller tasks.",
16602
- parameters: {
16603
- type: "object",
16604
- properties: {
16605
- action: { type: "string", enum: ["add", "done", "list"] },
16606
- items: { type: "array", items: { type: "string" } }
16607
- },
16608
- required: ["action"]
16708
+ if (action === "abort") {
16709
+ const tracker = deps.trackerRef.current;
16710
+ if (tracker) {
16711
+ deps.store.archivePlan(tracker.getPlan());
16712
+ }
16713
+ deps.trackerRef.current = null;
16714
+ deps.store.clearActive();
16715
+ return { success: true, output: t("plan.aborted") };
16716
+ }
16717
+ if (!deps.trackerRef.current) {
16718
+ return { success: false, output: t("plan.no_active") };
16719
+ }
16720
+ return {
16721
+ success: false,
16722
+ output: t("plan.unknown_action", { action })
16723
+ };
16724
+ }
16725
+ },
16726
+ {
16727
+ name: "todo",
16728
+ description: "Manage sub-tasks within current plan step. Use to break down complex steps into smaller tasks.",
16729
+ parameters: {
16730
+ type: "object",
16731
+ properties: {
16732
+ action: { type: "string", enum: ["add", "done", "list"] },
16733
+ items: { type: "array", items: { type: "string" } }
16609
16734
  },
16610
- handler: async (_ctx, args) => {
16611
- if (!this.tracker) {
16612
- return { success: false, output: t("plan.no_active") };
16735
+ required: ["action"]
16736
+ },
16737
+ handler: async (_ctx, args) => {
16738
+ if (!deps.trackerRef.current) {
16739
+ return { success: false, output: t("plan.no_active") };
16740
+ }
16741
+ const tracker = deps.trackerRef.current;
16742
+ const action = String(args.action);
16743
+ const currentStep = tracker.getCurrentStep();
16744
+ if (!currentStep) {
16745
+ return { success: false, output: t("plan.step_not_found") };
16746
+ }
16747
+ if (action === "add" && Array.isArray(args.items)) {
16748
+ const items = args.items.map(String);
16749
+ const existing = currentStep.subtasks ?? [];
16750
+ let nextId = existing.reduce((m, s) => Math.max(m, s.id), 0) + 1;
16751
+ for (const item of items) {
16752
+ existing.push({ id: nextId++, text: item, done: false });
16613
16753
  }
16614
- const action = String(args.action);
16615
- const currentStep = this.tracker.getCurrentStep();
16616
- if (!currentStep) {
16617
- return { success: false, output: t("plan.step_not_found") };
16754
+ currentStep.subtasks = existing;
16755
+ deps.store.saveActive(tracker.getPlan());
16756
+ const display = tracker.toPromptBlock();
16757
+ return {
16758
+ success: true,
16759
+ output: t("todo.added", {
16760
+ count: String(items.length),
16761
+ items: items.join(", ")
16762
+ }),
16763
+ display
16764
+ };
16765
+ }
16766
+ if (action === "done") {
16767
+ const items = Array.isArray(args.items) ? args.items.map(String) : [];
16768
+ if (items.length === 0) {
16769
+ return {
16770
+ success: false,
16771
+ output: t("todo.no_items")
16772
+ };
16618
16773
  }
16619
- if (action === "add" && Array.isArray(args.items)) {
16620
- const items = args.items.map(String);
16621
- const existing = currentStep.subtasks ?? [];
16622
- let nextId = existing.reduce((m, s) => Math.max(m, s.id), 0) + 1;
16623
- for (const item of items) {
16624
- existing.push({ id: nextId++, text: item, done: false });
16774
+ const subs = currentStep.subtasks ?? [];
16775
+ let marked = 0;
16776
+ for (const item of items) {
16777
+ const sub = subs.find((s) => !s.done && s.text.toLowerCase() === item.toLowerCase());
16778
+ if (sub) {
16779
+ sub.done = true;
16780
+ marked++;
16625
16781
  }
16626
- currentStep.subtasks = existing;
16627
- this.store.saveActive(this.tracker.getPlan());
16628
- const display = this.tracker.toPromptBlock();
16782
+ }
16783
+ if (marked === 0) {
16629
16784
  return {
16630
- success: true,
16631
- output: t("todo.added", {
16632
- count: String(items.length),
16785
+ success: false,
16786
+ output: t("todo.subtask_not_found", {
16633
16787
  items: items.join(", ")
16634
- }),
16635
- display
16788
+ })
16636
16789
  };
16637
16790
  }
16638
- if (action === "done") {
16639
- const items = Array.isArray(args.items) ? args.items.map(String) : [];
16640
- if (items.length === 0) {
16641
- return {
16642
- success: false,
16643
- output: t("todo.no_items")
16644
- };
16645
- }
16646
- const subs = currentStep.subtasks ?? [];
16647
- let marked = 0;
16648
- for (const item of items) {
16649
- const sub = subs.find((s) => !s.done && s.text.toLowerCase() === item.toLowerCase());
16650
- if (sub) {
16651
- sub.done = true;
16652
- marked++;
16653
- }
16654
- }
16655
- if (marked === 0) {
16656
- return {
16657
- success: false,
16658
- output: t("todo.subtask_not_found", {
16659
- items: items.join(", ")
16660
- })
16661
- };
16662
- }
16663
- currentStep.subtasks = subs;
16664
- this.store.saveActive(this.tracker.getPlan());
16665
- const doneCount = subs.filter((s) => s.done).length;
16666
- const display = this.tracker.toPromptBlock();
16667
- let output = t("todo.marked_done", {
16668
- count: String(marked)
16669
- });
16670
- output += ` (${doneCount}/${subs.length})`;
16671
- if (doneCount === subs.length) {
16672
- output += `
16791
+ currentStep.subtasks = subs;
16792
+ deps.store.saveActive(tracker.getPlan());
16793
+ const doneCount = subs.filter((s) => s.done).length;
16794
+ const display = tracker.toPromptBlock();
16795
+ let output = t("todo.marked_done", {
16796
+ count: String(marked)
16797
+ });
16798
+ output += ` (${doneCount}/${subs.length})`;
16799
+ if (doneCount === subs.length) {
16800
+ output += `
16673
16801
  All sub-tasks done — call plan update step=${currentStep.id} status=done to complete this step.`;
16674
- }
16675
- return { success: true, output, display };
16676
16802
  }
16677
- if (action === "list") {
16678
- const subs = currentStep.subtasks ?? [];
16679
- const lines = subs.length ? subs.map((s) => `${s.done ? "[x]" : "[ ]"} ${s.text}`) : ["(no sub-tasks)"];
16680
- return {
16681
- success: true,
16682
- output: `Step ${currentStep.id}: ${currentStep.description}
16803
+ return { success: true, output, display };
16804
+ }
16805
+ if (action === "list") {
16806
+ const subs = currentStep.subtasks ?? [];
16807
+ const lines = subs.length ? subs.map((s) => `${s.done ? "[x]" : "[ ]"} ${s.text}`) : ["(no sub-tasks)"];
16808
+ return {
16809
+ success: true,
16810
+ output: `Step ${currentStep.id}: ${currentStep.description}
16683
16811
  ${lines.join(`
16684
16812
  `)}`
16685
- };
16686
- }
16687
- return {
16688
- success: false,
16689
- output: t("todo.unknown_action", { action })
16690
16813
  };
16691
16814
  }
16692
- },
16693
- {
16694
- name: "verify",
16695
- description: "Run verification for the current step. Checks files mentioned in the step description.",
16696
- parameters: {
16697
- type: "object",
16698
- properties: {
16699
- step: { type: "number", description: "Step number to verify" }
16700
- }
16701
- },
16702
- handler: async (_ctx, args) => {
16703
- if (!this.tracker)
16704
- return { success: false, output: t("plan.no_active") };
16705
- const step = args.step ? this.tracker.getStep(Number(args.step)) : this.tracker.getCurrentStep();
16706
- if (!step)
16707
- return { success: false, output: t("plan.step_not_found") };
16708
- const result = await this.verifier.verifyStep(step.description);
16709
- return {
16710
- success: result.passed,
16711
- output: result.passed ? t("verify.passed") : t("verify.failed", {
16712
- details: result.failed.map((f) => f.message).join("; ")
16713
- })
16714
- };
16815
+ return {
16816
+ success: false,
16817
+ output: t("todo.unknown_action", { action })
16818
+ };
16819
+ }
16820
+ },
16821
+ {
16822
+ name: "verify",
16823
+ description: "Run verification for the current step. Checks files mentioned in the step description.",
16824
+ parameters: {
16825
+ type: "object",
16826
+ properties: {
16827
+ step: { type: "number", description: "Step number to verify" }
16715
16828
  }
16829
+ },
16830
+ handler: async (_ctx, args) => {
16831
+ if (!deps.trackerRef.current)
16832
+ return { success: false, output: t("plan.no_active") };
16833
+ const tracker = deps.trackerRef.current;
16834
+ const step = args.step ? tracker.getStep(Number(args.step)) : tracker.getCurrentStep();
16835
+ if (!step)
16836
+ return { success: false, output: t("plan.step_not_found") };
16837
+ const result = await deps.verifier.verifyStep(step.description);
16838
+ return {
16839
+ success: result.passed,
16840
+ output: result.passed ? t("verify.passed") : t("verify.failed", {
16841
+ details: result.failed.map((f) => f.message).join("; ")
16842
+ })
16843
+ };
16716
16844
  }
16717
- ];
16718
- }
16719
- getPlugin() {
16720
- return {
16721
- name: "execution",
16722
- onBeforeThink: (ctx) => {
16723
- if (ctx.contextManager && this.pendingMessages.length > 0) {
16724
- for (const m of this.pendingMessages.splice(0)) {
16725
- ctx.contextManager.addMessage(m);
16845
+ }
16846
+ ];
16847
+ }
16848
+ var init_plan_tool = __esm(() => {
16849
+ init_i18n();
16850
+ init_plan_coverage();
16851
+ });
16852
+
16853
+ // src/modules/execution/windows-commands.ts
16854
+ function firstWord(command) {
16855
+ const segs = command.trim().split(/[\s;|&]+/);
16856
+ return (segs[0] || "").replace(/[^\w-]/g, "").toLowerCase();
16857
+ }
16858
+ function forbiddenWindowsCommand(command) {
16859
+ const m = command.match(/^\s*cd\s+\S+\s*&&\s*(\S+)/);
16860
+ if (m)
16861
+ return forbiddenWindowsCommand(m[1]);
16862
+ const word = firstWord(command);
16863
+ if (word && FORBIDDEN_COMMANDS.has(word))
16864
+ return word;
16865
+ return null;
16866
+ }
16867
+ var FORBIDDEN_COMMANDS;
16868
+ var init_windows_commands = __esm(() => {
16869
+ FORBIDDEN_COMMANDS = new Set([
16870
+ "grep",
16871
+ "sed",
16872
+ "ls",
16873
+ "find",
16874
+ "rm",
16875
+ "touch",
16876
+ "which",
16877
+ "diff",
16878
+ "cp",
16879
+ "mv"
16880
+ ]);
16881
+ });
16882
+
16883
+ // src/modules/execution/execution-plugin.ts
16884
+ import { platform as platform4 } from "os";
16885
+ function createExecutionPlugin(deps) {
16886
+ return {
16887
+ name: "execution",
16888
+ onBeforeThink: (ctx) => {
16889
+ if (ctx.contextManager && deps.pendingMessages.length > 0) {
16890
+ for (const m of deps.pendingMessages.splice(0)) {
16891
+ ctx.contextManager.addMessage(m);
16892
+ }
16893
+ }
16894
+ if (deps.trackerRef.current?.isComplete()) {
16895
+ deps.stuckDetector.resetStepProgress();
16896
+ deps.state.consecutivePlanWarnings = 0;
16897
+ deps.state.lastStepId = -1;
16898
+ deps.state.stuckNotified = false;
16899
+ } else {
16900
+ const step = deps.trackerRef.current?.getCurrentStep();
16901
+ if (deps.trackerRef.current && step) {
16902
+ if (step.id !== deps.state.lastStepId) {
16903
+ deps.state.consecutivePlanWarnings = 0;
16904
+ deps.state.lastStepId = step.id;
16905
+ deps.state.stuckNotified = false;
16726
16906
  }
16727
- }
16728
- if (this.tracker?.isComplete()) {
16729
- this.stuckDetector.resetStepProgress();
16730
- this.consecutivePlanWarnings = 0;
16731
- this.lastStepId = -1;
16732
- this.stuckNotified = false;
16907
+ deps.stuckDetector.setCurrentStep(step.id, step.description);
16908
+ deps.stuckDetector.recordIteration(step.id);
16733
16909
  } else {
16734
- const step = this.tracker?.getCurrentStep();
16735
- if (this.tracker && step) {
16736
- if (step.id !== this.lastStepId) {
16737
- this.consecutivePlanWarnings = 0;
16738
- this.lastStepId = step.id;
16739
- this.stuckNotified = false;
16740
- }
16741
- this.stuckDetector.setCurrentStep(step.id, step.description);
16742
- this.stuckDetector.recordIteration(step.id);
16743
- } else {
16744
- this.stuckDetector.reset();
16745
- this.consecutivePlanWarnings = 0;
16746
- this.lastStepId = -1;
16747
- this.stuckNotified = false;
16748
- const iter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
16749
- if (iter === 3 && !this.tracker && ctx.contextManager) {
16750
- ctx.contextManager.addMessage({
16751
- role: "user",
16752
- content: `<system-summary>You have made 3 tool calls without creating a plan. For any task that involves creating files, installing packages, or multiple steps — you MUST use plan create BEFORE continuing. Use the plan tool now with concrete steps (exact filenames, commands, deliverables). Do NOT make any more write/edit/bash calls until you have a plan.</system-summary>`
16753
- });
16754
- }
16755
- if (iter >= 6 && !this.tracker && ctx.contextManager) {
16756
- ctx.contextManager.addMessage({
16757
- role: "user",
16758
- content: `<system-summary>STOP. ${iter} iterations without a plan. You MUST call plan create RIGHT NOW. No more tool calls until you create a plan.</system-summary>`
16759
- });
16760
- }
16910
+ deps.stuckDetector.reset();
16911
+ deps.state.consecutivePlanWarnings = 0;
16912
+ deps.state.lastStepId = -1;
16913
+ deps.state.stuckNotified = false;
16914
+ const iter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
16915
+ if (iter === 3 && !deps.trackerRef.current && ctx.contextManager) {
16916
+ ctx.contextManager.addMessage({
16917
+ role: "user",
16918
+ content: `<system-summary>You have made 3 tool calls without creating a plan. For any task that involves creating files, installing packages, or multiple steps — you MUST use plan create BEFORE continuing. Use the plan tool now with concrete steps (exact filenames, commands, deliverables). Do NOT make any more write/edit/bash calls until you have a plan.</system-summary>`
16919
+ });
16761
16920
  }
16762
- }
16763
- const stuckReason = this.stuckDetector.getStuckReason();
16764
- if (stuckReason) {
16765
- const logIt = this.stuckDetector.isStuck() ? !this.stuckNotified : true;
16766
- if (logIt) {
16767
- ctx.logger?.warn(stuckReason);
16768
- ctx.sessionLog?.plan("stuck-warning", stuckReason, typeof ctx.iteration === "number" ? ctx.iteration : undefined);
16769
- if (this.stuckDetector.isStuck())
16770
- this.stuckNotified = true;
16921
+ if (iter >= 6 && !deps.trackerRef.current && ctx.contextManager) {
16922
+ ctx.contextManager.addMessage({
16923
+ role: "user",
16924
+ content: `<system-summary>STOP. ${iter} iterations without a plan. You MUST call plan create RIGHT NOW. No more tool calls until you create a plan.</system-summary>`
16925
+ });
16771
16926
  }
16772
16927
  }
16773
- if (this.stuckDetector.isStuck() || this.stuckDetector.hasRepetitiveToolCalls() || this.stuckDetector.hasReadOnlyLoop()) {
16774
- const currentIter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
16775
- if (currentIter - this.lastRecoveryIteration >= STUCK_RECOVERY_COOLDOWN) {
16776
- const recovery = this.stuckDetector.getRecoveryMessage();
16777
- if (recovery && ctx.contextManager) {
16778
- const lastError = this.stuckDetector.getLastErrorOutput();
16779
- const skillHint = lastError ? `
16928
+ }
16929
+ const stuckReason = deps.stuckDetector.getStuckReason();
16930
+ if (stuckReason) {
16931
+ const logIt = deps.stuckDetector.isStuck() ? !deps.state.stuckNotified : true;
16932
+ if (logIt) {
16933
+ ctx.logger?.warn(stuckReason);
16934
+ ctx.sessionLog?.plan("stuck-warning", stuckReason, typeof ctx.iteration === "number" ? ctx.iteration : undefined);
16935
+ if (deps.stuckDetector.isStuck())
16936
+ deps.state.stuckNotified = true;
16937
+ }
16938
+ }
16939
+ if (deps.stuckDetector.isStuck() || deps.stuckDetector.hasRepetitiveToolCalls() || deps.stuckDetector.hasReadOnlyLoop()) {
16940
+ const currentIter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
16941
+ if (currentIter - deps.state.lastRecoveryIteration >= STUCK_RECOVERY_COOLDOWN) {
16942
+ const recovery = deps.stuckDetector.getRecoveryMessage();
16943
+ if (recovery && ctx.contextManager) {
16944
+ const lastError = deps.stuckDetector.getLastErrorOutput();
16945
+ const skillHint = lastError ? `
16780
16946
  If you have relevant skills available, consider loading one with load_skill for expert guidance.` : "";
16781
- const actionableHints = this.stuckDetector.getActionableHints();
16782
- const actionableHintStr = actionableHints.length > 0 ? `
16947
+ const actionableHints = deps.stuckDetector.getActionableHints();
16948
+ const actionableHintStr = actionableHints.length > 0 ? `
16783
16949
  ${t("exec.hints", { hints: actionableHints.map((h) => `- ${h}`).join(`
16784
16950
  `) })}` : "";
16785
- const alternative = this.stuckDetector.getToolAlternative();
16786
- const altHint = alternative ? `
16787
- Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}" instead.` : "";
16788
- ctx.contextManager.addMessage({
16789
- role: "user",
16790
- content: `<system-summary>${recovery}${skillHint}${actionableHintStr}${altHint}</system-summary>`
16791
- });
16792
- }
16793
- const hints = this.stuckDetector.getHints();
16794
- if (hints.length > 0 && ctx.contextManager) {
16795
- const hintMsg = t("exec.hints", {
16796
- hints: hints.map((h) => `- ${h}`).join(`
16951
+ const alternative = deps.stuckDetector.getToolAlternative();
16952
+ const altHint = alternative ? `
16953
+ Tool "${deps.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}" instead.` : "";
16954
+ ctx.contextManager.addMessage({
16955
+ role: "user",
16956
+ content: `<system-summary>${recovery}${skillHint}${actionableHintStr}${altHint}</system-summary>`
16957
+ });
16958
+ }
16959
+ const hints = deps.stuckDetector.getHints();
16960
+ if (hints.length > 0 && ctx.contextManager) {
16961
+ const hintMsg = t("exec.hints", {
16962
+ hints: hints.map((h) => `- ${h}`).join(`
16797
16963
  `)
16798
- });
16799
- ctx.contextManager.addMessage({
16800
- role: "user",
16801
- content: `<system-summary>${hintMsg}</system-summary>`
16802
- });
16803
- }
16804
- this.stuckDetector.recordEscalation();
16805
- this.lastRecoveryIteration = currentIter;
16806
- if (this.stuckDetector.shouldEscalate() && ctx.onMeta) {
16807
- const escalation = t("exec.escalation", {
16808
- stepId: String(this.tracker?.getCurrentStep()?.id ?? "?"),
16809
- description: this.tracker?.getCurrentStep()?.description ?? ""
16810
- });
16811
- ctx.onMeta(escalation);
16812
- }
16813
- if (this.stuckDetector.getIterationsOnCurrentStep() >= FORCE_SKIP_THRESHOLD && ctx.contextManager) {
16814
- const step = this.tracker?.getCurrentStep();
16815
- ctx.contextManager.addMessage({
16964
+ });
16965
+ ctx.contextManager.addMessage({
16966
+ role: "user",
16967
+ content: `<system-summary>${hintMsg}</system-summary>`
16968
+ });
16969
+ }
16970
+ deps.stuckDetector.recordEscalation();
16971
+ deps.state.lastRecoveryIteration = currentIter;
16972
+ if (deps.stuckDetector.shouldEscalate() && ctx.onMeta) {
16973
+ const escalation = t("exec.escalation", {
16974
+ stepId: String(deps.trackerRef.current?.getCurrentStep()?.id ?? "?"),
16975
+ description: deps.trackerRef.current?.getCurrentStep()?.description ?? ""
16976
+ });
16977
+ ctx.onMeta(escalation);
16978
+ }
16979
+ if (deps.stuckDetector.getIterationsOnCurrentStep() >= FORCE_SKIP_THRESHOLD && ctx.contextManager) {
16980
+ const step = deps.trackerRef.current?.getCurrentStep();
16981
+ ctx.contextManager.addMessage({
16982
+ role: "user",
16983
+ content: `<system-summary>STOP. Step ${step?.id ?? "?"} ("${step?.description ?? ""}") took ${deps.stuckDetector.getIterationsOnCurrentStep()} iterations with no progress. DO NOT continue this step. Immediately call: plan update step=${step?.id ?? "?"} status=done (if code works despite warnings) OR plan update step=${step?.id ?? "?"} status=skipped note="reason". Do NOT make any other tool calls before updating the plan.</system-summary>`
16984
+ });
16985
+ }
16986
+ }
16987
+ }
16988
+ },
16989
+ onBeforeTool: (_ctx, call) => {
16990
+ const warning = deps.checkPlanAlignment(call);
16991
+ if (warning) {
16992
+ deps.pendingMessages.push({
16993
+ role: "user",
16994
+ content: `<system-summary>${warning}</system-summary>`
16995
+ });
16996
+ deps.state.consecutivePlanWarnings++;
16997
+ if (deps.state.consecutivePlanWarnings >= MAX_PLAN_WARNINGS_BEFORE_BLOCK) {
16998
+ deps.pendingMessages.push({
16999
+ role: "user",
17000
+ content: `<system-summary>${t("exec.plan_blocked", { step: String(deps.trackerRef.current?.getCurrentStep()?.id ?? "?"), max: MAX_PLAN_WARNINGS_BEFORE_BLOCK })}</system-summary>`
17001
+ });
17002
+ return t("exec.plan_blocked", {
17003
+ step: String(deps.trackerRef.current?.getCurrentStep()?.id ?? "?"),
17004
+ max: MAX_PLAN_WARNINGS_BEFORE_BLOCK
17005
+ });
17006
+ }
17007
+ } else {
17008
+ deps.state.consecutivePlanWarnings = 0;
17009
+ }
17010
+ return true;
17011
+ },
17012
+ onToolCall: (ctx) => {
17013
+ const toolName = ctx?.toolName;
17014
+ const args = ctx?.args;
17015
+ if (toolName && args) {
17016
+ deps.stuckDetector.recordToolCall(toolName, args);
17017
+ }
17018
+ },
17019
+ onAfterTool: (ctx, call, result) => {
17020
+ if (call.name === "bash") {
17021
+ deps.stuckDetector.recordBashOutput(String(call.arguments?.command ?? ""), String(result.output ?? ""));
17022
+ }
17023
+ const toolText = String(result.output ?? "");
17024
+ if (/error TS\d+|\[Project typecheck failed\]|\[Syntax check failed\]/.test(toolText)) {
17025
+ deps.stuckDetector.recordToolError(call.name, toolText.slice(0, 300));
17026
+ }
17027
+ if (!result.success) {
17028
+ if (call.name === "bash" && platform4() === "win32") {
17029
+ const cmd = String(call.arguments?.command ?? "");
17030
+ const forbidden = forbiddenWindowsCommand(cmd);
17031
+ if (forbidden) {
17032
+ const n = (deps.forbiddenBashFailures.get(forbidden) || 0) + 1;
17033
+ deps.forbiddenBashFailures.set(forbidden, n);
17034
+ if (n === 2) {
17035
+ deps.pendingMessages.push({
16816
17036
  role: "user",
16817
- content: `<system-summary>STOP. Step ${step?.id ?? "?"} ("${step?.description ?? ""}") took ${this.stuckDetector.getIterationsOnCurrentStep()} iterations with no progress. DO NOT continue this step. Immediately call: plan update step=${step?.id ?? "?"} status=done (if code works despite warnings) OR plan update step=${step?.id ?? "?"} status=skipped note="reason". Do NOT make any other tool calls before updating the plan.</system-summary>`
17037
+ content: `<system-summary>${t("exec.forbidden_cmd", { cmd: forbidden })}</system-summary>`
16818
17038
  });
16819
17039
  }
16820
17040
  }
16821
17041
  }
16822
- },
16823
- onBeforeTool: (_ctx, call) => {
16824
- const warning = this.checkPlanAlignment(call);
16825
- if (warning) {
16826
- this.pendingMessages.push({
17042
+ deps.stuckDetector.recordToolError(call.name, result.output);
17043
+ const actionableHints = deps.stuckDetector.getActionableHints();
17044
+ const alternative = deps.stuckDetector.getToolAlternative();
17045
+ if (actionableHints.length > 0 || alternative) {
17046
+ const parts = [...actionableHints];
17047
+ if (alternative) {
17048
+ parts.push(`Tool "${call.name}" crashed. Try "${alternative}" instead.`);
17049
+ }
17050
+ deps.pendingMessages.push({
16827
17051
  role: "user",
16828
- content: `<system-summary>${warning}</system-summary>`
17052
+ content: `<system-summary>${t("exec.hints", { hints: parts.map((h) => `- ${h}`).join(`
17053
+ `) })}</system-summary>`
16829
17054
  });
16830
- this.consecutivePlanWarnings++;
16831
- if (this.consecutivePlanWarnings >= MAX_PLAN_WARNINGS_BEFORE_BLOCK) {
16832
- this.pendingMessages.push({
17055
+ }
17056
+ } else {
17057
+ deps.stuckDetector.recordToolSuccess();
17058
+ if (call.name === "bash" && result.success) {
17059
+ const cmd = String(call.arguments?.command ?? "");
17060
+ const testRun = detectTestResults(String(result.output ?? ""));
17061
+ if (testRun && testRun.failed > 0) {
17062
+ deps.pendingMessages.push({
16833
17063
  role: "user",
16834
- content: `<system-summary>${t("exec.plan_blocked", { step: String(this.tracker?.getCurrentStep()?.id ?? "?"), max: MAX_PLAN_WARNINGS_BEFORE_BLOCK })}</system-summary>`
17064
+ content: `<system-summary>${testRun.framework} reported ${testRun.failed} FAILING test(s) (${testRun.passed} passing). Do NOT mark the current step as done — fix the failing tests (read the failure output, correct the code) and re-run them until all pass.</system-summary>`
16835
17065
  });
16836
- return t("exec.plan_blocked", {
16837
- step: String(this.tracker?.getCurrentStep()?.id ?? "?"),
16838
- max: MAX_PLAN_WARNINGS_BEFORE_BLOCK
17066
+ } else if (testRun && testRun.failed === 0 && testRun.passed > 0) {
17067
+ deps.pendingMessages.push({
17068
+ role: "user",
17069
+ content: `<system-summary>${testRun.framework}: all ${testRun.passed} test(s) passed for "${cmd}". You may mark the current step as done via plan update step=N status=done.</system-summary>`
16839
17070
  });
16840
- }
16841
- } else {
16842
- this.consecutivePlanWarnings = 0;
16843
- }
16844
- return true;
16845
- },
16846
- onToolCall: (ctx) => {
16847
- const toolName = ctx?.toolName;
16848
- const args = ctx?.args;
16849
- if (toolName && args) {
16850
- this.stuckDetector.recordToolCall(toolName, args);
16851
- }
16852
- },
16853
- onAfterTool: (ctx, call, result) => {
16854
- if (call.name === "bash") {
16855
- this.stuckDetector.recordBashOutput(String(call.arguments?.command ?? ""), String(result.output ?? ""));
16856
- }
16857
- const toolText = String(result.output ?? "");
16858
- if (/error TS\d+|\[Project typecheck failed\]|\[Syntax check failed\]/.test(toolText)) {
16859
- this.stuckDetector.recordToolError(call.name, toolText.slice(0, 300));
16860
- }
16861
- if (!result.success) {
16862
- this.stuckDetector.recordToolError(call.name, result.output);
16863
- const actionableHints = this.stuckDetector.getActionableHints();
16864
- const alternative = this.stuckDetector.getToolAlternative();
16865
- if (actionableHints.length > 0 || alternative) {
16866
- const parts = [...actionableHints];
16867
- if (alternative) {
16868
- parts.push(`Tool "${call.name}" crashed. Try "${alternative}" instead.`);
16869
- }
16870
- this.pendingMessages.push({
17071
+ } else if (/node|tsx|ts-node|python|npm\s+(start|test|run)/.test(cmd)) {
17072
+ deps.pendingMessages.push({
16871
17073
  role: "user",
16872
- content: `<system-summary>${t("exec.hints", { hints: parts.map((h) => `- ${h}`).join(`
16873
- `) })}</system-summary>`
17074
+ content: `<system-summary>The command "${cmd}" completed successfully. If this was testing your code, mark the current step as done via plan update step=N status=done.</system-summary>`
16874
17075
  });
16875
17076
  }
16876
- } else {
16877
- this.stuckDetector.recordToolSuccess();
16878
- if (call.name === "bash" && result.success) {
16879
- const cmd = String(call.arguments?.command ?? "");
16880
- const testRun = detectTestResults(String(result.output ?? ""));
16881
- if (testRun && testRun.failed > 0) {
16882
- this.pendingMessages.push({
16883
- role: "user",
16884
- content: `<system-summary>${testRun.framework} reported ${testRun.failed} FAILING test(s) (${testRun.passed} passing). Do NOT mark the current step as done — fix the failing tests (read the failure output, correct the code) and re-run them until all pass.</system-summary>`
16885
- });
16886
- } else if (testRun && testRun.failed === 0 && testRun.passed > 0) {
16887
- this.pendingMessages.push({
16888
- role: "user",
16889
- content: `<system-summary>${testRun.framework}: all ${testRun.passed} test(s) passed for "${cmd}". You may mark the current step as done via plan update step=N status=done.</system-summary>`
16890
- });
16891
- } else if (/node|tsx|ts-node|python|npm\s+(start|test|run)/.test(cmd)) {
16892
- this.pendingMessages.push({
16893
- role: "user",
16894
- content: `<system-summary>The command "${cmd}" completed successfully. If this was testing your code, mark the current step as done via plan update step=N status=done.</system-summary>`
16895
- });
16896
- }
16897
- }
16898
17077
  }
16899
- if (result.success && (call.name === "write_file" || call.name === "edit_file")) {
16900
- const filePath = call.arguments?.path;
16901
- if (filePath) {
16902
- this.stuckDetector.recordFileRewrite(filePath);
16903
- if (this.stuckDetector.hasExcessiveRewrites()) {
16904
- const file = this.stuckDetector.getExcessiveRewriteFile();
16905
- const count = this.stuckDetector.getFileRewriteCount(file);
16906
- if (ctx.onMeta) {
16907
- ctx.onMeta(t("exec.file_rewrite_warning", {
16908
- file,
16909
- count: String(count)
16910
- }));
16911
- }
17078
+ }
17079
+ if (result.success && (call.name === "write_file" || call.name === "edit_file")) {
17080
+ const filePath = call.arguments?.path;
17081
+ if (filePath) {
17082
+ deps.stuckDetector.recordFileRewrite(filePath);
17083
+ if (deps.stuckDetector.hasExcessiveRewrites()) {
17084
+ const file = deps.stuckDetector.getExcessiveRewriteFile();
17085
+ const count = deps.stuckDetector.getFileRewriteCount(file);
17086
+ if (ctx.onMeta) {
17087
+ ctx.onMeta(t("exec.file_rewrite_warning", {
17088
+ file,
17089
+ count: String(count)
17090
+ }));
16912
17091
  }
16913
17092
  }
16914
- this.advancePlanIfStepComplete(ctx.contextManager, ctx.sessionLog);
16915
17093
  }
17094
+ deps.advancePlanIfStepComplete(ctx.contextManager, ctx.sessionLog);
17095
+ }
17096
+ }
17097
+ };
17098
+ }
17099
+ var STUCK_RECOVERY_COOLDOWN = 5, MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3, FORCE_SKIP_THRESHOLD = 10;
17100
+ var init_execution_plugin = __esm(() => {
17101
+ init_i18n();
17102
+ init_bash();
17103
+ init_windows_commands();
17104
+ });
17105
+
17106
+ // src/modules/execution/module.ts
17107
+ import { existsSync as existsSync30, readFileSync as readFileSync17 } from "fs";
17108
+ import { resolve as resolve18 } from "path";
17109
+
17110
+ class ExecutionModule {
17111
+ name = "execution";
17112
+ tracker = null;
17113
+ verifier;
17114
+ stuckDetector;
17115
+ auditor;
17116
+ store;
17117
+ baseDir;
17118
+ _auditSkipsRemaining = 0;
17119
+ pendingMessages = [];
17120
+ forbiddenBashFailures = new Map;
17121
+ state = {
17122
+ lastRecoveryIteration: -STUCK_RECOVERY_COOLDOWN,
17123
+ consecutivePlanWarnings: 0,
17124
+ lastStepId: -1,
17125
+ stuckNotified: false,
17126
+ depsGateHints: new Map
17127
+ };
17128
+ trackerRef = (() => {
17129
+ const self = this;
17130
+ return {
17131
+ get current() {
17132
+ return self.tracker;
17133
+ },
17134
+ set current(v) {
17135
+ self.tracker = v;
16916
17136
  }
16917
17137
  };
17138
+ })();
17139
+ constructor(baseDir, stuckThreshold = 6) {
17140
+ this.baseDir = baseDir;
17141
+ this.verifier = new StepVerifier(baseDir);
17142
+ this.stuckDetector = new StuckDetector(stuckThreshold);
17143
+ this.auditor = new Auditor(baseDir);
17144
+ this.store = new PlanStore(baseDir);
17145
+ }
17146
+ setPlan(plan) {
17147
+ this.tracker = new PlanTracker(plan);
17148
+ this.store.saveActive(plan);
17149
+ this._auditSkipsRemaining = 0;
17150
+ }
17151
+ restorePlan() {
17152
+ const plan = this.store.loadActive();
17153
+ if (!plan)
17154
+ return false;
17155
+ this.tracker = new PlanTracker(plan);
17156
+ this.tracker.syncCurrentStep();
17157
+ this._auditSkipsRemaining = 1;
17158
+ return true;
17159
+ }
17160
+ getTracker() {
17161
+ return this.tracker;
17162
+ }
17163
+ getStore() {
17164
+ return this.store;
17165
+ }
17166
+ getStuckDetector() {
17167
+ return this.stuckDetector;
17168
+ }
17169
+ async runFinalAudit() {
17170
+ if (!this.tracker)
17171
+ return null;
17172
+ if (this._auditSkipsRemaining > 0) {
17173
+ this._auditSkipsRemaining--;
17174
+ return null;
17175
+ }
17176
+ const plan = this.tracker.getPlan();
17177
+ const audit = await this.auditor.audit(plan);
17178
+ const pendingSteps = plan.steps.flatMap((s) => s.status !== "done" && s.status !== "skipped" ? [`${s.id}. ${s.description}`] : []);
17179
+ const done = plan.steps.filter((s) => s.status === "done").length;
17180
+ const passed = audit.passed && pendingSteps.length === 0;
17181
+ return {
17182
+ passed,
17183
+ done,
17184
+ total: plan.steps.length,
17185
+ pendingSteps,
17186
+ missingFiles: audit.missingFiles,
17187
+ summary: audit.summary
17188
+ };
17189
+ }
17190
+ getSystemPromptBlock() {
17191
+ if (!this.tracker)
17192
+ return null;
17193
+ return {
17194
+ content: this.tracker.toPromptBlock(),
17195
+ priority: "critical",
17196
+ essential: true,
17197
+ estimatedTokens: 200
17198
+ };
17199
+ }
17200
+ getToolDefinitions() {
17201
+ return createPlanToolDefinitions({
17202
+ store: this.store,
17203
+ verifier: this.verifier,
17204
+ trackerRef: this.trackerRef,
17205
+ preserveActive: () => this.preserveActive(),
17206
+ setPlan: (p) => this.setPlan(p),
17207
+ missingStepDeliverables: (s) => this.missingStepDeliverables(s),
17208
+ stillExistingDeliverables: (s) => this.stillExistingDeliverables(s),
17209
+ parseKinds: (a, st) => this.parseKinds(a, st),
17210
+ getTaskText: (c) => this.getTaskText(c),
17211
+ baseDir: this.baseDir
17212
+ });
17213
+ }
17214
+ getPlugin() {
17215
+ return createExecutionPlugin({
17216
+ stuckDetector: this.stuckDetector,
17217
+ trackerRef: this.trackerRef,
17218
+ pendingMessages: this.pendingMessages,
17219
+ forbiddenBashFailures: this.forbiddenBashFailures,
17220
+ state: this.state,
17221
+ checkPlanAlignment: (call) => this.checkPlanAlignment(call),
17222
+ advancePlanIfStepComplete: (cm, sl) => this.advancePlanIfStepComplete(cm, sl)
17223
+ });
16918
17224
  }
16919
17225
  preserveActive() {
16920
17226
  if (!this.tracker)
@@ -16945,19 +17251,18 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
16945
17251
  ];
16946
17252
  if (allowedAlways.includes(call.name))
16947
17253
  return null;
16948
- const stepPaths = (step.description.match(/\b[\w./\\-]+\.[a-z]+/gi) || []).map((p) => p.toLowerCase());
17254
+ const stepPaths = extractFileLikeTokens(stripUrls(step.description)).map((p) => p.toLowerCase());
16949
17255
  if (stepPaths.length === 0)
16950
17256
  return null;
16951
- const argStr = JSON.stringify(call.arguments).toLowerCase();
16952
- const callPaths = (argStr.match(/\b[\w./\\-]+\.[a-z]+/gi) || []).map((p) => p.toLowerCase());
17257
+ const argStr = JSON.stringify(call.arguments);
17258
+ const callPaths = extractFileLikeTokens(stripUrls(argStr)).map((p) => p.toLowerCase());
16953
17259
  if (callPaths.length === 0)
16954
17260
  return null;
16955
17261
  const plan = this.tracker.getPlan();
16956
17262
  const finishedPaths = new Set;
16957
17263
  for (const s of plan.steps) {
16958
17264
  if (s.status === "done" || s.status === "skipped") {
16959
- const ps = s.description.match(/\b[\w./\\-]+\.[a-z]+/gi)?.map((p) => p.toLowerCase()) ?? [];
16960
- ps.forEach((p) => finishedPaths.add(p));
17265
+ extractFileLikeTokens(stripUrls(s.description)).map((p) => p.toLowerCase()).forEach((p) => finishedPaths.add(p));
16961
17266
  }
16962
17267
  }
16963
17268
  const offPath = callPaths.some((p) => {
@@ -16979,7 +17284,7 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
16979
17284
  if (!step)
16980
17285
  return;
16981
17286
  const stepText = step.description.toLowerCase();
16982
- const stepPaths = step.description.match(/\b[\w./\\-]+\.[a-z]+/gi) || [];
17287
+ const stepPaths = extractFileLikeTokens(stripUrls(step.description)) || [];
16983
17288
  const isDepsStep = stepText.includes("install") || stepText.includes("зависим") || stepText.includes("init") || stepText.includes("инициализац");
16984
17289
  if (isDepsStep) {
16985
17290
  const lockFiles = [
@@ -16997,8 +17302,8 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
16997
17302
  const hasLockFile = lockFiles.some((f) => existsSync30(resolve18(this.baseDir, f)));
16998
17303
  if (!hasLockFile) {
16999
17304
  if (contextManager) {
17000
- const hints = this.depsGateHints.get(step.id) || 0;
17001
- this.depsGateHints.set(step.id, hints + 1);
17305
+ const hints = this.state.depsGateHints.get(step.id) || 0;
17306
+ this.state.depsGateHints.set(step.id, hints + 1);
17002
17307
  const force = hints >= 1;
17003
17308
  const content = force ? t("exec.step_gate_deps_force", { step: String(step.id) }) : t("exec.step_gate_deps", {
17004
17309
  step: String(step.id),
@@ -17015,23 +17320,27 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
17015
17320
  if (stepPaths.length === 0)
17016
17321
  return;
17017
17322
  const allExist = stepPaths.every((p) => existsSync30(resolve18(this.baseDir, p)));
17018
- if (!allExist)
17019
- return;
17020
- const emptyFiles = [];
17021
- for (const p of stepPaths) {
17022
- try {
17023
- const content = readFileSync17(resolve18(this.baseDir, p), "utf-8");
17024
- if (content.trim().length < 10) {
17025
- emptyFiles.push(p);
17026
- }
17027
- } catch {}
17028
- }
17029
- if (emptyFiles.length > 0 && contextManager) {
17030
- contextManager.addMessage({
17031
- role: "user",
17032
- content: `<system-summary>${t("exec.step_gate_empty", { step: String(step.id), files: emptyFiles.join(", ") })}</system-summary>`
17033
- });
17323
+ const allGone = stepPaths.every((p) => !existsSync30(resolve18(this.baseDir, p)));
17324
+ const satisfied = step.kind === "delete" ? allGone && !allExist : allExist;
17325
+ if (!satisfied)
17034
17326
  return;
17327
+ if (step.kind !== "delete") {
17328
+ const emptyFiles = [];
17329
+ for (const p of stepPaths) {
17330
+ try {
17331
+ const content = readFileSync17(resolve18(this.baseDir, p), "utf-8");
17332
+ if (content.trim().length < 10) {
17333
+ emptyFiles.push(p);
17334
+ }
17335
+ } catch {}
17336
+ }
17337
+ if (emptyFiles.length > 0 && contextManager) {
17338
+ contextManager.addMessage({
17339
+ role: "user",
17340
+ content: `<system-summary>${t("exec.step_gate_empty", { step: String(step.id), files: emptyFiles.join(", ") })}</system-summary>`
17341
+ });
17342
+ return;
17343
+ }
17035
17344
  }
17036
17345
  this.tracker?.updateStepStatus(step.id, "done");
17037
17346
  const hadNext = this.tracker?.advance() ?? false;
@@ -17058,18 +17367,49 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
17058
17367
  return [];
17059
17368
  return tokens.filter((p) => !findExistingFile(this.baseDir, p));
17060
17369
  }
17370
+ stillExistingDeliverables(step) {
17371
+ const tokens = extractFileLikeTokens(stripUrls(step.description));
17372
+ if (tokens.length === 0)
17373
+ return [];
17374
+ return tokens.filter((p) => findExistingFile(this.baseDir, p));
17375
+ }
17376
+ parseKinds(args, steps) {
17377
+ if (args.kinds === undefined)
17378
+ return { kinds: null, error: null };
17379
+ if (!Array.isArray(args.kinds)) {
17380
+ return { kinds: null, error: t("plan.kinds_not_array") };
17381
+ }
17382
+ const kindsRaw = args.kinds.map(String);
17383
+ if (kindsRaw.length !== steps.length) {
17384
+ return { kinds: null, error: t("plan.kinds_mismatch") };
17385
+ }
17386
+ const invalid = kindsRaw.filter((k) => k !== "create" && k !== "delete");
17387
+ if (invalid.length > 0) {
17388
+ return {
17389
+ kinds: null,
17390
+ error: t("plan.kinds_invalid", {
17391
+ kinds: invalid.join(", ")
17392
+ })
17393
+ };
17394
+ }
17395
+ return { kinds: kindsRaw, error: null };
17396
+ }
17061
17397
  getTaskText(ctx) {
17062
17398
  const manager = ctx?.contextManager;
17063
17399
  if (!manager)
17064
17400
  return "";
17065
17401
  const history = manager.getActiveHistory();
17066
- const firstUser = history.find((m) => m.role === "user");
17402
+ const firstUser = history.find((m) => {
17403
+ if (m.role !== "user")
17404
+ return false;
17405
+ const text = getMessageText(m.content);
17406
+ return !text.startsWith("<system-summary>") && !text.includes("[Compressed:");
17407
+ });
17067
17408
  if (!firstUser)
17068
17409
  return "";
17069
17410
  return getMessageText(firstUser.content).trim();
17070
17411
  }
17071
17412
  }
17072
- var STUCK_RECOVERY_COOLDOWN = 5, MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3, FORCE_SKIP_THRESHOLD = 10;
17073
17413
  var init_module = __esm(() => {
17074
17414
  init_i18n();
17075
17415
  init_tracker();
@@ -17077,9 +17417,9 @@ var init_module = __esm(() => {
17077
17417
  init_stuck_detector();
17078
17418
  init_auditor();
17079
17419
  init_plan_store();
17080
- init_plan_coverage();
17081
- init_bash();
17082
17420
  init_js_identifiers();
17421
+ init_plan_tool();
17422
+ init_execution_plugin();
17083
17423
  });
17084
17424
 
17085
17425
  // src/modules/security/session-encryption.ts
@@ -17087,10 +17427,10 @@ import {
17087
17427
  readFileSync as readFileSync18,
17088
17428
  writeFileSync as writeFileSync11,
17089
17429
  existsSync as existsSync31,
17090
- readdirSync as readdirSync10,
17430
+ readdirSync as readdirSync11,
17091
17431
  unlinkSync as unlinkSync4
17092
17432
  } from "fs";
17093
- import { join as join25 } from "path";
17433
+ import { join as join26 } from "path";
17094
17434
  import { homedir as homedir8 } from "os";
17095
17435
 
17096
17436
  class SessionFileEncryptor {
@@ -17099,7 +17439,7 @@ class SessionFileEncryptor {
17099
17439
  constructor(config) {
17100
17440
  this.config = { ...DEFAULT_SESSION_ENCRYPTION, ...config };
17101
17441
  this.encryptor = new ConfigEncryptor({
17102
- keyPath: config?.keyPath || join25(homedir8(), ".mma", ".session-encryption-key")
17442
+ keyPath: config?.keyPath || join26(homedir8(), ".mma", ".session-encryption-key")
17103
17443
  });
17104
17444
  }
17105
17445
  isEnabled() {
@@ -17183,9 +17523,9 @@ class SessionFileEncryptor {
17183
17523
  encryptSessionDirectory(sessionDir) {
17184
17524
  if (!this.config.enabled)
17185
17525
  return;
17186
- const files = readdirSync10(sessionDir);
17526
+ const files = readdirSync11(sessionDir);
17187
17527
  for (const file of files) {
17188
- const filePath = join25(sessionDir, file);
17528
+ const filePath = join26(sessionDir, file);
17189
17529
  if (existsSync31(filePath) && !file.endsWith(".enc")) {
17190
17530
  try {
17191
17531
  const content = readFileSync18(filePath, "utf8");
@@ -17199,10 +17539,10 @@ class SessionFileEncryptor {
17199
17539
  decryptSessionDirectory(sessionDir) {
17200
17540
  if (!this.config.enabled)
17201
17541
  return;
17202
- const files = readdirSync10(sessionDir);
17542
+ const files = readdirSync11(sessionDir);
17203
17543
  for (const file of files) {
17204
17544
  if (file.endsWith(".enc")) {
17205
- const encFilePath = join25(sessionDir, file);
17545
+ const encFilePath = join26(sessionDir, file);
17206
17546
  const decFilePath = encFilePath.slice(0, -4);
17207
17547
  try {
17208
17548
  const content = readFileSync18(encFilePath, "utf8");
@@ -17229,13 +17569,13 @@ var init_session_encryption = __esm(() => {
17229
17569
  import {
17230
17570
  existsSync as existsSync32,
17231
17571
  mkdirSync as mkdirSync15,
17232
- readdirSync as readdirSync11,
17572
+ readdirSync as readdirSync12,
17233
17573
  readFileSync as readFileSync19,
17234
17574
  rmSync as rmSync2,
17235
17575
  writeFileSync as writeFileSync12,
17236
17576
  appendFileSync as appendFileSync6
17237
17577
  } from "fs";
17238
- import { join as join26 } from "path";
17578
+ import { join as join27 } from "path";
17239
17579
  import { gzipSync } from "zlib";
17240
17580
 
17241
17581
  class SessionStore {
@@ -17249,7 +17589,7 @@ class SessionStore {
17249
17589
  }
17250
17590
  }
17251
17591
  getSessionDir(id) {
17252
- return join26(this.baseDir, id);
17592
+ return join27(this.baseDir, id);
17253
17593
  }
17254
17594
  updateEncryption(config) {
17255
17595
  if (config?.enabled) {
@@ -17265,16 +17605,16 @@ class SessionStore {
17265
17605
  mkdirSync15(this.baseDir, { recursive: true });
17266
17606
  }
17267
17607
  sessionDir(id) {
17268
- return join26(this.baseDir, id);
17608
+ return join27(this.baseDir, id);
17269
17609
  }
17270
17610
  metaPath(id) {
17271
- return join26(this.sessionDir(id), "meta.json");
17611
+ return join27(this.sessionDir(id), "meta.json");
17272
17612
  }
17273
17613
  historyPath(id) {
17274
- return join26(this.sessionDir(id), "history.jsonl");
17614
+ return join27(this.sessionDir(id), "history.jsonl");
17275
17615
  }
17276
17616
  sessionLogPath(id) {
17277
- return join26(this.sessionDir(id), "session.jsonl");
17617
+ return join27(this.sessionDir(id), "session.jsonl");
17278
17618
  }
17279
17619
  sessionExists(id) {
17280
17620
  return existsSync32(this.metaPath(id));
@@ -17398,7 +17738,7 @@ class SessionStore {
17398
17738
  listSessions() {
17399
17739
  if (!existsSync32(this.baseDir))
17400
17740
  return [];
17401
- const entries = readdirSync11(this.baseDir, { withFileTypes: true });
17741
+ const entries = readdirSync12(this.baseDir, { withFileTypes: true });
17402
17742
  const sessions = [];
17403
17743
  for (const entry of entries) {
17404
17744
  if (entry.isDirectory()) {
@@ -17428,7 +17768,7 @@ class SessionStore {
17428
17768
  if (existsSync32(historyPath)) {
17429
17769
  const content = readFileSync19(historyPath, "utf-8");
17430
17770
  const compressed = gzipSync(content);
17431
- const gzPath = join26(this.baseDir, `${session2.id}.jsonl.gz`);
17771
+ const gzPath = join27(this.baseDir, `${session2.id}.jsonl.gz`);
17432
17772
  writeFileSync12(gzPath, compressed);
17433
17773
  rmSync2(historyPath);
17434
17774
  }
@@ -17639,8 +17979,8 @@ class ProfileCompressor {
17639
17979
 
17640
17980
  // src/modules/user-profile/profile.ts
17641
17981
  import { readFileSync as readFileSync20, writeFileSync as writeFileSync13, existsSync as existsSync33, mkdirSync as mkdirSync16 } from "fs";
17642
- import { join as join27 } from "path";
17643
- import { homedir as homedir9, hostname, platform as platform4, type } from "os";
17982
+ import { join as join28 } from "path";
17983
+ import { homedir as homedir9, hostname, platform as platform5, type } from "os";
17644
17984
  import { env } from "process";
17645
17985
 
17646
17986
  class UserProfile {
@@ -17652,7 +17992,7 @@ class UserProfile {
17652
17992
  }
17653
17993
  collect() {
17654
17994
  this.info = {
17655
- platform: platform4(),
17995
+ platform: platform5(),
17656
17996
  os: `${type()} ${hostname()}`,
17657
17997
  hostname: hostname(),
17658
17998
  shell: env.SHELL || env.ComSpec || "unknown",
@@ -17666,10 +18006,10 @@ class UserProfile {
17666
18006
  if (!existsSync33(this.profileDir)) {
17667
18007
  mkdirSync16(this.profileDir, { recursive: true });
17668
18008
  }
17669
- writeFileSync13(join27(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
18009
+ writeFileSync13(join28(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
17670
18010
  }
17671
18011
  load() {
17672
- const path = join27(this.profileDir, "profile.json");
18012
+ const path = join28(this.profileDir, "profile.json");
17673
18013
  if (!existsSync33(path))
17674
18014
  return null;
17675
18015
  try {
@@ -17708,8 +18048,8 @@ class UserProfile {
17708
18048
  var init_profile = () => {};
17709
18049
 
17710
18050
  // src/modules/skills/loader.ts
17711
- import { readdirSync as readdirSync12, readFileSync as readFileSync21, existsSync as existsSync34, statSync as statSync6 } from "fs";
17712
- import { join as join28 } from "path";
18051
+ import { readdirSync as readdirSync13, readFileSync as readFileSync21, existsSync as existsSync34, statSync as statSync6 } from "fs";
18052
+ import { join as join29 } from "path";
17713
18053
 
17714
18054
  class SkillsLoader {
17715
18055
  loadFromDir(dirPath) {
@@ -17720,9 +18060,9 @@ class SkillsLoader {
17720
18060
  return skills;
17721
18061
  }
17722
18062
  scanDir(dirPath, skills) {
17723
- const entries = readdirSync12(dirPath);
18063
+ const entries = readdirSync13(dirPath);
17724
18064
  for (const entry of entries) {
17725
- const fullPath = join28(dirPath, entry);
18065
+ const fullPath = join29(dirPath, entry);
17726
18066
  const stat = statSync6(fullPath);
17727
18067
  if (stat.isDirectory()) {
17728
18068
  this.scanDir(fullPath, skills);
@@ -17966,10 +18306,10 @@ var init_browser2 = __esm(() => {
17966
18306
  });
17967
18307
 
17968
18308
  // src/modules/lsp/command.ts
17969
- import { delimiter, join as join29 } from "path";
18309
+ import { delimiter, join as join30 } from "path";
17970
18310
  import { existsSync as existsSync35 } from "fs";
17971
- import { platform as platform5 } from "os";
17972
- function resolveSpawnCommand(command, platformName = platform5(), pathEnv = process.env.PATH ?? "") {
18311
+ import { platform as platform6 } from "os";
18312
+ function resolveSpawnCommand(command, platformName = platform6(), pathEnv = process.env.PATH ?? "") {
17973
18313
  if (platformName !== "win32")
17974
18314
  return command;
17975
18315
  if (command.includes("/") || command.includes("\\") || WIN_EXTS.some((ext) => command.toLowerCase().endsWith(ext))) {
@@ -17978,7 +18318,7 @@ function resolveSpawnCommand(command, platformName = platform5(), pathEnv = proc
17978
18318
  const dirs = pathEnv.split(delimiter).filter(Boolean);
17979
18319
  for (const dir of dirs) {
17980
18320
  for (const ext of WIN_EXTS) {
17981
- const candidate = join29(dir, `${command}${ext}`);
18321
+ const candidate = join30(dir, `${command}${ext}`);
17982
18322
  if (existsSync35(candidate))
17983
18323
  return `${command}${ext}`;
17984
18324
  }
@@ -18006,7 +18346,7 @@ import {
18006
18346
  execSync as execSync2
18007
18347
  } from "child_process";
18008
18348
  import { resolve as resolve19 } from "path";
18009
- import { platform as platform6 } from "os";
18349
+ import { platform as platform7 } from "os";
18010
18350
 
18011
18351
  class LspClient {
18012
18352
  process = null;
@@ -18069,7 +18409,7 @@ class LspClient {
18069
18409
  }
18070
18410
  return new Promise((resolve20, reject) => {
18071
18411
  const args = config.args ?? [];
18072
- const isWin = platform6() === "win32";
18412
+ const isWin = platform7() === "win32";
18073
18413
  let spawnCommand = resolveSpawnCommand(config.command);
18074
18414
  let spawnArgs = args;
18075
18415
  const spawnOpts = {
@@ -18359,8 +18699,8 @@ var init_lsp = __esm(() => {
18359
18699
  });
18360
18700
 
18361
18701
  // src/modules/indexer/walker.ts
18362
- import { readdirSync as readdirSync13, readFileSync as readFileSync22, statSync as statSync7, existsSync as existsSync37, watch } from "fs";
18363
- import { join as join30, relative as relative2, extname as extname5 } from "path";
18702
+ import { readdirSync as readdirSync14, readFileSync as readFileSync22, statSync as statSync7, existsSync as existsSync37, watch } from "fs";
18703
+ import { join as join31, relative as relative2, extname as extname5 } from "path";
18364
18704
 
18365
18705
  class Indexer {
18366
18706
  baseDir;
@@ -18391,14 +18731,14 @@ class Indexer {
18391
18731
  return;
18392
18732
  let entries;
18393
18733
  try {
18394
- entries = readdirSync13(dir);
18734
+ entries = readdirSync14(dir);
18395
18735
  } catch {
18396
18736
  return;
18397
18737
  }
18398
18738
  for (const entry of entries) {
18399
18739
  if (count >= this.MAX_FILES)
18400
18740
  return;
18401
- const fullPath = join30(dir, entry);
18741
+ const fullPath = join31(dir, entry);
18402
18742
  const relPath = relative2(this.baseDir, fullPath);
18403
18743
  const stat = statSync7(fullPath);
18404
18744
  if (stat.isDirectory()) {
@@ -18462,13 +18802,13 @@ var init_walker = __esm(() => {
18462
18802
 
18463
18803
  // src/modules/indexer/cache.ts
18464
18804
  import { readFileSync as readFileSync23, writeFileSync as writeFileSync14, existsSync as existsSync38, mkdirSync as mkdirSync17, rmSync as rmSync3 } from "fs";
18465
- import { join as join31 } from "path";
18805
+ import { join as join32 } from "path";
18466
18806
 
18467
18807
  class IndexCache {
18468
18808
  cachePath;
18469
18809
  cache = null;
18470
18810
  constructor(cacheDir) {
18471
- this.cachePath = join31(cacheDir, "index-cache.json");
18811
+ this.cachePath = join32(cacheDir, "index-cache.json");
18472
18812
  }
18473
18813
  load() {
18474
18814
  if (this.cache)
@@ -18484,7 +18824,7 @@ class IndexCache {
18484
18824
  }
18485
18825
  save(result) {
18486
18826
  this.cache = result;
18487
- const dir = join31(this.cachePath, "..");
18827
+ const dir = join32(this.cachePath, "..");
18488
18828
  if (!existsSync38(dir))
18489
18829
  mkdirSync17(dir, { recursive: true });
18490
18830
  writeFileSync14(this.cachePath, JSON.stringify(result), "utf-8");
@@ -18502,10 +18842,10 @@ var init_cache = () => {};
18502
18842
 
18503
18843
  // src/modules/indexer/project-profile.ts
18504
18844
  import { readFileSync as readFileSync24, existsSync as existsSync39 } from "fs";
18505
- import { join as join32 } from "path";
18845
+ import { join as join33 } from "path";
18506
18846
  function detectManifest(baseDir) {
18507
18847
  for (const manifest of MANIFEST_ORDER) {
18508
- if (existsSync39(join32(baseDir, manifest)))
18848
+ if (existsSync39(join33(baseDir, manifest)))
18509
18849
  return manifest;
18510
18850
  }
18511
18851
  return null;
@@ -18522,7 +18862,7 @@ function cleanDependency(entry) {
18522
18862
  }
18523
18863
  function readPackageJson(baseDir) {
18524
18864
  try {
18525
- const raw = JSON.parse(readFileSync24(join32(baseDir, "package.json"), "utf-8"));
18865
+ const raw = JSON.parse(readFileSync24(join33(baseDir, "package.json"), "utf-8"));
18526
18866
  if (!raw || typeof raw !== "object")
18527
18867
  return null;
18528
18868
  const profile = {
@@ -18546,7 +18886,7 @@ function readPackageJson(baseDir) {
18546
18886
  }
18547
18887
  function readPyproject(baseDir) {
18548
18888
  try {
18549
- const content = readFileSync24(join32(baseDir, "pyproject.toml"), "utf-8");
18889
+ const content = readFileSync24(join33(baseDir, "pyproject.toml"), "utf-8");
18550
18890
  const profile = { runtime: "python", deps: [], devDeps: [], scripts: {} };
18551
18891
  const nameMatch = content.match(/^\s*name\s*=\s*"([^"]+)"/m);
18552
18892
  if (nameMatch)
@@ -18562,7 +18902,7 @@ function readPyproject(baseDir) {
18562
18902
  }
18563
18903
  function readCargo(baseDir) {
18564
18904
  try {
18565
- const content = readFileSync24(join32(baseDir, "Cargo.toml"), "utf-8");
18905
+ const content = readFileSync24(join33(baseDir, "Cargo.toml"), "utf-8");
18566
18906
  const profile = { runtime: "rust", deps: [], devDeps: [], scripts: {} };
18567
18907
  const nameMatch = content.match(/^\s*name\s*=\s*"([^"]+)"/m);
18568
18908
  if (nameMatch)
@@ -18586,7 +18926,7 @@ function readCargo(baseDir) {
18586
18926
  }
18587
18927
  function readGoMod(baseDir) {
18588
18928
  try {
18589
- const content = readFileSync24(join32(baseDir, "go.mod"), "utf-8");
18929
+ const content = readFileSync24(join33(baseDir, "go.mod"), "utf-8");
18590
18930
  const profile = { runtime: "go", deps: [], devDeps: [], scripts: {} };
18591
18931
  const moduleMatch = content.match(/^module\s+(\S+)/m);
18592
18932
  if (moduleMatch)
@@ -18604,7 +18944,7 @@ function readGoMod(baseDir) {
18604
18944
  }
18605
18945
  function readRequirements(baseDir) {
18606
18946
  try {
18607
- const content = readFileSync24(join32(baseDir, "requirements.txt"), "utf-8");
18947
+ const content = readFileSync24(join33(baseDir, "requirements.txt"), "utf-8");
18608
18948
  const profile = { runtime: "python", deps: [], devDeps: [], scripts: {} };
18609
18949
  for (const line of content.split(`
18610
18950
  `)) {
@@ -19058,7 +19398,7 @@ var init_mcp = __esm(() => {
19058
19398
 
19059
19399
  // src/modules/memory/module.ts
19060
19400
  import { homedir as homedir10 } from "os";
19061
- import { join as join33 } from "path";
19401
+ import { join as join34 } from "path";
19062
19402
 
19063
19403
  class MemoryModule {
19064
19404
  name = "memory";
@@ -19067,7 +19407,7 @@ class MemoryModule {
19067
19407
  if (storeOrDir instanceof MemoryStore) {
19068
19408
  this.store = storeOrDir;
19069
19409
  } else {
19070
- const dir = storeOrDir || join33(homedir10(), ".mma", "memory");
19410
+ const dir = storeOrDir || join34(homedir10(), ".mma", "memory");
19071
19411
  this.store = new MemoryStore(dir);
19072
19412
  }
19073
19413
  }
@@ -19159,7 +19499,7 @@ __export(exports_bootstrap, {
19159
19499
  bootstrap: () => bootstrap
19160
19500
  });
19161
19501
  import { homedir as homedir11 } from "os";
19162
- import { join as join34, resolve as resolve21 } from "path";
19502
+ import { join as join35, resolve as resolve21 } from "path";
19163
19503
  import { existsSync as existsSync40, readFileSync as readFileSync25, writeFileSync as writeFileSync15 } from "fs";
19164
19504
  function buildSystemInfo(config, baseDir, profileCompressed) {
19165
19505
  const now = new Date().toISOString().replace("T", " ").slice(0, 19);
@@ -19186,8 +19526,8 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
19186
19526
  `);
19187
19527
  }
19188
19528
  async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19189
- const dir = configDir || join34(homedir11(), ".mma");
19190
- const projectConfigPath = projectDir ? join34(projectDir, ".mmrc") : join34(process.cwd(), ".mmrc");
19529
+ const dir = configDir || join35(homedir11(), ".mma");
19530
+ const projectConfigPath = projectDir ? join35(projectDir, ".mmrc") : join35(process.cwd(), ".mmrc");
19191
19531
  const config = loadConfig({ configDir: dir, projectConfigPath });
19192
19532
  setLocale(config.locale);
19193
19533
  try {
@@ -19197,7 +19537,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19197
19537
  }
19198
19538
  } catch {}
19199
19539
  const logger = new Logger(config.logLevel);
19200
- logger.setLogDir(join34(dir, "logs"));
19540
+ logger.setLogDir(join35(dir, "logs"));
19201
19541
  logger.debug("MMA bootstrap", {
19202
19542
  version: config.version,
19203
19543
  model: config.model
@@ -19219,7 +19559,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19219
19559
  logger.info(`Model ${config.model} loaded in ${loadResult.loadTime}s`);
19220
19560
  }
19221
19561
  }
19222
- const profile = new UserProfile(join34(dir));
19562
+ const profile = new UserProfile(join35(dir));
19223
19563
  profile.load() || profile.collect();
19224
19564
  profile.save();
19225
19565
  const llmProvider = new OpenAICompatProvider({
@@ -19231,7 +19571,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19231
19571
  rateLimits: config.security?.rateLimits
19232
19572
  });
19233
19573
  const baseDir = projectDir ? resolve21(projectDir) : process.cwd();
19234
- const projectMapCacheDir = join34(baseDir, ".mma");
19574
+ const projectMapCacheDir = join35(baseDir, ".mma");
19235
19575
  const indexerModule = new IndexerModule({
19236
19576
  baseDir,
19237
19577
  cacheDir: projectMapCacheDir
@@ -19242,9 +19582,9 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19242
19582
  logger.warn(`Project indexing failed: ${err.message}`);
19243
19583
  }
19244
19584
  const skillsLoader = new SkillsLoader;
19245
- const builtinDir = join34(import.meta.dirname, "skills", "builtin");
19246
- const globalDir = join34(homedir11(), ".agents", "skills");
19247
- const projectSkillsDir = join34(baseDir, ".mma", "skills");
19585
+ const builtinDir = join35(import.meta.dirname, "skills", "builtin");
19586
+ const globalDir = join35(homedir11(), ".agents", "skills");
19587
+ const projectSkillsDir = join35(baseDir, ".mma", "skills");
19248
19588
  const availableSkills = skillsLoader.loadFromAllSources(builtinDir, globalDir, projectSkillsDir);
19249
19589
  const skillsBudget = Math.floor(config.contextWindow * config.skills.budget);
19250
19590
  const skillsModule = new SkillsModule(availableSkills, skillsBudget);
@@ -19258,11 +19598,11 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19258
19598
  essential: true,
19259
19599
  estimatedTokens: Math.ceil(systemInfoContent.length / 4)
19260
19600
  };
19261
- const agentsMdGlobal = join34(dir, "AGENTS.md");
19601
+ const agentsMdGlobal = join35(dir, "AGENTS.md");
19262
19602
  if (!existsSync40(agentsMdGlobal)) {
19263
19603
  writeFileSync15(agentsMdGlobal, "", "utf-8");
19264
19604
  }
19265
- const sessionDir = join34(dir, "sessions");
19605
+ const sessionDir = join35(dir, "sessions");
19266
19606
  const sessionStore = new SessionStore(sessionDir);
19267
19607
  sessionStore.init();
19268
19608
  const sessionManager = new SessionManager(sessionStore, {
@@ -19329,7 +19669,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19329
19669
  const mcpModule = new MCPModule(config);
19330
19670
  await mcpModule.initialize();
19331
19671
  moduleRegistry.register(mcpModule);
19332
- const memoryStore = new MemoryStore(join34(dir, "memory"));
19672
+ const memoryStore = new MemoryStore(join35(dir, "memory"));
19333
19673
  const memoryModule = new MemoryModule(memoryStore);
19334
19674
  moduleRegistry.register(memoryModule);
19335
19675
  if (config.browser.enabled) {
@@ -19380,8 +19720,8 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19380
19720
  pluginManager.register(plugin);
19381
19721
  pluginManager.register(plugin2);
19382
19722
  const pluginLoader = new PluginLoader;
19383
- const globalPluginsDir = join34(homedir11(), ".mma", "plugins");
19384
- const projectPluginsDir = join34(baseDir, ".mma", "plugins");
19723
+ const globalPluginsDir = join35(homedir11(), ".mma", "plugins");
19724
+ const projectPluginsDir = join35(baseDir, ".mma", "plugins");
19385
19725
  pluginLoader.loadFromDir(globalPluginsDir, pluginManager, logger);
19386
19726
  pluginLoader.loadFromDir(projectPluginsDir, pluginManager, logger);
19387
19727
  contextManager.onCompact = (summary) => {
@@ -19399,9 +19739,9 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
19399
19739
  const skipAgentsMd = noAgentsMd === true;
19400
19740
  if (!skipAgentsMd) {
19401
19741
  const agentsMdCandidates = [
19402
- join34(baseDir, "AGENTS.md"),
19403
- join34(baseDir, ".mma", "AGENTS.md"),
19404
- join34(dir, "AGENTS.md")
19742
+ join35(baseDir, "AGENTS.md"),
19743
+ join35(baseDir, ".mma", "AGENTS.md"),
19744
+ join35(dir, "AGENTS.md")
19405
19745
  ];
19406
19746
  for (const p of agentsMdCandidates) {
19407
19747
  if (existsSync40(p)) {
@@ -20261,7 +20601,7 @@ __export(exports_manifest, {
20261
20601
  });
20262
20602
  import { existsSync as existsSync41, readFileSync as readFileSync26, mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
20263
20603
  import { homedir as homedir13 } from "os";
20264
- import { join as join36 } from "path";
20604
+ import { join as join37 } from "path";
20265
20605
  function readManifest(path = MANIFEST_PATH) {
20266
20606
  try {
20267
20607
  if (existsSync41(path)) {
@@ -20272,7 +20612,7 @@ function readManifest(path = MANIFEST_PATH) {
20272
20612
  return { version: 1, certifications: [] };
20273
20613
  }
20274
20614
  function saveManifest(m, path = MANIFEST_PATH) {
20275
- mkdirSync18(join36(homedir13(), ".mma"), { recursive: true });
20615
+ mkdirSync18(join37(homedir13(), ".mma"), { recursive: true });
20276
20616
  writeFileSync16(path, JSON.stringify(m, null, 2), "utf-8");
20277
20617
  }
20278
20618
  function upsertCertification(entry, path = MANIFEST_PATH) {
@@ -20307,7 +20647,7 @@ function getCertMark(model, providerUrl, currentVersion, path = MANIFEST_PATH) {
20307
20647
  }
20308
20648
  var MANIFEST_PATH;
20309
20649
  var init_manifest = __esm(() => {
20310
- MANIFEST_PATH = join36(homedir13(), ".mma", "certifications.json");
20650
+ MANIFEST_PATH = join37(homedir13(), ".mma", "certifications.json");
20311
20651
  });
20312
20652
 
20313
20653
  // node_modules/yaml/dist/nodes/identity.js
@@ -27430,8 +27770,8 @@ var init_scenarios = __esm(() => {
27430
27770
  });
27431
27771
 
27432
27772
  // src/modules/certification/loader.ts
27433
- import { existsSync as existsSync42, readdirSync as readdirSync14, readFileSync as readFileSync27 } from "fs";
27434
- import { join as join37 } from "path";
27773
+ import { existsSync as existsSync42, readdirSync as readdirSync15, readFileSync as readFileSync27 } from "fs";
27774
+ import { join as join38 } from "path";
27435
27775
  function validateScenario(s) {
27436
27776
  const errors2 = [];
27437
27777
  const isSkip = s.mode === "skip";
@@ -27481,11 +27821,11 @@ function loadScenarios(userDir) {
27481
27821
  scenarios.push(s);
27482
27822
  }
27483
27823
  if (userDir && existsSync42(userDir)) {
27484
- for (const file of readdirSync14(userDir)) {
27824
+ for (const file of readdirSync15(userDir)) {
27485
27825
  if (!file.endsWith(".yaml") && !file.endsWith(".yml"))
27486
27826
  continue;
27487
27827
  try {
27488
- const raw = readFileSync27(join37(userDir, file), "utf-8");
27828
+ const raw = readFileSync27(join38(userDir, file), "utf-8");
27489
27829
  const data = $parse(raw);
27490
27830
  const parsed = normalizeScenario(data, file);
27491
27831
  const errs = validateScenario(parsed);
@@ -27539,7 +27879,7 @@ var init_loader3 = __esm(() => {
27539
27879
 
27540
27880
  // src/modules/certification/fact-checker.ts
27541
27881
  import { existsSync as existsSync43, readFileSync as readFileSync28, statSync as statSync8 } from "fs";
27542
- import { join as join38 } from "path";
27882
+ import { join as join39 } from "path";
27543
27883
  function checkSandbox(sandboxDir, checks, exitCode, output) {
27544
27884
  const failures = [];
27545
27885
  for (const check of checks) {
@@ -27556,13 +27896,13 @@ function runCheck(sandboxDir, check, exitCode, output) {
27556
27896
  case "outputContains":
27557
27897
  return output.includes(check.text);
27558
27898
  case "fileExists":
27559
- return isFile(join38(sandboxDir, check.path));
27899
+ return isFile(join39(sandboxDir, check.path));
27560
27900
  case "fileNotExists":
27561
- return !existsSync43(join38(sandboxDir, check.path));
27901
+ return !existsSync43(join39(sandboxDir, check.path));
27562
27902
  case "dirExists":
27563
- return isDir(join38(sandboxDir, check.path));
27903
+ return isDir(join39(sandboxDir, check.path));
27564
27904
  case "fileContent": {
27565
- const abs = join38(sandboxDir, check.path);
27905
+ const abs = join39(sandboxDir, check.path);
27566
27906
  if (!isFile(abs))
27567
27907
  return false;
27568
27908
  const content = readFileSync28(abs, "utf-8");
@@ -27573,7 +27913,7 @@ function runCheck(sandboxDir, check, exitCode, output) {
27573
27913
  return false;
27574
27914
  }
27575
27915
  case "fileRegex": {
27576
- const abs = join38(sandboxDir, check.path);
27916
+ const abs = join39(sandboxDir, check.path);
27577
27917
  if (!isFile(abs))
27578
27918
  return false;
27579
27919
  return new RegExp(check.pattern).test(readFileSync28(abs, "utf-8"));
@@ -27623,8 +27963,8 @@ var init_fact_checker = () => {};
27623
27963
  // src/modules/certification/runner.ts
27624
27964
  import { spawn as spawn7 } from "child_process";
27625
27965
  import { existsSync as existsSync44, mkdirSync as mkdirSync19, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
27626
- import { platform as platform7 } from "os";
27627
- import { join as join39, resolve as resolve22, dirname as dirname12 } from "path";
27966
+ import { platform as platform8 } from "os";
27967
+ import { join as join40, resolve as resolve22, dirname as dirname12 } from "path";
27628
27968
  async function runScenario(scenario, opts) {
27629
27969
  if (scenario.mode === "skip") {
27630
27970
  return {
@@ -27643,7 +27983,7 @@ async function runScenario(scenario, opts) {
27643
27983
  let passed = 0;
27644
27984
  let firstError;
27645
27985
  for (let i = 1;i <= reps; i++) {
27646
- const sandbox = join39(opts.sandboxBase, `run-${scenario.id}-${i}`);
27986
+ const sandbox = join40(opts.sandboxBase, `run-${scenario.id}-${i}`);
27647
27987
  let failures = [];
27648
27988
  let exitCode = -1;
27649
27989
  let output = "";
@@ -27704,20 +28044,20 @@ function prepareSandbox(sandbox, scenario, mmaRoot) {
27704
28044
  rmSync4(sandbox, { recursive: true, force: true });
27705
28045
  mkdirSync19(sandbox, { recursive: true });
27706
28046
  for (const f of scenario.fixtures ?? []) {
27707
- const src = join39(mmaRoot, f.source);
28047
+ const src = join40(mmaRoot, f.source);
27708
28048
  if (!existsSync44(src)) {
27709
28049
  throw new Error(`fixture missing: ${f.source}`);
27710
28050
  }
27711
- const dest = join39(sandbox, f.dest);
28051
+ const dest = join40(sandbox, f.dest);
27712
28052
  mkdirSync19(dirname12(dest), { recursive: true });
27713
28053
  cpSync2(src, dest);
27714
28054
  }
27715
28055
  }
27716
28056
  function resolveMmaEntry(mmaRoot) {
27717
- const dev = join39(mmaRoot, "src", "cli", "main.ts");
28057
+ const dev = join40(mmaRoot, "src", "cli", "main.ts");
27718
28058
  if (existsSync44(dev))
27719
28059
  return dev;
27720
- return join39(mmaRoot, "dist", "main.js");
28060
+ return join40(mmaRoot, "dist", "main.js");
27721
28061
  }
27722
28062
  function findMmaRoot(fromDir) {
27723
28063
  const candidates = [
@@ -27725,7 +28065,7 @@ function findMmaRoot(fromDir) {
27725
28065
  resolve22(fromDir, "..")
27726
28066
  ];
27727
28067
  for (const c of candidates) {
27728
- if (existsSync44(join39(c, "package.json")))
28068
+ if (existsSync44(join40(c, "package.json")))
27729
28069
  return c;
27730
28070
  }
27731
28071
  return process.cwd();
@@ -27734,7 +28074,7 @@ function killTree2(child) {
27734
28074
  const pid = child.pid;
27735
28075
  if (!pid)
27736
28076
  return;
27737
- if (platform7() === "win32") {
28077
+ if (platform8() === "win32") {
27738
28078
  spawn7("taskkill", ["/pid", String(pid), "/T", "/F"], {
27739
28079
  windowsHide: true,
27740
28080
  stdio: "ignore"
@@ -27793,11 +28133,11 @@ __export(exports_cli, {
27793
28133
  });
27794
28134
  import { rmSync as rmSync5 } from "fs";
27795
28135
  import { homedir as homedir14 } from "os";
27796
- import { join as join40, dirname as dirname13 } from "path";
28136
+ import { join as join41, dirname as dirname13 } from "path";
27797
28137
  import { fileURLToPath as fileURLToPath2 } from "url";
27798
28138
  import { existsSync as existsSync45, readFileSync as readFileSync29 } from "fs";
27799
28139
  function readVersion() {
27800
- const candidates = [join40(MMA_ROOT, "package.json")];
28140
+ const candidates = [join41(MMA_ROOT, "package.json")];
27801
28141
  for (const p of candidates) {
27802
28142
  if (existsSync45(p)) {
27803
28143
  try {
@@ -27837,7 +28177,7 @@ async function certify(opts) {
27837
28177
  return;
27838
28178
  }
27839
28179
  console.log(t("cli.cert_started", { model: opts.name, provider: providerUrl }));
27840
- const sandboxBase = join40(process.cwd(), ".mma", "certification");
28180
+ const sandboxBase = join41(process.cwd(), ".mma", "certification");
27841
28181
  const results = [];
27842
28182
  const total = selected.length;
27843
28183
  let idx = 0;
@@ -27951,7 +28291,7 @@ var init_cli = __esm(() => {
27951
28291
  init_manifest();
27952
28292
  HERE = dirname13(fileURLToPath2(import.meta.url));
27953
28293
  MMA_ROOT = findMmaRoot(HERE);
27954
- USER_SCENARIO_DIR = join40(homedir14(), ".mma", "certification", "scenarios");
28294
+ USER_SCENARIO_DIR = join41(homedir14(), ".mma", "certification", "scenarios");
27955
28295
  });
27956
28296
 
27957
28297
  // src/cli/repl-commands.ts
@@ -27960,15 +28300,15 @@ __export(exports_repl_commands, {
27960
28300
  registerAllCommands: () => registerAllCommands,
27961
28301
  COMMAND_GROUPS: () => COMMAND_GROUPS
27962
28302
  });
27963
- import { join as join42, dirname as dirname15 } from "path";
28303
+ import { join as join43, dirname as dirname15 } from "path";
27964
28304
  import { homedir as homedir16 } from "os";
27965
28305
  import { existsSync as existsSync47, readFileSync as readFileSync31 } from "fs";
27966
28306
  import { fileURLToPath as fileURLToPath4 } from "url";
27967
28307
  function readVersion3() {
27968
28308
  const here = dirname15(fileURLToPath4(import.meta.url));
27969
28309
  const candidates = [
27970
- join42(here, "..", "..", "package.json"),
27971
- join42(here, "..", "package.json")
28310
+ join43(here, "..", "..", "package.json"),
28311
+ join43(here, "..", "package.json")
27972
28312
  ];
27973
28313
  for (const p of candidates) {
27974
28314
  if (existsSync47(p)) {
@@ -28136,7 +28476,7 @@ function registerMmaCommands(ctx) {
28136
28476
  console.log(pc2.yellow(t("repl.wizard_running")));
28137
28477
  await ctx.withExclusiveInput(async () => {
28138
28478
  const answers = await runSetup(ctx.rl);
28139
- const configPath = join42(homedir16(), ".mma", "config.json");
28479
+ const configPath = join43(homedir16(), ".mma", "config.json");
28140
28480
  ctx.config.provider.type = answers.provider;
28141
28481
  ctx.config.provider.baseUrl = answers.apiBase;
28142
28482
  ctx.config.provider.apiKey = answers.apiKey;
@@ -28190,7 +28530,7 @@ Excluded blocks: ${info.excluded.length}`));
28190
28530
  return;
28191
28531
  }
28192
28532
  ctx.config.provider.type = name;
28193
- const configPath = join42(homedir16(), ".mma", "config.json");
28533
+ const configPath = join43(homedir16(), ".mma", "config.json");
28194
28534
  saveConfig(ctx.config, configPath);
28195
28535
  await ctx.agent.reconfigure(ctx.config);
28196
28536
  console.log(pc2.green(t("repl.provider_set", { name })));
@@ -28246,7 +28586,7 @@ Excluded blocks: ${info.excluded.length}`));
28246
28586
  return;
28247
28587
  }
28248
28588
  ctx.config.model = name;
28249
- const configPath = join42(homedir16(), ".mma", "config.json");
28589
+ const configPath = join43(homedir16(), ".mma", "config.json");
28250
28590
  saveConfig(ctx.config, configPath);
28251
28591
  await ctx.agent.reconfigure(ctx.config);
28252
28592
  console.log(pc2.green(t("repl.model_set", { name })));
@@ -28271,7 +28611,7 @@ Excluded blocks: ${info.excluded.length}`));
28271
28611
  return;
28272
28612
  }
28273
28613
  ctx.config.contextWindow = size;
28274
- const configPath = join42(homedir16(), ".mma", "config.json");
28614
+ const configPath = join43(homedir16(), ".mma", "config.json");
28275
28615
  saveConfig(ctx.config, configPath);
28276
28616
  await ctx.agent.reconfigure(ctx.config);
28277
28617
  console.log(pc2.green(t("cli.context_set", { size })));
@@ -28290,10 +28630,10 @@ Excluded blocks: ${info.excluded.length}`));
28290
28630
  ctx.agent.shutdown();
28291
28631
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
28292
28632
  const { homedir: homedir17 } = await import("os");
28293
- const { join: join43 } = await import("path");
28633
+ const { join: join44 } = await import("path");
28294
28634
  const configDir = ctx.configDir;
28295
28635
  const baseDir = ctx.baseDir;
28296
- const projectConfigPath = join43(baseDir, ".mmrc");
28636
+ const projectConfigPath = join44(baseDir, ".mmrc");
28297
28637
  const freshConfig = loadConfig2({ configDir, projectConfigPath });
28298
28638
  Object.assign(ctx.config, freshConfig);
28299
28639
  const { bootstrap: bootstrap2 } = await Promise.resolve().then(() => (init_bootstrap(), exports_bootstrap));
@@ -28593,14 +28933,14 @@ init_bootstrap();
28593
28933
  init_config2();
28594
28934
  init_setup();
28595
28935
  init_i18n();
28596
- import { join as join41, dirname as dirname14 } from "path";
28936
+ import { join as join42, dirname as dirname14 } from "path";
28597
28937
  import { homedir as homedir15 } from "os";
28598
28938
  import { existsSync as existsSync46, readFileSync as readFileSync30 } from "fs";
28599
28939
 
28600
28940
  // src/cli/security-commands.ts
28601
28941
  init_bootstrap();
28602
28942
  init_config2();
28603
- import { join as join35 } from "path";
28943
+ import { join as join36 } from "path";
28604
28944
  import { homedir as homedir12 } from "os";
28605
28945
 
28606
28946
  // src/modules/security/security-policies.ts
@@ -29130,7 +29470,7 @@ function createSecurityCommand(program2) {
29130
29470
  }
29131
29471
  });
29132
29472
  securityCmd.command("set-policy").argument("<preset>", t("cli.security.preset")).description(t("cli.security.set_policy")).action(async (preset) => {
29133
- const configPath = join35(homedir12(), ".mma", "config.json");
29473
+ const configPath = join36(homedir12(), ".mma", "config.json");
29134
29474
  const { config: appConfig } = await bootstrap();
29135
29475
  const validPresets = ["strict", "balanced", "permissive"];
29136
29476
  if (!validPresets.includes(preset)) {
@@ -29145,7 +29485,7 @@ function createSecurityCommand(program2) {
29145
29485
  console.log(t("cli.security.policy_description", { description: policy.description }));
29146
29486
  });
29147
29487
  securityCmd.command("enable-encryption").description(t("cli.security.enable_encryption")).action(async () => {
29148
- const configPath = join35(homedir12(), ".mma", "config.json");
29488
+ const configPath = join36(homedir12(), ".mma", "config.json");
29149
29489
  const { config: appConfig } = await bootstrap();
29150
29490
  appConfig.security = appConfig.security || {};
29151
29491
  appConfig.security.sessionEncryption = {
@@ -29157,7 +29497,7 @@ function createSecurityCommand(program2) {
29157
29497
  console.log(t("cli.security.encryption_enabled"));
29158
29498
  });
29159
29499
  securityCmd.command("disable-encryption").description(t("cli.security.disable_encryption")).action(async () => {
29160
- const configPath = join35(homedir12(), ".mma", "config.json");
29500
+ const configPath = join36(homedir12(), ".mma", "config.json");
29161
29501
  const { config: appConfig } = await bootstrap();
29162
29502
  appConfig.security = appConfig.security || {};
29163
29503
  appConfig.security.sessionEncryption = {
@@ -29169,7 +29509,7 @@ function createSecurityCommand(program2) {
29169
29509
  console.log(t("cli.security.encryption_disabled"));
29170
29510
  });
29171
29511
  securityCmd.command("enable-audit").description(t("cli.security.enable_audit")).action(async () => {
29172
- const configPath = join35(homedir12(), ".mma", "config.json");
29512
+ const configPath = join36(homedir12(), ".mma", "config.json");
29173
29513
  const { config: appConfig } = await bootstrap();
29174
29514
  appConfig.security = appConfig.security || {};
29175
29515
  appConfig.security.auditNotifier = {
@@ -29183,7 +29523,7 @@ function createSecurityCommand(program2) {
29183
29523
  console.log(t("cli.security.audit_enabled"));
29184
29524
  });
29185
29525
  securityCmd.command("disable-audit").description(t("cli.security.disable_audit")).action(async () => {
29186
- const configPath = join35(homedir12(), ".mma", "config.json");
29526
+ const configPath = join36(homedir12(), ".mma", "config.json");
29187
29527
  const { config: appConfig } = await bootstrap();
29188
29528
  appConfig.security = appConfig.security || {};
29189
29529
  appConfig.security.auditNotifier = {
@@ -29219,8 +29559,8 @@ import { fileURLToPath as fileURLToPath3 } from "url";
29219
29559
  function readVersion2() {
29220
29560
  const here = dirname14(fileURLToPath3(import.meta.url));
29221
29561
  const candidates = [
29222
- join41(here, "..", "..", "package.json"),
29223
- join41(here, "..", "package.json")
29562
+ join42(here, "..", "..", "package.json"),
29563
+ join42(here, "..", "package.json")
29224
29564
  ];
29225
29565
  for (const p of candidates) {
29226
29566
  if (existsSync46(p)) {
@@ -29238,7 +29578,7 @@ function createProgram() {
29238
29578
  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"));
29239
29579
  program2.command("init").description(t("cli.init")).action(async () => {
29240
29580
  const answers = await runSetup();
29241
- const configPath = join41(homedir15(), ".mma", "config.json");
29581
+ const configPath = join42(homedir15(), ".mma", "config.json");
29242
29582
  const { config } = await bootstrap();
29243
29583
  config.provider.type = answers.provider;
29244
29584
  config.provider.baseUrl = answers.apiBase;
@@ -29283,7 +29623,7 @@ function createProgram() {
29283
29623
  });
29284
29624
  const configCmd = program2.command("config").description(t("cli.manage_config"));
29285
29625
  configCmd.command("set").argument("<key>", t("cli.config_key")).argument("<value>", "Config value").description(t("cli.set_value")).action(async (key, value) => {
29286
- const configPath = join41(homedir15(), ".mma", "config.json");
29626
+ const configPath = join42(homedir15(), ".mma", "config.json");
29287
29627
  const { config } = await bootstrap();
29288
29628
  const keys = key.split(".");
29289
29629
  let obj = config;
@@ -29346,7 +29686,7 @@ function createProgram() {
29346
29686
  console.log(t("cli.model_hint"));
29347
29687
  });
29348
29688
  model.command("use").argument("<name>", "Model name").description(t("cli.set_model")).action(async (name) => {
29349
- const configPath = join41(homedir15(), ".mma", "config.json");
29689
+ const configPath = join42(homedir15(), ".mma", "config.json");
29350
29690
  const { config } = await bootstrap();
29351
29691
  config.model = name;
29352
29692
  saveConfig(config, configPath);
@@ -29382,7 +29722,7 @@ function createProgram() {
29382
29722
  await uncertify2(name, config);
29383
29723
  });
29384
29724
  program2.command("context").description(t("cli.manage_context")).argument("<size>", "Context window size in tokens").action(async (size) => {
29385
- const configPath = join41(homedir15(), ".mma", "config.json");
29725
+ const configPath = join42(homedir15(), ".mma", "config.json");
29386
29726
  const { config } = await bootstrap();
29387
29727
  const contextWindow = parseInt(size, 10);
29388
29728
  if (isNaN(contextWindow) || contextWindow < 1024) {
@@ -29400,7 +29740,7 @@ function createProgram() {
29400
29740
  console.log(t("cli.base_url"), config.provider.baseUrl);
29401
29741
  });
29402
29742
  provider.command("use").argument("<name>", "Provider name").description(t("cli.set_provider")).action(async (name) => {
29403
- const configPath = join41(homedir15(), ".mma", "config.json");
29743
+ const configPath = join42(homedir15(), ".mma", "config.json");
29404
29744
  const { config } = await bootstrap();
29405
29745
  config.provider.type = name;
29406
29746
  saveConfig(config, configPath);
@@ -30171,7 +30511,7 @@ class LineEditor {
30171
30511
 
30172
30512
  // src/cli/repl.ts
30173
30513
  import { existsSync as existsSync48, readFileSync as readFileSync32, writeFileSync as writeFileSync17 } from "fs";
30174
- import { join as join43, dirname as dirname16 } from "path";
30514
+ import { join as join44, dirname as dirname16 } from "path";
30175
30515
  import { homedir as homedir17 } from "os";
30176
30516
  import { fileURLToPath as fileURLToPath5 } from "url";
30177
30517
 
@@ -30677,8 +31017,8 @@ init_repl_commands();
30677
31017
  function readVersion4() {
30678
31018
  const here = dirname16(fileURLToPath5(import.meta.url));
30679
31019
  const candidates = [
30680
- join43(here, "..", "..", "package.json"),
30681
- join43(here, "..", "package.json")
31020
+ join44(here, "..", "..", "package.json"),
31021
+ join44(here, "..", "package.json")
30682
31022
  ];
30683
31023
  for (const p of candidates) {
30684
31024
  if (existsSync48(p)) {
@@ -30739,10 +31079,10 @@ class Repl {
30739
31079
  this.skillsModule = skillsModule;
30740
31080
  this.pluginManager = pluginManager;
30741
31081
  this.logger = logger;
30742
- this.configDir = configDir || join43(homedir17(), ".mma");
31082
+ this.configDir = configDir || join44(homedir17(), ".mma");
30743
31083
  this.baseDir = baseDir || process.cwd();
30744
31084
  this.noAgentsMd = noAgentsMd === true;
30745
- this.historyPath = join43(homedir17(), ".mma", "repl-history");
31085
+ this.historyPath = join44(homedir17(), ".mma", "repl-history");
30746
31086
  this.loadHistory();
30747
31087
  this.rl = process.stdin.isTTY ? new LineEditor({
30748
31088
  input: process.stdin,
@@ -31134,9 +31474,9 @@ ${t("image.clipboard_empty")}`));
31134
31474
  row(t("repl.agents_label"), pc2.red(t("repl.disabled")));
31135
31475
  } else {
31136
31476
  const agentsMdCandidates = [
31137
- join43(this.baseDir, "AGENTS.md"),
31138
- join43(this.baseDir, ".mma", "AGENTS.md"),
31139
- join43(this.configDir, "AGENTS.md")
31477
+ join44(this.baseDir, "AGENTS.md"),
31478
+ join44(this.baseDir, ".mma", "AGENTS.md"),
31479
+ join44(this.configDir, "AGENTS.md")
31140
31480
  ];
31141
31481
  const foundAgents = agentsMdCandidates.filter((p) => existsSync48(p));
31142
31482
  if (foundAgents.length > 0) {
@@ -31149,7 +31489,7 @@ ${t("image.clipboard_empty")}`));
31149
31489
  }
31150
31490
  const meta = this.sessionManager?.getActiveMeta();
31151
31491
  if (meta) {
31152
- const sessionPath = join43(this.configDir, "sessions", meta.id);
31492
+ const sessionPath = join44(this.configDir, "sessions", meta.id);
31153
31493
  row(t("repl.session_label"), `${pc2.cyan(meta.name)} ${pc2.dim(`(${meta.id.slice(0, 12)})`)} — ${meta.messageCount} msgs ${pc2.dim(sessionPath)}`);
31154
31494
  }
31155
31495
  const headerWidth = Math.max(50, Math.min(96, process.stdout.columns || 96));
@@ -31191,13 +31531,13 @@ init_config2();
31191
31531
  init_i18n();
31192
31532
  init_colors();
31193
31533
  import { existsSync as existsSync49, readFileSync as readFileSync33 } from "fs";
31194
- import { join as join44, dirname as dirname17 } from "path";
31534
+ import { join as join45, dirname as dirname17 } from "path";
31195
31535
  import { homedir as homedir18 } from "os";
31196
31536
  import { fileURLToPath as fileURLToPath6 } from "url";
31197
31537
 
31198
31538
  // src/modules/updater/checker.ts
31199
31539
  init_command();
31200
- import { platform as platform8 } from "os";
31540
+ import { platform as platform9 } from "os";
31201
31541
  var defaultRunner2 = async (command, args, options) => {
31202
31542
  const { execFile } = await import("child_process");
31203
31543
  return new Promise((resolve23) => {
@@ -31259,7 +31599,7 @@ class Updater {
31259
31599
  buildInstallCommand(latest) {
31260
31600
  const npmArgs = ["install", "-g", `${this.packageName}@${latest}`];
31261
31601
  const command = resolveSpawnCommand("npm");
31262
- if (platform8() === "win32" && /\.(cmd|bat)$/i.test(command)) {
31602
+ if (platform9() === "win32" && /\.(cmd|bat)$/i.test(command)) {
31263
31603
  return { command: "cmd.exe", args: ["/c", command, ...npmArgs] };
31264
31604
  }
31265
31605
  return { command, args: npmArgs };
@@ -31355,8 +31695,8 @@ class UpdaterModule {
31355
31695
  function readVersion5() {
31356
31696
  const here = dirname17(fileURLToPath6(import.meta.url));
31357
31697
  const candidates = [
31358
- join44(here, "..", "..", "package.json"),
31359
- join44(here, "..", "package.json")
31698
+ join45(here, "..", "..", "package.json"),
31699
+ join45(here, "..", "package.json")
31360
31700
  ];
31361
31701
  for (const p of candidates) {
31362
31702
  if (existsSync49(p)) {
@@ -31432,15 +31772,15 @@ async function main() {
31432
31772
  agent.shutdown();
31433
31773
  process.exit(exitCode);
31434
31774
  } else {
31435
- const configPath = join44(homedir18(), ".mma", "config.json");
31775
+ const configPath = join45(homedir18(), ".mma", "config.json");
31436
31776
  if (!existsSync49(configPath)) {
31437
31777
  console.log(pc2.yellow(`
31438
31778
  ` + t("cli.first_run") + `
31439
31779
  `));
31440
31780
  const answers = await runSetup();
31441
31781
  const config2 = loadConfig({
31442
- configDir: join44(homedir18(), ".mma"),
31443
- projectConfigPath: projectDir ? join44(projectDir, ".mmrc") : join44(process.cwd(), ".mmrc")
31782
+ configDir: join45(homedir18(), ".mma"),
31783
+ projectConfigPath: projectDir ? join45(projectDir, ".mmrc") : join45(process.cwd(), ".mmrc")
31444
31784
  });
31445
31785
  config2.provider.type = answers.provider;
31446
31786
  config2.provider.baseUrl = answers.apiBase;