micro-models-agent 0.7.10 → 0.8.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 (150) hide show
  1. package/dist/cli/commands.js +173 -0
  2. package/dist/cli/completer.js +168 -0
  3. package/dist/cli/index.js +2 -0
  4. package/dist/cli/main.js +95 -0
  5. package/dist/cli/repl.js +762 -0
  6. package/dist/cli/security-commands.js +166 -0
  7. package/dist/cli/setup.js +214 -0
  8. package/dist/config/config.js +123 -0
  9. package/dist/config/defaults.js +91 -0
  10. package/dist/config/experts.js +15 -0
  11. package/dist/config/index.js +3 -0
  12. package/dist/config/security.js +187 -0
  13. package/dist/config/types.js +1 -0
  14. package/dist/core/agent.js +626 -0
  15. package/dist/core/bootstrap.js +307 -0
  16. package/dist/core/index.js +2 -0
  17. package/dist/core/prompt-builder.js +55 -0
  18. package/dist/core/types.js +1 -0
  19. package/dist/i18n/en.json +405 -0
  20. package/dist/i18n/index.js +43 -0
  21. package/dist/i18n/ru.json +405 -0
  22. package/dist/index.js +22 -0
  23. package/dist/llm/index.js +4 -0
  24. package/dist/llm/model-loader.js +78 -0
  25. package/dist/llm/openai-compat.js +277 -0
  26. package/dist/llm/orchestrator.js +194 -0
  27. package/dist/llm/provider.js +2 -0
  28. package/dist/llm/response.js +39 -0
  29. package/dist/llm/token-counter.js +37 -0
  30. package/dist/llm/types.js +1 -0
  31. package/dist/logger/app-logger.js +76 -0
  32. package/dist/logger/index.js +1 -0
  33. package/dist/migration/backup.js +45 -0
  34. package/dist/migration/detect.js +50 -0
  35. package/dist/migration/index.js +2 -0
  36. package/dist/modules/browser/actions.js +46 -0
  37. package/dist/modules/browser/cookie-store.js +24 -0
  38. package/dist/modules/browser/index.js +5 -0
  39. package/dist/modules/browser/module.js +28 -0
  40. package/dist/modules/browser/session.js +287 -0
  41. package/dist/modules/browser/snapshot.js +114 -0
  42. package/dist/modules/browser/types.js +9 -0
  43. package/dist/modules/context/history.js +15 -0
  44. package/dist/modules/context/index.js +1 -0
  45. package/dist/modules/context/manager.js +179 -0
  46. package/dist/modules/execution/auditor.js +72 -0
  47. package/dist/modules/execution/index.js +6 -0
  48. package/dist/modules/execution/module.js +334 -0
  49. package/dist/modules/execution/moe-executor.js +196 -0
  50. package/dist/modules/execution/plan-validator.js +153 -0
  51. package/dist/modules/execution/planner.js +35 -0
  52. package/dist/modules/execution/stuck-detector.js +113 -0
  53. package/dist/modules/execution/tracker.js +53 -0
  54. package/dist/modules/execution/types.js +1 -0
  55. package/dist/modules/execution/verifier.js +149 -0
  56. package/dist/modules/hallucination/confidence.js +47 -0
  57. package/dist/modules/hallucination/consistency.js +32 -0
  58. package/dist/modules/hallucination/detector.js +41 -0
  59. package/dist/modules/hallucination/factual.js +128 -0
  60. package/dist/modules/hallucination/index.js +4 -0
  61. package/dist/modules/index.js +5 -0
  62. package/dist/modules/indexer/cache.js +38 -0
  63. package/dist/modules/indexer/index.js +3 -0
  64. package/dist/modules/indexer/module.js +192 -0
  65. package/dist/modules/indexer/walker.js +101 -0
  66. package/dist/modules/mcp/client.js +393 -0
  67. package/dist/modules/mcp/index.js +3 -0
  68. package/dist/modules/mcp/module.js +146 -0
  69. package/dist/modules/mcp/registry.js +15 -0
  70. package/dist/modules/memory/index.js +1 -0
  71. package/dist/modules/memory/search.js +26 -0
  72. package/dist/modules/memory/store.js +38 -0
  73. package/dist/modules/pipelines/engine.js +60 -0
  74. package/dist/modules/pipelines/index.js +3 -0
  75. package/dist/modules/pipelines/parser.js +53 -0
  76. package/dist/modules/pipelines/template.js +14 -0
  77. package/dist/modules/plugins/builtin/lint-on-write.js +121 -0
  78. package/dist/modules/plugins/builtin/notify.js +8 -0
  79. package/dist/modules/plugins/index.js +1 -0
  80. package/dist/modules/plugins/loader.js +28 -0
  81. package/dist/modules/plugins/manager.js +161 -0
  82. package/dist/modules/plugins/types.js +1 -0
  83. package/dist/modules/registry.js +45 -0
  84. package/dist/modules/security/audit-log.js +108 -0
  85. package/dist/modules/security/audit-notifier.js +292 -0
  86. package/dist/modules/security/command-validator.js +91 -0
  87. package/dist/modules/security/content-scanner.js +52 -0
  88. package/dist/modules/security/data-sanitizer.js +97 -0
  89. package/dist/modules/security/encryption.js +218 -0
  90. package/dist/modules/security/index.js +14 -0
  91. package/dist/modules/security/network-validator.js +79 -0
  92. package/dist/modules/security/path-validator.js +155 -0
  93. package/dist/modules/security/rate-limiter.js +119 -0
  94. package/dist/modules/security/security-policies.js +393 -0
  95. package/dist/modules/security/session-encryption.js +193 -0
  96. package/dist/modules/security/session-isolation.js +95 -0
  97. package/dist/modules/session/index.js +3 -0
  98. package/dist/modules/session/manager.js +167 -0
  99. package/dist/modules/session/module.js +28 -0
  100. package/dist/modules/session/store.js +174 -0
  101. package/dist/modules/session/types.js +1 -0
  102. package/dist/modules/skills/index.js +3 -0
  103. package/dist/modules/skills/loader.js +72 -0
  104. package/dist/modules/skills/matcher.js +27 -0
  105. package/dist/modules/skills/module.js +180 -0
  106. package/dist/modules/types.js +1 -0
  107. package/dist/modules/updater/checker.js +32 -0
  108. package/dist/modules/updater/index.js +1 -0
  109. package/dist/modules/user-profile/compressor.js +16 -0
  110. package/dist/modules/user-profile/index.js +1 -0
  111. package/dist/modules/user-profile/profile.js +68 -0
  112. package/dist/tools/approve.js +32 -0
  113. package/dist/tools/bash.js +77 -0
  114. package/dist/tools/browser.js +97 -0
  115. package/dist/tools/create-dir.js +57 -0
  116. package/dist/tools/delete-file.js +64 -0
  117. package/dist/tools/edit-file.js +78 -0
  118. package/dist/tools/executor.js +83 -0
  119. package/dist/tools/file-info.js +46 -0
  120. package/dist/tools/filter-tools.js +10 -0
  121. package/dist/tools/glob-tool.js +19 -0
  122. package/dist/tools/grep-tool.js +51 -0
  123. package/dist/tools/index.js +44 -0
  124. package/dist/tools/list-dir.js +40 -0
  125. package/dist/tools/load-skill.js +48 -0
  126. package/dist/tools/mcp-call.js +68 -0
  127. package/dist/tools/move-file.js +84 -0
  128. package/dist/tools/pipeline-run.js +39 -0
  129. package/dist/tools/question.js +142 -0
  130. package/dist/tools/read-file.js +65 -0
  131. package/dist/tools/registry.js +36 -0
  132. package/dist/tools/scope-check.js +30 -0
  133. package/dist/tools/search-history.js +64 -0
  134. package/dist/tools/subagent.js +130 -0
  135. package/dist/tools/types.js +1 -0
  136. package/dist/tools/user-input.js +123 -0
  137. package/dist/tools/web-browse.js +51 -0
  138. package/dist/tools/web-fetch.js +62 -0
  139. package/dist/tools/web-search.js +59 -0
  140. package/dist/tools/write-file.js +80 -0
  141. package/dist/ui/box.js +81 -0
  142. package/dist/ui/colors.js +4 -0
  143. package/dist/ui/diff.js +185 -0
  144. package/dist/ui/index.js +6 -0
  145. package/dist/ui/md-formatter.js +212 -0
  146. package/dist/ui/output.js +13 -0
  147. package/dist/ui/renderer.js +141 -0
  148. package/dist/ui/spinner.js +70 -0
  149. package/dist/ui/table.js +144 -0
  150. package/package.json +1 -1
