micro-models-agent 0.63.0 → 1.1.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 (186) hide show
  1. package/CHANGELOG.md +174 -0
  2. package/dist/cli/cache-line.js +30 -0
  3. package/dist/cli/command-suggest.js +38 -0
  4. package/dist/cli/commands.js +285 -60
  5. package/dist/cli/completer.js +16 -16
  6. package/dist/cli/json-payload.js +32 -0
  7. package/dist/cli/main.js +165 -77
  8. package/dist/cli/plugin-commands.js +5 -4
  9. package/dist/cli/relaunch.js +37 -0
  10. package/dist/cli/repl-commands.js +441 -307
  11. package/dist/cli/repl.js +360 -83
  12. package/dist/cli/run-result.js +12 -6
  13. package/dist/cli/security-commands.js +64 -60
  14. package/dist/cli/setup-order.js +57 -0
  15. package/dist/cli/setup-prompt.js +49 -0
  16. package/dist/cli/setup.js +52 -48
  17. package/dist/config/budget.js +48 -0
  18. package/dist/config/config.js +132 -70
  19. package/dist/config/defaults.js +37 -11
  20. package/dist/config/domains.js +9 -50
  21. package/dist/config/utils.js +56 -0
  22. package/dist/core/agent/audit-gate.js +49 -0
  23. package/dist/core/agent/compaction.js +89 -0
  24. package/dist/core/agent/constants.js +61 -0
  25. package/dist/core/agent/context-renderer.js +40 -0
  26. package/dist/core/agent/hallucination-gate.js +87 -0
  27. package/dist/core/agent/loop-state.js +53 -0
  28. package/dist/core/agent/prefix-monitor.js +101 -0
  29. package/dist/core/agent/reasoning-resolver.js +56 -0
  30. package/dist/core/agent/token-tracker.js +96 -0
  31. package/dist/core/agent/tool-batch.js +237 -0
  32. package/dist/core/agent/tool-output.js +62 -0
  33. package/dist/core/agent-moe.js +214 -69
  34. package/dist/core/agent.js +506 -546
  35. package/dist/core/bootstrap.js +297 -98
  36. package/dist/core/crash-handler.js +2 -1
  37. package/dist/core/prompt-builder.js +3 -0
  38. package/dist/core/prompt-overflow.js +307 -0
  39. package/dist/core/session-logger.js +34 -2
  40. package/dist/i18n/en.json +8 -4
  41. package/dist/i18n/ru.json +8 -4
  42. package/dist/index.js +5 -1
  43. package/dist/llm/cache-usage.js +76 -0
  44. package/dist/llm/image-utils.js +20 -16
  45. package/dist/llm/llm-errors.js +41 -0
  46. package/dist/llm/model-loader.js +30 -0
  47. package/dist/llm/openai-compat.js +287 -101
  48. package/dist/llm/orchestrator.js +140 -68
  49. package/dist/llm/provider-budget.js +68 -0
  50. package/dist/llm/provider.js +0 -1
  51. package/dist/llm/stream-state.js +26 -0
  52. package/dist/llm/token-counter.js +28 -0
  53. package/dist/logger/app-logger.js +12 -15
  54. package/dist/main.js +1755 -841
  55. package/dist/migration/detect.js +3 -1
  56. package/dist/modules/browser/actions.js +0 -3
  57. package/dist/modules/browser/bridge-client.js +2 -0
  58. package/dist/modules/browser/bridge-server.mjs +37 -4
  59. package/dist/modules/browser/driver.js +46 -4
  60. package/dist/modules/certification/cli.js +85 -42
  61. package/dist/modules/certification/loader.js +15 -1
  62. package/dist/modules/certification/manifest.js +126 -15
  63. package/dist/modules/certification/runner.js +4 -26
  64. package/dist/modules/certification/scenarios.js +184 -5
  65. package/dist/modules/certification/syntax-scenarios.js +51 -0
  66. package/dist/modules/context/chunk-query.js +25 -5
  67. package/dist/modules/context/fact-extractor.js +6 -2
  68. package/dist/modules/context/manager.js +23 -7
  69. package/dist/modules/execution/audit-runners.js +7 -1
  70. package/dist/modules/execution/auditor.js +3 -3
  71. package/dist/modules/execution/execution-plugin.js +22 -15
  72. package/dist/modules/execution/input-from.js +46 -0
  73. package/dist/modules/execution/module.js +107 -18
  74. package/dist/modules/execution/moe-executor.js +166 -54
  75. package/dist/modules/execution/plan-actions.js +524 -0
  76. package/dist/modules/execution/plan-steps.js +23 -0
  77. package/dist/modules/execution/plan-store.js +15 -3
  78. package/dist/modules/execution/plan-tool.js +6 -488
  79. package/dist/modules/execution/plan-validator.js +24 -0
  80. package/dist/modules/execution/stuck-detector.js +3 -18
  81. package/dist/modules/execution/tracker.js +14 -5
  82. package/dist/modules/execution/transient-error.js +30 -0
  83. package/dist/modules/execution/verifier.js +94 -7
  84. package/dist/modules/execution/windows-commands.js +11 -0
  85. package/dist/modules/hallucination/confidence.js +36 -23
  86. package/dist/modules/hallucination/consistency.js +3 -0
  87. package/dist/modules/hallucination/detector.js +8 -3
  88. package/dist/modules/hallucination/factual.js +26 -7
  89. package/dist/modules/hallucination/llm-judge.js +12 -2
  90. package/dist/modules/indexer/map-command.js +35 -0
  91. package/dist/modules/indexer/map-select.js +87 -0
  92. package/dist/modules/indexer/module.js +34 -22
  93. package/dist/modules/indexer/symbols.js +189 -0
  94. package/dist/modules/indexer/walker.js +96 -42
  95. package/dist/modules/lsp/check-tool.js +2 -1
  96. package/dist/modules/lsp/client.js +49 -32
  97. package/dist/modules/lsp/config.js +55 -2
  98. package/dist/modules/lsp/module.js +38 -5
  99. package/dist/modules/lsp/probe.js +4 -3
  100. package/dist/modules/lsp/project-root.js +41 -1
  101. package/dist/modules/lsp/startup-check.js +12 -4
  102. package/dist/modules/mcp/client.js +153 -104
  103. package/dist/modules/mcp/module.js +165 -41
  104. package/dist/modules/memory/module.js +4 -3
  105. package/dist/modules/plugins/builtin/lint-on-write.js +36 -6
  106. package/dist/modules/plugins/manager.js +47 -84
  107. package/dist/modules/pricing/index.js +17 -7
  108. package/dist/modules/pricing/prices.js +30 -12
  109. package/dist/modules/processes/index.js +1 -0
  110. package/dist/modules/processes/kill-tree.js +56 -0
  111. package/dist/modules/processes/registry.js +2 -54
  112. package/dist/modules/providers/cache.js +23 -0
  113. package/dist/modules/providers/factory.js +28 -0
  114. package/dist/modules/providers/fallback.js +7 -5
  115. package/dist/modules/providers/health.js +2 -1
  116. package/dist/modules/providers/index.js +1 -0
  117. package/dist/modules/providers/manager.js +17 -2
  118. package/dist/modules/providers/presets.js +79 -6
  119. package/dist/modules/reasoning/policy.js +40 -0
  120. package/dist/modules/reasoning/probe.js +111 -0
  121. package/dist/modules/security/audit-notifier.js +42 -27
  122. package/dist/modules/security/command-validator.js +25 -20
  123. package/dist/modules/security/encryption.js +6 -12
  124. package/dist/modules/security/network-validator.js +76 -5
  125. package/dist/modules/security/path-validator.js +77 -34
  126. package/dist/modules/security/rate-limiter.js +11 -0
  127. package/dist/modules/security/security-policies.js +1 -1
  128. package/dist/modules/security/session-encryption.js +13 -2
  129. package/dist/modules/security/session-isolation.js +2 -9
  130. package/dist/modules/session/manager.js +11 -0
  131. package/dist/modules/session/module.js +11 -3
  132. package/dist/modules/session/store.js +41 -5
  133. package/dist/modules/skills/loader.js +7 -1
  134. package/dist/modules/skills/module.js +2 -1
  135. package/dist/modules/updater/changelog-reader.js +94 -0
  136. package/dist/modules/updater/dev-detect.js +17 -0
  137. package/dist/modules/updater/index.js +1 -0
  138. package/dist/modules/updater/module.js +14 -3
  139. package/dist/output/bus.js +32 -0
  140. package/dist/output/channel.js +233 -0
  141. package/dist/output/format.js +14 -0
  142. package/dist/output/index.js +7 -0
  143. package/dist/output/json-sink.js +22 -0
  144. package/dist/output/machine.js +8 -0
  145. package/dist/output/session-sink.js +27 -0
  146. package/dist/output/types.js +1 -0
  147. package/dist/tools/approve.js +6 -2
  148. package/dist/tools/attach-image.js +11 -11
  149. package/dist/tools/auto-fixer.js +198 -0
  150. package/dist/tools/bash.js +142 -89
  151. package/dist/tools/chunk-query.js +10 -6
  152. package/dist/tools/download-file.js +1 -1
  153. package/dist/tools/edit-file.js +20 -2
  154. package/dist/tools/executor.js +54 -9
  155. package/dist/tools/glob-tool.js +7 -0
  156. package/dist/tools/grep-tool.js +15 -1
  157. package/dist/tools/index.js +3 -1
  158. package/dist/tools/list-dir.js +3 -1
  159. package/dist/tools/load-skill.js +2 -1
  160. package/dist/tools/mcp-call.js +1 -1
  161. package/dist/tools/move-file.js +5 -4
  162. package/dist/tools/path-utils.js +7 -0
  163. package/dist/tools/pipeline-run.js +1 -1
  164. package/dist/tools/prompt-io.js +28 -0
  165. package/dist/tools/question.js +12 -12
  166. package/dist/tools/scope-request.js +91 -0
  167. package/dist/tools/session-info.js +44 -0
  168. package/dist/tools/set-thinking.js +71 -0
  169. package/dist/tools/subagent.js +50 -9
  170. package/dist/tools/syntax-validator.js +177 -0
  171. package/dist/tools/user-input.js +16 -9
  172. package/dist/tools/write-file.js +17 -1
  173. package/dist/ui/diff.js +10 -0
  174. package/dist/ui/line-editor.js +179 -26
  175. package/dist/ui/line-math.js +20 -3
  176. package/dist/ui/md-formatter.js +100 -10
  177. package/dist/ui/output.js +5 -4
  178. package/dist/ui/plan-view.js +2 -7
  179. package/dist/ui/renderer.js +89 -85
  180. package/dist/ui/spinner.js +14 -4
  181. package/dist/utils/error.js +4 -0
  182. package/dist/utils/index.js +4 -0
  183. package/dist/utils/retry.js +17 -0
  184. package/dist/utils/sleep.js +23 -0
  185. package/dist/utils/truncate.js +9 -0
  186. package/package.json +1 -1
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Machine-readable result document for a completed `--json` run. The
3
+ * `diagnostics` array carries the warn/error bus events the human channel
4
+ * suppressed, so a machine consumer sees probe/config/security failures.
5
+ */
6
+ export function buildJsonResult(result, diagnostics) {
7
+ return {
8
+ success: result.success,
9
+ text: result.text,
10
+ error: result.error ?? null,
11
+ iterationCount: result.iterationCount,
12
+ contextUsed: result.contextUsed ?? null,
13
+ contextLimit: result.contextLimit ?? null,
14
+ promptTokens: result.promptTokens ?? null,
15
+ completionTokens: result.completionTokens ?? null,
16
+ totalTokens: result.totalTokens ?? null,
17
+ totalCost: result.totalCost ?? null,
18
+ costBreakdown: result.costBreakdown ?? [],
19
+ cache: result.cache ?? null,
20
+ diagnostics,
21
+ };
22
+ }
23
+ /** Failure document for a throw that never produced an `AgentResult`. */
24
+ export function buildJsonError(error, diagnostics = []) {
25
+ return {
26
+ success: false,
27
+ text: "",
28
+ error,
29
+ iterationCount: 0,
30
+ diagnostics,
31
+ };
32
+ }
package/dist/cli/main.js CHANGED
@@ -1,25 +1,44 @@
1
1
  import { createProgram } from "./commands";
