micro-models-agent 0.51.1 → 0.52.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 (107) hide show
  1. package/dist/cli/commands.js +162 -38
  2. package/dist/cli/completer.js +5 -5
  3. package/dist/cli/main.js +42 -54
  4. package/dist/cli/repl-commands.js +138 -38
  5. package/dist/cli/repl.js +175 -89
  6. package/dist/cli/run-result.js +11 -0
  7. package/dist/cli/security-commands.js +6 -6
  8. package/dist/cli/setup.js +21 -15
  9. package/dist/config/config.js +54 -27
  10. package/dist/config/defaults.js +17 -0
  11. package/dist/config/domains.js +179 -0
  12. package/dist/config/index.js +2 -1
  13. package/dist/config/security.js +28 -8
  14. package/dist/core/agent.js +162 -30
  15. package/dist/core/bootstrap.js +94 -17
  16. package/dist/core/crash-handler.js +51 -0
  17. package/dist/core/environment.js +199 -0
  18. package/dist/core/session-logger.js +60 -6
  19. package/dist/core/version.js +2 -0
  20. package/dist/i18n/en.json +120 -39
  21. package/dist/i18n/ru.json +91 -10
  22. package/dist/llm/openai-compat.js +191 -53
  23. package/dist/llm/orchestrator.js +5 -3
  24. package/dist/logger/app-logger.js +50 -4
  25. package/dist/main.js +1288 -635
  26. package/dist/modules/browser/session.js +4 -0
  27. package/dist/modules/certification/cli.js +58 -19
  28. package/dist/modules/certification/loader.js +2 -1
  29. package/dist/modules/certification/manifest.js +22 -14
  30. package/dist/modules/certification/runner.js +91 -5
  31. package/dist/modules/certification/scenarios.js +290 -7
  32. package/dist/modules/context/fact-extractor.js +6 -0
  33. package/dist/modules/context/manager.js +19 -2
  34. package/dist/modules/execution/audit-runners.js +61 -7
  35. package/dist/modules/execution/execution-plugin.js +219 -60
  36. package/dist/modules/execution/module.js +207 -18
  37. package/dist/modules/execution/moe-executor.js +33 -20
  38. package/dist/modules/execution/plan-store.js +39 -0
  39. package/dist/modules/execution/plan-tool.js +188 -19
  40. package/dist/modules/execution/planner.js +27 -23
  41. package/dist/modules/execution/stuck-detector.js +244 -8
  42. package/dist/modules/execution/tracker.js +8 -6
  43. package/dist/modules/execution/verifier.js +15 -2
  44. package/dist/modules/hallucination/detector.js +4 -0
  45. package/dist/modules/hallucination/factual.js +45 -5
  46. package/dist/modules/indexer/module.js +1 -0
  47. package/dist/modules/lsp/client.js +123 -12
  48. package/dist/modules/lsp/index.js +1 -1
  49. package/dist/modules/lsp/module.js +30 -2
  50. package/dist/modules/lsp/probe.js +11 -1
  51. package/dist/modules/lsp/startup-check.js +5 -2
  52. package/dist/modules/plugins/builtin/lint-on-write.js +144 -41
  53. package/dist/modules/plugins/manager.js +57 -13
  54. package/dist/modules/pricing/index.js +61 -0
  55. package/dist/modules/pricing/prices.js +129 -0
  56. package/dist/modules/providers/create.js +22 -0
  57. package/dist/modules/providers/fallback.js +79 -0
  58. package/dist/modules/providers/health.js +46 -0
  59. package/dist/modules/providers/index.js +5 -0
  60. package/dist/modules/providers/manager.js +161 -0
  61. package/dist/modules/providers/presets.js +128 -0
  62. package/dist/modules/providers/registry.js +22 -0
  63. package/dist/modules/providers/types.js +1 -0
  64. package/dist/modules/registry.js +1 -0
  65. package/dist/modules/security/command-validator.js +14 -0
  66. package/dist/modules/security/encryption.js +6 -6
  67. package/dist/modules/security/network-validator.js +17 -0
  68. package/dist/modules/security/path-validator.js +22 -26
  69. package/dist/modules/session/store.js +10 -10
  70. package/dist/tools/approve.js +1 -0
  71. package/dist/tools/attach-image.js +12 -0
  72. package/dist/tools/bash.js +27 -4
  73. package/dist/tools/browser.js +1 -0
  74. package/dist/tools/chunk-query.js +1 -0
  75. package/dist/tools/create-dir.js +1 -0
  76. package/dist/tools/delete-file.js +1 -0
  77. package/dist/tools/download-file.js +1 -0
  78. package/dist/tools/edit-file.js +2 -1
  79. package/dist/tools/enable-tools.js +1 -0
  80. package/dist/tools/executor.js +17 -7
  81. package/dist/tools/file-info.js +1 -0
  82. package/dist/tools/glob-tool.js +1 -0
  83. package/dist/tools/grep-tool.js +54 -13
  84. package/dist/tools/list-dir.js +1 -0
  85. package/dist/tools/load-skill.js +1 -0
  86. package/dist/tools/mcp-call.js +1 -0
  87. package/dist/tools/move-file.js +1 -0
  88. package/dist/tools/path-utils.js +51 -1
  89. package/dist/tools/pipeline-run.js +1 -0
  90. package/dist/tools/process-kill.js +11 -0
  91. package/dist/tools/process-list.js +1 -0
  92. package/dist/tools/process-log.js +9 -0
  93. package/dist/tools/question.js +1 -0
  94. package/dist/tools/read-file.js +94 -6
  95. package/dist/tools/recall.js +1 -0
  96. package/dist/tools/remember.js +1 -0
  97. package/dist/tools/scope-check.js +7 -5
  98. package/dist/tools/search-history.js +1 -0
  99. package/dist/tools/subagent.js +4 -4
  100. package/dist/tools/web-browse.js +1 -0
  101. package/dist/tools/web-fetch.js +27 -6
  102. package/dist/tools/web-search.js +70 -43
  103. package/dist/tools/write-file.js +1 -0
  104. package/dist/ui/line-editor.js +142 -23
  105. package/dist/ui/line-math.js +8 -4
  106. package/dist/ui/renderer.js +57 -7
  107. package/package.json +50 -48
