micro-models-agent 0.41.0 → 0.41.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/bin/mma.mjs +41 -41
  2. package/dist/cli/commands.js +9 -19
  3. package/dist/cli/completer.js +36 -37
  4. package/dist/cli/index.js +2 -2
  5. package/dist/cli/main.js +48 -23
  6. package/dist/cli/plugin-commands.js +36 -0
  7. package/dist/cli/repl-commands.js +40 -12
  8. package/dist/cli/repl.js +217 -87
  9. package/dist/cli/run-result.js +22 -0
  10. package/dist/cli/security-commands.js +5 -7
  11. package/dist/cli/setup.js +8 -26
  12. package/dist/config/config.js +52 -5
  13. package/dist/config/defaults.js +29 -5
  14. package/dist/config/experts.js +1 -1
  15. package/dist/config/index.js +3 -3
  16. package/dist/config/security.js +3 -10
  17. package/dist/core/agent-moe.js +2 -10
  18. package/dist/core/agent.js +273 -82
  19. package/dist/core/bootstrap.js +80 -13
  20. package/dist/core/index.js +2 -2
  21. package/dist/core/prompt-builder.js +23 -2
  22. package/dist/core/session-logger.js +46 -4
  23. package/dist/core/version.js +24 -0
  24. package/dist/i18n/en.json +75 -2
  25. package/dist/i18n/ru.json +74 -1
  26. package/dist/index.js +1 -1
  27. package/dist/llm/image-utils.js +4 -5
  28. package/dist/llm/index.js +4 -4
  29. package/dist/llm/model-loader.js +6 -6
  30. package/dist/llm/openai-compat.js +40 -34
  31. package/dist/llm/orchestrator.js +33 -29
  32. package/dist/llm/response.js +9 -9
  33. package/dist/logger/app-logger.js +1 -1
  34. package/dist/logger/index.js +1 -1
  35. package/dist/main.js +1111 -954
  36. package/dist/migration/backup.js +13 -13
  37. package/dist/migration/detect.js +11 -11
  38. package/dist/migration/index.js +2 -2
  39. package/dist/modules/artifacts/store.js +61 -0
  40. package/dist/modules/browser/actions.js +34 -4
  41. package/dist/modules/browser/bridge-client.js +199 -0
  42. package/dist/modules/browser/bridge-path.js +10 -0
  43. package/dist/modules/browser/bridge-server.mjs +202 -202
  44. package/dist/modules/browser/cookie-store.js +6 -6
  45. package/dist/modules/browser/driver.js +136 -0
  46. package/dist/modules/browser/index.js +7 -5
  47. package/dist/modules/browser/module.js +8 -7
  48. package/dist/modules/browser/session.js +87 -84
  49. package/dist/modules/browser/snapshot.js +92 -58
  50. package/dist/modules/browser/types.js +4 -1
  51. package/dist/modules/certification/cli.js +2 -4
  52. package/dist/modules/certification/fact-checker.js +1 -3
  53. package/dist/modules/certification/loader.js +3 -9
  54. package/dist/modules/certification/runner.js +1 -4
  55. package/dist/modules/context/chunk-query.js +100 -0
  56. package/dist/modules/context/fact-extractor.js +162 -0
  57. package/dist/modules/context/history.js +15 -0
  58. package/dist/modules/context/index.js +1 -1
  59. package/dist/modules/context/manager.js +160 -86
  60. package/dist/modules/execution/audit-runners.js +152 -0
  61. package/dist/modules/execution/auditor.js +177 -25
  62. package/dist/modules/execution/execution-plugin.js +272 -0
  63. package/dist/modules/execution/module.js +201 -544
  64. package/dist/modules/execution/moe-executor.js +25 -0
  65. package/dist/modules/execution/plan-store.js +1 -3
  66. package/dist/modules/execution/plan-tool.js +508 -0
  67. package/dist/modules/execution/plan-validator.js +10 -10
  68. package/dist/modules/execution/planner.js +6 -1
  69. package/dist/modules/execution/stuck-detector.js +173 -10
  70. package/dist/modules/execution/verifier.js +86 -42
  71. package/dist/modules/execution/windows-commands.js +41 -0
  72. package/dist/modules/hallucination/confidence.js +8 -1
  73. package/dist/modules/hallucination/detector.js +2 -5
  74. package/dist/modules/hallucination/factual.js +3 -64
  75. package/dist/modules/hallucination/index.js +1 -1
  76. package/dist/modules/hallucination/js-identifiers.js +190 -0
  77. package/dist/modules/hallucination/llm-judge.js +1 -3
  78. package/dist/modules/indexer/cache.js +9 -7
  79. package/dist/modules/indexer/index.js +3 -3
  80. package/dist/modules/indexer/module.js +95 -42
  81. package/dist/modules/indexer/project-profile.js +183 -0
  82. package/dist/modules/indexer/walker.js +17 -17
  83. package/dist/modules/lsp/check-tool.js +58 -0
  84. package/dist/modules/lsp/client.js +74 -31
  85. package/dist/modules/lsp/command.js +60 -0
  86. package/dist/modules/lsp/config.js +87 -33
  87. package/dist/modules/lsp/index.js +3 -3
  88. package/dist/modules/lsp/module.js +185 -21
  89. package/dist/modules/lsp/probe.js +76 -0
  90. package/dist/modules/lsp/project-root.js +32 -0
  91. package/dist/modules/lsp/startup-check.js +141 -0
  92. package/dist/modules/mcp/module.js +2 -6
  93. package/dist/modules/memory/index.js +1 -1
  94. package/dist/modules/memory/module.js +71 -23
  95. package/dist/modules/memory/search.js +11 -9
  96. package/dist/modules/memory/store.js +13 -13
  97. package/dist/modules/pipelines/engine.js +10 -10
  98. package/dist/modules/pipelines/index.js +3 -3
  99. package/dist/modules/pipelines/parser.js +17 -14
  100. package/dist/modules/pipelines/template.js +1 -1
  101. package/dist/modules/plugins/builtin/lint-on-write.js +21 -16
  102. package/dist/modules/plugins/builtin/notify.js +3 -2
  103. package/dist/modules/plugins/index.js +1 -1
  104. package/dist/modules/plugins/loader.js +59 -17
  105. package/dist/modules/plugins/manager.js +73 -17
  106. package/dist/modules/processes/detect.js +34 -0
  107. package/dist/modules/processes/index.js +1 -1
  108. package/dist/modules/processes/registry.js +135 -46
  109. package/dist/modules/registry.js +4 -2
  110. package/dist/modules/security/audit-notifier.js +39 -39
  111. package/dist/modules/security/command-validator.js +2 -8
  112. package/dist/modules/security/data-sanitizer.js +1 -9
  113. package/dist/modules/security/encryption.js +58 -56
  114. package/dist/modules/security/network-validator.js +1 -9
  115. package/dist/modules/security/path-validator.js +1 -3
  116. package/dist/modules/security/security-policies.js +3 -19
  117. package/dist/modules/security/session-encryption.js +1 -1
  118. package/dist/modules/security/session-isolation.js +8 -8
  119. package/dist/modules/session/index.js +3 -3
  120. package/dist/modules/session/module.js +5 -5
  121. package/dist/modules/session/store.js +3 -9
  122. package/dist/modules/skills/matcher.js +27 -0
  123. package/dist/modules/skills/module.js +1 -2
  124. package/dist/modules/updater/checker.js +70 -6
  125. package/dist/modules/updater/index.js +2 -1
  126. package/dist/modules/updater/module.js +116 -0
  127. package/dist/modules/user-profile/compressor.js +2 -2
  128. package/dist/modules/user-profile/index.js +1 -1
  129. package/dist/modules/user-profile/profile.js +9 -9
  130. package/dist/tools/attach-image.js +1 -1
  131. package/dist/tools/bash.js +178 -19
  132. package/dist/tools/browser.js +46 -29
  133. package/dist/tools/chunk-query.js +99 -0
  134. package/dist/tools/download-file.js +116 -0
  135. package/dist/tools/enable-tools.js +58 -0
  136. package/dist/tools/executor.js +4 -5
  137. package/dist/tools/file-info.js +13 -12
  138. package/dist/tools/filter-tools.js +9 -2
  139. package/dist/tools/glob-tool.js +11 -11
  140. package/dist/tools/grep-tool.js +1 -3
  141. package/dist/tools/hidden-tools-block.js +37 -0
  142. package/dist/tools/index.js +13 -2
  143. package/dist/tools/list-dir.js +18 -17
  144. package/dist/tools/load-skill.js +1 -3
  145. package/dist/tools/path-utils.js +4 -4
  146. package/dist/tools/pipeline-run.js +25 -25
  147. package/dist/tools/process-kill.js +11 -11
  148. package/dist/tools/process-list.js +20 -22
  149. package/dist/tools/process-log.js +22 -18
  150. package/dist/tools/question.js +1 -3
  151. package/dist/tools/read-file.js +10 -2
  152. package/dist/tools/recall.js +44 -37
  153. package/dist/tools/registry.js +15 -4
  154. package/dist/tools/remember.js +29 -29
  155. package/dist/tools/scope-check.js +9 -9
  156. package/dist/tools/subagent.js +54 -9
  157. package/dist/tools/user-input.js +1 -1
  158. package/dist/tools/web-browse.js +3 -3
  159. package/dist/tools/web-fetch.js +3 -3
  160. package/dist/tools/web-search.js +3 -3
  161. package/dist/tools/write-file.js +1 -3
  162. package/dist/ui/box.js +1 -5
  163. package/dist/ui/index.js +6 -6
  164. package/dist/ui/line-editor.js +703 -0
  165. package/dist/ui/line-math.js +69 -0
  166. package/dist/ui/md-formatter.js +33 -33
  167. package/dist/ui/output.js +5 -5
  168. package/dist/ui/plan-view.js +103 -0
  169. package/dist/ui/renderer.js +15 -10
  170. package/dist/ui/table.js +1 -1
  171. package/package.json +48 -48