2
- import { bootstrap } from "../core/bootstrap";
2
+ import { bootstrap, setOneShotMode, applyReasoningCliOverride } from "../core/bootstrap";
3
3
  import { Repl } from "./repl";
4
4
  import { Renderer } from "../ui/renderer";
5
5
  import { printRunResult } from "./run-result";
6
6
  import { runSetup } from "./setup";
7
- import { saveConfig } from "../config/config";
7
+ import { applyAnswersToConfig } from "./setup-order";
8
+ import { relaunchCli } from "./relaunch";
9
+ import { defaultOutputBus, getDefaultChannel, JsonOutputSink, writeMachineJson } from "../output";
10
+ import { buildJsonError, buildJsonResult } from "./json-payload";
11
+ import { saveConfig, loadConfig } from "../config/config";
8
12
  import { t } from "../i18n/index";
9
13
  import { pc } from "../ui/colors";
10
14
  import { existsSync } from "fs";
11
15
  import { join, dirname } from "path";
12
16
  import { homedir } from "os";
13
- import { UpdaterModule } from "../modules/updater/index";
17
+ import { fileURLToPath } from "url";
18
+ import { UpdaterModule, isDevEntryPath } from "../modules/updater/index";
14
19
  import { installCrashHandlers } from "../core/crash-handler";