@@ -3,30 +3,16 @@ import { bootstrap } from "../core/bootstrap";
3
3
  import { saveConfig } from "../config/config";
4
4
  import { runSetup } from "./setup";
5
5
  import { t } from "../i18n/index";
6
+ import { pc } from "../ui/colors";
6
7
  import { join, dirname } from "path";
7
8
  import { homedir } from "os";
8
- import { existsSync, readFileSync } from "fs";
9
+ import { existsSync } from "fs";
9
10
  import { createSecurityCommand } from "./security-commands";
10
11
  import { createPluginCommand } from "./plugin-commands";
11
- import { fileURLToPath } from "url";
12
- function readVersion() {
13
- const here = dirname(fileURLToPath(import.meta.url));
14
- const candidates = [join(here, "..", "..", "package.json"), join(here, "..", "package.json")];
15
- for (const p of candidates) {
16
- if (existsSync(p)) {
17
- try {
18
- const raw = JSON.parse(readFileSync(p, "utf8"));
19
- if (raw.version)
20
- return raw.version;
21
- }
22
- catch {
23
- // Broken package.json — fall through to the next candidate
24
- }
25
- }
26
- }
27
- return "0.0.0";
28
- }
29
- const version = readVersion();
12
+ import { HOSTED_BASE_URLS } from "../modules/providers/presets";
13
+ import { probeProviders, targetsFromProviders } from "../modules/providers/health";
14
+ import { readMmaVersion } from "../core/version";
15
+ const version = readMmaVersion();
30
16
  export function createProgram() {
31
17
  const program = new Command()
32
18
  .name("mma")
@@ -50,12 +36,13 @@ export function createProgram() {
50
36
  config.contextWindow = answers.contextWindow;
51
37
  config.maxToolIterations = answers.maxToolIterations;
52
38
  config.locale = answers.locale;
53
- // Apply security settings from wizard
39
+ // Apply security settings from wizard.
40
+ // Security is enabled by default (balanced); wizard answers only tighten it.
54
41
  if (config.security) {
55
- config.security.enabled =
56
- answers.securityBashBlock || answers.securityFlagsBlock || !answers.securityPathsDeny;
57
- config.security.bash.enabled = config.security.enabled;
58
- config.security.bash.blockDangerousFlags = answers.securityFlagsBlock;
42
+ config.security.enabled = true;
43
+ config.security.bash.enabled = true;
44
+ config.security.bash.blockDangerousFlags =
45
+ answers.securityFlagsBlock || config.security.bash.blockDangerousFlags;
59
46
  if (answers.securityBashBlock) {
60
47
  config.security.bash.blacklist = [
61
48
  ...new Set([
@@ -83,7 +70,7 @@ export function createProgram() {
83
70
  config.security.paths.denied = [];
84
71
  }
85
72
  }
86
- saveConfig(config, configPath);
73
+ saveConfig(config, configPath, dirname(configPath));
87
74
  console.log(t("cli.config_saved"));
88
75
  });
89
76
  const configCmd = program.command("config").description(t("cli.manage_config"));
@@ -113,7 +100,7 @@ export function createProgram() {
113
100
  obj[lastKey] = parseFloat(value);
114
101
  else
115
102
  obj[lastKey] = value;
116
- saveConfig(config, configPath);
103
+ saveConfig(config, configPath, dirname(configPath));
117
104
  console.log(t("cli.set_done", { key, value }));
118
105
  });
119
106
  configCmd
@@ -123,6 +110,33 @@ export function createProgram() {
123
110
  const { config } = await bootstrap();
124
111
  console.log(JSON.stringify(config, null, 2));
125
112
  });
113
+ configCmd
114
+ .command("migrate")
115
+ .description(t("cli.migrate_config"))
116
+ .action(async () => {
117
+ const { loadConfig } = await import("../config/config");
118
+ const { hasDomainFiles } = await import("../config/domains");
119
+ const configDir = join(homedir(), ".mma");
120
+ const configPath = join(configDir, "config.json");
121
+ if (hasDomainFiles(configDir)) {
122
+ console.log(pc.yellow(t("config.migrate_no_legacy")));
123
+ return;
124
+ }
125
+ if (!existsSync(configPath)) {
126
+ console.log(pc.yellow(t("config.migrate_no_legacy")));
127
+ return;
128
+ }
129
+ console.log(t("config.migrate_start"));
130
+ const { config } = loadConfig({ configDir, projectConfigPath: join(configDir, ".mmrc") });
131
+ saveConfig(config, configPath, configDir);
132
+ // Rename legacy file
133
+ const bakPath = configPath + ".bak";
134
+ const { renameSync } = await import("fs");
135
+ renameSync(configPath, bakPath);
136
+ const { readdirSync } = await import("fs");
137
+ const domainFiles = readdirSync(join(configDir, "config")).filter((f) => f.endsWith(".json"));
138
+ console.log(pc.green(t("config.migrate_done", { count: String(domainFiles.length) })));
139
+ });
126
140
  const model = program.command("model").description(t("cli.manage_models"));
127
141
  model
128
142
  .command("list")
@@ -131,8 +145,8 @@ export function createProgram() {
131
145
  const { config } = await bootstrap();
132
146
  console.log(t("cli.current_model"), config.model);
133
147
  // Fetch available models from provider
134
- const { OpenAICompatProvider } = await import("../llm/openai-compat");
135
- const provider = new OpenAICompatProvider({
148
+ const { createProvider } = await import("../modules/providers/create");
149
+ const provider = createProvider(config.provider.type, {
136
150
  model: config.model,
137
151
  baseUrl: config.provider.baseUrl,
138
152
  apiKey: config.provider.apiKey,
@@ -149,7 +163,7 @@ export function createProgram() {
149
163
  const { getCertMark } = await import("../modules/certification/manifest");
150
164
  for (const m of models) {
151
165
  const marker = m === config.model ? "* " : " ";
152
- const mark = getCertMark(m, config.provider.baseUrl, version);
166
+ const mark = getCertMark(m, config.provider.baseUrl, version, process.cwd());
153
167
  const cert = mark === "certified" ? "✔" : mark === "stale" ? "○" : "·";
154
168
  console.log(` ${marker}${cert} ${m}`);
155
169
  }
@@ -172,7 +186,7 @@ export function createProgram() {
172
186
  const configPath = join(homedir(), ".mma", "config.json");
173
187
  const { config } = await bootstrap();
174
188
  config.model = name;
175
- saveConfig(config, configPath);
189
+ saveConfig(config, configPath, dirname(configPath));
176
190
  console.log(t("cli.model_set", { name }));
177
191
  });
178
192
  model
@@ -199,6 +213,7 @@ export function createProgram() {
199
213
  force: cmdOpts.force === true,
200
214
  clean: cmdOpts.clean === true,
201
215
  config,
216
+ projectDir: process.cwd(),
202
217
  });
203
218
  });
204
219
  model
@@ -208,14 +223,14 @@ export function createProgram() {
208
223
  .action(async (name) => {
209
224
  const { config } = await bootstrap();
210
225
  const { certStatus } = await import("../modules/certification/cli");
211
- await certStatus(name, config);
226
+ await certStatus(name, config, process.cwd());
212
227
  });
213
228
  model
214
229
  .command("cert-list")
215
230
  .description(t("cli.cert_list"))
216
231
  .action(async () => {
217
232
  const { certList } = await import("../modules/certification/cli");
218
- await certList();
233
+ await certList(process.cwd());
219
234
  });
220
235
  model
221
236
  .command("uncertify")
@@ -224,7 +239,7 @@ export function createProgram() {
224
239
  .action(async (name) => {
225
240
  const { config } = await bootstrap();
226
241
  const { uncertify } = await import("../modules/certification/cli");
227
- await uncertify(name, config);
242
+ await uncertify(name, config, process.cwd());
228
243
  });
229
244
  // Context window command
230
245
  program
@@ -240,15 +255,53 @@ export function createProgram() {
240
255
  return;
241
256
  }
242
257
  config.contextWindow = contextWindow;
243
- saveConfig(config, configPath);
258
+ saveConfig(config, configPath, dirname(configPath));
244
259
  console.log(t("cli.context_set", { size: contextWindow }));
245
260
  });
246
261
  const provider = program.command("provider").description(t("cli.manage_providers"));
262
+ provider
263
+ .command("check")
264
+ .description(t("cli.provider_check"))
265
+ .option("--timeout <ms>", "Per-provider probe timeout in ms")
266
+ .action(async (opts) => {
267
+ const { config, agent } = await bootstrap();
268
+ const providers = agent.listProviders();
269
+ const targets = targetsFromProviders(providers.length > 0
270
+ ? providers
271
+ .map((p) => ({ name: p.label, provider: agent.getProviderFor(p.label) }))
272
+ .filter((x) => Boolean(x.provider))
273
+ : [
274
+ {
275
+ name: config.provider.type,
276
+ provider: agent.getProviderFor(config.provider.active || config.provider.type),
277
+ },
278
+ ].filter((x) => Boolean(x.provider)));
279
+ const results = await probeProviders(targets, {
280
+ timeoutMs: opts.timeout ? Number(opts.timeout) : 5000,
281
+ });
282
+ for (const r of results) {
283
+ const mark = r.ok ? pc.green("✓") : pc.red("✗");
284
+ const detail = r.ok
285
+ ? pc.dim(`${r.ms}ms, ${r.models} models`)
286
+ : pc.red(r.error ?? "failed");
287
+ console.log(` ${mark} ${r.name.padEnd(16)} ${detail}`);
288
+ }
289
+ });
247
290
  provider
248
291
  .command("list")
249
292
  .description(t("cli.list_providers"))
250
293
  .action(async () => {
251
- const { config } = await bootstrap();
294
+ const { config, agent } = await bootstrap();
295
+ const providers = agent.listProviders();
296
+ if (providers.length > 0) {
297
+ console.log(t("cli.current_provider"));
298
+ for (const p of providers) {
299
+ const marker = p.active ? pc.green("* ") : " ";
300
+ const prio = p.priority !== undefined ? ` ${pc.dim(`prio=${p.priority}`)}` : "";
301
+ console.log(` ${marker}${p.label} (${pc.dim(p.type)}) ${pc.dim(p.baseUrl)}${prio}`);
302
+ }
303
+ return;
304
+ }
252
305
  console.log(t("cli.current_provider"), config.provider.type);
253
306
  console.log(t("cli.base_url"), config.provider.baseUrl);
254
307
  });
@@ -258,10 +311,81 @@ export function createProgram() {
258
311
  .description(t("cli.set_provider"))
259
312
  .action(async (name) => {
260
313
  const configPath = join(homedir(), ".mma", "config.json");
261
- const { config } = await bootstrap();
314
+ const { config, agent } = await bootstrap();
315
+ if (config.provider.entries && config.provider.entries.length > 0) {
316
+ try {
317
+ await agent.setProvider(name);
318
+ console.log(t("cli.provider_set", { name }));
319
+ }
320
+ catch (e) {
321
+ console.log(pc.red(e.message));
322
+ }
323
+ return;
324
+ }
262
325
  config.provider.type = name;
263
- saveConfig(config, configPath);
326
+ const baseUrl = HOSTED_BASE_URLS[name];
327
+ if (baseUrl) {
328
+ config.provider.baseUrl = baseUrl;
329
+ }
330
+ saveConfig(config, configPath, dirname(configPath));
264
331
  console.log(t("cli.provider_set", { name }));
332
+ if (baseUrl) {
333
+ console.log(t("cli.provider_base_hint", { baseUrl }));
334
+ }
335
+ });
336
+ provider
337
+ .command("add")
338
+ .argument("<name>", "Provider type or label")
339
+ .option("--url <url>", "Base URL")
340
+ .option("--key <key>", "API key")
341
+ .option("--priority <n>", "Fallback priority (lower = tried first)")
342
+ .option("--context-window <n>", "Context window override for this entry")
343
+ .option("--rpm <n>", "Max requests per minute for this entry")
344
+ .option("--parallel <n>", "Max parallel tasks for this entry")
345
+ .description(t("cli.add_provider"))
346
+ .action(async (name, opts) => {
347
+ const configPath = join(homedir(), ".mma", "config.json");
348
+ const { config } = await bootstrap();
349
+ const entries = Array.isArray(config.provider.entries)
350
+ ? config.provider.entries
351
+ : [];
352
+ // If a legacy single provider exists, seed the list with it first.
353
+ if (entries.length === 0) {
354
+ entries.push({
355
+ type: config.provider.type,
356
+ label: config.provider.type,
357
+ baseUrl: config.provider.baseUrl,
358
+ apiKey: config.provider.apiKey,
359
+ });
360
+ }
361
+ const baseUrl = opts.url || HOSTED_BASE_URLS[name] || HOSTED_BASE_URLS[`opencode-${name}`] || "";
362
+ if (!baseUrl) {
363
+ console.log(pc.red(t("cli.provider_no_url", { name })));
364
+ return;
365
+ }
366
+ entries.push({
367
+ type: name,
368
+ label: name,
369
+ baseUrl,
370
+ apiKey: opts.key,
371
+ ...(opts.priority !== undefined ? { priority: Number(opts.priority) } : {}),
372
+ ...(opts.contextWindow !== undefined
373
+ ? { contextWindow: Number(opts.contextWindow) }
374
+ : {}),
375
+ ...(opts.rpm !== undefined || opts.parallel !== undefined
376
+ ? {
377
+ rateLimits: {
378
+ maxRequestsPerMinute: Number(opts.rpm ?? 0) || 60,
379
+ maxParallelTasks: Number(opts.parallel ?? 0) || 1,
380
+ },
381
+ }
382
+ : {}),
383
+ });
384
+ config.provider.entries = entries;
385
+ config.provider.active = config.provider.active || config.provider.type;
386
+ saveConfig(config, configPath, dirname(configPath));
387
+ console.log(pc.green(t("cli.provider_added", { name })));
388
+ console.log(t("cli.provider_switch_hint"));
265
389
  });
266
390
  const session = program.command("session").description(t("cli.manage_sessions"));
267
391
  session
@@ -30,15 +30,15 @@ export class SubcommandProvider {
30
30
  }
31
31
  export class ModelArgProvider {
32
32
  name = "model-args";
33
- models;
34
- constructor(models) {
35
- this.models = models;
33
+ getModels;
34
+ constructor(getModels) {
35
+ this.getModels = getModels;
36
36
  }
37
37
  match(ctx) {
38
- return ctx.tokenIndex === 2 && (ctx.tokens[0] === "/model" || ctx.tokens[0] === "/config");
38
+ return ctx.tokenIndex === 2 && ctx.tokens[0] === "/model" && ctx.tokens[1] === "use";
39
39
  }
40
40
  complete(ctx) {
41
- return this.models.filter((m) => m.startsWith(ctx.partial));
41
+ return this.getModels().filter((m) => m.startsWith(ctx.partial));
42
42
  }
43
43
  }
44
44
  export class ConfigKeyProvider {
package/dist/cli/main.js CHANGED
@@ -4,35 +4,19 @@ 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, loadConfig } from "../config/config";
7
+ import { saveConfig } from "../config/config";
8
8
  import { t } from "../i18n/index";
9
9
  import { pc } from "../ui/colors";
10
- import { existsSync, readFileSync } from "fs";
10
+ import { existsSync } from "fs";
11
11
  import { join, dirname } from "path";
12
12
  import { homedir } from "os";
13
- import { fileURLToPath } from "url";
14
13
  import { UpdaterModule } from "../modules/updater/index";
15
- function readVersion() {
16
- const here = dirname(fileURLToPath(import.meta.url));
17
- const candidates = [join(here, "..", "..", "package.json"), join(here, "..", "package.json")];
18
- for (const p of candidates) {
19
- if (existsSync(p)) {
20
- try {
21
- const raw = JSON.parse(readFileSync(p, "utf8"));
22
- if (raw.version)
23
- return raw.version;
24
- }
25
- catch {
26
- // fall through
27
- }
28
- }
29
- }
30
- return "0.0.0";
31
- }
14
+ import { installCrashHandlers } from "../core/crash-handler";
15
+ import { readMmaVersion } from "../core/version";
32
16
  /** Fire-and-forget self-update in the background (never blocks startup). */
33
17
  function startAutoUpdate(config) {
34
18
  try {
35
- const module = new UpdaterModule(config.updater, readVersion(), "micro-models-agent", {
19
+ const module = new UpdaterModule(config.updater, readMmaVersion(), "micro-models-agent", {
36
20
  info: (m) => process.stderr.write(pc.dim(m) + "\n"),
37
21
  warn: (m) => process.stderr.write(pc.yellow(m) + "\n"),
38
22
  error: (m) => process.stderr.write(pc.red(m) + "\n"),
@@ -46,6 +30,9 @@ function startAutoUpdate(config) {
46
30
  }
47
31
  }
48
32
  async function main() {
33
+ // Install global crash handlers first — every crash lands in
34
+ // ~/.mma/logs/crash.jsonl (with env report) instead of a bare stderr stack.
35
+ installCrashHandlers();
49
36
  const program = createProgram();
50
37
  program.parse(process.argv);
51
38
  const cmdNames = new Set(program.commands.map((c) => c.name()));
@@ -60,7 +47,10 @@ async function main() {
60
47
  }
61
48
  if (program.args.length > 0) {
62
49
  const prompt = program.args.join(" ");
63
- const { agent, config } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
50
+ const { agent, config, baseDir, legacyDetected } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
51
+ if (legacyDetected) {
52
+ console.error(pc.yellow(` ${t("config.legacy_hint")}`));
53
+ }
64
54
  const updater = exitOnComplete ? undefined : startAutoUpdate(config);
65
55
  if (jsonMode) {
66
56
  const result = await agent.run(prompt);
@@ -75,6 +65,8 @@ async function main() {
75
65
  promptTokens: result.promptTokens ?? null,
76
66
  completionTokens: result.completionTokens ?? null,
77
67
  totalTokens: result.totalTokens ?? null,
68
+ totalCost: result.totalCost ?? null,
69
+ costBreakdown: result.costBreakdown ?? [],
78
70
  }, null, 2));
79
71
  process.stdout.write("\n");
80
72
  await updater?.waitForIdle();
@@ -83,13 +75,14 @@ async function main() {
83
75
  const renderer = new Renderer({
84
76
  spinner: config.ui?.spinner ?? true,
85
77
  toolStyle: config.ui?.toolStyle ?? "inline",
78
+ baseDir,
86
79
  });
87
80
  const result = await agent.run(prompt, (chunk) => renderer.text(chunk), (meta) => renderer.meta(meta), (ev) => {
88
81
  if (ev.type === "start") {
89
- renderer.toolStart(ev.tool, ev.args);
82
+ renderer.toolStart(ev.tool, ev.args, undefined, ev.icon);
90
83
  }
91
84
  else {
92
- renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta);
85
+ renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd);
93
86
  }
94
87
  }, (phase) => {
95
88
  if (phase === "thinking") {
@@ -106,14 +99,23 @@ async function main() {
106
99
  process.exit(exitCode);
107
100
  }
108
101
  else {
109
- const configPath = join(homedir(), ".mma", "config.json");
110
- if (!existsSync(configPath)) {
102
+ const mmaDir = join(homedir(), ".mma");
103
+ const legacyConfigPath = join(mmaDir, "config.json");
104
+ let hasAnyConfig = existsSync(legacyConfigPath);
105
+ if (!hasAnyConfig) {
106
+ try {
107
+ const { hasDomainFiles } = await import("../config/domains");
108
+ hasAnyConfig = hasDomainFiles(mmaDir);
109
+ }
110
+ catch { }
111
+ }
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).
111
117
  console.log(pc.yellow("\n " + t("cli.first_run") + "\n"));
112
- const answers = await runSetup();
113
- const config = loadConfig({
114
- configDir: join(homedir(), ".mma"),
115
- projectConfigPath: projectDir ? join(projectDir, ".mmrc") : join(process.cwd(), ".mmrc"),
116
- });
118
+ const answers = await runSetup(repl.rl);
117
119
  config.provider.type = answers.provider;
118
120
  config.provider.baseUrl = answers.apiBase;
119
121
  config.provider.apiKey = answers.apiKey;
@@ -122,30 +124,17 @@ async function main() {
122
124
  config.maxToolIterations = answers.maxToolIterations;
123
125
  config.locale = answers.locale;
124
126
  if (config.security) {
125
- config.security.enabled =
126
- answers.securityBashBlock || answers.securityFlagsBlock || !answers.securityPathsDeny;
127
- config.security.bash.enabled = config.security.enabled;
128
- config.security.bash.blockDangerousFlags = answers.securityFlagsBlock;
127
+ config.security.enabled = true;
128
+ config.security.bash.enabled = true;
129
+ config.security.bash.blockDangerousFlags =
130
+ answers.securityFlagsBlock || config.security.bash.blockDangerousFlags;
129
131
  if (answers.securityBashBlock) {
130
132
  config.security.bash.blacklist = [
131
133
  ...new Set([
132
134
  ...config.security.bash.blacklist,
133
- "rm",
134
- "dd",
135
- "chmod",
136
- "wget",
137
- "curl",
138
- "scp",
139
- "ssh",
140
- "nc",
141
- "netcat",
142
- "sudo",
143
- "su",
144
- "kill",
145
- "pkill",
146
- "killall",
147
- "shutdown",
148
- "reboot",
135
+ "rm", "dd", "chmod", "wget", "curl", "scp", "ssh",
136
+ "nc", "netcat", "sudo", "su", "kill", "pkill",
137
+ "killall", "shutdown", "reboot",
149
138
  ]),
150
139
  ];
151
140
  }
@@ -153,11 +142,10 @@ async function main() {
153
142
  config.security.paths.denied = [];
154
143
  }
155
144
  }
156
- saveConfig(config, configPath);
145
+ saveConfig(config, legacyConfigPath, dirname(legacyConfigPath));
146
+ await agent.reconfigure(config);
157
147
  }
158
- const { agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, logger, } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
159
148
  startAutoUpdate(config);
160
- const repl = new Repl(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger, true);
161
149
  await repl.start();
162
150
  }
163
151
  }