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
|
@@ -4,10 +4,14 @@ import { renderTable } from "../ui/table";
|
|
|
4
4
|
import { t } from "../i18n/index";
|
|
5
5
|
import { runSetup } from "./setup";
|
|
6
6
|
import { saveConfig } from "../config/config";
|
|
7
|
+
import { HOSTED_BASE_URLS } from "../modules/providers/presets";
|
|
8
|
+
import { budgetBreakdownLines, setBudgetShare } from "../config/budget";
|
|
7
9
|
import { getMessageText } from "../llm/provider";
|
|
10
|
+
import { estimateTokens } from "../llm/token-counter";
|
|
8
11
|
import { join, dirname } from "path";
|
|
9
|
-
import { homedir } from "os";
|
|
10
12
|
import { existsSync } from "fs";
|
|
13
|
+
import { runMapAction } from "../modules/indexer/map-command";
|
|
14
|
+
import { errMsg } from "../utils";
|
|
11
15
|
const version = readMmaVersion();
|
|
12
16
|
export const COMMAND_GROUPS = {
|
|
13
17
|
help: "general",
|
|
@@ -24,6 +28,7 @@ export const COMMAND_GROUPS = {
|
|
|
24
28
|
reload: "agent",
|
|
25
29
|
wizard: "agent",
|
|
26
30
|
sysprompt: "agent",
|
|
31
|
+
map: "agent",
|
|
27
32
|
lsp: "agent",
|
|
28
33
|
sessions: "session",
|
|
29
34
|
new: "session",
|
|
@@ -58,18 +63,18 @@ function registerBuiltinCommands(ctx) {
|
|
|
58
63
|
description: t("repl.clear"),
|
|
59
64
|
usage: t("repl.clear_usage"),
|
|
60
65
|
action: () => {
|
|
61
|
-
|
|
66
|
+
ctx.output.clearScreen();
|
|
62
67
|
},
|
|
63
68
|
});
|
|
64
69
|
}
|
|
65
|
-
function
|
|
70
|
+
function registerRunCommands(ctx) {
|
|
66
71
|
ctx.registerCommand({
|
|
67
72
|
name: "run",
|
|
68
73
|
description: t("repl.run"),
|
|
69
74
|
usage: t("repl.run_usage"),
|
|
70
75
|
action: async (args) => {
|
|
71
76
|
if (args.length === 0) {
|
|
72
|
-
|
|
77
|
+
ctx.output.writeLine(t("repl.run_usage"));
|
|
73
78
|
return;
|
|
74
79
|
}
|
|
75
80
|
const prompt = args.join(" ");
|
|
@@ -84,7 +89,7 @@ function registerMmaCommands(ctx) {
|
|
|
84
89
|
action: async (args) => {
|
|
85
90
|
const source = args.join(" ");
|
|
86
91
|
if (!source) {
|
|
87
|
-
|
|
92
|
+
ctx.output.writeLine(t("repl.image_usage"));
|
|
88
93
|
return;
|
|
89
94
|
}
|
|
90
95
|
try {
|
|
@@ -96,7 +101,7 @@ function registerMmaCommands(ctx) {
|
|
|
96
101
|
if (source.toLowerCase() === "clipboard") {
|
|
97
102
|
const clipBuf = await readClipboardImage();
|
|
98
103
|
if (!clipBuf) {
|
|
99
|
-
|
|
104
|
+
ctx.output.writeLine(pc.yellow(t("image.clipboard_empty")));
|
|
100
105
|
return;
|
|
101
106
|
}
|
|
102
107
|
const { bufferToDataUrl } = await import("../llm/image-utils");
|
|
@@ -112,7 +117,7 @@ function registerMmaCommands(ctx) {
|
|
|
112
117
|
else {
|
|
113
118
|
const absPath = resolve(process.cwd(), source);
|
|
114
119
|
if (!existsSync(absPath)) {
|
|
115
|
-
|
|
120
|
+
ctx.output.writeLine(pc.red(t("image.not_found", { path: source })), "stderr");
|
|
116
121
|
return;
|
|
117
122
|
}
|
|
118
123
|
const result = await loadFileAsDataUrl(absPath);
|
|
@@ -121,7 +126,7 @@ function registerMmaCommands(ctx) {
|
|
|
121
126
|
}
|
|
122
127
|
const contextManager = ctx.agent.contextManager;
|
|
123
128
|
if (!contextManager) {
|
|
124
|
-
|
|
129
|
+
ctx.output.writeLine(pc.red(t("image.no_context")), "stderr");
|
|
125
130
|
return;
|
|
126
131
|
}
|
|
127
132
|
contextManager.addPendingImage({
|
|
@@ -129,76 +134,72 @@ function registerMmaCommands(ctx) {
|
|
|
129
134
|
image_url: { url: dataUrl },
|
|
130
135
|
});
|
|
131
136
|
const sizeKb = Math.round((dataUrl.length * 3) / 4 / 1024);
|
|
132
|
-
|
|
137
|
+
ctx.output.writeLine(pc.green(t("image.attached", { source: label, size: `${sizeKb} KB` })));
|
|
133
138
|
}
|
|
134
139
|
catch (err) {
|
|
135
|
-
|
|
140
|
+
ctx.output.writeLine(pc.red(t("image.error", { message: err.message })), "stderr");
|
|
136
141
|
}
|
|
137
142
|
},
|
|
138
143
|
});
|
|
144
|
+
}
|
|
145
|
+
function registerConfigCommands(ctx) {
|
|
139
146
|
ctx.registerCommand({
|
|
140
147
|
name: "config",
|
|
141
148
|
description: t("repl.config"),
|
|
142
149
|
usage: t("repl.config_usage"),
|
|
143
|
-
action: () => {
|
|
150
|
+
action: (args) => {
|
|
151
|
+
// Subcommand routing: `/config migrate`. A separate registration with
|
|
152
|
+
// a space in the name was unreachable — the dispatcher splits input on
|
|
153
|
+
// whitespace, so "/config migrate" always resolved to plain "config".
|
|
154
|
+
if ((args[0] ?? "").trim().toLowerCase() === "migrate") {
|
|
155
|
+
return runConfigMigrate(ctx);
|
|
156
|
+
}
|
|
144
157
|
const cfg = ctx.config;
|
|
145
158
|
const ctxW = cfg.contextWindow;
|
|
146
159
|
const sys = Math.floor(ctxW * cfg.contextBudget.systemPrompt);
|
|
147
160
|
const res = Math.floor(ctxW * cfg.contextBudget.responseReserve);
|
|
148
|
-
|
|
161
|
+
ctx.output.writeLine(`${t("repl.model")} ${cfg.model}`);
|
|
149
162
|
const providers = ctx.agent.listProviders();
|
|
150
163
|
const active = providers.find((p) => p.active);
|
|
151
164
|
if (active) {
|
|
152
|
-
|
|
165
|
+
ctx.output.writeLine(`${t("repl.provider")} ${active.label} (${pc.dim(active.type)}) → ${pc.dim(active.baseUrl)}`);
|
|
153
166
|
}
|
|
154
167
|
else {
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
168
|
+
ctx.output.writeLine(`${t("repl.provider")} ${cfg.provider.type} → ${cfg.provider.baseUrl}`);
|
|
169
|
+
}
|
|
170
|
+
ctx.output.writeLine(`${t("repl.context")} ${ctxW} (sys:${sys} res:${res} hist:${ctxW - sys - res})`);
|
|
171
|
+
ctx.output.writeLine(`${t("repl.max_iters")} ${cfg.maxToolIterations}`);
|
|
172
|
+
ctx.output.writeLine(`${t("repl.stuck_thresh")} ${cfg.stuckThreshold}`);
|
|
173
|
+
ctx.output.writeLine(`${t("repl.reasoning_label")} ${cfg.showReasoning ? pc.green(t("repl.show")) : pc.dim(t("repl.hide"))}`);
|
|
174
|
+
ctx.output.writeLine(`${t("repl.log_level")} ${cfg.logLevel}`);
|
|
175
|
+
ctx.output.writeLine(`${t("repl.locale")} ${cfg.locale}`);
|
|
163
176
|
const meta = ctx.sessionManager?.getActiveMeta();
|
|
164
177
|
if (meta) {
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
});
|
|
169
|
-
ctx.registerCommand({
|
|
170
|
-
name: "config migrate",
|
|
171
|
-
description: t("cli.migrate_config"),
|
|
172
|
-
action: async () => {
|
|
173
|
-
const { hasDomainFiles } = await import("../config/domains");
|
|
174
|
-
const { loadConfig: loadCfg } = await import("../config/config");
|
|
175
|
-
const configDir = ctx.configDir;
|
|
176
|
-
const configPath = join(configDir, "config.json");
|
|
177
|
-
if (hasDomainFiles(configDir)) {
|
|
178
|
-
console.log(pc.yellow(t("config.migrate_no_legacy")));
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
if (!existsSync(configPath)) {
|
|
182
|
-
console.log(pc.yellow(t("config.migrate_no_legacy")));
|
|
183
|
-
return;
|
|
178
|
+
ctx.output.writeLine(`${t("repl.session_label")} ${meta.name} (${meta.id}) — ${meta.messageCount} msgs`);
|
|
184
179
|
}
|
|
185
|
-
console.log(t("config.migrate_start"));
|
|
186
|
-
const { config } = loadCfg({ configDir, projectConfigPath: join(configDir, ".mmrc") });
|
|
187
|
-
saveConfig(config, configPath, configDir);
|
|
188
|
-
const { renameSync, readdirSync } = await import("fs");
|
|
189
|
-
renameSync(configPath, configPath + ".bak");
|
|
190
|
-
const domainFiles = readdirSync(join(configDir, "config")).filter((f) => f.endsWith(".json"));
|
|
191
|
-
console.log(pc.green(t("config.migrate_done", { count: String(domainFiles.length) })));
|
|
192
180
|
},
|
|
193
181
|
});
|
|
194
182
|
ctx.registerCommand({
|
|
195
183
|
name: "reasoning",
|
|
196
184
|
description: t("repl.reasoning"),
|
|
197
185
|
usage: t("repl.reasoning_usage"),
|
|
198
|
-
action: () => {
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
186
|
+
action: (args) => {
|
|
187
|
+
const level = args[0]?.trim().toLowerCase();
|
|
188
|
+
const VALID_LEVELS = ["auto", "none", "low", "medium", "high", "max"];
|
|
189
|
+
if (!level) {
|
|
190
|
+
// Toggle show/hide reasoning content
|
|
191
|
+
ctx.config.showReasoning = !ctx.config.showReasoning;
|
|
192
|
+
const status = ctx.config.showReasoning ? pc.green(t("repl.show")) : pc.dim(t("repl.hide"));
|
|
193
|
+
ctx.output.writeLine(t("repl.reasoning_status", { status }));
|
|
194
|
+
}
|
|
195
|
+
else if (VALID_LEVELS.includes(level)) {
|
|
196
|
+
// Set reasoning effort level
|
|
197
|
+
ctx.agent.setReasoningLevel(level);
|
|
198
|
+
ctx.output.writeLine(pc.green(t("repl.reasoning_level_set", { level })));
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
ctx.output.writeLine(pc.yellow(t("repl.reasoning_invalid_level", { level, valid: VALID_LEVELS.join(", ") })));
|
|
202
|
+
}
|
|
202
203
|
},
|
|
203
204
|
});
|
|
204
205
|
ctx.registerCommand({
|
|
@@ -208,16 +209,16 @@ function registerMmaCommands(ctx) {
|
|
|
208
209
|
action: () => {
|
|
209
210
|
const providers = ctx.agent.listProviders();
|
|
210
211
|
const active = providers.find((p) => p.active);
|
|
211
|
-
|
|
212
|
+
ctx.output.writeLine(`${t("repl.model")} ${ctx.config.model}`);
|
|
212
213
|
if (active) {
|
|
213
|
-
|
|
214
|
+
ctx.output.writeLine(`${t("repl.provider")} ${active.label} (${pc.dim(active.type)}) @ ${pc.dim(active.baseUrl)}`);
|
|
214
215
|
}
|
|
215
216
|
else {
|
|
216
|
-
|
|
217
|
+
ctx.output.writeLine(`${t("repl.provider")} ${ctx.config.provider.type} @ ${ctx.config.provider.baseUrl}`);
|
|
217
218
|
}
|
|
218
219
|
const meta = ctx.sessionManager?.getActiveMeta();
|
|
219
220
|
if (meta) {
|
|
220
|
-
|
|
221
|
+
ctx.output.writeLine(`${t("repl.session_label")} ${meta.name} (${meta.id}) — ${meta.messageCount} messages`);
|
|
221
222
|
}
|
|
222
223
|
},
|
|
223
224
|
});
|
|
@@ -227,10 +228,10 @@ function registerMmaCommands(ctx) {
|
|
|
227
228
|
aliases: ["setup"],
|
|
228
229
|
usage: t("repl.wizard_usage"),
|
|
229
230
|
action: async () => {
|
|
230
|
-
|
|
231
|
+
ctx.output.writeLine(pc.yellow(t("repl.wizard_running")));
|
|
231
232
|
await ctx.withExclusiveInput(async () => {
|
|
232
233
|
const answers = await runSetup(ctx.rl);
|
|
233
|
-
const configPath = join(
|
|
234
|
+
const configPath = join(ctx.configDir, "config.json");
|
|
234
235
|
ctx.config.provider.type = answers.provider;
|
|
235
236
|
ctx.config.provider.baseUrl = answers.apiBase;
|
|
236
237
|
ctx.config.provider.apiKey = answers.apiKey;
|
|
@@ -240,7 +241,7 @@ function registerMmaCommands(ctx) {
|
|
|
240
241
|
ctx.config.locale = answers.locale;
|
|
241
242
|
saveConfig(ctx.config, configPath, dirname(configPath));
|
|
242
243
|
await ctx.agent.reconfigure(ctx.config);
|
|
243
|
-
|
|
244
|
+
ctx.output.writeLine(pc.green(t("cli.config_saved")));
|
|
244
245
|
});
|
|
245
246
|
},
|
|
246
247
|
});
|
|
@@ -250,173 +251,268 @@ function registerMmaCommands(ctx) {
|
|
|
250
251
|
usage: t("repl.sysprompt_desc"),
|
|
251
252
|
action: () => {
|
|
252
253
|
const info = ctx.agent.getSystemPromptInfo();
|
|
253
|
-
|
|
254
|
-
|
|
254
|
+
ctx.output.writeLine(pc.bold(t("repl.sysprompt_tokens", { count: String(info.tokenCount) })));
|
|
255
|
+
ctx.output.writeLine(pc.dim("─".repeat(60)));
|
|
255
256
|
for (const line of info.text.split("\n")) {
|
|
256
|
-
|
|
257
|
+
ctx.output.writeLine(line);
|
|
257
258
|
}
|
|
258
|
-
|
|
259
|
+
ctx.output.writeLine(pc.dim("─".repeat(60)));
|
|
259
260
|
if (info.excluded.length > 0) {
|
|
260
|
-
|
|
261
|
+
ctx.output.writeLine(pc.yellow(t("repl.excluded_blocks", { count: String(info.excluded.length) })));
|
|
261
262
|
for (const e of info.excluded) {
|
|
262
|
-
|
|
263
|
+
ctx.output.writeLine(pc.dim(` - ${e.slice(0, 80)}${e.length > 80 ? "..." : ""}`));
|
|
263
264
|
}
|
|
264
265
|
}
|
|
265
266
|
},
|
|
266
267
|
});
|
|
267
268
|
ctx.registerCommand({
|
|
268
|
-
name: "
|
|
269
|
-
description: t("repl.
|
|
270
|
-
usage: t("repl.
|
|
269
|
+
name: "map",
|
|
270
|
+
description: t("repl.map"),
|
|
271
|
+
usage: t("repl.map_usage"),
|
|
271
272
|
action: async (args) => {
|
|
272
|
-
const
|
|
273
|
-
if (!
|
|
274
|
-
|
|
275
|
-
if (providers.length === 0) {
|
|
276
|
-
console.log(`${t("repl.provider_current")} ${ctx.config.provider.type}`);
|
|
277
|
-
console.log(` ${ctx.config.provider.baseUrl}`);
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
console.log(t("repl.provider_current"));
|
|
281
|
-
for (const p of providers) {
|
|
282
|
-
const marker = p.active ? pc.green("* ") : " ";
|
|
283
|
-
console.log(` ${marker}${p.label} (${pc.dim(p.type)}) ${pc.dim(p.baseUrl)}`);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
273
|
+
const indexer = ctx.agent.getModule("indexer");
|
|
274
|
+
if (!indexer) {
|
|
275
|
+
ctx.output.writeLine(pc.yellow(t("indexer.not_indexed")));
|
|
286
276
|
return;
|
|
287
277
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
278
|
+
const action = args[0] || "summary";
|
|
279
|
+
const query = args.slice(1).join(" ");
|
|
280
|
+
const result = await runMapAction(indexer, action, query);
|
|
281
|
+
ctx.output.writeLine(result.output);
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
function registerProviderCommands(ctx) {
|
|
286
|
+
const list = () => {
|
|
287
|
+
const providers = ctx.agent.listProviders();
|
|
288
|
+
if (providers.length === 0) {
|
|
289
|
+
ctx.output.writeLine(`${t("repl.provider_current")} ${ctx.config.provider.type}`);
|
|
290
|
+
ctx.output.writeLine(` ${ctx.config.provider.baseUrl}`);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
ctx.output.writeLine(t("repl.provider_current"));
|
|
294
|
+
for (const p of providers) {
|
|
295
|
+
const marker = p.active ? pc.green("* ") : " ";
|
|
296
|
+
ctx.output.writeLine(` ${marker}${p.label} (${pc.dim(p.type)}) ${pc.dim(p.baseUrl)}`);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
const use = async (args) => {
|
|
300
|
+
const name = args[0];
|
|
301
|
+
if (!name) {
|
|
302
|
+
ctx.output.writeLine(t("repl.provider_usage"));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
await ctx.agent.setProvider(name);
|
|
307
|
+
ctx.refreshModelCache();
|
|
308
|
+
ctx.output.writeLine(pc.green(t("repl.provider_set", { name })));
|
|
309
|
+
}
|
|
310
|
+
catch (e) {
|
|
311
|
+
ctx.output.writeLine(pc.red(e.message), "stderr");
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
const add = async (args) => {
|
|
315
|
+
const flags = {};
|
|
316
|
+
let name;
|
|
317
|
+
for (let i = 0; i < args.length; i++) {
|
|
318
|
+
const arg = args[i];
|
|
319
|
+
if (arg.startsWith("--")) {
|
|
320
|
+
const value = args[i + 1];
|
|
321
|
+
if (value === undefined || value.startsWith("--")) {
|
|
322
|
+
ctx.output.writeLine(pc.red(t("repl.provider_add_missing_value", { flag: arg })), "stderr");
|
|
292
323
|
return;
|
|
293
324
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
325
|
+
flags[arg.slice(2)] = value;
|
|
326
|
+
i++;
|
|
327
|
+
}
|
|
328
|
+
else if (name === undefined) {
|
|
329
|
+
name = arg;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (!name) {
|
|
333
|
+
ctx.output.writeLine(t("repl.provider_usage"));
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const entries = Array.isArray(ctx.config.provider.entries) ? ctx.config.provider.entries : [];
|
|
337
|
+
if (entries.length === 0) {
|
|
338
|
+
entries.push({
|
|
339
|
+
type: ctx.config.provider.type,
|
|
340
|
+
label: ctx.config.provider.type,
|
|
341
|
+
baseUrl: ctx.config.provider.baseUrl,
|
|
342
|
+
apiKey: ctx.config.provider.apiKey,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
const baseUrl = flags.url || HOSTED_BASE_URLS[name] || HOSTED_BASE_URLS[`opencode-${name}`] || "";
|
|
346
|
+
if (!baseUrl) {
|
|
347
|
+
ctx.output.writeLine(pc.red(t("cli.provider_no_url", { name })), "stderr");
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
entries.push({
|
|
351
|
+
type: name,
|
|
352
|
+
label: name,
|
|
353
|
+
baseUrl,
|
|
354
|
+
apiKey: flags.key,
|
|
355
|
+
...(flags.priority !== undefined ? { priority: Number(flags.priority) } : {}),
|
|
356
|
+
...(flags["context-window"] !== undefined
|
|
357
|
+
? { contextWindow: Number(flags["context-window"]) }
|
|
358
|
+
: {}),
|
|
359
|
+
...(flags.rpm !== undefined || flags.parallel !== undefined
|
|
360
|
+
? {
|
|
361
|
+
rateLimits: {
|
|
362
|
+
maxRequestsPerMinute: Number(flags.rpm ?? 0) || 60,
|
|
363
|
+
maxParallelTasks: Number(flags.parallel ?? 0) || 1,
|
|
364
|
+
},
|
|
365
|
+
}
|
|
366
|
+
: {}),
|
|
367
|
+
});
|
|
368
|
+
ctx.config.provider.entries = entries;
|
|
369
|
+
ctx.config.provider.active = ctx.config.provider.active || ctx.config.provider.type;
|
|
370
|
+
const configPath = join(ctx.configDir, "config.json");
|
|
371
|
+
saveConfig(ctx.config, configPath, dirname(configPath));
|
|
372
|
+
await ctx.agent.reconfigure(ctx.config);
|
|
373
|
+
ctx.output.writeLine(pc.green(t("cli.provider_added", { name })));
|
|
374
|
+
ctx.output.writeLine(t("repl.provider_switch_hint", { name }));
|
|
375
|
+
};
|
|
376
|
+
const handlers = {
|
|
377
|
+
list,
|
|
378
|
+
use,
|
|
379
|
+
add,
|
|
380
|
+
};
|
|
381
|
+
ctx.registerCommand({
|
|
382
|
+
name: "provider",
|
|
383
|
+
description: t("repl.provider_list"),
|
|
384
|
+
usage: t("repl.provider_usage"),
|
|
385
|
+
action: async (args) => {
|
|
386
|
+
const subcmd = args[0];
|
|
387
|
+
const handler = subcmd ? handlers[subcmd] : handlers.list;
|
|
388
|
+
if (!handler) {
|
|
389
|
+
ctx.output.writeLine(t("repl.provider_usage"));
|
|
302
390
|
return;
|
|
303
391
|
}
|
|
304
|
-
|
|
392
|
+
await handler(args.slice(1));
|
|
305
393
|
},
|
|
306
394
|
});
|
|
395
|
+
const listModels = async () => {
|
|
396
|
+
ctx.output.writeLine(`${t("repl.model_current")} ${ctx.config.model}`);
|
|
397
|
+
const { OpenAICompatProvider } = await import("../llm/openai-compat");
|
|
398
|
+
const provider = new OpenAICompatProvider({
|
|
399
|
+
model: ctx.config.model,
|
|
400
|
+
baseUrl: ctx.config.provider.baseUrl,
|
|
401
|
+
apiKey: ctx.config.provider.apiKey,
|
|
402
|
+
contextWindow: ctx.config.contextWindow,
|
|
403
|
+
});
|
|
404
|
+
const { Spinner } = await import("../ui/spinner");
|
|
405
|
+
const s = new Spinner({ channel: ctx.output });
|
|
406
|
+
s.start(t("cli.fetching_models"));
|
|
407
|
+
try {
|
|
408
|
+
const models = await provider.listModels();
|
|
409
|
+
s.stop();
|
|
410
|
+
if (models.length > 0) {
|
|
411
|
+
ctx.refreshModelCache();
|
|
412
|
+
ctx.output.writeLine(t("cli.available_models"));
|
|
413
|
+
const { getCertMark } = await import("../modules/certification/manifest");
|
|
414
|
+
for (const m of models) {
|
|
415
|
+
const marker = m === ctx.config.model ? pc.green("* ") : " ";
|
|
416
|
+
const mark = getCertMark(m, ctx.config.provider.baseUrl, version, process.cwd());
|
|
417
|
+
const cert = mark === "certified" ? pc.green("✔") : mark === "stale" ? pc.yellow("○") : pc.dim("·");
|
|
418
|
+
const label = mark === "certified"
|
|
419
|
+
? ` ${pc.green(t("cli.cert_label"))}`
|
|
420
|
+
: mark === "stale"
|
|
421
|
+
? ` ${pc.yellow(t("cli.cert_stale_label"))}`
|
|
422
|
+
: "";
|
|
423
|
+
ctx.output.writeLine(` ${marker}${cert} ${m}${label}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
ctx.output.writeLine(t("cli.no_models_found"));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
catch (err) {
|
|
431
|
+
s.stop();
|
|
432
|
+
ctx.output.writeLine(t("cli.model_fetch_failed", { error: String(err) }), "stderr");
|
|
433
|
+
}
|
|
434
|
+
ctx.output.writeLine(t("cli.model_hint"));
|
|
435
|
+
};
|
|
436
|
+
const useModel = async (args) => {
|
|
437
|
+
const name = args[0];
|
|
438
|
+
if (!name) {
|
|
439
|
+
ctx.output.writeLine(t("repl.model_usage"));
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
ctx.config.model = name;
|
|
443
|
+
const configPath = join(ctx.configDir, "config.json");
|
|
444
|
+
saveConfig(ctx.config, configPath, dirname(configPath));
|
|
445
|
+
await ctx.agent.reconfigure(ctx.config);
|
|
446
|
+
ctx.output.writeLine(pc.green(t("repl.model_set", { name })));
|
|
447
|
+
};
|
|
448
|
+
const modelHandlers = {
|
|
449
|
+
list: listModels,
|
|
450
|
+
use: useModel,
|
|
451
|
+
};
|
|
307
452
|
ctx.registerCommand({
|
|
308
453
|
name: "model",
|
|
309
454
|
description: t("repl.model_list"),
|
|
310
455
|
usage: t("repl.model_usage"),
|
|
311
456
|
action: async (args) => {
|
|
312
457
|
const subcmd = args[0];
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const provider = new OpenAICompatProvider({
|
|
317
|
-
model: ctx.config.model,
|
|
318
|
-
baseUrl: ctx.config.provider.baseUrl,
|
|
319
|
-
apiKey: ctx.config.provider.apiKey,
|
|
320
|
-
contextWindow: ctx.config.contextWindow,
|
|
321
|
-
});
|
|
322
|
-
const { Spinner } = await import("../ui/spinner");
|
|
323
|
-
const s = new Spinner();
|
|
324
|
-
s.start(t("cli.fetching_models"));
|
|
325
|
-
try {
|
|
326
|
-
const models = await provider.listModels();
|
|
327
|
-
s.stop();
|
|
328
|
-
if (models.length > 0) {
|
|
329
|
-
ctx.refreshModelCache();
|
|
330
|
-
console.log(t("cli.available_models"));
|
|
331
|
-
const { getCertMark } = await import("../modules/certification/manifest");
|
|
332
|
-
for (const m of models) {
|
|
333
|
-
const marker = m === ctx.config.model ? pc.green("* ") : " ";
|
|
334
|
-
const mark = getCertMark(m, ctx.config.provider.baseUrl, version, process.cwd());
|
|
335
|
-
const cert = mark === "certified"
|
|
336
|
-
? pc.green("✔")
|
|
337
|
-
: mark === "stale"
|
|
338
|
-
? pc.yellow("○")
|
|
339
|
-
: pc.dim("·");
|
|
340
|
-
console.log(` ${marker}${cert} ${m}`);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
else {
|
|
344
|
-
console.log(t("cli.no_models_found"));
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
catch (err) {
|
|
348
|
-
s.stop();
|
|
349
|
-
console.log(t("cli.model_fetch_failed", { error: String(err) }));
|
|
350
|
-
}
|
|
351
|
-
console.log(t("cli.model_hint"));
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
if (subcmd === "use") {
|
|
355
|
-
const name = args[1];
|
|
356
|
-
if (!name) {
|
|
357
|
-
console.log(t("repl.model_usage"));
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
ctx.config.model = name;
|
|
361
|
-
const configPath = join(homedir(), ".mma", "config.json");
|
|
362
|
-
saveConfig(ctx.config, configPath, dirname(configPath));
|
|
363
|
-
await ctx.agent.reconfigure(ctx.config);
|
|
364
|
-
console.log(pc.green(t("repl.model_set", { name })));
|
|
458
|
+
const handler = subcmd ? modelHandlers[subcmd] : modelHandlers.list;
|
|
459
|
+
if (!handler) {
|
|
460
|
+
ctx.output.writeLine(t("repl.model_usage"));
|
|
365
461
|
return;
|
|
366
462
|
}
|
|
367
|
-
|
|
463
|
+
await handler(args.slice(1));
|
|
368
464
|
},
|
|
369
465
|
});
|
|
370
466
|
ctx.registerCommand({
|
|
371
467
|
name: "context",
|
|
372
468
|
description: t("cli.manage_context"),
|
|
373
|
-
usage: "
|
|
469
|
+
usage: t("repl.context_usage"),
|
|
374
470
|
action: async (args) => {
|
|
471
|
+
const save = async () => {
|
|
472
|
+
const configPath = join(ctx.configDir, "config.json");
|
|
473
|
+
saveConfig(ctx.config, configPath, dirname(configPath));
|
|
474
|
+
await ctx.agent.reconfigure(ctx.config);
|
|
475
|
+
};
|
|
375
476
|
if (args.length === 0) {
|
|
376
|
-
|
|
377
|
-
|
|
477
|
+
for (const line of budgetBreakdownLines(ctx.config))
|
|
478
|
+
ctx.output.writeLine(line);
|
|
479
|
+
ctx.output.writeLine(pc.dim(t("repl.context_usage")));
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (args[0] === "system" || args[0] === "reserve") {
|
|
483
|
+
const key = args[0];
|
|
484
|
+
const value = Number(args[1]);
|
|
485
|
+
if (setBudgetShare(ctx.config, key, value) !== null) {
|
|
486
|
+
ctx.output.writeLine(pc.yellow(t("cli.context_invalid_fraction")));
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
await save();
|
|
490
|
+
ctx.output.writeLine(pc.green(t("cli.context_fraction_set", { key, value })));
|
|
378
491
|
return;
|
|
379
492
|
}
|
|
380
493
|
const size = parseInt(args[0], 10);
|
|
381
494
|
if (isNaN(size) || size < 1024) {
|
|
382
|
-
|
|
495
|
+
ctx.output.writeLine(t("cli.invalid_context_size"));
|
|
383
496
|
return;
|
|
384
497
|
}
|
|
385
498
|
ctx.config.contextWindow = size;
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
await ctx.agent.reconfigure(ctx.config);
|
|
389
|
-
console.log(pc.green(t("cli.context_set", { size })));
|
|
499
|
+
await save();
|
|
500
|
+
ctx.output.writeLine(pc.green(t("cli.context_set", { size })));
|
|
390
501
|
},
|
|
391
502
|
});
|
|
503
|
+
}
|
|
504
|
+
function registerAgentCommands(ctx) {
|
|
392
505
|
ctx.registerCommand({
|
|
393
506
|
name: "reload",
|
|
394
507
|
description: t("repl.reload"),
|
|
395
508
|
usage: t("repl.reload_usage"),
|
|
396
509
|
action: async () => {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
ctx.
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const { join } = await import("path");
|
|
404
|
-
const configDir = ctx.configDir;
|
|
405
|
-
const baseDir = ctx.baseDir;
|
|
406
|
-
const projectConfigPath = join(baseDir, ".mmrc");
|
|
407
|
-
const { config: freshConfig } = loadConfig({ configDir, projectConfigPath });
|
|
408
|
-
Object.assign(ctx.config, freshConfig);
|
|
409
|
-
const { bootstrap } = await import("../core/bootstrap");
|
|
410
|
-
const result = await bootstrap(configDir, baseDir, false, false);
|
|
411
|
-
ctx.agent = result.agent;
|
|
412
|
-
ctx.sessionManager = result.sessionManager;
|
|
413
|
-
ctx.skillsModule = result.skillsModule;
|
|
414
|
-
ctx.pluginManager = result.pluginManager;
|
|
415
|
-
ctx.setupCompleter();
|
|
416
|
-
console.log(pc.green(t("repl.reloaded")));
|
|
417
|
-
console.log(`${t("repl.model")} ${ctx.config.model}`);
|
|
418
|
-
console.log(`${t("repl.context")} ${ctx.config.contextWindow}`);
|
|
419
|
-
console.log(`${t("repl.provider")} ${ctx.config.provider.type} @ ${ctx.config.provider.baseUrl}`);
|
|
510
|
+
ctx.output.writeLine(pc.yellow(t("repl.reloading")));
|
|
511
|
+
await ctx.reload();
|
|
512
|
+
ctx.output.writeLine(pc.green(t("repl.reloaded")));
|
|
513
|
+
ctx.output.writeLine(`${t("repl.model")} ${ctx.config.model}`);
|
|
514
|
+
ctx.output.writeLine(`${t("repl.context")} ${ctx.config.contextWindow}`);
|
|
515
|
+
ctx.output.writeLine(`${t("repl.provider")} ${ctx.config.provider.type} @ ${ctx.config.provider.baseUrl}`);
|
|
420
516
|
},
|
|
421
517
|
});
|
|
422
518
|
ctx.registerCommand({
|
|
@@ -425,18 +521,18 @@ function registerMmaCommands(ctx) {
|
|
|
425
521
|
usage: t("repl.plugins_usage"),
|
|
426
522
|
action: (args) => {
|
|
427
523
|
if (!ctx.pluginManager) {
|
|
428
|
-
|
|
524
|
+
ctx.output.writeLine(t("cli.plugins.none"));
|
|
429
525
|
return;
|
|
430
526
|
}
|
|
431
527
|
const showAll = args.includes("--all");
|
|
432
528
|
const infos = ctx.pluginManager.getPluginInfos();
|
|
433
529
|
const filtered = showAll ? infos : infos.filter(({ plugin }) => !plugin.isBuiltin);
|
|
434
530
|
if (filtered.length === 0) {
|
|
435
|
-
|
|
531
|
+
ctx.output.writeLine(t("cli.plugins.none"));
|
|
436
532
|
return;
|
|
437
533
|
}
|
|
438
534
|
const mmaVersion = version;
|
|
439
|
-
|
|
535
|
+
ctx.output.writeLine(t("cli.plugins.header", {
|
|
440
536
|
count: String(filtered.length),
|
|
441
537
|
mma: mmaVersion,
|
|
442
538
|
}));
|
|
@@ -444,16 +540,18 @@ function registerMmaCommands(ctx) {
|
|
|
444
540
|
const v = plugin.version || "-";
|
|
445
541
|
const origin = source || "builtin";
|
|
446
542
|
const mark = plugin.isBuiltin ? t("cli.plugins.builtin_mark") : " ";
|
|
447
|
-
|
|
543
|
+
ctx.output.writeLine(` ${mark} ${plugin.name} v${v} [${origin}]`);
|
|
448
544
|
}
|
|
449
545
|
if (!showAll) {
|
|
450
546
|
const builtinCount = infos.filter(({ plugin }) => plugin.isBuiltin).length;
|
|
451
547
|
if (builtinCount > 0) {
|
|
452
|
-
|
|
548
|
+
ctx.output.writeLine(pc.dim(` (${builtinCount} builtin hidden — /plugins --all to show)`));
|
|
453
549
|
}
|
|
454
550
|
}
|
|
455
551
|
},
|
|
456
552
|
});
|
|
553
|
+
}
|
|
554
|
+
function registerLspCommands(ctx) {
|
|
457
555
|
ctx.registerCommand({
|
|
458
556
|
name: "lsp",
|
|
459
557
|
description: t("repl.lsp"),
|
|
@@ -463,7 +561,7 @@ function registerMmaCommands(ctx) {
|
|
|
463
561
|
// Access LSP module via the agent's module registry
|
|
464
562
|
const lspModule = ctx.agent.getModule("lsp");
|
|
465
563
|
if (!lspModule) {
|
|
466
|
-
|
|
564
|
+
ctx.output.writeLine(pc.yellow(t("repl.lsp_not_available")));
|
|
467
565
|
return;
|
|
468
566
|
}
|
|
469
567
|
switch (subcommand) {
|
|
@@ -471,57 +569,62 @@ function registerMmaCommands(ctx) {
|
|
|
471
569
|
const disabled = lspModule.getDisabledServers();
|
|
472
570
|
const failures = lspModule.getFailureCounts();
|
|
473
571
|
const config = ctx.config.lsp;
|
|
474
|
-
|
|
475
|
-
|
|
572
|
+
ctx.output.writeLine(pc.bold(t("repl.lsp_status_header")));
|
|
573
|
+
ctx.output.writeLine(`${t("repl.lsp_enabled")} ${config?.enabled ? pc.green("yes") : pc.red("no")}`);
|
|
476
574
|
if (disabled.length > 0) {
|
|
477
|
-
|
|
575
|
+
ctx.output.writeLine(pc.yellow(`${t("repl.lsp_disabled_servers")} ${disabled.join(", ")}`));
|
|
478
576
|
}
|
|
479
577
|
if (failures.size > 0) {
|
|
480
|
-
|
|
578
|
+
ctx.output.writeLine(pc.dim(t("repl.lsp_failure_counts")));
|
|
481
579
|
for (const [server, count] of failures) {
|
|
482
|
-
|
|
580
|
+
ctx.output.writeLine(pc.dim(` ${server}: ${count}`));
|
|
483
581
|
}
|
|
484
582
|
}
|
|
485
583
|
if (disabled.length === 0 && failures.size === 0) {
|
|
486
|
-
|
|
584
|
+
ctx.output.writeLine(pc.green(t("repl.lsp_all_ok")));
|
|
487
585
|
}
|
|
488
586
|
break;
|
|
489
587
|
}
|
|
490
588
|
case "restart": {
|
|
491
|
-
|
|
589
|
+
ctx.output.writeLine(pc.yellow(t("repl.lsp_restarting")));
|
|
492
590
|
lspModule.resetDisabledServers();
|
|
493
|
-
|
|
591
|
+
ctx.output.writeLine(pc.green(t("repl.lsp_restarted")));
|
|
494
592
|
break;
|
|
495
593
|
}
|
|
496
594
|
case "check": {
|
|
497
595
|
const path = args[1];
|
|
498
596
|
if (!path) {
|
|
499
|
-
|
|
597
|
+
ctx.output.writeLine(pc.yellow(t("repl.lsp_check_usage")));
|
|
500
598
|
return;
|
|
501
599
|
}
|
|
502
|
-
|
|
503
|
-
//
|
|
600
|
+
ctx.output.writeLine(pc.dim(t("repl.lsp_checking", { path })));
|
|
601
|
+
// Запуск тула lsp_check через публичный API агента (не через приватные deps)
|
|
504
602
|
try {
|
|
505
|
-
const result = await ctx.agent.
|
|
603
|
+
const result = await ctx.agent.runTool("lsp_check", { path });
|
|
506
604
|
if (result?.output) {
|
|
507
|
-
|
|
605
|
+
ctx.output.writeLine(result.output);
|
|
508
606
|
}
|
|
509
607
|
}
|
|
510
608
|
catch (e) {
|
|
511
|
-
|
|
512
|
-
error: e instanceof Error ? e.message : String(e),
|
|
513
|
-
})));
|
|
609
|
+
ctx.output.writeLine(pc.red(t("repl.lsp_check_error", { error: errMsg(e) })), "stderr");
|
|
514
610
|
}
|
|
515
611
|
break;
|
|
516
612
|
}
|
|
517
613
|
default: {
|
|
518
|
-
|
|
614
|
+
ctx.output.writeLine(pc.dim(t("repl.lsp_usage")));
|
|
519
615
|
break;
|
|
520
616
|
}
|
|
521
617
|
}
|
|
522
618
|
},
|
|
523
619
|
});
|
|
524
620
|
}
|
|
621
|
+
function registerMmaCommands(ctx) {
|
|
622
|
+
registerRunCommands(ctx);
|
|
623
|
+
registerConfigCommands(ctx);
|
|
624
|
+
registerProviderCommands(ctx);
|
|
625
|
+
registerAgentCommands(ctx);
|
|
626
|
+
registerLspCommands(ctx);
|
|
627
|
+
}
|
|
525
628
|
function registerSessionCommands(ctx) {
|
|
526
629
|
if (!ctx.sessionManager)
|
|
527
630
|
return;
|
|
@@ -534,7 +637,7 @@ function registerSessionCommands(ctx) {
|
|
|
534
637
|
const sessions = ctx.sessionManager.list();
|
|
535
638
|
const active = ctx.sessionManager.getActive();
|
|
536
639
|
if (sessions.length === 0) {
|
|
537
|
-
|
|
640
|
+
ctx.output.writeLine(t("session.no_sessions_hint"));
|
|
538
641
|
return;
|
|
539
642
|
}
|
|
540
643
|
const rows = sessions.map((s) => [
|
|
@@ -551,9 +654,9 @@ function registerSessionCommands(ctx) {
|
|
|
551
654
|
t("session.col_updated"),
|
|
552
655
|
t("session.col_msgs"),
|
|
553
656
|
], rows)) {
|
|
554
|
-
|
|
657
|
+
ctx.output.writeLine(line);
|
|
555
658
|
}
|
|
556
|
-
|
|
659
|
+
ctx.output.writeLine(pc.dim(`\n ${t("repl.resume_hint")}`));
|
|
557
660
|
},
|
|
558
661
|
});
|
|
559
662
|
ctx.registerCommand({
|
|
@@ -565,9 +668,9 @@ function registerSessionCommands(ctx) {
|
|
|
565
668
|
const name = args.join(" ") || undefined;
|
|
566
669
|
const meta = ctx.sessionManager.create(name);
|
|
567
670
|
ctx.agent.clearContext();
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
671
|
+
ctx.output.clearScreen();
|
|
672
|
+
ctx.output.writeLine(`${t("session.created", { name: meta.name })} (${pc.dim(meta.id.slice(0, 12))})`);
|
|
673
|
+
ctx.output.writeLine(pc.dim(` ${t("session.chat_cleared")}\n`));
|
|
571
674
|
},
|
|
572
675
|
});
|
|
573
676
|
ctx.registerCommand({
|
|
@@ -579,54 +682,55 @@ function registerSessionCommands(ctx) {
|
|
|
579
682
|
const query = args.join(" ");
|
|
580
683
|
const sessions = ctx.sessionManager.list();
|
|
581
684
|
if (!query) {
|
|
582
|
-
|
|
685
|
+
ctx.output.writeLine(pc.dim(t("session.available")));
|
|
583
686
|
const active = ctx.sessionManager.getActive();
|
|
584
687
|
for (const s of sessions) {
|
|
585
688
|
const marker = s.id === active ? pc.green(" *") : " ";
|
|
586
|
-
|
|
689
|
+
ctx.output.writeLine(pc.dim(` ${marker} ${s.id.slice(0, 12)} ${s.name}`));
|
|
587
690
|
}
|
|
588
|
-
|
|
691
|
+
ctx.output.writeLine(pc.dim(`\n ${t("repl.resume_usage")}`));
|
|
589
692
|
return;
|
|
590
693
|
}
|
|
591
694
|
const match = sessions.find((s) => s.id === query ||
|
|
592
695
|
s.id.startsWith(query) ||
|
|
593
696
|
s.name.toLowerCase().includes(query.toLowerCase()));
|
|
594
697
|
if (!match) {
|
|
595
|
-
|
|
698
|
+
ctx.output.writeLine(t("session.no_match", { query }));
|
|
596
699
|
return;
|
|
597
700
|
}
|
|
598
701
|
ctx.sessionManager.setActive(match.id);
|
|
599
702
|
const history = ctx.sessionManager.loadHistory();
|
|
600
703
|
ctx.agent.setContext(history);
|
|
601
|
-
|
|
602
|
-
|
|
704
|
+
ctx.output.clearScreen();
|
|
705
|
+
ctx.output.writeLine(pc.bold(pc.green(t("session.resumed", { name: match.name }))) +
|
|
603
706
|
" " +
|
|
604
707
|
pc.dim(`(${match.id.slice(0, 12)})`) +
|
|
605
708
|
" — " +
|
|
606
709
|
match.messageCount +
|
|
607
|
-
"
|
|
608
|
-
|
|
710
|
+
" " +
|
|
711
|
+
t("repl.msgs"));
|
|
712
|
+
ctx.output.writeLine(pc.dim("─".repeat(50)));
|
|
609
713
|
if (history.length === 0) {
|
|
610
|
-
|
|
714
|
+
ctx.output.writeLine(pc.dim(t("session.no_history")));
|
|
611
715
|
}
|
|
612
716
|
else {
|
|
613
|
-
|
|
614
|
-
|
|
717
|
+
ctx.output.writeLine(pc.dim(t("session.chat_history")));
|
|
718
|
+
ctx.output.writeLine("");
|
|
615
719
|
for (const msg of history) {
|
|
616
720
|
if (msg.role === "user") {
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
721
|
+
ctx.output.writeLine(pc.cyan(t("session.user_label") + ":"));
|
|
722
|
+
ctx.output.writeLine(getMessageText(msg.content));
|
|
723
|
+
ctx.output.writeLine("");
|
|
620
724
|
}
|
|
621
725
|
else if (msg.role === "assistant") {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
726
|
+
ctx.output.writeLine(pc.green(t("session.assistant_label") + ":"));
|
|
727
|
+
ctx.output.writeLine(getMessageText(msg.content));
|
|
728
|
+
ctx.output.writeLine("");
|
|
625
729
|
}
|
|
626
730
|
}
|
|
627
731
|
}
|
|
628
|
-
|
|
629
|
-
|
|
732
|
+
ctx.output.writeLine(pc.dim("─".repeat(50)));
|
|
733
|
+
ctx.output.writeLine(pc.dim(` ${t("session.chat_loaded")}`));
|
|
630
734
|
},
|
|
631
735
|
});
|
|
632
736
|
ctx.registerCommand({
|
|
@@ -636,16 +740,16 @@ function registerSessionCommands(ctx) {
|
|
|
636
740
|
action: (args) => {
|
|
637
741
|
const name = args.join(" ");
|
|
638
742
|
if (!name) {
|
|
639
|
-
|
|
743
|
+
ctx.output.writeLine(t("repl.rename_usage"));
|
|
640
744
|
return;
|
|
641
745
|
}
|
|
642
746
|
const active = ctx.sessionManager.getActive();
|
|
643
747
|
if (!active) {
|
|
644
|
-
|
|
748
|
+
ctx.output.writeLine(t("session.no_active"));
|
|
645
749
|
return;
|
|
646
750
|
}
|
|
647
751
|
ctx.sessionManager.rename(active, name);
|
|
648
|
-
|
|
752
|
+
ctx.output.writeLine(t("session.renamed", { name }));
|
|
649
753
|
},
|
|
650
754
|
});
|
|
651
755
|
ctx.registerCommand({
|
|
@@ -656,106 +760,136 @@ function registerSessionCommands(ctx) {
|
|
|
656
760
|
action: (args) => {
|
|
657
761
|
const query = args[0];
|
|
658
762
|
if (!query) {
|
|
659
|
-
|
|
763
|
+
ctx.output.writeLine(t("repl.delete_usage"));
|
|
660
764
|
return;
|
|
661
765
|
}
|
|
662
766
|
const sessions = ctx.sessionManager.list();
|
|
663
767
|
const match = sessions.find((s) => s.id === query || s.id.startsWith(query));
|
|
664
768
|
if (!match) {
|
|
665
|
-
|
|
769
|
+
ctx.output.writeLine(t("session.no_match", { query }));
|
|
666
770
|
return;
|
|
667
771
|
}
|
|
668
772
|
ctx.sessionManager.delete(match.id);
|
|
669
|
-
|
|
773
|
+
ctx.output.writeLine(`${t("session.deleted", { id: match.id })}: ${match.name}`);
|
|
670
774
|
},
|
|
671
775
|
});
|
|
672
776
|
}
|
|
673
777
|
function registerSkillCommands(ctx) {
|
|
674
778
|
if (!ctx.skillsModule)
|
|
675
779
|
return;
|
|
780
|
+
const listSkills = () => {
|
|
781
|
+
const available = ctx.skillsModule.getAvailable();
|
|
782
|
+
if (available.length === 0) {
|
|
783
|
+
ctx.output.writeLine(t("repl.no_skills"));
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
ctx.output.writeLine(pc.bold(t("repl.available_skills")));
|
|
787
|
+
for (const skill of available) {
|
|
788
|
+
const tokens = estimateTokens(skill.content);
|
|
789
|
+
const loaded = ctx.skillsModule.getLoaded().some((s) => s.name === skill.name);
|
|
790
|
+
const marker = loaded ? pc.green(" [loaded]") : "";
|
|
791
|
+
ctx.output.writeLine(` ${pc.cyan(skill.name)}${marker} ${pc.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
const listLoaded = () => {
|
|
795
|
+
const loaded = ctx.skillsModule.getLoaded();
|
|
796
|
+
const budget = ctx.skillsModule.getBudget();
|
|
797
|
+
if (loaded.length === 0) {
|
|
798
|
+
ctx.output.writeLine(t("repl.no_loaded"));
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
ctx.output.writeLine(pc.bold(t("repl.loaded_skills")));
|
|
802
|
+
for (const skill of loaded) {
|
|
803
|
+
const tokens = estimateTokens(skill.content);
|
|
804
|
+
ctx.output.writeLine(` ${pc.cyan(skill.name)} ${pc.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
|
|
805
|
+
}
|
|
806
|
+
ctx.output.writeLine(pc.dim(`\n ${t("repl.budget", { used: budget.used, total: budget.total, remaining: budget.remaining })}`));
|
|
807
|
+
};
|
|
808
|
+
const loadSkill = (args) => {
|
|
809
|
+
const arg = args.join(" ");
|
|
810
|
+
if (!arg) {
|
|
811
|
+
ctx.output.writeLine(t("repl.skill_load_usage"));
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
const result = ctx.skillsModule.loadByName(arg);
|
|
815
|
+
if (result.success) {
|
|
816
|
+
ctx.output.writeLine(pc.green(result.message));
|
|
817
|
+
}
|
|
818
|
+
else {
|
|
819
|
+
ctx.output.writeLine(pc.red(result.message), "stderr");
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
const unloadSkill = (args) => {
|
|
823
|
+
const arg = args.join(" ");
|
|
824
|
+
if (!arg) {
|
|
825
|
+
ctx.output.writeLine(t("repl.skill_unload_usage"));
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
if (ctx.skillsModule.unload(arg)) {
|
|
829
|
+
ctx.output.writeLine(pc.green(t("repl.skill_unloaded", { name: arg })));
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
ctx.output.writeLine(pc.red(t("repl.skill_not_loaded", { name: arg })), "stderr");
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
const searchSkills = (args) => {
|
|
836
|
+
const arg = args.join(" ");
|
|
837
|
+
if (!arg) {
|
|
838
|
+
ctx.output.writeLine(t("repl.skill_search_usage"));
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
const results = ctx.skillsModule.search(arg);
|
|
842
|
+
if (results.length === 0) {
|
|
843
|
+
ctx.output.writeLine(t("repl.no_skill_match", { query: arg }));
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
ctx.output.writeLine(pc.bold(t("repl.skills_matching", { query: arg })));
|
|
847
|
+
for (const skill of results) {
|
|
848
|
+
const tokens = estimateTokens(skill.content);
|
|
849
|
+
ctx.output.writeLine(` ${pc.cyan(skill.name)} ${pc.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
const skillHandlers = {
|
|
853
|
+
list: listSkills,
|
|
854
|
+
loaded: listLoaded,
|
|
855
|
+
load: loadSkill,
|
|
856
|
+
unload: unloadSkill,
|
|
857
|
+
search: searchSkills,
|
|
858
|
+
};
|
|
676
859
|
ctx.registerCommand({
|
|
677
860
|
name: "skill",
|
|
678
861
|
description: t("repl.skill"),
|
|
679
862
|
usage: t("repl.skill_usage"),
|
|
680
863
|
action: (args) => {
|
|
681
864
|
const subcmd = args[0];
|
|
682
|
-
const
|
|
683
|
-
if (!
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
console.log(t("repl.no_skills"));
|
|
687
|
-
return;
|
|
688
|
-
}
|
|
689
|
-
console.log(pc.bold(t("repl.available_skills")));
|
|
690
|
-
for (const skill of available) {
|
|
691
|
-
const tokens = Math.ceil(skill.content.length / 4);
|
|
692
|
-
const loaded = ctx.skillsModule.getLoaded().some((s) => s.name === skill.name);
|
|
693
|
-
const marker = loaded ? pc.green(" [loaded]") : "";
|
|
694
|
-
console.log(` ${pc.cyan(skill.name)}${marker} ${pc.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
|
|
695
|
-
}
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
|
-
if (subcmd === "loaded") {
|
|
699
|
-
const loaded = ctx.skillsModule.getLoaded();
|
|
700
|
-
const budget = ctx.skillsModule.getBudget();
|
|
701
|
-
if (loaded.length === 0) {
|
|
702
|
-
console.log(t("repl.no_loaded"));
|
|
703
|
-
return;
|
|
704
|
-
}
|
|
705
|
-
console.log(pc.bold(t("repl.loaded_skills")));
|
|
706
|
-
for (const skill of loaded) {
|
|
707
|
-
const tokens = Math.ceil(skill.content.length / 4);
|
|
708
|
-
console.log(` ${pc.cyan(skill.name)} ${pc.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
|
|
709
|
-
}
|
|
710
|
-
console.log(pc.dim(`\n ${t("repl.budget", { used: budget.used, total: budget.total, remaining: budget.remaining })}`));
|
|
865
|
+
const handler = subcmd ? skillHandlers[subcmd] : skillHandlers.list;
|
|
866
|
+
if (!handler) {
|
|
867
|
+
ctx.output.writeLine(pc.red(t("repl.skill_unknown_sub", { subcmd })), "stderr");
|
|
868
|
+
ctx.output.writeLine(t("repl.skill_usage"));
|
|
711
869
|
return;
|
|
712
870
|
}
|
|
713
|
-
|
|
714
|
-
if (!arg) {
|
|
715
|
-
console.log(t("repl.skill_load_usage"));
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
const result = ctx.skillsModule.loadByName(arg);
|
|
719
|
-
if (result.success) {
|
|
720
|
-
console.log(pc.green(result.message));
|
|
721
|
-
}
|
|
722
|
-
else {
|
|
723
|
-
console.log(pc.red(result.message));
|
|
724
|
-
}
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
if (subcmd === "unload") {
|
|
728
|
-
if (!arg) {
|
|
729
|
-
console.log(t("repl.skill_unload_usage"));
|
|
730
|
-
return;
|
|
731
|
-
}
|
|
732
|
-
if (ctx.skillsModule.unload(arg)) {
|
|
733
|
-
console.log(pc.green(t("repl.skill_unloaded", { name: arg })));
|
|
734
|
-
}
|
|
735
|
-
else {
|
|
736
|
-
console.log(pc.red(t("repl.skill_not_loaded", { name: arg })));
|
|
737
|
-
}
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
if (subcmd === "search") {
|
|
741
|
-
if (!arg) {
|
|
742
|
-
console.log(t("repl.skill_search_usage"));
|
|
743
|
-
return;
|
|
744
|
-
}
|
|
745
|
-
const results = ctx.skillsModule.search(arg);
|
|
746
|
-
if (results.length === 0) {
|
|
747
|
-
console.log(t("repl.no_skill_match", { query: arg }));
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
|
-
console.log(pc.bold(t("repl.skills_matching", { query: arg })));
|
|
751
|
-
for (const skill of results) {
|
|
752
|
-
const tokens = Math.ceil(skill.content.length / 4);
|
|
753
|
-
console.log(` ${pc.cyan(skill.name)} ${pc.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
|
|
754
|
-
}
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
|
-
console.log(pc.red(t("repl.skill_unknown_sub", { subcmd })));
|
|
758
|
-
console.log(t("repl.skill_usage"));
|
|
871
|
+
handler(args.slice(1));
|
|
759
872
|
},
|
|
760
873
|
});
|
|
761
874
|
}
|
|
875
|
+
async function runConfigMigrate(ctx) {
|
|
876
|
+
const { hasDomainFiles } = await import("../config/domains");
|
|
877
|
+
const { loadConfig: loadCfg } = await import("../config/config");
|
|
878
|
+
const configDir = ctx.configDir;
|
|
879
|
+
const configPath = join(configDir, "config.json");
|
|
880
|
+
if (hasDomainFiles(configDir)) {
|
|
881
|
+
ctx.output.writeLine(pc.yellow(t("config.migrate_no_legacy")));
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
if (!existsSync(configPath)) {
|
|
885
|
+
ctx.output.writeLine(pc.yellow(t("config.migrate_no_legacy")));
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
ctx.output.writeLine(t("config.migrate_start"));
|
|
889
|
+
const { config } = loadCfg({ configDir, projectConfigPath: join(configDir, ".mmrc") });
|
|
890
|
+
saveConfig(config, configPath, configDir);
|
|
891
|
+
const { renameSync, readdirSync } = await import("fs");
|
|
892
|
+
renameSync(configPath, configPath + ".bak");
|
|
893
|
+
const domainFiles = readdirSync(join(configDir, "config")).filter((f) => f.endsWith(".json"));
|
|
894
|
+
ctx.output.writeLine(pc.green(t("config.migrate_done", { count: String(domainFiles.length) })));
|
|
895
|
+
}
|