@@ -0,0 +1,69 @@
1
+ import stringWidth from "string-width";
2
+ const ANSI_RE = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
3
+ export function stripAnsi(s) {
4
+ return s.replace(ANSI_RE, "");
5
+ }
6
+ export function charLen(s) {
7
+ return Array.from(s).length;
8
+ }
9
+ export function wrapLine(line, width) {
10
+ const w = width > 0 ? width : 80;
11
+ const chars = Array.from(line);
12
+ const rows = [];
13
+ let start = 0;
14
+ let cells = 0;
15
+ for (let i = 0; i < chars.length; i++) {
16
+ const ch = chars[i];
17
+ if (ch === "\n") {
18
+ rows.push({ text: chars.slice(start, i).join(""), charStart: start });
19
+ start = i + 1;
20
+ cells = 0;
21
+ continue;
22
+ }
23
+ const cw = stringWidth(ch);
24
+ if (cells + cw > w && cells > 0) {
25
+ rows.push({ text: chars.slice(start, i).join(""), charStart: start });
26
+ start = i;
27
+ cells = cw;
28
+ }
29
+ else {
30
+ cells += cw;
31
+ }
32
+ }
33
+ rows.push({ text: chars.slice(start).join(""), charStart: start });
34
+ return rows;
35
+ }
36
+ export function buildLayout(lines, width) {
37
+ const rows = [];
38
+ for (let b = 0; b < lines.length; b++) {
39
+ for (const r of wrapLine(lines[b], width)) {
40
+ rows.push({ bufRow: b, charStart: r.charStart, text: r.text });
41
+ }
42
+ }
43
+ return rows;
44
+ }
45
+ export function layoutIndexFor(rows, bufRow, col) {
46
+ let idx = 0;
47
+ for (let i = 0; i < rows.length; i++) {
48
+ if (rows[i].bufRow === bufRow && rows[i].charStart <= col)
49
+ idx = i;
50
+ }
51
+ return idx;
52
+ }
53
+ export function visualColAt(rows, idx, col) {
54
+ const r = rows[idx];
55
+ const upTo = col - r.charStart;
56
+ return stringWidth(Array.from(r.text).slice(0, upTo).join(""));
57
+ }
58
+ export function charOffsetFor(rows, idx, visualCol) {
59
+ const r = rows[idx];
60
+ const chars = Array.from(r.text);
61
+ let cells = 0;
62
+ for (let i = 0; i < chars.length; i++) {
63
+ const cw = stringWidth(chars[i]);
64
+ if (cells + cw > visualCol)
65
+ return r.charStart + i;
66
+ cells += cw;
67
+ }
68
+ return r.charStart + chars.length;
69
+ }
@@ -1,6 +1,6 @@
1
- import { pc } from './colors';
2
- import { t } from '../i18n/index';
3
- import { formatTable, getTerminalWidth } from './table';
1
+ import { pc } from "./colors";
2
+ import { t } from "../i18n/index";
3
+ import { formatTable, getTerminalWidth } from "./table";
4
4
  const KW = /\b(import|export|from|const|let|var|function|return|if|else|for|while|do|switch|case|break|continue|new|class|extends|implements|interface|type|enum|namespace|module|declare|abstract|async|await|yield|throw|try|catch|finally|typeof|instanceof|in|of|as|is|keyof|readonly|static|private|public|protected|get|set|constructor|this|super|void|never|any|unknown|boolean|string|number|symbol|object|true|false|null|undefined|default)\b/g;