15
20
  import { readMmaVersion } from "../core/version";
21
+ import { closestCommand } from "./command-suggest";
22
+ import { errMsg } from "../utils";
23
+ /**
24
+ * Set only for one-shot `--json` runs (a positional prompt exists). Module-scoped
25
+ * so the top-level `main().catch` can still emit a valid document when `bootstrap`
26
+ * (or anything before the JSON branch) throws with empty stdout. Interactive
27
+ * `mma --json` (no prompt) must not set it, or a REPL crash would emit JSON.
28
+ */
29
+ let jsonMode = false;
16
30
  /** Fire-and-forget self-update in the background (never blocks startup). */
17
31
  function startAutoUpdate(config) {
32
+ // Dev runs load TypeScript sources; the published bundle runs dist/main.js.
33
+ // Never auto-update from a checkout — its package.json version may trail npm
34
+ // and global-installing over the user's install is destructive.
35
+ if (isDevEntryPath(fileURLToPath(import.meta.url)))
36
+ return undefined;
18
37
  try {
19
38
  const module = new UpdaterModule(config.updater, readMmaVersion(), "micro-models-agent", {
20
- info: (m) => process.stderr.write(pc.dim(m) + "\n"),
21
- warn: (m) => process.stderr.write(pc.yellow(m) + "\n"),
22
- error: (m) => process.stderr.write(pc.red(m) + "\n"),
39
+ info: (m) => defaultOutputBus.log("info", "updater", m),
40
+ warn: (m) => defaultOutputBus.log("warn", "updater", m),
41
+ error: (m) => defaultOutputBus.log("error", "updater", m),
23
42
  });
24
43
  module.start();
25
44
  return module;
@@ -33,73 +52,145 @@ async function main() {
33
52
  // Install global crash handlers first — every crash lands in
34
53
  // ~/.mma/logs/crash.jsonl (with env report) instead of a bare stderr stack.
35
54
  installCrashHandlers();
55
+ // Eagerly create the default channel so it subscribes to `defaultOutputBus`
56
+ // BEFORE any config/bootstrap code can emit (config warnings, migration
57
+ // summary, updater notices). The channel is created lazily by
58
+ // `getDefaultChannel()`; without this call every bus event emitted before the
59
+ // first channel use would have no terminal subscriber and be dropped.
60
+ getDefaultChannel();
36
61
  const program = createProgram();
62
+ // Определяем подкоманду ДО parse: commander запускает async-action синхронно
63
+ // из parse(), поэтому флаг one-shot нужно выставить заранее, иначе bootstrap
64
+ // успеет стартовать фоновый startup health check (ребёнок переживает
65
+ // process.exit и на Windows держит stdout-пайп — команда «виснет»).
66
+ const valueOpts = new Set(["-d", "--dir", "--reasoning"]);
67
+ const argv = process.argv.slice(2);
68
+ let firstPositional;
69
+ for (let i = 0; i < argv.length; i++) {
70
+ const a = argv[i];
71
+ if (a.startsWith("-")) {
72
+ if (valueOpts.has(a))
73
+ i++;
74
+ continue;
75
+ }
76
+ firstPositional = a;
77
+ break;
78
+ }
79
+ if (firstPositional && program.commands.some((c) => c.name() === firstPositional)) {
80
+ setOneShotMode(true);
81
+ }
37
82
  program.parse(process.argv);
38
83
  const cmdNames = new Set(program.commands.map((c) => c.name()));
39
84
  const opts = program.opts();
40
85
  const noAgentsMd = opts.noAgentsMd === true;
41
86
  const projectDir = opts.dir;
42
87
  const exitOnComplete = opts.exitOnComplete === true;
43
- const jsonMode = opts.json === true;
88
+ const jsonOpt = opts.json === true;
89
+ const reasoningLevel = opts.reasoning;
44
90
  const isSubcommand = program.args.length > 0 && cmdNames.has(program.args[0]);
45
91
  if (isSubcommand) {
46
92
  return;
47
93
  }
94
+ // Typo guard: a misspelled subcommand must not be silently sent to the LLM
95
+ // as a prompt ("mma sesion list" → chat). Warn and exit when args[0] is a
96
+ // near-miss of a known command; free-form prompts never look like that.
48
97
  if (program.args.length > 0) {
98
+ const suggestion = closestCommand(program.args[0], [...cmdNames]);
99
+ if (suggestion && suggestion !== program.args[0]) {
100
+ getDefaultChannel().writeLine(pc.yellow(`${t("cli.unknown_command", { input: program.args[0], suggestion })}`), "stderr");
101
+ process.exit(2);
102
+ }
103
+ }
104
+ if (program.args.length > 0) {
105
+ // `--json` owns stdout for ONE-SHOT runs only: silence the human channel
106
+ // before bootstrap so config warnings cannot pollute the machine-readable
107
+ // document, and subscribe the sink BEFORE bootstrap so warn/error events
108
+ // emitted during bootstrap are captured and surfaced as diagnostics.
109
+ // Info-level migration notices are intentionally not captured (diagnostics
110
+ // are warn/error only). A no-prompt invocation (`mma --json`) opens the
111
+ // interactive REPL, so neither quiet nor the sink is set there.
112
+ if (jsonOpt) {
113
+ jsonMode = true;
114
+ getDefaultChannel().setQuiet(true);
115
+ }
116
+ const jsonSink = jsonOpt ? new JsonOutputSink(defaultOutputBus) : undefined;
49
117
  const prompt = program.args.join(" ");
50
- const { agent, config, baseDir, legacyDetected } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
118
+ const { agent, config, baseDir, legacyDetected } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete, reasoningLevel);
51
119
  if (legacyDetected) {
52
- console.error(pc.yellow(` ${t("config.legacy_hint")}`));
120
+ getDefaultChannel().writeLine(pc.yellow(` ${t("config.legacy_hint")}`), "stderr");
53
121
  }
54
122
  const updater = exitOnComplete ? undefined : startAutoUpdate(config);
55
123
  if (jsonMode) {
56
- const result = await agent.run(prompt);
124
+ let result;
125
+ try {
126
+ result = await agent.run(prompt);
127
+ }
128
+ catch (err) {
129
+ // --json must never emit a raw stack trace: an AbortError or a
130
+ // crashing tool/plugin still yields a valid machine-readable result,
131
+ // and shutdown/waitForIdle still run.
132
+ const message = errMsg(err);
133
+ agent.shutdown();
134
+ const diagnostics = jsonSink?.diagnostics() ?? [];
135
+ jsonSink?.dispose();
136
+ writeMachineJson(buildJsonError(message, diagnostics));
137
+ await updater?.waitForIdle(10_000);
138
+ process.exit(1);
139
+ }
57
140
  agent.shutdown();
58
- process.stdout.write(JSON.stringify({
59
- success: result.success,
60
- text: result.text,
61
- error: result.error ?? null,
62
- iterationCount: result.iterationCount,
63
- contextUsed: result.contextUsed ?? null,
64
- contextLimit: result.contextLimit ?? null,
65
- promptTokens: result.promptTokens ?? null,
66
- completionTokens: result.completionTokens ?? null,
67
- totalTokens: result.totalTokens ?? null,
68
- totalCost: result.totalCost ?? null,
69
- costBreakdown: result.costBreakdown ?? [],
70
- }, null, 2));
71
- process.stdout.write("\n");
72
- await updater?.waitForIdle();
141
+ const diagnostics = jsonSink?.diagnostics() ?? [];
142
+ jsonSink?.dispose();
143
+ writeMachineJson(buildJsonResult(result, diagnostics));
144
+ await updater?.waitForIdle(10_000);
73
145
  process.exit(result.success ? 0 : 1);
74
146
  }
147
+ // TODO(output): one-shot runs don't surface the context-probe warning
148
+ // (only Repl.start() renders it). Surface it here when bootstrap exposes
149
+ // the probe result to this branch.
75
150
  const renderer = new Renderer({
76
151
  spinner: config.ui?.spinner ?? true,
77
- toolStyle: config.ui?.toolStyle ?? "inline",
78
152
  baseDir,
79
153
  });
80
- const result = await agent.run(prompt, (chunk) => renderer.text(chunk), (meta) => renderer.meta(meta), (ev) => {
81
- if (ev.type === "start") {
82
- renderer.toolStart(ev.tool, ev.args, undefined, ev.icon);
83
- }
84
- else {
85
- renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd);
86
- }
87
- }, (phase) => {
88
- if (phase === "thinking") {
89
- renderer.thinkingStart();
90
- }
91
- else {
92
- renderer.thinkingEnd();
93
- }
94
- });
154
+ renderer.showLoader();
155
+ let result;
156
+ try {
157
+ result = await agent.run(prompt, (chunk) => renderer.text(chunk), (meta) => renderer.meta(meta), (ev) => {
158
+ if (ev.type === "start") {
159
+ renderer.toolStart(ev.tool, ev.args, undefined, ev.icon);
160
+ }
161
+ else {
162
+ renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd);
163
+ }
164
+ }, (phase) => {
165
+ if (phase === "thinking") {
166
+ renderer.thinkingStart();
167
+ }
168
+ else {
169
+ renderer.thinkingEnd();
170
+ }
171
+ });
172
+ }
173
+ catch (err) {
174
+ // A throw from the agent loop must not skip shutdown/updater or dump a
175
+ // bare stack — surface it as a clean error result.
176
+ result = {
177
+ success: false,
178
+ text: "",
179
+ error: errMsg(err),
180
+ iterationCount: 0,
181
+ };
182
+ }
95
183
  renderer.flush();
184
+ if (result.provider && result.model) {
185
+ renderer.footer(result.model, result.provider, result.llmDurationMs ?? result.durationMs ?? 0);
186
+ }
96
187
  const exitCode = printRunResult(result, () => renderer.flush());
97
188
  agent.shutdown();
98
189
  await updater?.waitForIdle();
99
190
  process.exit(exitCode);
100
191
  }
