micro-models-agent 0.45.0 → 0.46.2

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 (218) hide show
  1. package/README.md +312 -312
  2. package/dist/cli/commands.js +323 -0
  3. package/dist/cli/completer.js +167 -0
  4. package/dist/cli/index.js +2 -0
  5. package/dist/cli/main.js +165 -0
  6. package/dist/cli/plugin-commands.js +36 -0
  7. package/dist/cli/repl-commands.js +661 -0
  8. package/dist/cli/repl.js +616 -0
  9. package/dist/cli/run-result.js +22 -0
  10. package/dist/cli/security-commands.js +164 -0
  11. package/dist/cli/setup.js +231 -0
  12. package/dist/config/config.js +249 -0
  13. package/dist/config/defaults.js +124 -0
  14. package/dist/config/experts.js +15 -0
  15. package/dist/config/index.js +3 -0
  16. package/dist/config/security.js +193 -0
  17. package/dist/config/types.js +1 -0
  18. package/dist/core/agent-moe.js +102 -0
  19. package/dist/core/agent.js +886 -0
  20. package/dist/core/bootstrap.js +404 -0
  21. package/dist/core/index.js +2 -0
  22. package/dist/core/prompt-builder.js +76 -0
  23. package/dist/core/session-logger.js +197 -0
  24. package/dist/core/types.js +1 -0
  25. package/dist/core/version.js +24 -0
  26. package/dist/core/workspace.js +76 -0
  27. package/dist/i18n/en.json +598 -0
  28. package/dist/i18n/index.js +46 -0
  29. package/dist/i18n/ru.json +598 -0
  30. package/dist/index.js +22 -0
  31. package/dist/llm/image-utils.js +143 -0
  32. package/dist/llm/index.js +4 -0
  33. package/dist/llm/model-loader.js +78 -0
  34. package/dist/llm/openai-compat.js +359 -0
  35. package/dist/llm/orchestrator.js +198 -0
  36. package/dist/llm/provider.js +10 -0
  37. package/dist/llm/response.js +39 -0
  38. package/dist/llm/token-counter.js +39 -0
  39. package/dist/llm/types.js +1 -0
  40. package/dist/logger/app-logger.js +143 -0
  41. package/dist/logger/file-log.js +151 -0
  42. package/dist/logger/index.js +1 -0
  43. package/dist/main.js +690 -168
  44. package/dist/migration/backup.js +45 -0
  45. package/dist/migration/detect.js +50 -0
  46. package/dist/migration/index.js +2 -0
  47. package/dist/modules/artifacts/store.js +61 -0
  48. package/dist/modules/browser/actions.js +76 -0
  49. package/dist/modules/browser/bridge-client.js +199 -0
  50. package/dist/modules/browser/bridge-path.js +10 -0
  51. package/dist/modules/browser/bridge-server.mjs +202 -202
  52. package/dist/modules/browser/cookie-store.js +24 -0
  53. package/dist/modules/browser/driver.js +136 -0
  54. package/dist/modules/browser/index.js +7 -0
  55. package/dist/modules/browser/module.js +29 -0
  56. package/dist/modules/browser/session.js +338 -0
  57. package/dist/modules/browser/snapshot.js +148 -0
  58. package/dist/modules/browser/types.js +12 -0
  59. package/dist/modules/certification/cli.js +174 -0
  60. package/dist/modules/certification/fact-checker.js +82 -0
  61. package/dist/modules/certification/loader.js +105 -0
  62. package/dist/modules/certification/manifest.js +50 -0
  63. package/dist/modules/certification/runner.js +159 -0
  64. package/dist/modules/certification/scenarios.js +124 -0
  65. package/dist/modules/certification/types.js +1 -0
  66. package/dist/modules/context/chunk-query.js +100 -0
  67. package/dist/modules/context/fact-extractor.js +162 -0
  68. package/dist/modules/context/history.js +15 -0
  69. package/dist/modules/context/index.js +1 -0
  70. package/dist/modules/context/manager.js +423 -0
  71. package/dist/modules/execution/audit-runners.js +152 -0
  72. package/dist/modules/execution/auditor.js +218 -0
  73. package/dist/modules/execution/execution-plugin.js +272 -0
  74. package/dist/modules/execution/index.js +8 -0
  75. package/dist/modules/execution/module.js +436 -0
  76. package/dist/modules/execution/moe-executor.js +291 -0
  77. package/dist/modules/execution/plan-coverage.js +68 -0
  78. package/dist/modules/execution/plan-persister.js +46 -0
  79. package/dist/modules/execution/plan-store.js +157 -0
  80. package/dist/modules/execution/plan-tool.js +508 -0
  81. package/dist/modules/execution/plan-validator.js +153 -0
  82. package/dist/modules/execution/planner.js +90 -0
  83. package/dist/modules/execution/stuck-detector.js +510 -0
  84. package/dist/modules/execution/tracker.js +67 -0
  85. package/dist/modules/execution/types.js +1 -0
  86. package/dist/modules/execution/verifier.js +222 -0
  87. package/dist/modules/execution/windows-commands.js +41 -0
  88. package/dist/modules/hallucination/confidence.js +66 -0
  89. package/dist/modules/hallucination/consistency.js +26 -0
  90. package/dist/modules/hallucination/detector.js +43 -0
  91. package/dist/modules/hallucination/factual.js +129 -0
  92. package/dist/modules/hallucination/index.js +5 -0
  93. package/dist/modules/hallucination/js-identifiers.js +262 -0
  94. package/dist/modules/hallucination/llm-judge.js +101 -0
  95. package/dist/modules/index.js +5 -0
  96. package/dist/modules/indexer/cache.js +40 -0
  97. package/dist/modules/indexer/index.js +3 -0
  98. package/dist/modules/indexer/module.js +245 -0
  99. package/dist/modules/indexer/project-profile.js +183 -0
  100. package/dist/modules/indexer/walker.js +101 -0
  101. package/dist/modules/lsp/check-tool.js +58 -0
  102. package/dist/modules/lsp/client.js +278 -0
  103. package/dist/modules/lsp/command.js +60 -0
  104. package/dist/modules/lsp/config.js +135 -0
  105. package/dist/modules/lsp/index.js +3 -0
  106. package/dist/modules/lsp/module.js +232 -0
  107. package/dist/modules/lsp/probe.js +76 -0
  108. package/dist/modules/lsp/project-root.js +32 -0
  109. package/dist/modules/lsp/startup-check.js +141 -0
  110. package/dist/modules/lsp/types.js +1 -0
  111. package/dist/modules/mcp/client.js +399 -0
  112. package/dist/modules/mcp/index.js +3 -0
  113. package/dist/modules/mcp/module.js +142 -0
  114. package/dist/modules/mcp/registry.js +15 -0
  115. package/dist/modules/memory/index.js +1 -0
  116. package/dist/modules/memory/module.js +96 -0
  117. package/dist/modules/memory/search.js +42 -0
  118. package/dist/modules/memory/store.js +69 -0
  119. package/dist/modules/pipelines/engine.js +60 -0
  120. package/dist/modules/pipelines/index.js +3 -0
  121. package/dist/modules/pipelines/parser.js +56 -0
  122. package/dist/modules/pipelines/template.js +14 -0
  123. package/dist/modules/plugins/builtin/lint-on-write.js +231 -0
  124. package/dist/modules/plugins/builtin/notify.js +9 -0
  125. package/dist/modules/plugins/index.js +1 -0
  126. package/dist/modules/plugins/loader.js +70 -0
  127. package/dist/modules/plugins/manager.js +217 -0
  128. package/dist/modules/plugins/types.js +1 -0
  129. package/dist/modules/processes/detect.js +34 -0
  130. package/dist/modules/processes/index.js +2 -0
  131. package/dist/modules/processes/registry.js +327 -0
  132. package/dist/modules/processes/runner.js +23 -0
  133. package/dist/modules/registry.js +47 -0
  134. package/dist/modules/security/audit-log.js +136 -0
  135. package/dist/modules/security/audit-notifier.js +292 -0
  136. package/dist/modules/security/command-validator.js +205 -0
  137. package/dist/modules/security/content-scanner.js +53 -0
  138. package/dist/modules/security/data-sanitizer.js +89 -0
  139. package/dist/modules/security/encryption.js +242 -0
  140. package/dist/modules/security/index.js +14 -0
  141. package/dist/modules/security/network-validator.js +71 -0
  142. package/dist/modules/security/path-validator.js +207 -0
  143. package/dist/modules/security/rate-limiter.js +119 -0
  144. package/dist/modules/security/security-policies.js +531 -0
  145. package/dist/modules/security/session-encryption.js +210 -0
  146. package/dist/modules/security/session-isolation.js +95 -0
  147. package/dist/modules/session/index.js +3 -0
  148. package/dist/modules/session/manager.js +172 -0
  149. package/dist/modules/session/module.js +24 -0
  150. package/dist/modules/session/store.js +222 -0
  151. package/dist/modules/session/types.js +1 -0
  152. package/dist/modules/skills/index.js +2 -0
  153. package/dist/modules/skills/loader.js +72 -0
  154. package/dist/modules/skills/matcher.js +27 -0
  155. package/dist/modules/skills/module.js +129 -0
  156. package/dist/modules/types.js +1 -0
  157. package/dist/modules/updater/checker.js +96 -0
  158. package/dist/modules/updater/index.js +2 -0
  159. package/dist/modules/updater/module.js +116 -0
  160. package/dist/modules/user-profile/compressor.js +16 -0
  161. package/dist/modules/user-profile/index.js +1 -0
  162. package/dist/modules/user-profile/profile.js +68 -0
  163. package/dist/skills/builtin/git.md +36 -36
  164. package/dist/skills/builtin/typescript.md +35 -35
  165. package/dist/tools/approve.js +32 -0
  166. package/dist/tools/attach-image.js +89 -0
  167. package/dist/tools/bash.js +496 -0
  168. package/dist/tools/browser.js +114 -0
  169. package/dist/tools/chunk-query.js +99 -0
  170. package/dist/tools/create-dir.js +55 -0
  171. package/dist/tools/delete-file.js +62 -0
  172. package/dist/tools/download-file.js +116 -0
  173. package/dist/tools/edit-file.js +79 -0
  174. package/dist/tools/enable-tools.js +58 -0
  175. package/dist/tools/executor.js +144 -0
  176. package/dist/tools/file-info.js +46 -0
  177. package/dist/tools/filter-tools.js +17 -0
  178. package/dist/tools/glob-tool.js +26 -0
  179. package/dist/tools/grep-tool.js +84 -0
  180. package/dist/tools/hidden-tools-block.js +37 -0
  181. package/dist/tools/index.js +78 -0
  182. package/dist/tools/list-dir.js +48 -0
  183. package/dist/tools/load-skill.js +42 -0
  184. package/dist/tools/mcp-call.js +68 -0
  185. package/dist/tools/move-file.js +85 -0
  186. package/dist/tools/path-utils.js +51 -0
  187. package/dist/tools/pipeline-run.js +144 -0
  188. package/dist/tools/preview.js +2 -0
  189. package/dist/tools/process-kill.js +29 -0
  190. package/dist/tools/process-list.js +36 -0
  191. package/dist/tools/process-log.js +45 -0
  192. package/dist/tools/question.js +140 -0
  193. package/dist/tools/read-file.js +91 -0
  194. package/dist/tools/recall.js +117 -0
  195. package/dist/tools/registry.js +47 -0
  196. package/dist/tools/remember.js +67 -0
  197. package/dist/tools/scope-check.js +30 -0
  198. package/dist/tools/search-history.js +84 -0
  199. package/dist/tools/subagent.js +196 -0
  200. package/dist/tools/types.js +1 -0
  201. package/dist/tools/user-input.js +123 -0
  202. package/dist/tools/web-browse.js +86 -0
  203. package/dist/tools/web-fetch.js +98 -0
  204. package/dist/tools/web-search.js +78 -0
  205. package/dist/tools/write-file.js +81 -0
  206. package/dist/ui/box.js +77 -0
  207. package/dist/ui/colors.js +4 -0
  208. package/dist/ui/diff.js +178 -0
  209. package/dist/ui/index.js +6 -0
  210. package/dist/ui/line-editor.js +703 -0
  211. package/dist/ui/line-math.js +69 -0
  212. package/dist/ui/md-formatter.js +212 -0
  213. package/dist/ui/output.js +13 -0
  214. package/dist/ui/plan-view.js +103 -0
  215. package/dist/ui/renderer.js +209 -0
  216. package/dist/ui/spinner.js +70 -0
  217. package/dist/ui/table.js +144 -0
  218. package/package.json +48 -48
