micro-models-agent 0.63.3 → 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.
- package/CHANGELOG.md +148 -1
- package/dist/cli/cache-line.js +30 -0
- package/dist/cli/command-suggest.js +38 -0
- package/dist/cli/commands.js +285 -60
- package/dist/cli/completer.js +16 -16
- package/dist/cli/json-payload.js +32 -0
- package/dist/cli/main.js +165 -77
- package/dist/cli/plugin-commands.js +5 -4
- package/dist/cli/relaunch.js +37 -0
- package/dist/cli/repl-commands.js +441 -307
- package/dist/cli/repl.js +360 -83
- package/dist/cli/run-result.js +12 -6
- package/dist/cli/security-commands.js +64 -60
- package/dist/cli/setup-order.js +57 -0
- package/dist/cli/setup-prompt.js +49 -0
- package/dist/cli/setup.js +52 -48
- package/dist/config/budget.js +48 -0
- package/dist/config/config.js +132 -70
- package/dist/config/defaults.js +37 -11
- package/dist/config/domains.js +9 -50
- package/dist/config/utils.js +56 -0
- package/dist/core/agent/audit-gate.js +49 -0
- package/dist/core/agent/compaction.js +89 -0
- package/dist/core/agent/constants.js +61 -0
- package/dist/core/agent/context-renderer.js +40 -0
- package/dist/core/agent/hallucination-gate.js +87 -0
- package/dist/core/agent/loop-state.js +53 -0
- package/dist/core/agent/prefix-monitor.js +101 -0
- package/dist/core/agent/reasoning-resolver.js +56 -0
- package/dist/core/agent/token-tracker.js +96 -0
- package/dist/core/agent/tool-batch.js +237 -0
- package/dist/core/agent/tool-output.js +62 -0
- package/dist/core/agent-moe.js +214 -69
- package/dist/core/agent.js +506 -546
- package/dist/core/bootstrap.js +297 -98
- package/dist/core/crash-handler.js +2 -1
- package/dist/core/prompt-builder.js +3 -0
- package/dist/core/prompt-overflow.js +307 -0
- package/dist/core/session-logger.js +34 -2
- package/dist/i18n/en.json +7 -4
- package/dist/i18n/ru.json +7 -4
- package/dist/index.js +5 -1
- package/dist/llm/cache-usage.js +76 -0
- package/dist/llm/image-utils.js +20 -16
- package/dist/llm/llm-errors.js +41 -0
- package/dist/llm/model-loader.js +30 -0
- package/dist/llm/openai-compat.js +287 -101
- package/dist/llm/orchestrator.js +140 -68
- package/dist/llm/provider-budget.js +68 -0
- package/dist/llm/provider.js +0 -1
- package/dist/llm/stream-state.js +26 -0
- package/dist/llm/token-counter.js +28 -0
- package/dist/logger/app-logger.js +12 -15
- package/dist/main.js +1606 -800
- package/dist/migration/detect.js +3 -1
- package/dist/modules/browser/actions.js +0 -3
- package/dist/modules/browser/bridge-client.js +2 -0
- package/dist/modules/browser/driver.js +46 -4
- package/dist/modules/certification/cli.js +85 -42
- package/dist/modules/certification/loader.js +15 -1
- package/dist/modules/certification/manifest.js +126 -15
- package/dist/modules/certification/runner.js +4 -26
- package/dist/modules/certification/scenarios.js +184 -5
- package/dist/modules/certification/syntax-scenarios.js +51 -0
- package/dist/modules/context/chunk-query.js +25 -5
- package/dist/modules/context/fact-extractor.js +6 -2
- package/dist/modules/context/manager.js +23 -7
- package/dist/modules/execution/audit-runners.js +7 -1
- package/dist/modules/execution/auditor.js +3 -3
- package/dist/modules/execution/execution-plugin.js +22 -15
- package/dist/modules/execution/input-from.js +46 -0
- package/dist/modules/execution/module.js +107 -18
- package/dist/modules/execution/moe-executor.js +166 -54
- package/dist/modules/execution/plan-actions.js +524 -0
- package/dist/modules/execution/plan-steps.js +23 -0
- package/dist/modules/execution/plan-store.js +15 -3
- package/dist/modules/execution/plan-tool.js +6 -488
- package/dist/modules/execution/plan-validator.js +24 -0
- package/dist/modules/execution/stuck-detector.js +3 -18
- package/dist/modules/execution/tracker.js +14 -5
- package/dist/modules/execution/transient-error.js +30 -0
- package/dist/modules/execution/verifier.js +94 -7
- package/dist/modules/execution/windows-commands.js +11 -0
- package/dist/modules/hallucination/confidence.js +36 -23
- package/dist/modules/hallucination/consistency.js +3 -0
- package/dist/modules/hallucination/detector.js +8 -3
- package/dist/modules/hallucination/factual.js +26 -7
- package/dist/modules/hallucination/llm-judge.js +12 -2
- package/dist/modules/indexer/map-command.js +35 -0
- package/dist/modules/indexer/map-select.js +87 -0
- package/dist/modules/indexer/module.js +34 -22
- package/dist/modules/indexer/symbols.js +189 -0
- package/dist/modules/indexer/walker.js +96 -42
- package/dist/modules/lsp/check-tool.js +2 -1
- package/dist/modules/lsp/client.js +49 -32
- package/dist/modules/lsp/config.js +55 -2
- package/dist/modules/lsp/module.js +38 -5
- package/dist/modules/lsp/probe.js +4 -3
- package/dist/modules/lsp/project-root.js +41 -1
- package/dist/modules/lsp/startup-check.js +12 -4
- package/dist/modules/mcp/client.js +153 -104
- package/dist/modules/mcp/module.js +165 -41
- package/dist/modules/memory/module.js +4 -3
- package/dist/modules/plugins/builtin/lint-on-write.js +36 -6
- package/dist/modules/plugins/manager.js +47 -84
- package/dist/modules/pricing/index.js +17 -7
- package/dist/modules/pricing/prices.js +30 -12
- package/dist/modules/processes/index.js +1 -0
- package/dist/modules/processes/kill-tree.js +56 -0
- package/dist/modules/processes/registry.js +2 -54
- package/dist/modules/providers/cache.js +23 -0
- package/dist/modules/providers/factory.js +28 -0
- package/dist/modules/providers/fallback.js +7 -5
- package/dist/modules/providers/health.js +2 -1
- package/dist/modules/providers/index.js +1 -0
- package/dist/modules/providers/manager.js +17 -2
- package/dist/modules/providers/presets.js +79 -6
- package/dist/modules/reasoning/policy.js +40 -0
- package/dist/modules/reasoning/probe.js +111 -0
- package/dist/modules/security/audit-notifier.js +42 -27
- package/dist/modules/security/command-validator.js +25 -20
- package/dist/modules/security/encryption.js +6 -12
- package/dist/modules/security/network-validator.js +76 -5
- package/dist/modules/security/path-validator.js +77 -34
- package/dist/modules/security/rate-limiter.js +11 -0
- package/dist/modules/security/security-policies.js +1 -1
- package/dist/modules/security/session-encryption.js +13 -2
- package/dist/modules/security/session-isolation.js +2 -9
- package/dist/modules/session/manager.js +11 -0
- package/dist/modules/session/module.js +11 -3
- package/dist/modules/session/store.js +41 -5
- package/dist/modules/skills/loader.js +7 -1
- package/dist/modules/skills/module.js +2 -1
- package/dist/modules/updater/changelog-reader.js +94 -0
- package/dist/modules/updater/dev-detect.js +17 -0
- package/dist/modules/updater/index.js +1 -0
- package/dist/modules/updater/module.js +14 -3
- package/dist/output/bus.js +32 -0
- package/dist/output/channel.js +233 -0
- package/dist/output/format.js +14 -0
- package/dist/output/index.js +7 -0
- package/dist/output/json-sink.js +22 -0
- package/dist/output/machine.js +8 -0
- package/dist/output/session-sink.js +27 -0
- package/dist/output/types.js +1 -0
- package/dist/tools/approve.js +6 -2
- package/dist/tools/attach-image.js +11 -11
- package/dist/tools/auto-fixer.js +198 -0
- package/dist/tools/bash.js +142 -89
- package/dist/tools/chunk-query.js +10 -6
- package/dist/tools/download-file.js +1 -1
- package/dist/tools/edit-file.js +20 -2
- package/dist/tools/executor.js +54 -9
- package/dist/tools/glob-tool.js +7 -0
- package/dist/tools/grep-tool.js +15 -1
- package/dist/tools/index.js +3 -1
- package/dist/tools/list-dir.js +3 -1
- package/dist/tools/load-skill.js +2 -1
- package/dist/tools/mcp-call.js +1 -1
- package/dist/tools/move-file.js +5 -4
- package/dist/tools/path-utils.js +7 -0
- package/dist/tools/pipeline-run.js +1 -1
- package/dist/tools/prompt-io.js +28 -0
- package/dist/tools/question.js +12 -12
- package/dist/tools/scope-request.js +91 -0
- package/dist/tools/session-info.js +44 -0
- package/dist/tools/set-thinking.js +71 -0
- package/dist/tools/subagent.js +50 -9
- package/dist/tools/syntax-validator.js +177 -0
- package/dist/tools/user-input.js +16 -9
- package/dist/tools/write-file.js +17 -1
- package/dist/ui/diff.js +10 -0
- package/dist/ui/line-editor.js +179 -26
- package/dist/ui/line-math.js +20 -3
- package/dist/ui/md-formatter.js +100 -10
- package/dist/ui/output.js +5 -4
- package/dist/ui/plan-view.js +2 -7
- package/dist/ui/renderer.js +89 -85
- package/dist/ui/spinner.js +14 -4
- package/dist/utils/error.js +4 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/retry.js +17 -0
- package/dist/utils/sleep.js +23 -0
- package/dist/utils/truncate.js +9 -0
- 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 {
|
|
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 {
|
|
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) =>
|
|
21
|
-
warn: (m) =>
|
|
22
|
-
error: (m) =>
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
config
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22
|
+
getDefaultChannel().writeLine(t("cli.plugins.none"));
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
24
25
|
const mmaVersion = readMmaVersion();
|
|
25
|
-
|
|
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
|
-
|
|
31
|
+
getDefaultChannel().writeLine(` ${builtin} ${plugin.name} v${version} [${origin}]`);
|
|
31
32
|
}
|
|
32
33
|
if (!all) {
|
|
33
|
-
|
|
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
|
+
}
|