101
192
  else {
102
- const mmaDir = join(homedir(), ".mma");
193
+ const mmaDir = process.env.MMA_CONFIG_DIR || join(homedir(), ".mma");
103
194
  const legacyConfigPath = join(mmaDir, "config.json");
104
195
  let hasAnyConfig = existsSync(legacyConfigPath);
105
196
  if (!hasAnyConfig) {
@@ -109,45 +200,42 @@ async function main() {
109
200
  }
110
201
  catch { }
111
202
  }
112
- const { agent, config, sessionManager, skillsModule, pluginManager, execModule, configDir, baseDir, logger, envReport, } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
113
- const repl = new Repl(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger, true, envReport, undefined, execModule);
114
- if (!hasAnyConfig) {
115
- // First run: wizard via the REPL's LineEditor (avoids Bun stdin conflict
116
- // from creating a second readline on process.stdin).
117
- console.log(pc.yellow("\n " + t("cli.first_run") + "\n"));
118
- const answers = await runSetup(repl.rl);
119
- config.provider.type = answers.provider;
120
- config.provider.baseUrl = answers.apiBase;
121
- config.provider.apiKey = answers.apiKey;
122
- config.model = answers.model;
123
- config.contextWindow = answers.contextWindow;
124
- config.maxToolIterations = answers.maxToolIterations;
125
- config.locale = answers.locale;
126
- if (config.security) {
127
- config.security.enabled = true;
128
- config.security.bash.enabled = true;
129
- config.security.bash.blockDangerousFlags =
130
- answers.securityFlagsBlock || config.security.bash.blockDangerousFlags;
131
- if (answers.securityBashBlock) {
132
- config.security.bash.blacklist = [
133
- ...new Set([
134
- ...config.security.bash.blacklist,
135
- "rm", "dd", "chmod", "wget", "curl", "scp", "ssh",
136
- "nc", "netcat", "sudo", "su", "kill", "pkill",
137
- "killall", "shutdown", "reboot",
138
- ]),
139
- ];
140
- }
141
- if (!answers.securityPathsDeny) {
142
- config.security.paths.denied = [];
143
- }
144
- }
203
+ const projectConfigPath = projectDir ? join(projectDir, ".mmrc") : join(process.cwd(), ".mmrc");
204
+ // First run: collect settings, persist them, then RE-EXEC the CLI in a fresh
205
+ // process and exit. The wizard and the REPL must not share a process: on
206
+ // Bun/Windows the raw-mode LineEditor only receives keypresses when it is
207
+ // the first consumer of stdin, and reusing one editor across
208
+ // wizard → bootstrap → REPL leaks prompt/frame state. The relaunched
209
+ // process sees the saved config, skips the wizard, and creates its editor
210
+ // first thing. `MMA_POST_SETUP=1` guards against a re-run loop.
211
+ if (!hasAnyConfig && !exitOnComplete && process.env.MMA_POST_SETUP !== "1") {
212
+ getDefaultChannel().writeLine("\n " + t("cli.first_run") + "\n");
213
+ const answers = await runSetup();
214
+ const { config } = loadConfig({ configDir: mmaDir, projectConfigPath });
215
+ // Preserve the CLI --reasoning override (bootstrap in the fresh process
216
+ // would apply it too, but persist it now so the child reads the final
217
+ // config).
218
+ applyReasoningCliOverride(config, reasoningLevel);
219
+ applyAnswersToConfig(config, answers);
145
220
  saveConfig(config, legacyConfigPath, dirname(legacyConfigPath));
146
- await agent.reconfigure(config);
221
+ getDefaultChannel().writeLine(pc.dim(` ${t("cli.setup_saved_restart")}`));
222
+ relaunchCli();
147
223
  }
224
+ const { agent, config, sessionManager, skillsModule, pluginManager, execModule, configDir, baseDir, logger, envReport, contextProbe, } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete, reasoningLevel);
225
+ const repl = new Repl(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger, true, envReport, undefined, execModule, contextProbe);
148
226
  startAutoUpdate(config);
149
227
  await repl.start();
150
228
  }