5
5
  const STRING = /('[^']*'|"[^"]*"|`[^`]*`)/g;
6
6
  const COMMENT = /(\/\/[^\n]*|\/\*[\s\S]*?\*\/|#[^\n]*)/g;
@@ -8,21 +8,21 @@ const NUMBER = /\b(\d+\.?\d*)\b/g;
8
8
  const TAG = /<\/?([A-Z][a-zA-Z0-9]*|[a-z][a-zA-Z0-9-]*)/g;
9
9
  const ATTR = /\b([a-zA-Z-]+=)"([^"]*)"/g;
10
10
  function highlight(code, lang) {
11
- if (lang === 'html' || lang === 'xml' || lang === 'svg') {
11
+ if (lang === "html" || lang === "xml" || lang === "svg") {
12
12
  code = code.replace(COMMENT, (m) => pc.dim(pc.green(m)));
13
- code = code.replace(ATTR, (_m, attr, val) => pc.yellow(attr) + '=' + pc.green('"' + val + '"'));
14
- code = code.replace(TAG, (_m, tag) => '<' + pc.cyan(tag));
13
+ code = code.replace(ATTR, (_m, attr, val) => pc.yellow(attr) + "=" + pc.green('"' + val + '"'));
14
+ code = code.replace(TAG, (_m, tag) => "<" + pc.cyan(tag));
15
15
  }
16
- else if (lang === 'css' || lang === 'scss' || lang === 'less') {
16
+ else if (lang === "css" || lang === "scss" || lang === "less") {
17
17
  code = code.replace(COMMENT, (m) => pc.dim(pc.green(m)));
18
18
  code = code.replace(STRING, (m) => pc.green(m));
19
19
  code = code.replace(NUMBER, (m) => pc.yellow(m));
20
20
  }
21
- else if (lang === 'bash' || lang === 'sh' || lang === 'shell' || lang === 'zsh') {
21
+ else if (lang === "bash" || lang === "sh" || lang === "shell" || lang === "zsh") {
22
22
  code = code.replace(COMMENT, (m) => pc.dim(pc.green(m)));
23
23
  code = code.replace(STRING, (m) => pc.green(m));
24
24
  }
25
- else if (lang === 'json') {
25
+ else if (lang === "json") {
26
26
  code = code.replace(STRING, (m) => pc.green(m));
27
27
  code = code.replace(NUMBER, (m) => pc.yellow(m));
28
28
  }
@@ -37,7 +37,7 @@ function highlight(code, lang) {
37
37
  }
38
38
  function isTableRow(line) {
39
39
  const trimmed = line.trim();
40
- if (!trimmed.startsWith('|'))
40
+ if (!trimmed.startsWith("|"))
41
41
  return false;
42
42
  const pipes = trimmed.match(/\|/g);
43
43
  return (pipes?.length ?? 0) >= 2;
@@ -46,9 +46,9 @@ function isHorizontalRule(line) {
46
46
  return /^(-{3,}|\*{3,}|_{3,})\s*$/.test(line.trim());
47
47
  }
48
48
  export class FormattingStream {
49
- buffer = '';
49
+ buffer = "";
50
50
  inCodeBlock = false;
51
- codeLang = '';
51
+ codeLang = "";
52
52
  codeLines = [];
53
53
  tableBuffer = [];
54
54
  onWrite;
@@ -60,7 +60,7 @@ export class FormattingStream {
60
60
  write(chunk) {
61
61
  this.buffer += chunk;
62
62
  let idx;
63
- while ((idx = this.buffer.indexOf('\n')) !== -1) {
63
+ while ((idx = this.buffer.indexOf("\n")) !== -1) {
64
64
  const line = this.buffer.slice(0, idx);
65
65
  this.buffer = this.buffer.slice(idx + 1);
66
66
  this.processLine(line);
@@ -75,8 +75,8 @@ export class FormattingStream {
75
75
  }
76
76
  if (this.buffer.length > 0) {
77
77
  const line = this.buffer;
78
- this.buffer = '';
79
- if (line.trim() === '```' || line.trim().startsWith('```')) {
78
+ this.buffer = "";
79
+ if (line.trim() === "```" || line.trim().startsWith("```")) {
80
80
  if (this.codeLines.length > 0) {
81
81
  this.emitCodeBlock();
82
82
  }
@@ -86,7 +86,7 @@ export class FormattingStream {
86
86
  }
87
87
  }
88
88
  processLine(line) {
89
- if (line.trim() === '```' || line.trim().startsWith('```')) {
89
+ if (line.trim() === "```" || line.trim().startsWith("```")) {
90
90
  if (this.inCodeBlock) {
91
91
  this.emitCodeBlock();
92
92
  }
@@ -128,18 +128,18 @@ export class FormattingStream {
128
128
  emitCodeBlock() {
129
129
  this.inCodeBlock = false;
130
130
  if (this.codeLines.length > 0) {
131
- const code = this.codeLines.join('\n');
131
+ const code = this.codeLines.join("\n");
132
132
  const highlighted = highlight(code, this.codeLang);
133
- const lang = this.codeLang ? ` ${this.codeLang} ` : ' ';
134
- const top = pc.dim(`┌─${lang}${''.repeat(Math.max(0, this.width - 3 - lang.length))}┐`);
133
+ const lang = this.codeLang ? ` ${this.codeLang} ` : " ";
134
+ const top = pc.dim(`┌─${lang}${"".repeat(Math.max(0, this.width - 3 - lang.length))}┐`);
135
135
  this.onWrite(top);
136
- for (const l of highlighted.split('\n')) {
137
- this.onWrite(pc.dim('') + l);
136
+ for (const l of highlighted.split("\n")) {
137
+ this.onWrite(pc.dim("") + l);
138
138
  }
139
- this.onWrite(pc.dim(`└${''.repeat(Math.max(0, this.width - 1))}┘`));
139
+ this.onWrite(pc.dim(`└${"".repeat(Math.max(0, this.width - 1))}┘`));
140
140
  }
141
141
  this.codeLines = [];
142
- this.codeLang = '';
142
+ this.codeLang = "";
143
143
  }
144
144
  formatLine(line) {
145
145
  let result = line;
@@ -152,18 +152,18 @@ export class FormattingStream {
152
152
  return pc.cyan(pc.bold(content));
153
153
  }
154
154
  if (isHorizontalRule(result)) {
155
- return pc.dim(''.repeat(Math.max(0, Math.min(this.width, 60))));
155
+ return pc.dim("".repeat(Math.max(0, Math.min(this.width, 60))));
156
156
  }
157
157
  const quote = result.match(/^(>+)\s?(.*)$/);
158
158
  if (quote) {
159
159
  const depth = Math.min(quote[1].length, 4);
160
- const marker = pc.dim(''.repeat(depth));
160
+ const marker = pc.dim("".repeat(depth));
161
161
  return `${marker} ${this.formatInline(quote[2])}`;
162
162
  }
163
163
  const checkbox = result.match(/^[-*]\s+\[([ xX])\]\s+(.+)$/);
164
164
  if (checkbox) {
165
- const checked = checkbox[1].toLowerCase() === 'x';
166
- const mark = checked ? pc.green('') : pc.dim('');
165
+ const checked = checkbox[1].toLowerCase() === "x";
166
+ const mark = checked ? pc.green("") : pc.dim("");
167
167
  return ` ${mark} ${this.formatInline(checkbox[2])}`;
168
168
  }
169
169
  const ordered = result.match(/^(\d+)[.)]\s+(.+)$/);
@@ -172,7 +172,7 @@ export class FormattingStream {
172
172
  }
173
173
  const bullet = result.match(/^[-*]\s+(.+)$/);
174
174
  if (bullet) {
175
- return ` ${pc.dim('')} ${this.formatInline(bullet[1])}`;
175
+ return ` ${pc.dim("")} ${this.formatInline(bullet[1])}`;
176
176
  }
177
177
  return this.formatInline(result);
178
178
  }
@@ -187,7 +187,7 @@ export class FormattingStream {
187
187
  result = result.replace(/\*([^*]+)\*/g, (_, s) => pc.italic(s));
188
188
  result = result.replace(/(^|\s)_([^_\n]+)_(?=\s|$)/g, (_m, pre, s) => pre + pc.italic(s));
189
189
  result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, url) => {
190
- const short = url.length > 80 ? url.slice(0, 77) + '' : url;
190
+ const short = url.length > 80 ? url.slice(0, 77) + "" : url;
191
191
  return `${pc.cyan(label)} ${pc.dim(`(${short})`)}`;
192
192
  });
193
193
  result = result.replace(/\u0000(\d+)\u0000/g, (_m, i) => codeSpans[Number(i)]);
@@ -199,14 +199,14 @@ export function formatMarkdown(text) {
199
199
  const stream = new FormattingStream((l) => lines.push(l));
200
200
  stream.write(text);
201
201
  stream.flush();
202
- return lines.join('\n');
202
+ return lines.join("\n");
203
203
  }
204
204
  export function formatError(text) {
205
- return pc.red(pc.bold(t('ui.error_prefix')) + text);
205
+ return pc.red(pc.bold(t("ui.error_prefix")) + text);
206
206
  }
207
207
  export function formatSuccess(text) {
208
- return pc.green(pc.bold(t('ui.success_prefix')) + text);
208
+ return pc.green(pc.bold(t("ui.success_prefix")) + text);
209
209
  }
210
210
  export function formatWarning(text) {
211
- return pc.yellow(pc.bold(t('ui.warning_prefix')) + text);
211
+ return pc.yellow(pc.bold(t("ui.warning_prefix")) + text);
212
212
  }
package/dist/ui/output.js CHANGED
@@ -1,13 +1,13 @@
1
- import { formatMarkdown, formatError, formatSuccess, formatWarning } from './md-formatter';
1
+ import { formatMarkdown, formatError, formatSuccess, formatWarning } from "./md-formatter";
2
2
  export function writeOutput(text) {
3
- process.stdout.write(formatMarkdown(text) + '\n');
3
+ process.stdout.write(formatMarkdown(text) + "\n");
4
4
  }
5
5
  export function writeError(text) {
6
- process.stderr.write(formatError(text) + '\n');
6
+ process.stderr.write(formatError(text) + "\n");
7
7
  }
8
8
  export function writeSuccess(text) {
9
- process.stdout.write(formatSuccess(text) + '\n');
9
+ process.stdout.write(formatSuccess(text) + "\n");
10
10
  }
11
11
  export function writeWarning(text) {
12
- process.stdout.write(formatWarning(text) + '\n');
12
+ process.stdout.write(formatWarning(text) + "\n");
13
13
  }
@@ -0,0 +1,103 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { join } from "path";
3
+ import { pc } from "./colors";
4
+ import { extractFileLikeTokens, stripUrls } from "../modules/hallucination/js-identifiers";
5
+ import { t } from "../i18n/index";
6
+ export function readActivePlan(baseDir) {
7
+ const p = join(baseDir, ".mma", "plans", "active.json");
8
+ if (!existsSync(p))
9
+ return null;
10
+ try {
11
+ const raw = readFileSync(p, "utf-8");
12
+ if (!raw.trim())
13
+ return null;
14
+ const parsed = JSON.parse(raw);
15
+ if (!parsed || !Array.isArray(parsed.steps))
16
+ return null;
17
+ return {
18
+ id: parsed.id || "plan",
19
+ title: parsed.title || "Plan",
20
+ steps: parsed.steps,
21
+ createdAt: parsed.createdAt || new Date().toISOString(),
22
+ baseDir: parsed.baseDir || baseDir,
23
+ name: parsed.name,
24
+ };
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ }
30
+ export function currentStepIndex(plan) {
31
+ const idx = plan.steps.findIndex((s) => s.status !== "done" && s.status !== "skipped");
32
+ return idx === -1 ? 0 : idx;
33
+ }
34
+ function stepMark(step) {
35
+ switch (step.status) {
36
+ case "done":
37
+ return pc.green("[x]");
38
+ case "in_progress":
39
+ return pc.yellow("[*]");
40
+ case "failed":
41
+ return pc.red("[!]");
42
+ case "skipped":
43
+ return pc.dim("[-]");
44
+ default:
45
+ return pc.dim("[ ]");
46
+ }
47
+ }
48
+ export function formatPlanChecklist(plan) {
49
+ const done = plan.steps.filter((s) => s.status === "done" || s.status === "skipped").length;
50
+ const total = plan.steps.length;
51
+ const pct = total === 0 ? 0 : Math.round((done / total) * 100);
52
+ const cur = currentStepIndex(plan);
53
+ const barLen = 15;
54
+ const filled = Math.round((pct / 100) * barLen);
55
+ const bar = pc.green("█".repeat(filled)) + pc.dim("░".repeat(barLen - filled));
56
+ const title = plan.title.replace(/^\[\d+[^]]*\]\s*/, "");
57
+ const lines = [
58
+ `${pc.cyan(`[${plan.id}]`)} ${pc.bold(title)} ${bar} ${pc.dim(`${done}/${total} ${pct}%`)}`,
59
+ ];
60
+ for (let i = 0; i < plan.steps.length; i++) {
61
+ const step = plan.steps[i];
62
+ const active = i === cur;
63
+ const desc = active ? pc.yellow(step.description) : pc.dim(step.description);
64
+ lines.push(` ${stepMark(step)} ${step.id}. ${desc}`);
65
+ for (const sub of step.subtasks ?? []) {
66
+ lines.push(` ${sub.done ? pc.green("[x]") : pc.dim("[ ]")} ${sub.text}`);
67
+ }
68
+ }
69
+ return lines;
70
+ }
71
+ const STRUCTURAL_TOOLS = new Set([
72
+ "plan",
73
+ "todo",
74
+ "verify",
75
+ "project_map",
76
+ "lsp_check",
77
+ "enable_tools",
78
+ ]);
79
+ function truncate(s, max = 48) {
80
+ return s.length > max ? `${s.slice(0, max - 1)}…` : s;
81
+ }
82
+ export function stepContextForTool(plan, tool, args) {
83
+ if (!plan || STRUCTURAL_TOOLS.has(tool))
84
+ return null;
85
+ const argStr = JSON.stringify(args);
86
+ const callPaths = extractFileLikeTokens(stripUrls(argStr)).map((p) => p.toLowerCase());
87
+ if (callPaths.length > 0) {
88
+ for (const step of plan.steps) {
89
+ if (step.status === "done" || step.status === "skipped")
90
+ continue;
91
+ const stepPaths = extractFileLikeTokens(stripUrls(step.description)).map((p) => p.toLowerCase());
92
+ if (stepPaths.length > 0 &&
93
+ stepPaths.some((s) => callPaths.some((c) => c.includes(s) || s.includes(c)))) {
94
+ return t("ui.step_context", { id: step.id, desc: truncate(step.description) });
95
+ }
96
+ }
97
+ }
98
+ const cur = currentStepIndex(plan);
99
+ const step = plan.steps[cur];
100
+ if (!step)
101
+ return null;
102
+ return t("ui.step_context", { id: step.id, desc: truncate(step.description) });
103
+ }
@@ -118,29 +118,36 @@ export class Renderer {
118
118
  thinkingEnd() {
119
119
  this.spinner.stop();
120
120
  }
121
- toolStart(tool, args) {
121
+ toolStart(tool, args, stepContext) {
122
122
  this.endCard();
123
123
  this.spinner.stop();
124
124
  const summary = summarizeArgs(args);
125
+ const step = stepContext ? ` ${pc.cyan(`← ${stepContext}`)}` : "";
125
126
  if (!this.rich) {
126
- this.out.write(`\n${pc.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc.dim(summary)}` : ""}\n`);
127
+ this.out.write(`\n${pc.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc.dim(summary)}` : ""}${step}\n`);
127
128
  return;
128
129
  }
129
130
  this.card = { tool, args, body: [], start: Date.now() };
130
131
  if (this.toolStyle === "inline") {
131
132
  const marker = toolMarker(tool);
132
- this.out.write(`\n${pc.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc.dim(summary)}` : ""}\n`);
133
+ this.out.write(`\n${pc.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc.dim(summary)}` : ""}${step}\n`);
133
134
  return;
134
135
  }
135
- this.spinner.start(`${pc.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc.dim(summary)}` : ""}`);
136
+ this.spinner.start(`${pc.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc.dim(summary)}` : ""}${step}`);
137
+ }
138
+ /** Live plan checklist — prints the plan block as-is (already colored). */
139
+ planBlock(lines) {
140
+ this.endCard();
141
+ this.spinner.stop();
142
+ for (const line of lines) {
143
+ this.out.write(`${line}\n`);
144
+ }
136
145
  }
137
146
  toolEnd(_tool, duration, error, ctxDelta) {
138
147
  this.spinner.stop();
139
148
  if (!this.rich) {
140
149
  if (ctxDelta !== undefined && ctxDelta !== 0) {
141
- const deltaStr = ctxDelta > 0
142
- ? pc.green(`+${ctxDelta}`)
143
- : pc.yellow(`${ctxDelta} ↓`);
150
+ const deltaStr = ctxDelta > 0 ? pc.green(`+${ctxDelta}`) : pc.yellow(`${ctxDelta} ↓`);
144
151
  this.out.write(`${pc.dim("ctx")} ${deltaStr}\n`);
145
152
  }
146
153
  return;
@@ -151,9 +158,7 @@ export class Renderer {
151
158
  const marker = error ? pc.red("✗") : pc.green("✓");
152
159
  let footer = `${marker} ${pc.dim(`${duration}ms`)}`;
153
160
  if (ctxDelta !== undefined && ctxDelta !== 0) {
154
- const deltaStr = ctxDelta > 0
155
- ? pc.green(`+${ctxDelta}`)
156
- : pc.yellow(`${ctxDelta} ↓`);
161
+ const deltaStr = ctxDelta > 0 ? pc.green(`+${ctxDelta}`) : pc.yellow(`${ctxDelta} ↓`);
157
162
  footer += ` ${pc.dim("ctx")} ${deltaStr}`;
158
163
  }
159
164
  if (this.toolStyle === "inline") {
package/dist/ui/table.js CHANGED
@@ -106,7 +106,7 @@ export function formatTable(tableLines, opts = {}) {
106
106
  for (let c = 0; c < columnCount; c++) {
107
107
  const raw = row[c] ?? "";
108
108
  const cell = truncate(raw, widths[c]);
109
- const a = isHeader && align[c] === undefined ? "center" : align[c] ?? "left";
109
+ const a = isHeader && align[c] === undefined ? "center" : (align[c] ?? "left");
110
110
  cells.push(pad(cell, widths[c], a));
111
111
  }
112
112
  const content = cells.map((c) => pc.dim("│ ") + c).join("") + pc.dim("│");
package/package.json CHANGED
@@ -1,48 +1,48 @@
1
- {
2
- "name": "micro-models-agent",
3
- "version": "0.41.0",
4
- "description": "Micro Models Agent (MMA) — LLM agent harness for small models (Qwen3.5-9B, 32K-64K context)",
5
- "type": "module",
6
- "bin": {
7
- "mma": "bin/mma.mjs"
8
- },
9
- "files": [
10
- "dist/",
11
- "bin/"
12
- ],
13
- "engines": {
14
- "node": ">=20"
15
- },
16
- "scripts": {
17
- "mma": "bun run src/cli/main.ts",
18
- "build": "bun run build:tsc && bun run build:copy-assets",
19
- "build:tsc": "tsc -p tsconfig.build.json",
20
- "build:copy-assets": "bun run scripts/copy-assets.ts",
21
- "build:prod": "bun run 'build:bundle' && bun run 'build:copy-assets'",
22
- "build:clean": "cmd /c \"if exist dist rmdir /s /q dist\"",
23
- "build:bundle": "bun build ./src/cli/main.ts --outfile ./dist/main.js --target node --format esm --external playwright",
24
- "dev": "bun --watch src/cli/main.ts",
25
- "typecheck": "tsc --noEmit",
26
- "test": "bun test",
27
- "test:watch": "bun test --watch",
28
- "test:integration": "vitest run --config vitest.integration.config.ts",
29
- "format": "prettier --write \"src/**/*.{ts,js,mjs,json}\" \"tests/**/*.{ts,js,json}\" \"scripts/**/*.ts\" \"*.{ts,json}\"",
30
- "format:check": "prettier --check \"src/**/*.{ts,js,mjs,json}\" \"tests/**/*.{ts,js,json}\" \"scripts/**/*.ts\" \"*.{ts,json}\""
31
- },
32
- "dependencies": {
33
- "commander": "^12.0.0",
34
- "js-tiktoken": "^1.0.0",
35
- "jsonrepair": "^3.15.0",
36
- "picocolors": "^1.1.1",
37
- "playwright": "^1.62.0",
38
- "string-width": "^8.2.2",
39
- "yaml": "^2.9.0"
40
- },
41
- "devDependencies": {
42
- "@types/bun": "^1.3.14",
43
- "@types/node": "^22.20.1",
44
- "prettier": "^3.9.6",
45
- "typescript": "^5.9.3",
46
- "vitest": "^4.1.10"
47
- }
48
- }
1
+ {
2
+ "name": "micro-models-agent",
3
+ "version": "0.41.2",
4
+ "description": "Micro Models Agent (MMA) — LLM agent harness for small models (Qwen3.5-9B, 32K-64K context)",
5
+ "type": "module",
6
+ "bin": {
7
+ "mma": "bin/mma.mjs"
8
+ },
9
+ "files": [
10
+ "dist/",
11
+ "bin/"
12
+ ],
13
+ "engines": {
14
+ "node": ">=20"
15
+ },
16
+ "scripts": {
17
+ "mma": "bun run src/cli/main.ts",
18
+ "build": "bun run build:tsc && bun run build:copy-assets",
19
+ "build:tsc": "tsc -p tsconfig.build.json",
20
+ "build:copy-assets": "bun run scripts/copy-assets.ts",
21
+ "build:prod": "bun run 'build:bundle' && bun run 'build:copy-assets'",
22
+ "build:clean": "cmd /c \"if exist dist rmdir /s /q dist\"",
23
+ "build:bundle": "bun build ./src/cli/main.ts --outfile ./dist/main.js --target node --format esm --external playwright",
24
+ "dev": "bun --watch src/cli/main.ts",
25
+ "typecheck": "tsc --noEmit",
26
+ "test": "bun test",
27
+ "test:watch": "bun test --watch",
28
+ "test:integration": "vitest run --config vitest.integration.config.ts",
29
+ "format": "prettier --write \"src/**/*.{ts,js,mjs,json}\" \"tests/**/*.{ts,js,json}\" \"scripts/**/*.ts\" \"*.{ts,json}\"",
30
+ "format:check": "prettier --check \"src/**/*.{ts,js,mjs,json}\" \"tests/**/*.{ts,js,json}\" \"scripts/**/*.ts\" \"*.{ts,json}\""
31
+ },
32
+ "dependencies": {
33
+ "commander": "^12.0.0",
34
+ "js-tiktoken": "^1.0.0",
35
+ "jsonrepair": "^3.15.0",
36
+ "picocolors": "^1.1.1",
37
+ "playwright": "^1.62.0",
38
+ "string-width": "^8.2.2",
39
+ "yaml": "^2.9.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/bun": "^1.3.14",
43
+ "@types/node": "^22.20.1",
44
+ "prettier": "^3.9.6",
45
+ "typescript": "^5.9.3",
46
+ "vitest": "^4.1.10"
47
+ }
48
+ }