@@ -0,0 +1,762 @@
1
+ import * as readline from "readline";
2
+ import { pc } from "../ui/colors";
3
+ import { existsSync, readFileSync, writeFileSync } from "fs";
4
+ import { join } from "path";
5
+ import { homedir } from "os";
6
+ import { Completer, SlashCommandProvider, SessionNameProvider, SubcommandProvider, SkillNameProvider, } from "./completer";
7
+ import { Renderer } from "../ui/renderer";
8
+ import { box } from "../ui/box";
9
+ import { renderTable } from "../ui/table";
10
+ import { t } from "../i18n/index";
11
+ import { runSetup } from "./setup";
12
+ import { saveConfig } from "../config/config";
13
+ const COMMAND_GROUPS = {
14
+ help: "general",
15
+ exit: "general",
16
+ clear: "general",
17
+ run: "general",
18
+ config: "agent",
19
+ status: "agent",
20
+ reasoning: "agent",
21
+ provider: "agent",
22
+ model: "agent",
23
+ wizard: "agent",
24
+ sessions: "session",
25
+ new: "session",
26
+ resume: "session",
27
+ rename: "session",
28
+ delete: "session",
29
+ skill: "skill",
30
+ };
31
+ function formatContextBar(used, limit) {
32
+ const pct = Math.min(100, Math.round((used / limit) * 100));
33
+ const barLen = 20;
34
+ const filled = Math.round((pct / 100) * barLen);
35
+ const bar = pc.green("█".repeat(filled)) + pc.dim("░".repeat(barLen - filled));
36
+ const pctStr = pct >= 75 ? pc.yellow(`${pct}%`) : pc.dim(`${pct}%`);
37
+ return ` ${bar} ${pctStr} ${pc.dim(`(${used} / ${limit} tokens)`)}`;
38
+ }
39
+ export class Repl {
40
+ rl;
41
+ commands = new Map();
42
+ completer = new Completer();
43
+ running = false;
44
+ agent;
45
+ config;
46
+ sessionManager;
47
+ skillsModule;
48
+ pluginManager;
49
+ historyPath;
50
+ history = [];
51
+ maxHistory = 1000;
52
+ lastEscTime = 0;
53
+ doubleEscDelay = 500;
54
+ configDir;
55
+ baseDir;
56
+ noAgentsMd;
57
+ constructor(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd) {
58
+ this.agent = agent;
59
+ this.config = config;
60
+ this.sessionManager = sessionManager;
61
+ this.skillsModule = skillsModule;
62
+ this.pluginManager = pluginManager;
63
+ this.configDir = configDir || join(homedir(), ".mma");
64
+ this.baseDir = baseDir || process.cwd();
65
+ this.noAgentsMd = noAgentsMd === true;
66
+ this.historyPath = join(homedir(), ".mma", "repl-history");
67
+ this.loadHistory();
68
+ this.registerBuiltinCommands();
69
+ this.registerMmaCommands();
70
+ this.registerSessionCommands();
71
+ this.registerSkillCommands();
72
+ this.setupCompleter();
73
+ this.rl = readline.createInterface({
74
+ input: process.stdin,
75
+ output: process.stdout,
76
+ prompt: pc.cyan("> "),
77
+ history: this.history,
78
+ historySize: this.maxHistory,
79
+ tabSize: 2,
80
+ completer: (line) => {
81
+ const [matches, partial] = this.completer.complete(line);
82
+ if (matches.length > 0)
83
+ return [matches, partial];
84
+ return [[], line];
85
+ },
86
+ });
87
+ this.setupListeners();
88
+ }
89
+ loadHistory() {
90
+ if (existsSync(this.historyPath)) {
91
+ try {
92
+ const raw = readFileSync(this.historyPath, "utf-8");
93
+ this.history = raw.split("\n").filter(Boolean).slice(-this.maxHistory);
94
+ }
95
+ catch {
96
+ this.history = [];
97
+ }
98
+ }
99
+ }
100
+ saveHistory() {
101
+ const allHistory = this.history.slice(-this.maxHistory);
102
+ writeFileSync(this.historyPath, allHistory.join("\n"), "utf-8");
103
+ }
104
+ registerBuiltinCommands() {
105
+ this.registerCommand({
106
+ name: "help",
107
+ description: t("repl.help"),
108
+ usage: t("repl.help_usage"),
109
+ action: () => this.showHelp(),
110
+ });
111
+ this.registerCommand({
112
+ name: "exit",
113
+ description: t("repl.exit"),
114
+ aliases: ["quit", "q"],
115
+ usage: t("repl.exit_usage"),
116
+ action: () => this.stop(),
117
+ });
118
+ this.registerCommand({
119
+ name: "clear",
120
+ description: t("repl.clear"),
121
+ usage: t("repl.clear_usage"),
122
+ action: () => {
123
+ console.clear();
124
+ },
125
+ });
126
+ }
127
+ registerMmaCommands() {
128
+ this.registerCommand({
129
+ name: "run",
130
+ description: t("repl.run"),
131
+ usage: t("repl.run_usage"),
132
+ action: async (args) => {
133
+ if (args.length === 0) {
134
+ console.log(t("repl.run_usage"));
135
+ return;
136
+ }
137
+ const prompt = args.join(" ");
138
+ await this.runAgent(prompt);
139
+ },
140
+ });
141
+ this.registerCommand({
142
+ name: "config",
143
+ description: t("repl.config"),
144
+ usage: t("repl.config_usage"),
145
+ action: () => {
146
+ const ctx = this.config.contextWindow;
147
+ const sys = Math.floor(ctx * this.config.contextBudget.systemPrompt);
148
+ const res = Math.floor(ctx * this.config.contextBudget.responseReserve);
149
+ console.log(`${t("repl.model")} ${this.config.model}`);
150
+ console.log(`${t("repl.provider")} ${this.config.provider.type} → ${this.config.provider.baseUrl}`);
151
+ console.log(`${t("repl.context")} ${ctx} (sys:${sys} res:${res} hist:${ctx - sys - res})`);
152
+ console.log(`${t("repl.max_iters")} ${this.config.maxToolIterations}`);
153
+ console.log(`${t("repl.stuck_thresh")} ${this.config.stuckThreshold}`);
154
+ console.log(`${t("repl.reasoning_label")} ${this.config.showReasoning ? pc.green(t("repl.show")) : pc.dim(t("repl.hide"))}`);
155
+ console.log(`${t("repl.log_level")} ${this.config.logLevel}`);
156
+ console.log(`${t("repl.locale")} ${this.config.locale}`);
157
+ const meta = this.sessionManager?.getActiveMeta();
158
+ if (meta) {
159
+ console.log(`${t("repl.session_label")} ${meta.name} (${meta.id}) — ${meta.messageCount} msgs`);
160
+ }
161
+ },
162
+ });
163
+ this.registerCommand({
164
+ name: "reasoning",
165
+ description: t("repl.reasoning"),
166
+ usage: t("repl.reasoning_usage"),
167
+ action: () => {
168
+ this.config.showReasoning = !this.config.showReasoning;
169
+ const status = this.config.showReasoning
170
+ ? pc.green(t("repl.show"))
171
+ : pc.dim(t("repl.hide"));
172
+ console.log(t("repl.reasoning_status", { status }));
173
+ },
174
+ });
175
+ this.registerCommand({
176
+ name: "status",
177
+ description: t("repl.status"),
178
+ usage: t("repl.status_usage"),
179
+ action: () => {
180
+ console.log(`${t("repl.model")} ${this.config.model}`);
181
+ console.log(`${t("repl.provider")} ${this.config.provider.type} @ ${this.config.provider.baseUrl}`);
182
+ const meta = this.sessionManager?.getActiveMeta();
183
+ if (meta) {
184
+ console.log(`${t("repl.session_label")} ${meta.name} (${meta.id}) — ${meta.messageCount} messages`);
185
+ }
186
+ },
187
+ });
188
+ this.registerCommand({
189
+ name: "wizard",
190
+ description: t("repl.wizard"),
191
+ aliases: ["setup"],
192
+ usage: t("repl.wizard_usage"),
193
+ action: async () => {
194
+ console.log(pc.yellow(t("repl.wizard_running")));
195
+ const answers = await runSetup();
196
+ const configPath = join(homedir(), ".mma", "config.json");
197
+ this.config.provider.type = answers.provider;
198
+ this.config.provider.baseUrl = answers.apiBase;
199
+ this.config.provider.apiKey = answers.apiKey;
200
+ this.config.model = answers.model;
201
+ this.config.contextWindow = answers.contextWindow;
202
+ this.config.maxToolIterations = answers.maxToolIterations;
203
+ this.config.locale = answers.locale;
204
+ saveConfig(this.config, configPath);
205
+ console.log(pc.green(t("cli.config_saved")));
206
+ },
207
+ });
208
+ this.registerCommand({
209
+ name: "provider",
210
+ description: t("repl.provider_list"),
211
+ usage: t("repl.provider_usage"),
212
+ action: (args) => {
213
+ const subcmd = args[0];
214
+ if (!subcmd || subcmd === "list") {
215
+ console.log(`${t("repl.provider_current")} ${this.config.provider.type}`);
216
+ console.log(` ${this.config.provider.baseUrl}`);
217
+ return;
218
+ }
219
+ if (subcmd === "use") {
220
+ const name = args[1];
221
+ if (!name) {
222
+ console.log(t("repl.provider_usage"));
223
+ return;
224
+ }
225
+ this.config.provider.type = name;
226
+ const configPath = join(homedir(), ".mma", "config.json");
227
+ saveConfig(this.config, configPath);
228
+ console.log(pc.green(t("repl.provider_set", { name })));
229
+ return;
230
+ }
231
+ console.log(t("repl.provider_usage"));
232
+ },
233
+ });
234
+ this.registerCommand({
235
+ name: "model",
236
+ description: t("repl.model_list"),
237
+ usage: t("repl.model_usage"),
238
+ action: (args) => {
239
+ const subcmd = args[0];
240
+ if (!subcmd || subcmd === "list") {
241
+ console.log(`${t("repl.model_current")} ${this.config.model}`);
242
+ return;
243
+ }
244
+ if (subcmd === "use") {
245
+ const name = args[1];
246
+ if (!name) {
247
+ console.log(t("repl.model_usage"));
248
+ return;
249
+ }
250
+ this.config.model = name;
251
+ const configPath = join(homedir(), ".mma", "config.json");
252
+ saveConfig(this.config, configPath);
253
+ console.log(pc.green(t("repl.model_set", { name })));
254
+ return;
255
+ }
256
+ console.log(t("repl.model_usage"));
257
+ },
258
+ });
259
+ }
260
+ registerSessionCommands() {
261
+ if (!this.sessionManager)
262
+ return;
263
+ this.registerCommand({
264
+ name: "sessions",
265
+ description: t("repl.sessions"),
266
+ aliases: ["ls"],
267
+ usage: t("repl.sessions_usage"),
268
+ action: () => {
269
+ const sessions = this.sessionManager.list();
270
+ const active = this.sessionManager.getActive();
271
+ if (sessions.length === 0) {
272
+ console.log(t("session.no_sessions_hint"));
273
+ return;
274
+ }
275
+ const rows = sessions.map((s) => [
276
+ s.id === active ? pc.green("●") : "",
277
+ s.id.slice(0, 12),
278
+ s.name,
279
+ s.updatedAt.slice(0, 19).replace("T", " "),
280
+ String(s.messageCount),
281
+ ]);
282
+ for (const line of renderTable([
283
+ t("session.col_active"),
284
+ t("session.col_id"),
285
+ t("session.col_name"),
286
+ t("session.col_updated"),
287
+ t("session.col_msgs"),
288
+ ], rows)) {
289
+ console.log(line);
290
+ }
291
+ console.log(pc.dim(`\n ${t("repl.resume_hint")}`));
292
+ },
293
+ });
294
+ this.registerCommand({
295
+ name: "new",
296
+ description: t("repl.new"),
297
+ aliases: ["create"],
298
+ usage: t("repl.new_usage"),
299
+ action: (args) => {
300
+ const name = args.join(" ") || undefined;
301
+ const meta = this.sessionManager.create(name);
302
+ this.agent.clearContext();
303
+ console.clear();
304
+ console.log(`${t("session.created", { name: meta.name })} (${pc.dim(meta.id.slice(0, 12))})`);
305
+ console.log(pc.dim(` ${t("session.chat_cleared")}\n`));
306
+ },
307
+ });
308
+ this.registerCommand({
309
+ name: "resume",
310
+ description: t("repl.resume"),
311
+ aliases: ["switch", "use"],
312
+ usage: t("repl.resume_usage"),
313
+ action: (args) => {
314
+ const query = args.join(" ");
315
+ const sessions = this.sessionManager.list();
316
+ if (!query) {
317
+ console.log(pc.dim(t("session.available")));
318
+ const active = this.sessionManager.getActive();
319
+ for (const s of sessions) {
320
+ const marker = s.id === active ? pc.green(" *") : " ";
321
+ console.log(pc.dim(` ${marker} ${s.id.slice(0, 12)} ${s.name}`));
322
+ }
323
+ console.log(pc.dim(`\n ${t("repl.resume_usage")}`));
324
+ return;
325
+ }
326
+ const match = sessions.find((s) => s.id === query ||
327
+ s.id.startsWith(query) ||
328
+ s.name.toLowerCase().includes(query.toLowerCase()));
329
+ if (!match) {
330
+ console.log(t("session.no_match", { query }));
331
+ return;
332
+ }
333
+ this.sessionManager.setActive(match.id);
334
+ const history = this.sessionManager.loadHistory();
335
+ this.agent.setContext(history);
336
+ console.clear();
337
+ console.log(pc.bold(pc.green(t("session.resumed", { name: match.name }))) +
338
+ " " +
339
+ pc.dim(`(${match.id.slice(0, 12)})`) +
340
+ " — " +
341
+ match.messageCount +
342
+ " msgs");
343
+ console.log(pc.dim("─".repeat(50)));
344
+ if (history.length === 0) {
345
+ console.log(pc.dim(t("session.no_history")));
346
+ }
347
+ else {
348
+ console.log(pc.dim(t("session.chat_history")));
349
+ console.log();
350
+ for (const msg of history) {
351
+ if (msg.role === "user") {
352
+ console.log(pc.cyan(t("session.user_label") + ":"));
353
+ console.log(msg.content);
354
+ console.log();
355
+ }
356
+ else if (msg.role === "assistant") {
357
+ console.log(pc.green(t("session.assistant_label") + ":"));
358
+ console.log(msg.content);
359
+ console.log();
360
+ }
361
+ }
362
+ }
363
+ console.log(pc.dim("─".repeat(50)));
364
+ console.log(pc.dim(` ${t("session.chat_loaded")}`));
365
+ },
366
+ });
367
+ this.registerCommand({
368
+ name: "rename",
369
+ description: t("repl.rename"),
370
+ usage: t("repl.rename_usage"),
371
+ action: (args) => {
372
+ const name = args.join(" ");
373
+ if (!name) {
374
+ console.log(t("repl.rename_usage"));
375
+ return;
376
+ }
377
+ const active = this.sessionManager.getActive();
378
+ if (!active) {
379
+ console.log(t("session.no_active"));
380
+ return;
381
+ }
382
+ this.sessionManager.rename(active, name);
383
+ console.log(t("session.renamed", { name }));
384
+ },
385
+ });
386
+ this.registerCommand({
387
+ name: "delete",
388
+ description: t("repl.delete"),
389
+ aliases: ["rm"],
390
+ usage: t("repl.delete_usage"),
391
+ action: (args) => {
392
+ const query = args[0];
393
+ if (!query) {
394
+ console.log(t("repl.delete_usage"));
395
+ return;
396
+ }
397
+ const sessions = this.sessionManager.list();
398
+ const match = sessions.find((s) => s.id === query || s.id.startsWith(query));
399
+ if (!match) {
400
+ console.log(t("session.no_match", { query }));
401
+ return;
402
+ }
403
+ this.sessionManager.delete(match.id);
404
+ console.log(`${t("session.deleted", { id: match.id })}: ${match.name}`);
405
+ },
406
+ });
407
+ }
408
+ registerSkillCommands() {
409
+ if (!this.skillsModule)
410
+ return;
411
+ this.registerCommand({
412
+ name: "skill",
413
+ description: t("repl.skill"),
414
+ usage: t("repl.skill_usage"),
415
+ action: (args) => {
416
+ const subcmd = args[0];
417
+ const arg = args.slice(1).join(" ");
418
+ if (!subcmd || subcmd === "list") {
419
+ const available = this.skillsModule.getAvailable();
420
+ if (available.length === 0) {
421
+ console.log(t("repl.no_skills"));
422
+ return;
423
+ }
424
+ console.log(pc.bold(t("repl.available_skills")));
425
+ for (const skill of available) {
426
+ const tokens = Math.ceil(skill.content.length / 4);
427
+ const loaded = this.skillsModule.getLoaded().some((s) => s.name === skill.name);
428
+ const marker = loaded ? pc.green(" [loaded]") : "";
429
+ console.log(` ${pc.cyan(skill.name)}${marker} ${pc.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
430
+ }
431
+ return;
432
+ }
433
+ if (subcmd === "loaded") {
434
+ const loaded = this.skillsModule.getLoaded();
435
+ const budget = this.skillsModule.getBudget();
436
+ if (loaded.length === 0) {
437
+ console.log(t("repl.no_loaded"));
438
+ return;
439
+ }
440
+ console.log(pc.bold(t("repl.loaded_skills")));
441
+ for (const skill of loaded) {
442
+ const tokens = Math.ceil(skill.content.length / 4);
443
+ console.log(` ${pc.cyan(skill.name)} ${pc.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
444
+ }
445
+ console.log(pc.dim(`\n ${t("repl.budget", { used: budget.used, total: budget.total, remaining: budget.remaining })}`));
446
+ return;
447
+ }
448
+ if (subcmd === "load") {
449
+ if (!arg) {
450
+ console.log(t("repl.skill_load_usage"));
451
+ return;
452
+ }
453
+ const result = this.skillsModule.loadByName(arg);
454
+ if (result.success) {
455
+ console.log(pc.green(result.message));
456
+ }
457
+ else {
458
+ console.log(pc.red(result.message));
459
+ }
460
+ return;
461
+ }
462
+ if (subcmd === "unload") {
463
+ if (!arg) {
464
+ console.log(t("repl.skill_unload_usage"));
465
+ return;
466
+ }
467
+ if (this.skillsModule.unload(arg)) {
468
+ console.log(pc.green(t("repl.skill_unloaded", { name: arg })));
469
+ }
470
+ else {
471
+ console.log(pc.red(t("repl.skill_not_loaded", { name: arg })));
472
+ }
473
+ return;
474
+ }
475
+ if (subcmd === "search") {
476
+ if (!arg) {
477
+ console.log(t("repl.skill_search_usage"));
478
+ return;
479
+ }
480
+ const results = this.skillsModule.search(arg);
481
+ if (results.length === 0) {
482
+ console.log(t("repl.no_skill_match", { query: arg }));
483
+ return;
484
+ }
485
+ console.log(pc.bold(t("repl.skills_matching", { query: arg })));
486
+ for (const skill of results) {
487
+ const tokens = Math.ceil(skill.content.length / 4);
488
+ console.log(` ${pc.cyan(skill.name)} ${pc.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
489
+ }
490
+ return;
491
+ }
492
+ console.log(pc.red(t("repl.skill_unknown_sub", { subcmd })));
493
+ console.log(t("repl.skill_usage"));
494
+ },
495
+ });
496
+ }
497
+ setupCompleter() {
498
+ const slashCommands = Array.from(this.commands.keys());
499
+ this.completer.registerProvider(new SlashCommandProvider(slashCommands));
500
+ if (this.sessionManager) {
501
+ this.completer.registerProvider(new SessionNameProvider(() => this.sessionManager.list().map((s) => s.name)));
502
+ }
503
+ if (this.skillsModule) {
504
+ this.completer.registerProvider(new SubcommandProvider("skill", [
505
+ "list",
506
+ "loaded",
507
+ "load",
508
+ "unload",
509
+ "search",
510
+ ]));
511
+ this.completer.registerProvider(new SkillNameProvider(this.skillsModule));
512
+ }
513
+ }
514
+ setupListeners() {
515
+ let multiLineBuffer = "";
516
+ let inMultiLine = false;
517
+ this.rl.on("line", async (line) => {
518
+ const trimmed = line.trim();
519
+ if (trimmed) {
520
+ this.history.push(trimmed);
521
+ if (this.history.length > this.maxHistory) {
522
+ this.history = this.history.slice(-this.maxHistory);
523
+ }
524
+ }
525
+ if (inMultiLine) {
526
+ multiLineBuffer += "\n" + line;
527
+ if (!this.isMultiLineInput(multiLineBuffer)) {
528
+ inMultiLine = false;
529
+ const fullInput = multiLineBuffer.trim();
530
+ multiLineBuffer = "";
531
+ if (fullInput) {
532
+ if (fullInput.startsWith("/")) {
533
+ await this.executeCommand(fullInput);
534
+ }
535
+ else {
536
+ await this.runAgent(fullInput);
537
+ }
538
+ }
539
+ if (this.running) {
540
+ this.rl.setPrompt(pc.cyan("> "));
541
+ this.rl.prompt();
542
+ }
543
+ }
544
+ else {
545
+ this.rl.setPrompt(pc.cyan("... "));
546
+ this.rl.prompt();
547
+ }
548
+ return;
549
+ }
550
+ if (this.isMultiLineInput(trimmed)) {
551
+ inMultiLine = true;
552
+ multiLineBuffer = trimmed;
553
+ this.rl.setPrompt(pc.cyan("... "));
554
+ this.rl.prompt();
555
+ return;
556
+ }
557
+ if (!trimmed) {
558
+ this.rl.prompt();
559
+ return;
560
+ }
561
+ if (trimmed.startsWith("/")) {
562
+ await this.executeCommand(trimmed);
563
+ }
564
+ else {
565
+ await this.runAgent(trimmed);
566
+ }
567
+ if (this.running) {
568
+ this.rl.prompt();
569
+ }
570
+ });
571
+ this.rl.on("close", () => {
572
+ this.running = false;
573
+ });
574
+ if (process.stdin.isTTY) {
575
+ readline.emitKeypressEvents(process.stdin);
576
+ process.stdin.on("keypress", (str, key) => {
577
+ if (key.name === "escape") {
578
+ const now = Date.now();
579
+ if (now - this.lastEscTime < this.doubleEscDelay) {
580
+ console.log(pc.yellow("\n\n[Ctrl+C] Остановка агента..."));
581
+ this.agent.shutdown();
582
+ this.running = false;
583
+ this.saveHistory();
584
+ this.rl.close();
585
+ process.exit(0);
586
+ }
587
+ this.lastEscTime = now;
588
+ }
589
+ });
590
+ }
591
+ }
592
+ isMultiLineInput(line) {
593
+ if (line.endsWith("\\"))
594
+ return true;
595
+ const openBraces = (line.match(/\{/g) || []).length;
596
+ const closeBraces = (line.match(/\}/g) || []).length;
597
+ if (openBraces > closeBraces)
598
+ return true;
599
+ return false;
600
+ }
601
+ async runAgent(input) {
602
+ process.stdout.write("\n" + pc.green(t("repl.agent")));
603
+ const renderer = new Renderer({ spinner: this.config.ui?.spinner ?? true });
604
+ const result = await this.agent.run(input, (c) => renderer.text(c), (m) => renderer.meta(m), (ev) => {
605
+ if (ev.type === "start") {
606
+ renderer.toolStart(ev.tool, ev.args);
607
+ }
608
+ else {
609
+ renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error);
610
+ }
611
+ }, (phase) => {
612
+ if (phase === "thinking") {
613
+ renderer.thinkingStart();
614
+ }
615
+ else {
616
+ renderer.thinkingEnd();
617
+ }
618
+ });
619
+ renderer.flush();
620
+ process.stdout.write("\n");
621
+ if (!result.success) {
622
+ console.error(pc.red(`${t("error.prefix")}${result.error}`));
623
+ }
624
+ this.showContextBar(result);
625
+ }
626
+ registerCommand(cmd) {
627
+ this.commands.set(cmd.name, cmd);
628
+ if (cmd.aliases) {
629
+ for (const alias of cmd.aliases) {
630
+ this.commands.set(alias, cmd);
631
+ }
632
+ }
633
+ }
634
+ async executeCommand(input) {
635
+ const parts = input.split(/\s+/);
636
+ const name = parts[0].slice(1);
637
+ const args = parts.slice(1);
638
+ const cmd = this.commands.get(name);
639
+ if (!cmd) {
640
+ console.log(pc.red(t("cli.unknown_cmd", { name })), t("cli.help_hint"));
641
+ return;
642
+ }
643
+ try {
644
+ await cmd.action(args);
645
+ }
646
+ catch (err) {
647
+ console.error(pc.red(t("error.command_error", {
648
+ message: err instanceof Error ? err.message : String(err),
649
+ })));
650
+ }
651
+ }
652
+ showHelp() {
653
+ const order = ["general", "agent", "session", "skill"];
654
+ const seen = new Set();
655
+ for (const groupKey of order) {
656
+ const cmds = [];
657
+ for (const [name, cmd] of this.commands) {
658
+ if (cmd.aliases?.includes(name))
659
+ continue;
660
+ if (seen.has(cmd.name))
661
+ continue;
662
+ const group = COMMAND_GROUPS[cmd.name] ?? "general";
663
+ if (group !== groupKey)
664
+ continue;
665
+ seen.add(cmd.name);
666
+ cmds.push(cmd);
667
+ }
668
+ if (cmds.length === 0)
669
+ continue;
670
+ console.log(pc.bold(t(`repl.group.${groupKey}`)));
671
+ for (const cmd of cmds) {
672
+ const aliases = cmd.aliases?.length
673
+ ? ` (${pc.dim(cmd.aliases.join(", "))})`
674
+ : "";
675
+ console.log(` ${pc.cyan("/" + cmd.name)}${aliases} ${pc.dim(cmd.description)}`);
676
+ if (cmd.usage) {
677
+ console.log(` ${pc.dim(cmd.usage)}`);
678
+ }
679
+ }
680
+ console.log();
681
+ }
682
+ }
683
+ showContextBar(result) {
684
+ if (result.contextUsed !== undefined &&
685
+ result.contextLimit !== undefined &&
686
+ result.contextLimit > 0) {
687
+ console.log();
688
+ console.log(formatContextBar(result.contextUsed, result.contextLimit));
689
+ }
690
+ }
691
+ start() {
692
+ this.running = true;
693
+ const info = [];
694
+ const row = (label, value) => {
695
+ info.push(` ${pc.yellow(label)} ${value}`);
696
+ };
697
+ const ctx = this.config.contextWindow;
698
+ const sysBudget = Math.floor(ctx * this.config.contextBudget.systemPrompt);
699
+ const resBudget = Math.floor(ctx * this.config.contextBudget.responseReserve);
700
+ const histBudget = ctx - sysBudget - resBudget;
701
+ row(t("repl.model"), pc.white(this.config.model));
702
+ row(t("repl.provider"), `${this.config.provider.type} → ${pc.dim(this.config.provider.baseUrl)}`);
703
+ row(t("repl.context"), `${pc.white(String(ctx))} ${pc.dim(`(sys:${sysBudget} res:${resBudget} hist:${histBudget})`)}`);
704
+ if (this.skillsModule) {
705
+ const budget = this.skillsModule.getBudget();
706
+ row(t("repl.skills_label"), `${pc.white(String(this.skillsModule.getAvailable().length))} available, ${pc.dim(`budget: ${budget.total} tokens`)}`);
707
+ }
708
+ if (this.pluginManager) {
709
+ const plugins = this.pluginManager
710
+ .getAllPlugins()
711
+ .filter((p) => !p.isBuiltin);
712
+ if (plugins.length > 0) {
713
+ const pluginNames = plugins.map((p) => p.name).join(", ");
714
+ row(t("repl.plugins_label"), `${pc.white(String(plugins.length))} active ${pc.dim(`(${pluginNames})`)}`);
715
+ }
716
+ }
717
+ const mcpServers = this.config.mcpServers || {};
718
+ const enabledServers = Object.entries(mcpServers).filter(([, s]) => s.enabled !== false);
719
+ if (enabledServers.length > 0) {
720
+ const names = enabledServers.map(([name]) => name).join(", ");
721
+ row(t("repl.mcp_label"), `${pc.white(String(enabledServers.length))} ${pc.dim(`(${names})`)}`);
722
+ }
723
+ const cwd = process.cwd();
724
+ row(t("repl.work_dir"), pc.dim(cwd));
725
+ if (this.noAgentsMd) {
726
+ row(t("repl.agents_label"), pc.red(t("repl.disabled")));
727
+ }
728
+ else {
729
+ const agentsMdCandidates = [
730
+ join(this.baseDir, "AGENTS.md"),
731
+ join(this.baseDir, ".mma", "AGENTS.md"),
732
+ join(this.configDir, "AGENTS.md"),
733
+ ];
734
+ const foundAgents = agentsMdCandidates.filter((p) => existsSync(p));
735
+ if (foundAgents.length > 0) {
736
+ for (const p of foundAgents) {
737
+ row(t("repl.agents_label"), pc.dim(p));
738
+ }
739
+ }
740
+ else {
741
+ row(t("repl.agents_label"), pc.dim(t("repl.not_found")));
742
+ }
743
+ }
744
+ const meta = this.sessionManager?.getActiveMeta();
745
+ if (meta) {
746
+ const sessionPath = join(this.configDir, "sessions", meta.id);
747
+ row(t("repl.session_label"), `${pc.cyan(meta.name)} ${pc.dim(`(${meta.id.slice(0, 12)})`)} — ${meta.messageCount} msgs ${pc.dim(sessionPath)}`);
748
+ }
749
+ const headerWidth = Math.max(50, Math.min(96, process.stdout.columns || 96));
750
+ for (const line of box(info, { title: t("repl.title"), width: headerWidth })) {
751
+ console.log(line);
752
+ }
753
+ console.log();
754
+ this.rl.prompt();
755
+ }
756
+ stop() {
757
+ this.running = false;
758
+ this.saveHistory();
759
+ this.agent.shutdown();
760
+ this.rl.close();
761
+ }
762
+ }