151
229
  }
152
- main();
230
+ main().catch((err) => {
231
+ // A throw before/during bootstrap would otherwise leave `--json` callers with
232
+ // empty stdout; emit a valid failure document instead of a human stack.
233
+ if (jsonMode) {
234
+ writeMachineJson(buildJsonError(errMsg(err)));
235
+ }
236
+ else {
237
+ getDefaultChannel().writeLine(err instanceof Error ? (err.stack ?? err.message) : String(err), "stderr");
238
+ }
239
+ process.exit(1);
240
+ });
153
241
  export { main };
@@ -1,6 +1,7 @@
1
1
  import { bootstrap } from "../core/bootstrap";
2
2
  import { readMmaVersion } from "../core/version";
3
3
  import { t } from "../i18n/index";
4
+ import { getDefaultChannel } from "../output";
4
5
  /**
5
6
  * Create the `mma plugins` subcommand group.
6
7
  */
@@ -18,19 +19,19 @@ export function createPluginCommand(program) {
18
19
  infos = infos.filter(({ plugin }) => !plugin.isBuiltin);
19
20
  }
20
21
  if (infos.length === 0) {
21
- console.log(t("cli.plugins.none"));
22
+ getDefaultChannel().writeLine(t("cli.plugins.none"));
22
23
  return;
23
24
  }