@@ -0,0 +1,661 @@
1
+ import { pc } from "../ui/colors";
2
+ import { renderTable } from "../ui/table";
3
+ import { t } from "../i18n/index";
4
+ import { runSetup } from "./setup";
5
+ import { saveConfig } from "../config/config";
6
+ import { getMessageText } from "../llm/provider";
7
+ import { join, dirname } from "path";
8
+ import { homedir } from "os";
9
+ import { existsSync, readFileSync } from "fs";
10
+ import { fileURLToPath } from "url";
11
+ function readVersion() {
12
+ const here = dirname(fileURLToPath(import.meta.url));
13
+ const candidates = [join(here, "..", "..", "package.json"), join(here, "..", "package.json")];
14
+ for (const p of candidates) {
15
+ if (existsSync(p)) {
16
+ try {
17
+ const raw = JSON.parse(readFileSync(p, "utf8"));
18
+ if (raw.version)
19
+ return raw.version;
20
+ }
21
+ catch {
22
+ // Broken package.json — fall through to the next candidate
23
+ }
24
+ }
25
+ }
26
+ return "0.0.0";
27
+ }
28
+ const version = readVersion();
29
+ export const COMMAND_GROUPS = {
30
+ help: "general",
31
+ exit: "general",
32
+ clear: "general",
33
+ run: "general",
34
+ image: "general",
35
+ config: "agent",
36
+ status: "agent",
37
+ reasoning: "agent",
38
+ provider: "agent",
39
+ model: "agent",
40
+ context: "agent",
41
+ reload: "agent",
42
+ wizard: "agent",
43
+ sysprompt: "agent",
44
+ sessions: "session",
45
+ new: "session",
46
+ resume: "session",
47
+ rename: "session",
48
+ delete: "session",
49
+ skill: "skill",
50
+ plugins: "agent",
51
+ };
52
+ export function registerAllCommands(ctx) {
53
+ registerBuiltinCommands(ctx);
54
+ registerMmaCommands(ctx);
55
+ registerSessionCommands(ctx);
56
+ registerSkillCommands(ctx);
57
+ }
58
+ function registerBuiltinCommands(ctx) {
59
+ ctx.registerCommand({
60
+ name: "help",
61
+ description: t("repl.help"),
62
+ usage: t("repl.help_usage"),
63
+ action: () => ctx.showHelp(),
64
+ });
65
+ ctx.registerCommand({
66
+ name: "exit",
67
+ description: t("repl.exit"),
68
+ aliases: ["quit", "q"],
69
+ usage: t("repl.exit_usage"),
70
+ action: () => ctx.stop(),
71
+ });
72
+ ctx.registerCommand({
73
+ name: "clear",
74
+ description: t("repl.clear"),
75
+ usage: t("repl.clear_usage"),
76
+ action: () => {
77
+ console.clear();
78
+ },
79
+ });
80
+ }
81
+ function registerMmaCommands(ctx) {
82
+ ctx.registerCommand({
83
+ name: "run",
84
+ description: t("repl.run"),
85
+ usage: t("repl.run_usage"),
86
+ action: async (args) => {
87
+ if (args.length === 0) {
88
+ console.log(t("repl.run_usage"));
89
+ return;
90
+ }
91
+ const prompt = args.join(" ");
92
+ await ctx.runAgent(prompt);
93
+ },
94
+ });
95
+ ctx.registerCommand({
96
+ name: "image",
97
+ description: t("repl.image"),
98
+ aliases: ["img"],
99
+ usage: t("repl.image_usage"),
100
+ action: async (args) => {
101
+ const source = args.join(" ");
102
+ if (!source) {
103
+ console.log(t("repl.image_usage"));
104
+ return;
105
+ }
106
+ try {
107
+ const { loadFileAsDataUrl, loadUrlAsDataUrl, readClipboardImage } = await import("../llm/image-utils");
108
+ const { existsSync } = await import("fs");
109
+ const { resolve } = await import("path");
110
+ let dataUrl;
111
+ let label;
112
+ if (source.toLowerCase() === "clipboard") {
113
+ const clipBuf = await readClipboardImage();
114
+ if (!clipBuf) {
115
+ console.log(pc.yellow(t("image.clipboard_empty")));
116
+ return;
117
+ }
118
+ const { bufferToDataUrl } = await import("../llm/image-utils");
119
+ const result = await bufferToDataUrl(clipBuf);
120
+ dataUrl = result.dataUrl;
121
+ label = "clipboard";
122
+ }
123
+ else if (source.startsWith("http://") || source.startsWith("https://")) {
124
+ const result = await loadUrlAsDataUrl(source);
125
+ dataUrl = result.dataUrl;
126
+ label = source;
127
+ }
128
+ else {
129
+ const absPath = resolve(process.cwd(), source);
130
+ if (!existsSync(absPath)) {
131
+ console.log(pc.red(t("image.not_found", { path: source })));
132
+ return;
133
+ }
134
+ const result = await loadFileAsDataUrl(absPath);
135
+ dataUrl = result.dataUrl;
136
+ label = source;
137
+ }
138
+ const contextManager = ctx.agent.contextManager;
139
+ if (!contextManager) {
140
+ console.log(pc.red(t("image.no_context")));
141
+ return;
142
+ }
143
+ contextManager.addPendingImage({
144
+ type: "image_url",
145
+ image_url: { url: dataUrl },
146
+ });
147
+ const sizeKb = Math.round((dataUrl.length * 3) / 4 / 1024);
148
+ console.log(pc.green(t("image.attached", { source: label, size: `${sizeKb} KB` })));
149
+ }
150
+ catch (err) {
151
+ console.log(pc.red(t("image.error", { message: err.message })));
152
+ }
153
+ },
154
+ });
155
+ ctx.registerCommand({
156
+ name: "config",
157
+ description: t("repl.config"),
158
+ usage: t("repl.config_usage"),
159
+ action: () => {
160
+ const cfg = ctx.config;
161
+ const ctxW = cfg.contextWindow;
162
+ const sys = Math.floor(ctxW * cfg.contextBudget.systemPrompt);
163
+ const res = Math.floor(ctxW * cfg.contextBudget.responseReserve);
164
+ console.log(`${t("repl.model")} ${cfg.model}`);
165
+ console.log(`${t("repl.provider")} ${cfg.provider.type} → ${cfg.provider.baseUrl}`);
166
+ console.log(`${t("repl.context")} ${ctxW} (sys:${sys} res:${res} hist:${ctxW - sys - res})`);
167
+ console.log(`${t("repl.max_iters")} ${cfg.maxToolIterations}`);
168
+ console.log(`${t("repl.stuck_thresh")} ${cfg.stuckThreshold}`);
169
+ console.log(`${t("repl.reasoning_label")} ${cfg.showReasoning ? pc.green(t("repl.show")) : pc.dim(t("repl.hide"))}`);
170
+ console.log(`${t("repl.log_level")} ${cfg.logLevel}`);
171
+ console.log(`${t("repl.locale")} ${cfg.locale}`);
172
+ const meta = ctx.sessionManager?.getActiveMeta();
173
+ if (meta) {
174
+ console.log(`${t("repl.session_label")} ${meta.name} (${meta.id}) — ${meta.messageCount} msgs`);
175
+ }
176
+ },
177
+ });
178
+ ctx.registerCommand({
179
+ name: "reasoning",
180
+ description: t("repl.reasoning"),
181
+ usage: t("repl.reasoning_usage"),
182
+ action: () => {
183
+ ctx.config.showReasoning = !ctx.config.showReasoning;
184
+ const status = ctx.config.showReasoning ? pc.green(t("repl.show")) : pc.dim(t("repl.hide"));
185
+ console.log(t("repl.reasoning_status", { status }));
186
+ },
187
+ });
188
+ ctx.registerCommand({
189
+ name: "status",
190
+ description: t("repl.status"),
191
+ usage: t("repl.status_usage"),
192
+ action: () => {
193
+ console.log(`${t("repl.model")} ${ctx.config.model}`);
194
+ console.log(`${t("repl.provider")} ${ctx.config.provider.type} @ ${ctx.config.provider.baseUrl}`);
195
+ const meta = ctx.sessionManager?.getActiveMeta();
196
+ if (meta) {
197
+ console.log(`${t("repl.session_label")} ${meta.name} (${meta.id}) — ${meta.messageCount} messages`);
198
+ }
199
+ },
200
+ });
201
+ ctx.registerCommand({
202
+ name: "wizard",
203
+ description: t("repl.wizard"),
204
+ aliases: ["setup"],
205
+ usage: t("repl.wizard_usage"),
206
+ action: async () => {
207
+ console.log(pc.yellow(t("repl.wizard_running")));
208
+ await ctx.withExclusiveInput(async () => {
209
+ const answers = await runSetup(ctx.rl);
210
+ const configPath = join(homedir(), ".mma", "config.json");
211
+ ctx.config.provider.type = answers.provider;
212
+ ctx.config.provider.baseUrl = answers.apiBase;
213
+ ctx.config.provider.apiKey = answers.apiKey;
214
+ ctx.config.model = answers.model;
215
+ ctx.config.contextWindow = answers.contextWindow;
216
+ ctx.config.maxToolIterations = answers.maxToolIterations;
217
+ ctx.config.locale = answers.locale;
218
+ saveConfig(ctx.config, configPath);
219
+ await ctx.agent.reconfigure(ctx.config);
220
+ console.log(pc.green(t("cli.config_saved")));
221
+ });
222
+ },
223
+ });
224
+ ctx.registerCommand({
225
+ name: "sysprompt",
226
+ description: t("repl.sysprompt_desc"),
227
+ usage: t("repl.sysprompt_desc"),
228
+ action: () => {
229
+ const info = ctx.agent.getSystemPromptInfo();
230
+ console.log(pc.bold(`System prompt (${info.tokenCount} tokens):`));
231
+ console.log(pc.dim("─".repeat(60)));
232
+ for (const line of info.text.split("\n")) {
233
+ console.log(line);
234
+ }
235
+ console.log(pc.dim("─".repeat(60)));
236
+ if (info.excluded.length > 0) {
237
+ console.log(pc.yellow(`\nExcluded blocks: ${info.excluded.length}`));
238
+ for (const e of info.excluded) {
239
+ console.log(pc.dim(` - ${e.slice(0, 80)}${e.length > 80 ? "..." : ""}`));
240
+ }
241
+ }
242
+ },
243
+ });
244
+ ctx.registerCommand({
245
+ name: "provider",
246
+ description: t("repl.provider_list"),
247
+ usage: t("repl.provider_usage"),
248
+ action: async (args) => {
249
+ const subcmd = args[0];
250
+ if (!subcmd || subcmd === "list") {
251
+ console.log(`${t("repl.provider_current")} ${ctx.config.provider.type}`);
252
+ console.log(` ${ctx.config.provider.baseUrl}`);
253
+ return;
254
+ }
255
+ if (subcmd === "use") {
256
+ const name = args[1];
257
+ if (!name) {
258
+ console.log(t("repl.provider_usage"));
259
+ return;
260
+ }
261
+ ctx.config.provider.type = name;
262
+ const configPath = join(homedir(), ".mma", "config.json");
263
+ saveConfig(ctx.config, configPath);
264
+ await ctx.agent.reconfigure(ctx.config);
265
+ console.log(pc.green(t("repl.provider_set", { name })));
266
+ return;
267
+ }
268
+ console.log(t("repl.provider_usage"));
269
+ },
270
+ });
271
+ ctx.registerCommand({
272
+ name: "model",
273
+ description: t("repl.model_list"),
274
+ usage: t("repl.model_usage"),
275
+ action: async (args) => {
276
+ const subcmd = args[0];
277
+ if (!subcmd || subcmd === "list") {
278
+ console.log(`${t("repl.model_current")} ${ctx.config.model}`);
279
+ const { OpenAICompatProvider } = await import("../llm/openai-compat");
280
+ const provider = new OpenAICompatProvider({
281
+ model: ctx.config.model,
282
+ baseUrl: ctx.config.provider.baseUrl,
283
+ apiKey: ctx.config.provider.apiKey,
284
+ contextWindow: ctx.config.contextWindow,
285
+ });
286
+ const { Spinner } = await import("../ui/spinner");
287
+ const s = new Spinner();
288
+ s.start(t("cli.fetching_models"));
289
+ try {
290
+ const models = await provider.listModels();
291
+ s.stop();
292
+ if (models.length > 0) {
293
+ console.log(t("cli.available_models"));
294
+ const { getCertMark } = await import("../modules/certification/manifest");
295
+ for (const m of models) {
296
+ const marker = m === ctx.config.model ? pc.green("* ") : " ";
297
+ const mark = getCertMark(m, ctx.config.provider.baseUrl, version);
298
+ const cert = mark === "certified"
299
+ ? pc.green("✔")
300
+ : mark === "stale"
301
+ ? pc.yellow("○")
302
+ : pc.dim("·");
303
+ console.log(` ${marker}${cert} ${m}`);
304
+ }
305
+ }
306
+ else {
307
+ console.log(t("cli.no_models_found"));
308
+ }
309
+ }
310
+ catch (err) {
311
+ s.stop();
312
+ console.log(t("cli.model_fetch_failed", { error: String(err) }));
313
+ }
314
+ console.log(t("cli.model_hint"));
315
+ return;
316
+ }
317
+ if (subcmd === "use") {
318
+ const name = args[1];
319
+ if (!name) {
320
+ console.log(t("repl.model_usage"));
321
+ return;
322
+ }
323
+ ctx.config.model = name;
324
+ const configPath = join(homedir(), ".mma", "config.json");
325
+ saveConfig(ctx.config, configPath);
326
+ await ctx.agent.reconfigure(ctx.config);
327
+ console.log(pc.green(t("repl.model_set", { name })));
328
+ return;
329
+ }
330
+ console.log(t("repl.model_usage"));
331
+ },
332
+ });
333
+ ctx.registerCommand({
334
+ name: "context",
335
+ description: t("cli.manage_context"),
336
+ usage: "/context <size>",
337
+ action: async (args) => {
338
+ if (args.length === 0) {
339
+ console.log(`/context ${t("repl.context")} ${ctx.config.contextWindow}`);
340
+ console.log(`Usage: /context <size> (min 1024)`);
341
+ return;
342
+ }
343
+ const size = parseInt(args[0], 10);
344
+ if (isNaN(size) || size < 1024) {
345
+ console.log(t("cli.invalid_context_size"));
346
+ return;
347
+ }
348
+ ctx.config.contextWindow = size;
349
+ const configPath = join(homedir(), ".mma", "config.json");
350
+ saveConfig(ctx.config, configPath);
351
+ await ctx.agent.reconfigure(ctx.config);
352
+ console.log(pc.green(t("cli.context_set", { size })));
353
+ },
354
+ });
355
+ ctx.registerCommand({
356
+ name: "reload",
357
+ description: t("repl.reload"),
358
+ usage: t("repl.reload_usage"),
359
+ action: async () => {
360
+ console.log(pc.yellow(t("repl.reloading")));
361
+ if (ctx.sessionManager && ctx.config.session.autoSave) {
362
+ const active = ctx.sessionManager.getActiveMeta();
363
+ if (active) {
364
+ // Session is already auto-saved on each message
365
+ }
366
+ }
367
+ ctx.agent.shutdown();
368
+ const { loadConfig } = await import("../config/config");
369
+ const { homedir } = await import("os");
370
+ const { join } = await import("path");
371
+ const configDir = ctx.configDir;
372
+ const baseDir = ctx.baseDir;
373
+ const projectConfigPath = join(baseDir, ".mmrc");
374
+ const freshConfig = loadConfig({ configDir, projectConfigPath });
375
+ Object.assign(ctx.config, freshConfig);
376
+ const { bootstrap } = await import("../core/bootstrap");
377
+ const result = await bootstrap(configDir, baseDir, false, false);
378
+ ctx.agent = result.agent;
379
+ ctx.sessionManager = result.sessionManager;
380
+ ctx.skillsModule = result.skillsModule;
381
+ ctx.pluginManager = result.pluginManager;
382
+ ctx.setupCompleter();
383
+ console.log(pc.green(t("repl.reloaded")));
384
+ console.log(`${t("repl.model")} ${ctx.config.model}`);
385
+ console.log(`${t("repl.context")} ${ctx.config.contextWindow}`);
386
+ console.log(`${t("repl.provider")} ${ctx.config.provider.type} @ ${ctx.config.provider.baseUrl}`);
387
+ },
388
+ });
389
+ ctx.registerCommand({
390
+ name: "plugins",
391
+ description: t("repl.plugins"),
392
+ usage: t("repl.plugins_usage"),
393
+ action: (args) => {
394
+ if (!ctx.pluginManager) {
395
+ console.log(t("cli.plugins.none"));
396
+ return;
397
+ }
398
+ const showAll = args.includes("--all");
399
+ const infos = ctx.pluginManager.getPluginInfos();
400
+ const filtered = showAll ? infos : infos.filter(({ plugin }) => !plugin.isBuiltin);
401
+ if (filtered.length === 0) {
402
+ console.log(t("cli.plugins.none"));
403
+ return;
404
+ }
405
+ const mmaVersion = version;
406
+ console.log(t("cli.plugins.header", {
407
+ count: String(filtered.length),
408
+ mma: mmaVersion,
409
+ }));
410
+ for (const { plugin, source } of filtered) {
411
+ const v = plugin.version || "-";
412
+ const origin = source || "builtin";
413
+ const mark = plugin.isBuiltin ? t("cli.plugins.builtin_mark") : " ";
414
+ console.log(` ${mark} ${plugin.name} v${v} [${origin}]`);
415
+ }
416
+ if (!showAll) {
417
+ const builtinCount = infos.filter(({ plugin }) => plugin.isBuiltin).length;
418
+ if (builtinCount > 0) {
419
+ console.log(pc.dim(` (${builtinCount} builtin hidden — /plugins --all to show)`));
420
+ }
421
+ }
422
+ },
423
+ });
424
+ }
425
+ function registerSessionCommands(ctx) {
426
+ if (!ctx.sessionManager)
427
+ return;
428
+ ctx.registerCommand({
429
+ name: "sessions",
430
+ description: t("repl.sessions"),
431
+ aliases: ["ls"],
432
+ usage: t("repl.sessions_usage"),
433
+ action: () => {
434
+ const sessions = ctx.sessionManager.list();
435
+ const active = ctx.sessionManager.getActive();
436
+ if (sessions.length === 0) {
437
+ console.log(t("session.no_sessions_hint"));
438
+ return;
439
+ }
440
+ const rows = sessions.map((s) => [
441
+ s.id === active ? pc.green("●") : "",
442
+ s.id.slice(0, 12),
443
+ s.name,
444
+ s.updatedAt.slice(0, 19).replace("T", " "),
445
+ String(s.messageCount),
446
+ ]);
447
+ for (const line of renderTable([
448
+ t("session.col_active"),
449
+ t("session.col_id"),
450
+ t("session.col_name"),
451
+ t("session.col_updated"),
452
+ t("session.col_msgs"),
453
+ ], rows)) {
454
+ console.log(line);
455
+ }
456
+ console.log(pc.dim(`\n ${t("repl.resume_hint")}`));
457
+ },
458
+ });
459
+ ctx.registerCommand({
460
+ name: "new",
461
+ description: t("repl.new"),
462
+ aliases: ["create"],
463
+ usage: t("repl.new_usage"),
464
+ action: (args) => {
465
+ const name = args.join(" ") || undefined;
466
+ const meta = ctx.sessionManager.create(name);
467
+ ctx.agent.clearContext();
468
+ console.clear();
469
+ console.log(`${t("session.created", { name: meta.name })} (${pc.dim(meta.id.slice(0, 12))})`);
470
+ console.log(pc.dim(` ${t("session.chat_cleared")}\n`));
471
+ },
472
+ });
473
+ ctx.registerCommand({
474
+ name: "resume",
475
+ description: t("repl.resume"),
476
+ aliases: ["switch", "use"],
477
+ usage: t("repl.resume_usage"),
478
+ action: (args) => {
479
+ const query = args.join(" ");
480
+ const sessions = ctx.sessionManager.list();
481
+ if (!query) {
482
+ console.log(pc.dim(t("session.available")));
483
+ const active = ctx.sessionManager.getActive();
484
+ for (const s of sessions) {
485
+ const marker = s.id === active ? pc.green(" *") : " ";
486
+ console.log(pc.dim(` ${marker} ${s.id.slice(0, 12)} ${s.name}`));
487
+ }
488
+ console.log(pc.dim(`\n ${t("repl.resume_usage")}`));
489
+ return;
490
+ }
491
+ const match = sessions.find((s) => s.id === query ||
492
+ s.id.startsWith(query) ||
493
+ s.name.toLowerCase().includes(query.toLowerCase()));
494
+ if (!match) {
495
+ console.log(t("session.no_match", { query }));
496
+ return;
497
+ }
498
+ ctx.sessionManager.setActive(match.id);
499
+ const history = ctx.sessionManager.loadHistory();
500
+ ctx.agent.setContext(history);
501
+ console.clear();
502
+ console.log(pc.bold(pc.green(t("session.resumed", { name: match.name }))) +
503
+ " " +
504
+ pc.dim(`(${match.id.slice(0, 12)})`) +
505
+ " — " +
506
+ match.messageCount +
507
+ " msgs");
508
+ console.log(pc.dim("─".repeat(50)));
509
+ if (history.length === 0) {
510
+ console.log(pc.dim(t("session.no_history")));
511
+ }
512
+ else {
513
+ console.log(pc.dim(t("session.chat_history")));
514
+ console.log();
515
+ for (const msg of history) {
516
+ if (msg.role === "user") {
517
+ console.log(pc.cyan(t("session.user_label") + ":"));
518
+ console.log(getMessageText(msg.content));
519
+ console.log();
520
+ }
521
+ else if (msg.role === "assistant") {
522
+ console.log(pc.green(t("session.assistant_label") + ":"));
523
+ console.log(getMessageText(msg.content));
524
+ console.log();
525
+ }
526
+ }
527
+ }
528
+ console.log(pc.dim("─".repeat(50)));
529
+ console.log(pc.dim(` ${t("session.chat_loaded")}`));
530
+ },
531
+ });
532
+ ctx.registerCommand({
533
+ name: "rename",
534
+ description: t("repl.rename"),
535
+ usage: t("repl.rename_usage"),
536
+ action: (args) => {
537
+ const name = args.join(" ");
538
+ if (!name) {
539
+ console.log(t("repl.rename_usage"));
540
+ return;
541
+ }
542
+ const active = ctx.sessionManager.getActive();
543
+ if (!active) {
544
+ console.log(t("session.no_active"));
545
+ return;
546
+ }
547
+ ctx.sessionManager.rename(active, name);
548
+ console.log(t("session.renamed", { name }));
549
+ },
550
+ });
551
+ ctx.registerCommand({
552
+ name: "delete",
553
+ description: t("repl.delete"),
554
+ aliases: ["rm"],
555
+ usage: t("repl.delete_usage"),
556
+ action: (args) => {
557
+ const query = args[0];
558
+ if (!query) {
559
+ console.log(t("repl.delete_usage"));
560
+ return;
561
+ }
562
+ const sessions = ctx.sessionManager.list();
563
+ const match = sessions.find((s) => s.id === query || s.id.startsWith(query));
564
+ if (!match) {
565
+ console.log(t("session.no_match", { query }));
566
+ return;
567
+ }
568
+ ctx.sessionManager.delete(match.id);
569
+ console.log(`${t("session.deleted", { id: match.id })}: ${match.name}`);
570
+ },
571
+ });
572
+ }
573
+ function registerSkillCommands(ctx) {
574
+ if (!ctx.skillsModule)
575
+ return;
576
+ ctx.registerCommand({
577
+ name: "skill",
578
+ description: t("repl.skill"),
579
+ usage: t("repl.skill_usage"),
580
+ action: (args) => {
581
+ const subcmd = args[0];
582
+ const arg = args.slice(1).join(" ");
583
+ if (!subcmd || subcmd === "list") {
584
+ const available = ctx.skillsModule.getAvailable();
585
+ if (available.length === 0) {
586
+ console.log(t("repl.no_skills"));
587
+ return;
588
+ }
589
+ console.log(pc.bold(t("repl.available_skills")));
590
+ for (const skill of available) {
591
+ const tokens = Math.ceil(skill.content.length / 4);
592
+ const loaded = ctx.skillsModule.getLoaded().some((s) => s.name === skill.name);
593
+ const marker = loaded ? pc.green(" [loaded]") : "";
594
+ console.log(` ${pc.cyan(skill.name)}${marker} ${pc.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
595
+ }
596
+ return;
597
+ }
598
+ if (subcmd === "loaded") {
599
+ const loaded = ctx.skillsModule.getLoaded();
600
+ const budget = ctx.skillsModule.getBudget();
601
+ if (loaded.length === 0) {
602
+ console.log(t("repl.no_loaded"));
603
+ return;
604
+ }
605
+ console.log(pc.bold(t("repl.loaded_skills")));
606
+ for (const skill of loaded) {
607
+ const tokens = Math.ceil(skill.content.length / 4);
608
+ console.log(` ${pc.cyan(skill.name)} ${pc.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
609
+ }
610
+ console.log(pc.dim(`\n ${t("repl.budget", { used: budget.used, total: budget.total, remaining: budget.remaining })}`));
611
+ return;
612
+ }
613
+ if (subcmd === "load") {
614
+ if (!arg) {
615
+ console.log(t("repl.skill_load_usage"));
616
+ return;
617
+ }
618
+ const result = ctx.skillsModule.loadByName(arg);
619
+ if (result.success) {
620
+ console.log(pc.green(result.message));
621
+ }
622
+ else {
623
+ console.log(pc.red(result.message));
624
+ }
625
+ return;
626
+ }
627
+ if (subcmd === "unload") {
628
+ if (!arg) {
629
+ console.log(t("repl.skill_unload_usage"));
630
+ return;
631
+ }
632
+ if (ctx.skillsModule.unload(arg)) {
633
+ console.log(pc.green(t("repl.skill_unloaded", { name: arg })));
634
+ }
635
+ else {
636
+ console.log(pc.red(t("repl.skill_not_loaded", { name: arg })));
637
+ }
638
+ return;
639
+ }
640
+ if (subcmd === "search") {
641
+ if (!arg) {
642
+ console.log(t("repl.skill_search_usage"));
643
+ return;
644
+ }
645
+ const results = ctx.skillsModule.search(arg);
646
+ if (results.length === 0) {
647
+ console.log(t("repl.no_skill_match", { query: arg }));
648
+ return;
649
+ }
650
+ console.log(pc.bold(t("repl.skills_matching", { query: arg })));
651
+ for (const skill of results) {
652
+ const tokens = Math.ceil(skill.content.length / 4);
653
+ console.log(` ${pc.cyan(skill.name)} ${pc.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
654
+ }
655
+ return;
656
+ }
657
+ console.log(pc.red(t("repl.skill_unknown_sub", { subcmd })));
658
+ console.log(t("repl.skill_usage"));
659
+ },
660
+ });
661
+ }