24
25
  const mmaVersion = readMmaVersion();
25
- console.log(t("cli.plugins.header", { count: String(infos.length), mma: mmaVersion }));
26
+ getDefaultChannel().writeLine(t("cli.plugins.header", { count: String(infos.length), mma: mmaVersion }));
26
27
  for (const { plugin, source } of infos) {
27
28
  const version = plugin.version || "-";
28
29
  const origin = source || "builtin";
29
30
  const builtin = plugin.isBuiltin ? t("cli.plugins.builtin_mark") : " ";
30
- console.log(` ${builtin} ${plugin.name} v${version} [${origin}]`);
31
+ getDefaultChannel().writeLine(` ${builtin} ${plugin.name} v${version} [${origin}]`);
31
32
  }
32
33
  if (!all) {
33
- console.log(t("cli.plugins.only_external"));
34
+ getDefaultChannel().writeLine(t("cli.plugins.only_external"));
34
35
  }
35
36
  });
36
37
  }
@@ -0,0 +1,37 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { getDefaultChannel } from "../output";
3
+ /**
4
+ * Args that relaunch the current CLI in a fresh process: drop the runtime's
5
+ * argv[0] (the bun/node binary) and keep the entry script plus every user arg.
6
+ */
7
+ export function relaunchArgs(argv) {
8
+ return argv.slice(1);
9
+ }
10
+ /**
11
+ * Re-exec the current CLI in a brand-new process with inherited stdio, then
12
+ * exit with its status.
13
+ *
14
+ * First-run setup uses this so the wizard and the REPL never share a process:
15
+ * on Bun/Windows the raw-mode `LineEditor` only receives keypresses when it is
16
+ * the FIRST consumer of stdin, and reusing one editor across the
17
+ * wizard → bootstrap → REPL phases leaks prompt/frame state. A fresh process
18
+ * makes the REPL editor the first stdin consumer by construction.
19
+ *
20
+ * `MMA_POST_SETUP=1` is set on the child so a failed config write can never
21
+ * loop the wizard.
22
+ */
23
+ export function relaunchCli() {
24
+ const args = relaunchArgs(process.argv);
25
+ const entry = args[0];
26
+ if (!entry)
27
+ process.exit(0);
28
+ const child = spawnSync(process.execPath, args, {
29
+ stdio: "inherit",
30
+ env: { ...process.env, MMA_POST_SETUP: "1" },
31
+ });
32
+ if (child.error) {
33
+ getDefaultChannel().writeLine(`Failed to relaunch the CLI: ${child.error.message}`, "stderr");
34
+ process.exit(1);
35
+ }
36
+ process.exit(child.status ?? 0);
37
+ }