micro-models-agent 0.4.0 → 0.6.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 (119) hide show
  1. package/dist/agent/context.d.ts +3 -6
  2. package/dist/agent/context.d.ts.map +1 -1
  3. package/dist/agent/context.js +8 -41
  4. package/dist/agent/context.js.map +1 -1
  5. package/dist/agent/error-escalation.d.ts +6 -6
  6. package/dist/agent/error-escalation.d.ts.map +1 -1
  7. package/dist/agent/error-escalation.js +67 -34
  8. package/dist/agent/error-escalation.js.map +1 -1
  9. package/dist/agent/loop.d.ts.map +1 -1
  10. package/dist/agent/loop.js +14 -79
  11. package/dist/agent/loop.js.map +1 -1
  12. package/dist/agent/memory.d.ts +0 -29
  13. package/dist/agent/memory.d.ts.map +1 -1
  14. package/dist/agent/memory.js +1 -78
  15. package/dist/agent/memory.js.map +1 -1
  16. package/dist/agent/modes/registry.d.ts +1 -1
  17. package/dist/agent/modes/registry.d.ts.map +1 -1
  18. package/dist/agent/modes/registry.js +26 -27
  19. package/dist/agent/modes/registry.js.map +1 -1
  20. package/dist/agent/modes/types.d.ts +0 -5
  21. package/dist/agent/modes/types.d.ts.map +1 -1
  22. package/dist/agent/system-prompt.d.ts +1 -1
  23. package/dist/agent/system-prompt.d.ts.map +1 -1
  24. package/dist/agent/system-prompt.js +95 -147
  25. package/dist/agent/system-prompt.js.map +1 -1
  26. package/dist/agent/tool-executor.d.ts +2 -6
  27. package/dist/agent/tool-executor.d.ts.map +1 -1
  28. package/dist/agent/tool-executor.js +79 -65
  29. package/dist/agent/tool-executor.js.map +1 -1
  30. package/dist/agent/user-profile.d.ts +1 -21
  31. package/dist/agent/user-profile.d.ts.map +1 -1
  32. package/dist/agent/user-profile.js +41 -174
  33. package/dist/agent/user-profile.js.map +1 -1
  34. package/dist/agent/write-enforcer.d.ts +3 -7
  35. package/dist/agent/write-enforcer.d.ts.map +1 -1
  36. package/dist/agent/write-enforcer.js +7 -71
  37. package/dist/agent/write-enforcer.js.map +1 -1
  38. package/dist/cli/config.d.ts.map +1 -1
  39. package/dist/cli/config.js +2 -25
  40. package/dist/cli/config.js.map +1 -1
  41. package/dist/cli.d.ts +1 -1
  42. package/dist/cli.d.ts.map +1 -1
  43. package/dist/cli.js +102 -139
  44. package/dist/cli.js.map +1 -1
  45. package/dist/config/config.d.ts +15 -0
  46. package/dist/config/config.d.ts.map +1 -1
  47. package/dist/config/config.js +4 -38
  48. package/dist/config/config.js.map +1 -1
  49. package/dist/config/types.d.ts +0 -2
  50. package/dist/config/types.d.ts.map +1 -1
  51. package/dist/logger.d.ts +10 -3
  52. package/dist/logger.d.ts.map +1 -1
  53. package/dist/logger.js +89 -38
  54. package/dist/logger.js.map +1 -1
  55. package/dist/repl/commands.d.ts +16 -16
  56. package/dist/repl/commands.d.ts.map +1 -1
  57. package/dist/repl/commands.js +474 -321
  58. package/dist/repl/commands.js.map +1 -1
  59. package/dist/repl/index.d.ts +4 -4
  60. package/dist/repl/index.d.ts.map +1 -1
  61. package/dist/repl/index.js +60 -56
  62. package/dist/repl/index.js.map +1 -1
  63. package/dist/session/store.d.ts +4 -7
  64. package/dist/session/store.d.ts.map +1 -1
  65. package/dist/session/store.js +35 -71
  66. package/dist/session/store.js.map +1 -1
  67. package/dist/setup-wizard.d.ts.map +1 -1
  68. package/dist/setup-wizard.js +5 -23
  69. package/dist/setup-wizard.js.map +1 -1
  70. package/dist/styling/engine.d.ts +7 -12
  71. package/dist/styling/engine.d.ts.map +1 -1
  72. package/dist/styling/engine.js +85 -134
  73. package/dist/styling/engine.js.map +1 -1
  74. package/dist/styling/index.d.ts +2 -2
  75. package/dist/styling/index.d.ts.map +1 -1
  76. package/dist/styling/index.js +0 -1
  77. package/dist/styling/index.js.map +1 -1
  78. package/dist/styling/loader.d.ts +2 -2
  79. package/dist/styling/loader.d.ts.map +1 -1
  80. package/dist/styling/loader.js +99 -47
  81. package/dist/styling/loader.js.map +1 -1
  82. package/dist/subagent/chat.d.ts +19 -0
  83. package/dist/subagent/chat.d.ts.map +1 -0
  84. package/dist/subagent/chat.js +279 -0
  85. package/dist/subagent/chat.js.map +1 -0
  86. package/dist/subagent/context.d.ts +9 -0
  87. package/dist/subagent/context.d.ts.map +1 -0
  88. package/dist/subagent/context.js +75 -0
  89. package/dist/subagent/context.js.map +1 -0
  90. package/dist/subagent/definition.d.ts +7 -0
  91. package/dist/subagent/definition.d.ts.map +1 -0
  92. package/dist/subagent/definition.js +26 -0
  93. package/dist/subagent/definition.js.map +1 -0
  94. package/dist/subagent/errors.d.ts +24 -0
  95. package/dist/subagent/errors.d.ts.map +1 -0
  96. package/dist/subagent/errors.js +85 -0
  97. package/dist/subagent/errors.js.map +1 -0
  98. package/dist/subagent/validation.d.ts +17 -0
  99. package/dist/subagent/validation.d.ts.map +1 -0
  100. package/dist/subagent/validation.js +83 -0
  101. package/dist/subagent/validation.js.map +1 -0
  102. package/dist/tools/hooks.d.ts +2 -0
  103. package/dist/tools/hooks.d.ts.map +1 -1
  104. package/dist/tools/hooks.js +118 -16
  105. package/dist/tools/hooks.js.map +1 -1
  106. package/dist/tools/parse-tool-call.d.ts.map +1 -1
  107. package/dist/tools/parse-tool-call.js +16 -14
  108. package/dist/tools/parse-tool-call.js.map +1 -1
  109. package/dist/tools/registry.d.ts +5 -3
  110. package/dist/tools/registry.d.ts.map +1 -1
  111. package/dist/tools/registry.js +47 -28
  112. package/dist/tools/registry.js.map +1 -1
  113. package/dist/tools/subagent.d.ts +4 -57
  114. package/dist/tools/subagent.d.ts.map +1 -1
  115. package/dist/tools/subagent.js +112 -556
  116. package/dist/tools/subagent.js.map +1 -1
  117. package/dist/tools/types.d.ts +2 -10
  118. package/dist/tools/types.d.ts.map +1 -1
  119. package/package.json +1 -1
@@ -1,72 +1,106 @@
1
- import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync } from 'node:fs';
2
- import { join, basename } from 'node:path';
3
- import { globSync } from 'node:fs';
4
- import { execSync } from 'node:child_process';
5
- import { executeBash } from '../tools/bash.js';
6
- import { runAgentLoop } from '../agent/loop.js';
7
- import { loadMemory, saveMemory, searchMemory, loadAllMemory, ensureMemoryDir, removeMemoryEntry, exportMemory } from '../agent/memory.js';
8
- import { loadProfile, appendNote } from '../agent/user-profile.js';
9
- import { listSkillsBySource } from '../skills/loader.js';
10
- import { listSessions, loadSession, createNamedSession, createAutoSession, formatSessionList } from '../session/store.js';
11
- import { rebuildCodeIndex, rebuildProjectMap, getOrBuildCodeIndex, getOrBuildProjectMap } from '../indexer/cache.js';
12
- import { getTheme, listThemes, switchTheme } from '../styling/index.js';
13
- import { loadThemeConfig, installTheme, removeTheme } from '../styling/loader.js';
14
- import { loadConfig, saveConfig } from '../config/config.js';
15
- import { logInfo } from '../logger.js';
16
- import { PluginManager } from '../plugins/manager.js';
17
- import { loadAllPluginsWithInfo } from '../plugins/loader.js';
18
- /** Возвращает список всех доступных слэш-команд */
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, } from "node:fs";
2
+ import { join, basename } from "node:path";
3
+ import { globSync } from "node:fs";
4
+ import { execSync } from "node:child_process";
5
+ import { executeBash } from "../tools/bash.js";
6
+ import { runAgentLoop } from "../agent/loop.js";
7
+ import { loadMemory, saveMemory, searchMemory, loadAllMemory, ensureMemoryDir, removeMemoryEntry, exportMemory, } from "../agent/memory.js";
8
+ import { loadProfile, appendNote, } from "../agent/user-profile.js";
9
+ import { listSkillsBySource } from "../skills/loader.js";
10
+ import { listSessions, loadSession, createNamedSession, createAutoSession, formatSessionList, } from "../session/store.js";
11
+ import { rebuildCodeIndex, rebuildProjectMap, getOrBuildCodeIndex, getOrBuildProjectMap, } from "../indexer/cache.js";
12
+ import { getTheme, listThemes, switchTheme } from "../styling/index.js";
13
+ import { loadThemeConfig, installTheme, removeTheme, } from "../styling/loader.js";
14
+ import { loadConfig, saveConfig } from "../config/config.js";
15
+ import { logInfo } from "../logger.js";
16
+ /** Возвращает список всех доступных слэш-команд */
19
17
  export function getSlashCommands() {
20
18
  return [
21
- { name: 'exit', description: 'exit MMA', handler: cmdExit },
22
- { name: 'quit', description: 'exit MMA', handler: cmdExit },
23
- { name: 'help', description: 'show help', handler: cmdHelp },
24
- { name: 'bash', description: 'run shell command', handler: cmdBash },
25
- { name: 'read', description: 'display file', handler: cmdRead },
26
- { name: 'write', description: 'write file (end with EOF)', handler: cmdWrite },
27
- { name: 'edit', description: 'replace line in file', handler: cmdEdit },
28
- { name: 'find', description: 'find files by glob', handler: cmdFind },
29
- { name: 'search', description: 'search file contents', handler: cmdSearch },
30
- { name: 'git', description: 'git operations', handler: cmdGit },
31
- { name: 'memory', description: 'memory management', handler: cmdMemory },
32
- { name: 'skill', description: 'list skills', handler: cmdSkill },
33
- { name: 'plugins', description: 'list plugins', handler: cmdPlugins },
34
- { name: 'sessions', description: 'list sessions', handler: cmdSessions },
35
- { name: 'resume', description: 'resume session', handler: cmdResume },
36
- { name: 'save', description: 'save session', handler: cmdSave },
37
- { name: 'clear', description: 'clear screen (visual only)', handler: cmdClear },
38
- { name: 'new', description: 'reset session (clear context)', handler: cmdNew },
39
- { name: 'stop', description: 'stop agent loop', handler: cmdStop },
40
- { name: 'init', description: 'initialize project & create AGENTS.md', handler: cmdInit },
41
- { name: 'index', description: 'index management', handler: cmdIndex },
42
- { name: 'theme', description: 'theme management (list/switch/show/install/create/remove)', handler: cmdTheme },
43
- { name: 'profile', description: 'show/edit user profile', handler: cmdProfile },
44
- { name: 'tools', description: 'list available agent tools', handler: cmdTools },
45
- { name: 'export', description: 'export session (normal or debug)', handler: cmdExport },
19
+ { name: "exit", description: "exit MMA", handler: cmdExit },
20
+ { name: "quit", description: "exit MMA", handler: cmdExit },
21
+ { name: "help", description: "show help", handler: cmdHelp },
22
+ { name: "bash", description: "run shell command", handler: cmdBash },
23
+ { name: "read", description: "display file", handler: cmdRead },
24
+ {
25
+ name: "write",
26
+ description: "write file (end with EOF)",
27
+ handler: cmdWrite,
28
+ },
29
+ { name: "edit", description: "replace line in file", handler: cmdEdit },
30
+ { name: "find", description: "find files by glob", handler: cmdFind },
31
+ { name: "search", description: "search file contents", handler: cmdSearch },
32
+ { name: "git", description: "git operations", handler: cmdGit },
33
+ { name: "memory", description: "memory management", handler: cmdMemory },
34
+ { name: "skill", description: "list skills", handler: cmdSkill },
35
+ { name: "sessions", description: "list sessions", handler: cmdSessions },
36
+ { name: "resume", description: "resume session", handler: cmdResume },
37
+ { name: "save", description: "save session", handler: cmdSave },
38
+ {
39
+ name: "clear",
40
+ description: "clear screen (visual only)",
41
+ handler: cmdClear,
42
+ },
43
+ {
44
+ name: "new",
45
+ description: "reset session (clear context)",
46
+ handler: cmdNew,
47
+ },
48
+ { name: "stop", description: "stop agent loop", handler: cmdStop },
49
+ {
50
+ name: "init",
51
+ description: "initialize project & create AGENTS.md",
52
+ handler: cmdInit,
53
+ },
54
+ { name: "index", description: "index management", handler: cmdIndex },
55
+ {
56
+ name: "theme",
57
+ description: "theme management (list/switch/show/install/create/remove)",
58
+ handler: cmdTheme,
59
+ },
60
+ {
61
+ name: "profile",
62
+ description: "show/edit user profile",
63
+ handler: cmdProfile,
64
+ },
65
+ {
66
+ name: "tools",
67
+ description: "list available agent tools",
68
+ handler: cmdTools,
69
+ },
70
+ {
71
+ name: "export",
72
+ description: "export session (normal or debug)",
73
+ handler: cmdExport,
74
+ },
46
75
  ];
47
76
  }
48
- /** Парсит строку ввода на слэш-команду и аргументы */
77
+ /** Парсит строку ввода на слэш-команду и аргументы */
49
78
  export function parseSlashCommand(line) {
50
- if (!line.startsWith('/'))
79
+ if (!line.startsWith("/"))
51
80
  return null;
52
- const spaceIdx = line.indexOf(' ');
81
+ const spaceIdx = line.indexOf(" ");
53
82
  const cmd = spaceIdx === -1 ? line.slice(1) : line.slice(1, spaceIdx);
54
- const args = spaceIdx === -1 ? '' : line.slice(spaceIdx + 1);
83
+ const args = spaceIdx === -1 ? "" : line.slice(spaceIdx + 1);
55
84
  return { cmd, args };
56
85
  }
57
86
  function cmdExit(_args, ctx) {
58
87
  if (ctx.conversationHistory.length > 0) {
59
- createAutoSession(ctx.conversationHistory, ctx.modelLabel, process.cwd(), { totalTokens: 0, compactCount: 0 });
60
- ctx.renderer.writeSuccess('Session auto-saved.');
88
+ createAutoSession(ctx.conversationHistory, ctx.modelLabel, process.cwd(), {
89
+ totalTokens: 0,
90
+ compactCount: 0,
91
+ });
92
+ ctx.renderer.writeSuccess("Session auto-saved.");
61
93
  }
62
- ctx.renderer.writeDim('Bye!');
94
+ ctx.renderer.writeDim("Bye!");
63
95
  ctx.renderer.destroy();
64
96
  }
65
97
  function formatHelpCmd(theme, cmd) {
66
98
  const parts = cmd.split(/\s+/);
67
99
  const cmdName = parts[0];
68
- const rest = parts.slice(1).join(' ');
69
- const styled = cmdName.startsWith('/') ? theme.toolName(cmdName) : theme.toolName(cmdName);
100
+ const rest = parts.slice(1).join(" ");
101
+ const styled = cmdName.startsWith("/")
102
+ ? theme.toolName(cmdName)
103
+ : theme.toolName(cmdName);
70
104
  if (!rest)
71
105
  return styled;
72
106
  return `${styled} ${theme.dim(rest)}`;
@@ -75,65 +109,79 @@ function cmdHelp(_args, ctx) {
75
109
  const theme = getTheme();
76
110
  const msgCount = ctx.conversationHistory.length;
77
111
  const cwd = process.cwd();
78
- ctx.renderer.writeDim(` ── session info ──`);
112
+ ctx.renderer.writeDim(` в”Ђв”Ђ session info в”Ђв”Ђ`);
79
113
  if (ctx.sessionId) {
80
114
  ctx.renderer.writeDim(` session: ${ctx.sessionId}`);
81
115
  }
82
116
  ctx.renderer.writeDim(` model: ${ctx.modelLabel}`);
83
- ctx.renderer.writeDim(` mode: ${ctx.currentMode || 'base'}`);
117
+ ctx.renderer.writeDim(` mode: ${ctx.currentMode || "base"}`);
84
118
  ctx.renderer.writeDim(` messages: ${msgCount}`);
85
119
  ctx.renderer.writeDim(` cwd: ${cwd}`);
86
120
  ctx.renderer.writeNewline();
87
121
  const slashHelp = [
88
- { cmd: '/bash <cmd>', desc: 'run shell command' },
89
- { cmd: '/read <file>', desc: 'display file with line numbers' },
90
- { cmd: '/edit <file> <n>', desc: "replace line N in file with rest of args" },
91
- { cmd: '/write <file>', desc: 'write file (end with EOF on its own line)' },
92
- { cmd: '/find <pattern>', desc: 'find files by glob pattern' },
93
- { cmd: '/search <pattern>', desc: 'search file contents' },
94
- { cmd: '/git <args>', desc: 'git operations' },
95
- { cmd: '/memory', desc: 'memory management (show/add/search/remove/export)' },
96
- { cmd: '/skill', desc: 'list installed skills' },
97
- { cmd: '/plugins', desc: 'list plugins' },
98
- { cmd: '/tools', desc: 'list agent tools' },
99
- { cmd: '/sessions', desc: 'list saved sessions' },
100
- { cmd: '/resume <id>', desc: 'resume a session' },
101
- { cmd: '/save [name]', desc: 'save current session' },
102
- { cmd: '/clear', desc: 'clear screen (visual only)' },
103
- { cmd: '/new', desc: 'reset session (clear history)' },
104
- { cmd: '/export [mode]', desc: 'export session (normal or debug)' },
105
- { cmd: '/init', desc: 'analyze project & create AGENTS.md' },
106
- { cmd: '/index [cmd]', desc: 'index status/show/map/rebuild' },
107
- { cmd: '/profile [note]', desc: 'show profile or add note' },
108
- { cmd: '/profile name <n>', desc: 'save your name' },
109
- { cmd: '/profile show', desc: 'show profile content' },
110
- { cmd: '/mode fast|base|pro', desc: 'switch agent mode' },
111
- { cmd: '/stop', desc: 'stop agent loop' },
112
- { cmd: '/exit', desc: 'exit MMA' },
122
+ { cmd: "/bash <cmd>", desc: "run shell command" },
123
+ { cmd: "/read <file>", desc: "display file with line numbers" },
124
+ {
125
+ cmd: "/edit <file> <n>",
126
+ desc: "replace line N in file with rest of args",
127
+ },
128
+ { cmd: "/write <file>", desc: "write file (end with EOF on its own line)" },
129
+ { cmd: "/find <pattern>", desc: "find files by glob pattern" },
130
+ { cmd: "/search <pattern>", desc: "search file contents" },
131
+ { cmd: "/git <args>", desc: "git operations" },
132
+ {
133
+ cmd: "/memory",
134
+ desc: "memory management (show/add/search/remove/export)",
135
+ },
136
+ { cmd: "/skill", desc: "list installed skills" },
137
+ { cmd: "/tools", desc: "list agent tools" },
138
+ { cmd: "/sessions", desc: "list saved sessions" },
139
+ { cmd: "/resume <id>", desc: "resume a session" },
140
+ { cmd: "/save [name]", desc: "save current session" },
141
+ { cmd: "/clear", desc: "clear screen (visual only)" },
142
+ { cmd: "/new", desc: "reset session (clear history)" },
143
+ { cmd: "/export [mode]", desc: "export session (normal or debug)" },
144
+ { cmd: "/init", desc: "analyze project & create AGENTS.md" },
145
+ { cmd: "/index [cmd]", desc: "index status/show/map/rebuild" },
146
+ { cmd: "/profile [note]", desc: "show profile or add note" },
147
+ { cmd: "/profile name <n>", desc: "save your name" },
148
+ { cmd: "/profile show", desc: "show profile content" },
149
+ { cmd: "/mode fast|base|pro", desc: "switch agent mode" },
150
+ { cmd: "/stop", desc: "stop agent loop" },
151
+ { cmd: "/exit", desc: "exit MMA" },
113
152
  ];
114
- ctx.renderer.writeLine(` ${theme.highlight('/ commands')}`);
153
+ ctx.renderer.writeLine(` ${theme.highlight("/ commands")}`);
115
154
  for (const entry of slashHelp) {
116
- ctx.renderer.writeLine(` ${formatHelpCmd(theme, entry.cmd)} ${theme.dim(`— ${entry.desc}`)}`);
155
+ ctx.renderer.writeLine(` ${formatHelpCmd(theme, entry.cmd)} ${theme.dim(`— ${entry.desc}`)}`);
117
156
  }
118
157
  ctx.renderer.writeNewline();
119
158
  const cliHelp = [
120
- { cmd: 'mma provider list', desc: 'list providers' },
121
- { cmd: 'mma provider add <name> <url>', desc: 'add provider (Ollama, vLLM, Together...)' },
122
- { cmd: 'mma provider use <name>', desc: 'switch active provider' },
123
- { cmd: 'mma provider remove <name>', desc: 'remove provider' },
124
- { cmd: 'mma model list', desc: 'list models' },
125
- { cmd: 'mma model add <name> <id> <ctx>', desc: 'add model (e.g. qwen3.5-9b ... 32768)' },
126
- { cmd: 'mma model use <name>', desc: 'switch active model' },
127
- { cmd: 'mma model params [name]', desc: 'show model parameters' },
128
- { cmd: 'mma model set <name> k=v', desc: 'change parameter (temperature=0.3)' },
129
- { cmd: 'mma config show', desc: 'show full configuration' },
130
- { cmd: 'mma config reset', desc: 'reset to defaults' },
131
- { cmd: 'mma update', desc: 'check for updates' },
132
- { cmd: 'mma update install', desc: 'install latest version' },
159
+ { cmd: "mma provider list", desc: "list providers" },
160
+ {
161
+ cmd: "mma provider add <name> <url>",
162
+ desc: "add provider (Ollama, vLLM, Together...)",
163
+ },
164
+ { cmd: "mma provider use <name>", desc: "switch active provider" },
165
+ { cmd: "mma provider remove <name>", desc: "remove provider" },
166
+ { cmd: "mma model list", desc: "list models" },
167
+ {
168
+ cmd: "mma model add <name> <id> <ctx>",
169
+ desc: "add model (e.g. qwen3.5-9b ... 32768)",
170
+ },
171
+ { cmd: "mma model use <name>", desc: "switch active model" },
172
+ { cmd: "mma model params [name]", desc: "show model parameters" },
173
+ {
174
+ cmd: "mma model set <name> k=v",
175
+ desc: "change parameter (temperature=0.3)",
176
+ },
177
+ { cmd: "mma config show", desc: "show full configuration" },
178
+ { cmd: "mma config reset", desc: "reset to defaults" },
179
+ { cmd: "mma update", desc: "check for updates" },
180
+ { cmd: "mma update install", desc: "install latest version" },
133
181
  ];
134
- ctx.renderer.writeLine(` ${theme.highlight('CLI commands')} ${theme.dim('(exit REPL, then run)')}`);
182
+ ctx.renderer.writeLine(` ${theme.highlight("CLI commands")} ${theme.dim("(exit REPL, then run)")}`);
135
183
  for (const entry of cliHelp) {
136
- ctx.renderer.writeLine(` ${formatHelpCmd(theme, entry.cmd)} ${theme.dim(`— ${entry.desc}`)}`);
184
+ ctx.renderer.writeLine(` ${formatHelpCmd(theme, entry.cmd)} ${theme.dim(`— ${entry.desc}`)}`);
137
185
  }
138
186
  }
139
187
  async function cmdBash(args, ctx) {
@@ -145,8 +193,8 @@ async function cmdBash(args, ctx) {
145
193
  }
146
194
  function cmdRead(args, ctx) {
147
195
  try {
148
- const content = readFileSync(args, 'utf-8');
149
- const lines = content.split('\n');
196
+ const content = readFileSync(args, "utf-8");
197
+ const lines = content.split("\n");
150
198
  const maxLines = 200;
151
199
  ctx.renderer.writeNewline();
152
200
  const shown = lines.slice(0, maxLines);
@@ -162,23 +210,30 @@ function cmdRead(args, ctx) {
162
210
  }
163
211
  let _currentWritePath = null;
164
212
  let _writeBuffer = [];
165
- export function getWriteState() { return { path: _currentWritePath, buffer: _writeBuffer }; }
166
- export function clearWriteState() { _currentWritePath = null; _writeBuffer = []; }
167
- export function isWriteMode() { return _currentWritePath !== null; }
213
+ export function getWriteState() {
214
+ return { path: _currentWritePath, buffer: _writeBuffer };
215
+ }
216
+ export function clearWriteState() {
217
+ _currentWritePath = null;
218
+ _writeBuffer = [];
219
+ }
220
+ export function isWriteMode() {
221
+ return _currentWritePath !== null;
222
+ }
168
223
  function cmdWrite(args, ctx) {
169
224
  if (!args) {
170
- ctx.renderer.writeInfo(' Usage: /write <file> (then type content, end with EOF)');
225
+ ctx.renderer.writeInfo(" Usage: /write <file> (then type content, end with EOF)");
171
226
  }
172
227
  else {
173
228
  _currentWritePath = args;
174
- ctx.renderer.writeDim(' Type your content. End with EOF on its own line:');
229
+ ctx.renderer.writeDim(" Type your content. End with EOF on its own line:");
175
230
  }
176
231
  }
177
232
  export function handleWriteLine(line, renderer) {
178
- if (line.trim() === 'EOF') {
233
+ if (line.trim() === "EOF") {
179
234
  const theme = getTheme();
180
235
  try {
181
- writeFileSync(_currentWritePath, _writeBuffer.join('\n'), 'utf-8');
236
+ writeFileSync(_currentWritePath, _writeBuffer.join("\n"), "utf-8");
182
237
  renderer.writeSuccess(`written to ${theme.filePath(_currentWritePath)}`);
183
238
  }
184
239
  catch (e) {
@@ -194,26 +249,26 @@ export function handleWriteLine(line, renderer) {
194
249
  function cmdEdit(args, ctx) {
195
250
  const parts = args.split(/\s+/);
196
251
  if (parts.length < 3) {
197
- ctx.renderer.writeInfo(' Usage: /edit <file> <line> <new content>');
198
- ctx.renderer.writeInfo(' Example: /edit src/index.ts 5 const x = 1;');
252
+ ctx.renderer.writeInfo(" Usage: /edit <file> <line> <new content>");
253
+ ctx.renderer.writeInfo(" Example: /edit src/index.ts 5 const x = 1;");
199
254
  return;
200
255
  }
201
256
  const [filePath, lineStr, ...rest] = parts;
202
257
  const lineNum = parseInt(lineStr, 10);
203
258
  if (isNaN(lineNum) || lineNum < 1) {
204
- ctx.renderer.writeDim(' Line number must be a positive integer');
259
+ ctx.renderer.writeDim(" Line number must be a positive integer");
205
260
  return;
206
261
  }
207
- const newContent = rest.join(' ');
262
+ const newContent = rest.join(" ");
208
263
  try {
209
- const content = readFileSync(filePath, 'utf-8');
210
- const lines = content.split('\n');
264
+ const content = readFileSync(filePath, "utf-8");
265
+ const lines = content.split("\n");
211
266
  if (lineNum > lines.length) {
212
267
  ctx.renderer.writeWarning(` File has only ${lines.length} lines`);
213
268
  return;
214
269
  }
215
270
  lines[lineNum - 1] = newContent;
216
- writeFileSync(filePath, lines.join('\n'), 'utf-8');
271
+ writeFileSync(filePath, lines.join("\n"), "utf-8");
217
272
  const theme = getTheme();
218
273
  ctx.renderer.writeSuccess(`Line ${lineNum} updated in ${theme.filePath(filePath)}`);
219
274
  }
@@ -223,11 +278,18 @@ function cmdEdit(args, ctx) {
223
278
  }
224
279
  function cmdFind(args, ctx) {
225
280
  try {
226
- const IGNORE = ['node_modules', '.git', 'dist', '.mma', 'build', '__pycache__'];
227
- const fixedPattern = args.includes('/') || args.includes('\\') ? args : `**/${args}`;
228
- const results = globSync(fixedPattern).filter(f => !f.split(/[/\\]/).some(p => IGNORE.includes(p)));
281
+ const IGNORE = [
282
+ "node_modules",
283
+ ".git",
284
+ "dist",
285
+ ".mma",
286
+ "build",
287
+ "__pycache__",
288
+ ];
289
+ const fixedPattern = args.includes("/") || args.includes("\\") ? args : `**/${args}`;
290
+ const results = globSync(fixedPattern).filter((f) => !f.split(/[/\\]/).some((p) => IGNORE.includes(p)));
229
291
  if (results.length === 0) {
230
- ctx.renderer.writeDim(' No matches');
292
+ ctx.renderer.writeDim(" No matches");
231
293
  return;
232
294
  }
233
295
  const theme = getTheme();
@@ -241,18 +303,18 @@ function cmdFind(args, ctx) {
241
303
  }
242
304
  function cmdSearch(args, ctx) {
243
305
  try {
244
- const result = execSync(`findstr /s /n /c:"${args}" *.ts *.js *.tsx *.jsx *.json *.md`, { encoding: 'utf-8', timeout: 10000 });
245
- const allLines = result.trim().split('\n');
306
+ const result = execSync(`findstr /s /n /c:"${args}" *.ts *.js *.tsx *.jsx *.json *.md`, { encoding: "utf-8", timeout: 10000 });
307
+ const allLines = result.trim().split("\n");
246
308
  const lines = allLines.slice(0, 30);
247
- if (lines.length === 0 || (lines.length === 1 && lines[0] === '')) {
248
- ctx.renderer.writeDim(' No matches');
309
+ if (lines.length === 0 || (lines.length === 1 && lines[0] === "")) {
310
+ ctx.renderer.writeDim(" No matches");
249
311
  return;
250
312
  }
251
313
  const theme = getTheme();
252
314
  for (const line of lines) {
253
315
  const match = line.match(/^([^:]+):(\d+):(.*)/);
254
316
  if (match) {
255
- ctx.renderer.writeLine(` ${theme.filePath(match[1])}${theme.dim(':')}${theme.lineNumbers(match[2])}${theme.dim(':')}${match[3]}`);
317
+ ctx.renderer.writeLine(` ${theme.filePath(match[1])}${theme.dim(":")}${theme.lineNumbers(match[2])}${theme.dim(":")}${match[3]}`);
256
318
  }
257
319
  else {
258
320
  ctx.renderer.writeLine(`${theme.indent}${line}`);
@@ -264,7 +326,7 @@ function cmdSearch(args, ctx) {
264
326
  }
265
327
  catch (e) {
266
328
  if (e.status === 1) {
267
- ctx.renderer.writeDim(' No matches');
329
+ ctx.renderer.writeDim(" No matches");
268
330
  }
269
331
  else {
270
332
  ctx.renderer.writeError(e.message);
@@ -287,19 +349,19 @@ function cmdMemory(args, ctx) {
287
349
  ctx.renderer.writeLine(summary);
288
350
  }
289
351
  else {
290
- ctx.renderer.writeDim(' Project memory is empty. Use /memory add <type> <text>');
352
+ ctx.renderer.writeDim(" Project memory is empty. Use /memory add <type> <text>");
291
353
  }
292
354
  return;
293
355
  }
294
356
  const parts = args.trim().split(/\s+/);
295
357
  const subcommand = parts[0];
296
358
  const rest = parts.slice(1);
297
- const type = rest[0] || '';
298
- const text = rest.slice(1).join(' ') || '';
359
+ const type = rest[0] || "";
360
+ const text = rest.slice(1).join(" ") || "";
299
361
  switch (subcommand) {
300
- case 'show': {
362
+ case "show": {
301
363
  if (!type) {
302
- ctx.renderer.writeInfo(' Usage: /memory show <type> (project|conventions|decisions|errors|history)');
364
+ ctx.renderer.writeInfo(" Usage: /memory show <type> (project|conventions|decisions|errors|history)");
303
365
  return;
304
366
  }
305
367
  const content = loadMemory(type);
@@ -308,24 +370,24 @@ function cmdMemory(args, ctx) {
308
370
  ctx.renderer.writeNewline();
309
371
  break;
310
372
  }
311
- case 'add': {
373
+ case "add": {
312
374
  if (!type || !text) {
313
- ctx.renderer.writeInfo(' Usage: /memory add <type> <text>');
314
- ctx.renderer.writeInfo(' Types: project, conventions, decisions, errors, history');
375
+ ctx.renderer.writeInfo(" Usage: /memory add <type> <text>");
376
+ ctx.renderer.writeInfo(" Types: project, conventions, decisions, errors, history");
315
377
  return;
316
378
  }
317
379
  saveMemory(type, text.slice(0, 60), text);
318
380
  ctx.renderer.writeSuccess(`Saved to ${theme.filePath(`.mma/memory/${type}.md`)}`);
319
381
  break;
320
382
  }
321
- case 'search': {
383
+ case "search": {
322
384
  if (!type) {
323
- ctx.renderer.writeInfo(' Usage: /memory search <query>');
385
+ ctx.renderer.writeInfo(" Usage: /memory search <query>");
324
386
  return;
325
387
  }
326
388
  const results = searchMemory(type);
327
389
  if (results.length === 0) {
328
- ctx.renderer.writeDim(' No matches');
390
+ ctx.renderer.writeDim(" No matches");
329
391
  return;
330
392
  }
331
393
  ctx.renderer.writeInfo(` Found ${results.length} matches:`);
@@ -334,15 +396,15 @@ function cmdMemory(args, ctx) {
334
396
  }
335
397
  break;
336
398
  }
337
- case 'remove': {
399
+ case "remove": {
338
400
  if (!type) {
339
- ctx.renderer.writeInfo(' Usage: /memory remove <type> <line-number>');
340
- ctx.renderer.writeInfo(' First use /memory show <type> to see entries with line numbers.');
401
+ ctx.renderer.writeInfo(" Usage: /memory remove <type> <line-number>");
402
+ ctx.renderer.writeInfo(" First use /memory show <type> to see entries with line numbers.");
341
403
  return;
342
404
  }
343
405
  const lineNum = parseInt(text, 10);
344
406
  if (isNaN(lineNum) || lineNum < 1) {
345
- ctx.renderer.writeDim(' Line number must be a positive integer');
407
+ ctx.renderer.writeDim(" Line number must be a positive integer");
346
408
  return;
347
409
  }
348
410
  const removed = removeMemoryEntry(type, lineNum);
@@ -350,105 +412,111 @@ function cmdMemory(args, ctx) {
350
412
  ctx.renderer.writeSuccess(`Entry removed from ${theme.filePath(`${type}.md`)}`);
351
413
  }
352
414
  else {
353
- ctx.renderer.writeError(' Could not remove entry (line number out of range?)');
415
+ ctx.renderer.writeError(" Could not remove entry (line number out of range?)");
354
416
  }
355
417
  break;
356
418
  }
357
- case 'export': {
419
+ case "export": {
358
420
  const exported = exportMemory();
359
421
  if (exported) {
360
422
  ctx.renderer.writeSuccess(`Memory exported to ${theme.filePath(exported)}`);
361
423
  }
362
424
  else {
363
- ctx.renderer.writeDim(' No memory to export');
425
+ ctx.renderer.writeDim(" No memory to export");
364
426
  }
365
427
  break;
366
428
  }
367
429
  default: {
368
- ctx.renderer.writeInfo(' Usage: /memory [show <type>|add <type> <text>|search <query>|remove <type> <line>|export]');
430
+ ctx.renderer.writeInfo(" Usage: /memory [show <type>|add <type> <text>|search <query>|remove <type> <line>|export]");
369
431
  }
370
432
  }
371
433
  }
372
434
  function cmdSkill(args, ctx) {
373
- const subcmd = args.trim() || 'list';
435
+ const subcmd = args.trim() || "list";
374
436
  const theme = getTheme();
375
437
  switch (subcmd) {
376
- case 'list': {
438
+ case "list": {
377
439
  const bySource = listSkillsBySource(process.cwd());
378
- const total = bySource.builtin.length + bySource.project.length + bySource.global.length;
440
+ const total = bySource.builtin.length +
441
+ bySource.project.length +
442
+ bySource.global.length;
379
443
  if (total === 0) {
380
- ctx.renderer.writeDim(' No skills available.');
381
- ctx.renderer.writeDim(' Place skills in .claude/skills/<name>/SKILL.md or install via npm.');
444
+ ctx.renderer.writeDim(" No skills available.");
445
+ ctx.renderer.writeDim(" Place skills in .claude/skills/<name>/SKILL.md or install via npm.");
382
446
  return;
383
447
  }
384
448
  ctx.renderer.writeInfo(` Skills (${total} total):`);
385
449
  if (bySource.builtin.length > 0) {
386
- ctx.renderer.writeLine(` ${theme.info('built-in:')}`);
450
+ ctx.renderer.writeLine(` ${theme.info("built-in:")}`);
387
451
  for (const s of bySource.builtin)
388
452
  ctx.renderer.writeLine(` ${theme.toolName(`@${s}`)}`);
389
453
  }
390
454
  if (bySource.project.length > 0) {
391
- ctx.renderer.writeLine(` ${theme.info('project:')}`);
455
+ ctx.renderer.writeLine(` ${theme.info("project:")}`);
392
456
  for (const s of bySource.project)
393
457
  ctx.renderer.writeLine(` ${theme.toolName(`@${s}`)}`);
394
458
  }
395
459
  if (bySource.global.length > 0) {
396
- ctx.renderer.writeLine(` ${theme.info('global:')}`);
460
+ ctx.renderer.writeLine(` ${theme.info("global:")}`);
397
461
  for (const s of bySource.global)
398
462
  ctx.renderer.writeLine(` ${theme.toolName(`@${s}`)}`);
399
463
  }
400
- ctx.renderer.writeDim(` ${theme.dim('Reference:')} ${theme.symbolName('@skill-name')}`);
464
+ ctx.renderer.writeDim(` ${theme.dim("Reference:")} ${theme.symbolName("@skill-name")}`);
401
465
  break;
402
466
  }
403
467
  default:
404
- ctx.renderer.writeInfo(' Usage: /skill [list]');
405
- }
406
- }
407
- async function cmdPlugins(_args, ctx) {
408
- const pluginManager = new PluginManager();
409
- await loadAllPluginsWithInfo(pluginManager);
410
- const plugins = pluginManager.listPlugins();
411
- const theme = getTheme();
412
- if (plugins.length === 0) {
413
- ctx.renderer.writeDim(' No plugins available.');
414
- ctx.renderer.writeDim(' Place plugins in .mma/plugins/<name>/');
415
- return;
416
- }
417
- ctx.renderer.writeInfo(` Available plugins (${plugins.length}):`);
418
- for (const p of plugins) {
419
- const version = p.version ? ` ${theme.dim(`v${p.version}`)}` : '';
420
- const source = theme.dim(`[${p.source}]`);
421
- const status = p.active ? theme.success('active') : theme.dim('inactive');
422
- ctx.renderer.writeLine(` ${theme.highlight(p.name)}${version} ${source} ${status}`);
468
+ ctx.renderer.writeInfo(" Usage: /skill [list]");
423
469
  }
424
470
  }
425
471
  function cmdTools(_args, ctx) {
426
472
  const defs = ctx.registry.getDefinitions();
427
473
  const theme = getTheme();
428
474
  const categories = {
429
- native: { names: new Set(['subagent', 'scaffold', 'project_map']), color: (s) => theme.toolName(s) },
430
- bash: { names: new Set(['bash']), color: (s) => theme.symbolName(s) },
431
- fs: { names: new Set(['read_file', 'write_file', 'edit_file', 'list_dir', 'create_dir', 'delete_file', 'copy_file', 'move_file', 'file_info', 'glob_search', 'grep_search', 'tree']), color: (s) => theme.filePath(s) },
432
- web: { names: new Set(['web_search', 'web_fetch', 'web_browse']), color: (s) => theme.link(s) },
475
+ native: {
476
+ names: new Set(["subagent", "scaffold", "project_map"]),
477
+ color: (s) => theme.toolName(s),
478
+ },
479
+ bash: { names: new Set(["bash"]), color: (s) => theme.symbolName(s) },
480
+ fs: {
481
+ names: new Set([
482
+ "read_file",
483
+ "write_file",
484
+ "edit_file",
485
+ "list_dir",
486
+ "create_dir",
487
+ "delete_file",
488
+ "copy_file",
489
+ "move_file",
490
+ "file_info",
491
+ "glob_search",
492
+ "grep_search",
493
+ "tree",
494
+ ]),
495
+ color: (s) => theme.filePath(s),
496
+ },
497
+ web: {
498
+ names: new Set(["web_search", "web_fetch", "web_browse"]),
499
+ color: (s) => theme.link(s),
500
+ },
433
501
  };
434
502
  const categoryMeta = [
435
- { key: 'native', label: 'native' },
436
- { key: 'bash', label: 'exec' },
437
- { key: 'fs', label: 'files' },
438
- { key: 'web', label: 'web' },
503
+ { key: "native", label: "native" },
504
+ { key: "bash", label: "exec" },
505
+ { key: "fs", label: "files" },
506
+ { key: "web", label: "web" },
439
507
  ];
440
508
  ctx.renderer.writeNewline();
441
509
  ctx.renderer.writeInfo(` tools (${defs.length}):`);
442
510
  for (const meta of categoryMeta) {
443
511
  const cat = categories[meta.key];
444
- const items = defs.filter(d => cat.names.has(d.function.name));
512
+ const items = defs.filter((d) => cat.names.has(d.function.name));
445
513
  if (items.length === 0)
446
514
  continue;
447
- ctx.renderer.writeLine(` ${theme.dim(meta.label + ':')}`);
515
+ ctx.renderer.writeLine(` ${theme.dim(meta.label + ":")}`);
448
516
  for (const d of items) {
449
- const desc = d.function.description.split('\n')[0];
450
- const shortDesc = desc.length > 55 ? desc.slice(0, 52) + '…' : desc;
451
- ctx.renderer.writeLine(` ${cat.color(d.function.name)} ${theme.dim('—')} ${theme.dim(shortDesc)}`);
517
+ const desc = d.function.description.split("\n")[0];
518
+ const shortDesc = desc.length > 55 ? desc.slice(0, 52) + "…" : desc;
519
+ ctx.renderer.writeLine(` ${cat.color(d.function.name)} ${theme.dim("—")} ${theme.dim(shortDesc)}`);
452
520
  }
453
521
  }
454
522
  ctx.renderer.writeNewline();
@@ -461,8 +529,8 @@ function cmdSessions(_args, ctx) {
461
529
  async function cmdResume(args, ctx) {
462
530
  const theme = getTheme();
463
531
  if (!args.trim()) {
464
- ctx.renderer.writeInfo(' Usage: /resume <session-id>');
465
- ctx.renderer.writeInfo(' Use /sessions to see available sessions.');
532
+ ctx.renderer.writeInfo(" Usage: /resume <session-id>");
533
+ ctx.renderer.writeInfo(" Use /sessions to see available sessions.");
466
534
  return;
467
535
  }
468
536
  const session = loadSession(args.trim());
@@ -476,44 +544,50 @@ async function cmdResume(args, ctx) {
476
544
  ctx.renderer.writeNewline();
477
545
  // Display previous conversation and rebuild history
478
546
  ctx.history.length = 0;
479
- const displayMsgs = session.messages.filter(m => m.role !== 'system');
547
+ const displayMsgs = session.messages.filter((m) => m.role !== "system");
480
548
  for (const msg of displayMsgs) {
481
549
  ctx.renderer.writeNewline();
482
- ctx.renderer.writeDim('─'.repeat(40));
483
- const tag = msg.role === 'user' ? 'user' : 'assistant';
484
- ctx.renderer.writeBracketTag(tag, ` ${msg.content.slice(0, 200)}${msg.content.length > 200 ? '…' : ''}`);
550
+ ctx.renderer.writeDim("в”Ђ".repeat(40));
551
+ const tag = msg.role === "user" ? "user" : "assistant";
552
+ ctx.renderer.writeBracketTag(tag, ` ${msg.content.slice(0, 200)}${msg.content.length > 200 ? "…" : ""}`);
485
553
  if (msg.content.length > 200) {
486
- ctx.renderer.writeDim(' (message truncated, full content loaded)');
554
+ ctx.renderer.writeDim(" (message truncated, full content loaded)");
487
555
  }
488
- ctx.renderer.writeDim('─'.repeat(40));
556
+ ctx.renderer.writeDim("в”Ђ".repeat(40));
489
557
  ctx.renderer.writeNewline();
490
- ctx.history.push({ role: msg.role === 'tool' ? 'assistant' : msg.role, content: msg.content });
558
+ ctx.history.push({
559
+ role: msg.role === "tool" ? "assistant" : msg.role,
560
+ content: msg.content,
561
+ });
491
562
  }
492
- const lastUser = [...displayMsgs].reverse().find(m => m.role === 'user');
563
+ const lastUser = [...displayMsgs].reverse().find((m) => m.role === "user");
493
564
  if (lastUser) {
494
565
  // Feed history WITHOUT the last user message to avoid duplication
495
- const prevMsgs = ctx.history.filter(m => m.content !== lastUser.content || m.role !== 'user');
566
+ const prevMsgs = ctx.history.filter((m) => m.content !== lastUser.content || m.role !== "user");
496
567
  const result = await runAgentLoop(ctx.provider, ctx.registry, lastUser.content, undefined, prevMsgs, ctx.renderer);
497
568
  for (const msg of result.messages) {
498
- if (msg.role !== 'system') {
499
- const dup = ctx.history.find(m => m.content === msg.content && m.role === msg.role);
569
+ if (msg.role !== "system") {
570
+ const dup = ctx.history.find((m) => m.content === msg.content && m.role === msg.role);
500
571
  if (!dup)
501
- ctx.history.push({ role: msg.role, content: msg.content });
572
+ ctx.history.push({
573
+ role: msg.role,
574
+ content: msg.content,
575
+ });
502
576
  }
503
577
  }
504
578
  }
505
579
  }
506
580
  function cmdSave(args, ctx) {
507
581
  if (ctx.history.length === 0) {
508
- ctx.renderer.writeDim(' No conversation to save.');
582
+ ctx.renderer.writeDim(" No conversation to save.");
509
583
  return;
510
584
  }
511
- const name = args.trim() || '';
585
+ const name = args.trim() || "";
512
586
  const providerLabel = `${ctx.provider.model}`;
513
587
  const filePath = createNamedSession(name, ctx.history, providerLabel, process.cwd(), { totalTokens: 0, compactCount: 0 });
514
- const id = filePath.split(/[/\\]/).pop()?.replace('.json', '') || '';
588
+ const id = filePath.split(/[/\\]/).pop()?.replace(".json", "") || "";
515
589
  const theme = getTheme();
516
- ctx.renderer.writeSuccess(`Session saved: ${theme.highlight(id)}${name ? ` (${theme.dim(name)})` : ''}`);
590
+ ctx.renderer.writeSuccess(`Session saved: ${theme.highlight(id)}${name ? ` (${theme.dim(name)})` : ""}`);
517
591
  }
518
592
  function cmdClear(_args, ctx) {
519
593
  ctx.renderer.clear();
@@ -521,19 +595,23 @@ function cmdClear(_args, ctx) {
521
595
  function cmdNew(_args, ctx) {
522
596
  ctx.history.length = 0;
523
597
  ctx.renderer.clear();
524
- ctx.renderer.writeInfo(' Context cleared. Starting new session.');
598
+ ctx.renderer.writeInfo(" Context cleared. Starting new session.");
525
599
  }
526
600
  function cmdStop(_args, ctx) {
527
- ctx.renderer.writeDim(' Stopping agent loop...');
601
+ ctx.renderer.writeDim(" Stopping agent loop...");
528
602
  ctx.renderer.destroy();
529
603
  }
530
604
  const AUTO_HEADERS = new Set([
531
- '## Description', '## Git', '## Tech Stack',
532
- '## Key Dependencies', '## Commands',
533
- '## Conventions', '## Project Structure',
605
+ "## Description",
606
+ "## Git",
607
+ "## Tech Stack",
608
+ "## Key Dependencies",
609
+ "## Commands",
610
+ "## Conventions",
611
+ "## Project Structure",
534
612
  ]);
535
613
  export function stripAutoSections(content) {
536
- const lines = content.split('\n');
614
+ const lines = content.split("\n");
537
615
  const result = [];
538
616
  let skip = false;
539
617
  for (const line of lines) {
@@ -543,13 +621,25 @@ export function stripAutoSections(content) {
543
621
  if (!skip)
544
622
  result.push(line);
545
623
  }
546
- return result.join('\n').trim();
624
+ return result.join("\n").trim();
547
625
  }
548
626
  export function getGitInfo() {
549
627
  try {
550
- const remote = execSync('git config --get remote.origin.url', { encoding: 'utf-8', timeout: 5000, stdio: 'pipe' }).trim();
551
- const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8', timeout: 5000, stdio: 'pipe' }).trim();
552
- const lastCommit = execSync('git log --oneline -1', { encoding: 'utf-8', timeout: 5000, stdio: 'pipe' }).trim();
628
+ const remote = execSync("git config --get remote.origin.url", {
629
+ encoding: "utf-8",
630
+ timeout: 5000,
631
+ stdio: "pipe",
632
+ }).trim();
633
+ const branch = execSync("git rev-parse --abbrev-ref HEAD", {
634
+ encoding: "utf-8",
635
+ timeout: 5000,
636
+ stdio: "pipe",
637
+ }).trim();
638
+ const lastCommit = execSync("git log --oneline -1", {
639
+ encoding: "utf-8",
640
+ timeout: 5000,
641
+ stdio: "pipe",
642
+ }).trim();
553
643
  return { remote, branch, lastCommit };
554
644
  }
555
645
  catch {
@@ -560,65 +650,98 @@ function cmdInit(_args, ctx) {
560
650
  const theme = getTheme();
561
651
  const cwd = process.cwd();
562
652
  ctx.renderer.writeNewline();
563
- ctx.renderer.writeInfo(' Analyzing project...');
564
- const agentsPath = join(cwd, 'AGENTS.md');
653
+ ctx.renderer.writeInfo(" Analyzing project...");
654
+ const agentsPath = join(cwd, "AGENTS.md");
565
655
  const existed = existsSync(agentsPath);
566
656
  // Read existing AGENTS.md first to preserve custom sections
567
- let preservedContent = '';
657
+ let preservedContent = "";
568
658
  if (existed) {
569
- const existingContent = readFileSync(agentsPath, 'utf-8');
659
+ const existingContent = readFileSync(agentsPath, "utf-8");
570
660
  preservedContent = stripAutoSections(existingContent);
571
661
  }
572
662
  // Scan project
573
663
  const projectName = basename(cwd);
574
- let description = '';
664
+ let description = "";
575
665
  let scripts = {};
576
666
  let deps = [];
577
667
  let devDeps = [];
578
- const pkgPath = join(cwd, 'package.json');
668
+ const pkgPath = join(cwd, "package.json");
579
669
  if (existsSync(pkgPath)) {
580
670
  try {
581
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
582
- description = pkg.description || '';
671
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
672
+ description = pkg.description || "";
583
673
  scripts = pkg.scripts || {};
584
674
  deps = Object.keys(pkg.dependencies || {});
585
675
  devDeps = Object.keys(pkg.devDependencies || {});
586
676
  }
587
- catch { /* ignore */ }
677
+ catch {
678
+ /* ignore */
679
+ }
588
680
  }
589
681
  const langCounts = {};
590
- const exts = ['ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs', 'py', 'go', 'rs', 'java', 'rb', 'php', 'c', 'cpp', 'h', 'css', 'scss', 'html', 'vue', 'svelte', 'prisma'];
682
+ const exts = [
683
+ "ts",
684
+ "tsx",
685
+ "js",
686
+ "jsx",
687
+ "mjs",
688
+ "cjs",
689
+ "py",
690
+ "go",
691
+ "rs",
692
+ "java",
693
+ "rb",
694
+ "php",
695
+ "c",
696
+ "cpp",
697
+ "h",
698
+ "css",
699
+ "scss",
700
+ "html",
701
+ "vue",
702
+ "svelte",
703
+ "prisma",
704
+ ];
591
705
  try {
592
706
  walkDirForLangs(cwd, exts, langCounts);
593
707
  }
594
- catch { /* ignore */ }
595
- const mainLang = Object.entries(langCounts).sort((a, b) => b[1] - a[1]).slice(0, 3);
596
- let stack = '';
597
- if (existsSync(join(cwd, 'tsconfig.json')))
598
- stack += 'TypeScript, ';
599
- if (existsSync(join(cwd, 'next.config.js')) || existsSync(join(cwd, 'next.config.mjs')))
600
- stack += 'Next.js, ';
601
- if (existsSync(join(cwd, 'astro.config.mjs')))
602
- stack += 'Astro, ';
603
- if (existsSync(join(cwd, 'vite.config.ts')) || existsSync(join(cwd, 'vite.config.js')))
604
- stack += 'Vite, ';
605
- if (existsSync(join(cwd, 'vitest.config.ts')) || existsSync(join(cwd, 'vitest.config.js')))
606
- stack += 'Vitest, ';
607
- if (existsSync(join(cwd, 'jest.config.js')) || existsSync(join(cwd, 'jest.config.ts')))
608
- stack += 'Jest, ';
609
- if (existsSync(join(cwd, '.eslintrc.js')) || existsSync(join(cwd, 'eslint.config.js')))
610
- stack += 'ESLint, ';
611
- if (existsSync(join(cwd, 'prisma/schema.prisma')))
612
- stack += 'Prisma, ';
613
- if (existsSync(join(cwd, 'Dockerfile')))
614
- stack += 'Docker, ';
615
- if (existsSync(join(cwd, 'docker-compose.yml')))
616
- stack += 'Docker Compose, ';
617
- if (existsSync(join(cwd, '.github/workflows')))
618
- stack += 'GitHub Actions, ';
619
- const buildCmd = scripts.build || scripts.compile || '';
620
- const testCmd = scripts.test || '';
621
- const devCmd = scripts.dev || scripts.start || scripts['dev:watch'] || '';
708
+ catch {
709
+ /* ignore */
710
+ }
711
+ const mainLang = Object.entries(langCounts)
712
+ .sort((a, b) => b[1] - a[1])
713
+ .slice(0, 3);
714
+ let stack = "";
715
+ if (existsSync(join(cwd, "tsconfig.json")))
716
+ stack += "TypeScript, ";
717
+ if (existsSync(join(cwd, "next.config.js")) ||
718
+ existsSync(join(cwd, "next.config.mjs")))
719
+ stack += "Next.js, ";
720
+ if (existsSync(join(cwd, "astro.config.mjs")))
721
+ stack += "Astro, ";
722
+ if (existsSync(join(cwd, "vite.config.ts")) ||
723
+ existsSync(join(cwd, "vite.config.js")))
724
+ stack += "Vite, ";
725
+ if (existsSync(join(cwd, "vitest.config.ts")) ||
726
+ existsSync(join(cwd, "vitest.config.js")))
727
+ stack += "Vitest, ";
728
+ if (existsSync(join(cwd, "jest.config.js")) ||
729
+ existsSync(join(cwd, "jest.config.ts")))
730
+ stack += "Jest, ";
731
+ if (existsSync(join(cwd, ".eslintrc.js")) ||
732
+ existsSync(join(cwd, "eslint.config.js")))
733
+ stack += "ESLint, ";
734
+ if (existsSync(join(cwd, "prisma/schema.prisma")))
735
+ stack += "Prisma, ";
736
+ if (existsSync(join(cwd, "Dockerfile")))
737
+ stack += "Docker, ";
738
+ if (existsSync(join(cwd, "docker-compose.yml")))
739
+ stack += "Docker Compose, ";
740
+ if (existsSync(join(cwd, ".github/workflows")))
741
+ stack += "GitHub Actions, ";
742
+ const buildCmd = scripts.build || scripts.compile || "";
743
+ const testCmd = scripts.test || "";
744
+ const devCmd = scripts.dev || scripts.start || scripts["dev:watch"] || "";
622
745
  let agentsContent = `# ${projectName}\n\n`;
623
746
  agentsContent += `LLM agent instructions for the project \`${projectName}\`.\n\n`;
624
747
  if (description) {
@@ -627,34 +750,36 @@ function cmdInit(_args, ctx) {
627
750
  // Git info
628
751
  const gitInfo = getGitInfo();
629
752
  if (gitInfo) {
630
- agentsContent += '## Git\n\n';
753
+ agentsContent += "## Git\n\n";
631
754
  agentsContent += `- **Remote**: ${gitInfo.remote}\n`;
632
755
  agentsContent += `- **Branch**: ${gitInfo.branch}\n`;
633
756
  agentsContent += `- **Last commit**: ${gitInfo.lastCommit}\n\n`;
634
757
  }
635
758
  if (stack || mainLang.length > 0) {
636
- agentsContent += '## Tech Stack\n\n';
759
+ agentsContent += "## Tech Stack\n\n";
637
760
  if (stack)
638
- agentsContent += `- **Framework/Tools**: ${stack.replace(/, $/, '')}\n`;
761
+ agentsContent += `- **Framework/Tools**: ${stack.replace(/, $/, "")}\n`;
639
762
  for (const [lang, count] of mainLang) {
640
763
  agentsContent += `- **${lang.toUpperCase()}**: ${count} files\n`;
641
764
  }
642
- agentsContent += '\n';
765
+ agentsContent += "\n";
643
766
  }
644
767
  if (deps.length > 0 || devDeps.length > 0) {
645
- agentsContent += '## Key Dependencies\n\n';
768
+ agentsContent += "## Key Dependencies\n\n";
646
769
  if (deps.length > 0) {
647
- const core = deps.filter(d => !d.startsWith('@types/')).slice(0, 10);
648
- agentsContent += `- Runtime: ${core.join(', ')}\n`;
770
+ const core = deps.filter((d) => !d.startsWith("@types/")).slice(0, 10);
771
+ agentsContent += `- Runtime: ${core.join(", ")}\n`;
649
772
  }
650
773
  if (devDeps.length > 0) {
651
- const tools = devDeps.filter(d => !d.startsWith('@types/')).slice(0, 10);
652
- agentsContent += `- Dev: ${tools.join(', ')}\n`;
774
+ const tools = devDeps
775
+ .filter((d) => !d.startsWith("@types/"))
776
+ .slice(0, 10);
777
+ agentsContent += `- Dev: ${tools.join(", ")}\n`;
653
778
  }
654
- agentsContent += '\n';
779
+ agentsContent += "\n";
655
780
  }
656
781
  if (buildCmd || testCmd || devCmd) {
657
- agentsContent += '## Commands\n\n';
782
+ agentsContent += "## Commands\n\n";
658
783
  if (buildCmd)
659
784
  agentsContent += `\`\`\`bash\n# build\nnpm run build\n\`\`\`\n\n`;
660
785
  if (testCmd)
@@ -665,47 +790,71 @@ function cmdInit(_args, ctx) {
665
790
  agentsContent += `## Conventions\n\n`;
666
791
  agentsContent += `- Use \`<tool_call>\` JSON format for tool invocation\n`;
667
792
  agentsContent += `- Use filesystem tools for file operations, bash for npm/git only\n`;
668
- if (existsSync(join(cwd, '.gitignore'))) {
793
+ if (existsSync(join(cwd, ".gitignore"))) {
669
794
  try {
670
- const gitignore = readFileSync(join(cwd, '.gitignore'), 'utf-8');
671
- const ignores = gitignore.split('\n').filter(l => l.trim() && !l.startsWith('#')).slice(0, 10);
795
+ const gitignore = readFileSync(join(cwd, ".gitignore"), "utf-8");
796
+ const ignores = gitignore
797
+ .split("\n")
798
+ .filter((l) => l.trim() && !l.startsWith("#"))
799
+ .slice(0, 10);
672
800
  if (ignores.length > 0) {
673
- agentsContent += `- Ignored paths: \`${ignores.join('`, `')}\`\n`;
801
+ agentsContent += `- Ignored paths: \`${ignores.join("`, `")}\`\n`;
674
802
  }
675
803
  }
676
- catch { /* ignore */ }
804
+ catch {
805
+ /* ignore */
806
+ }
677
807
  }
678
- agentsContent += '\n';
808
+ agentsContent += "\n";
679
809
  agentsContent += `## Project Structure\n\n`;
680
810
  try {
681
811
  const top = readdirSync(cwd, { withFileTypes: true })
682
- .filter(f => !f.name.startsWith('.') && f.name !== 'node_modules' && f.name !== 'dist')
812
+ .filter((f) => !f.name.startsWith(".") &&
813
+ f.name !== "node_modules" &&
814
+ f.name !== "dist")
683
815
  .slice(0, 20);
684
- agentsContent += '```\n';
816
+ agentsContent += "```\n";
685
817
  for (const f of top) {
686
818
  agentsContent += f.isDirectory() ? `${f.name}/\n` : `${f.name}\n`;
687
819
  }
688
- agentsContent += '```\n\n';
820
+ agentsContent += "```\n\n";
821
+ }
822
+ catch {
823
+ /* ignore */
689
824
  }
690
- catch { /* ignore */ }
691
825
  // Append preserved custom sections from existing AGENTS.md
692
826
  if (preservedContent) {
693
- agentsContent += preservedContent + '\n\n';
827
+ agentsContent += preservedContent + "\n\n";
694
828
  }
695
829
  try {
696
- writeFileSync(agentsPath, agentsContent, 'utf-8');
697
- const verb = existed ? 'Updated' : 'Created';
698
- ctx.renderer.writeSuccess(` ${verb} ${theme.filePath('AGENTS.md')}`);
830
+ writeFileSync(agentsPath, agentsContent, "utf-8");
831
+ const verb = existed ? "Updated" : "Created";
832
+ ctx.renderer.writeSuccess(` ${verb} ${theme.filePath("AGENTS.md")}`);
699
833
  ctx.renderer.writeNewline();
700
834
  ctx.renderer.writeLine(agentsContent);
701
- logInfo('REPL', `AGENTS.md ${verb.toLowerCase()}d: ${agentsPath}`);
835
+ logInfo("REPL", `AGENTS.md ${verb.toLowerCase()}d: ${agentsPath}`);
702
836
  }
703
837
  catch (e) {
704
838
  ctx.renderer.writeError(` Failed to write AGENTS.md: ${e.message}`);
705
839
  }
706
840
  }
707
841
  function walkDirForLangs(dir, exts, counts) {
708
- const IGNORE = new Set(['node_modules', '.git', '.mma', 'dist', 'build', '__pycache__', 'target', '.next', '.nuxt', 'coverage', '.cache', 'venv', '.venv', 'env']);
842
+ const IGNORE = new Set([
843
+ "node_modules",
844
+ ".git",
845
+ ".mma",
846
+ "dist",
847
+ "build",
848
+ "__pycache__",
849
+ "target",
850
+ ".next",
851
+ ".nuxt",
852
+ "coverage",
853
+ ".cache",
854
+ "venv",
855
+ ".venv",
856
+ "env",
857
+ ]);
709
858
  let entries;
710
859
  try {
711
860
  entries = readdirSync(dir, { withFileTypes: true });
@@ -714,14 +863,14 @@ function walkDirForLangs(dir, exts, counts) {
714
863
  return;
715
864
  }
716
865
  for (const entry of entries) {
717
- if (IGNORE.has(entry.name) || entry.name.startsWith('.'))
866
+ if (IGNORE.has(entry.name) || entry.name.startsWith("."))
718
867
  continue;
719
868
  const fullPath = join(dir, entry.name);
720
869
  if (entry.isDirectory()) {
721
870
  walkDirForLangs(fullPath, exts, counts);
722
871
  }
723
872
  else {
724
- const ext = entry.name.split('.').pop()?.toLowerCase() || '';
873
+ const ext = entry.name.split(".").pop()?.toLowerCase() || "";
725
874
  if (exts.includes(ext)) {
726
875
  counts[ext] = (counts[ext] || 0) + 1;
727
876
  }
@@ -729,40 +878,40 @@ function walkDirForLangs(dir, exts, counts) {
729
878
  }
730
879
  }
731
880
  function cmdIndex(args, ctx) {
732
- const subcmd = args.trim() || 'status';
881
+ const subcmd = args.trim() || "status";
733
882
  switch (subcmd) {
734
- case 'rebuild': {
735
- ctx.renderer.writeInfo(' Rebuilding code index...');
883
+ case "rebuild": {
884
+ ctx.renderer.writeInfo(" Rebuilding code index...");
736
885
  const { formatted } = rebuildCodeIndex(process.cwd());
737
886
  ctx.renderer.writeLine(formatted);
738
- ctx.renderer.writeSuccess('Code index rebuilt');
887
+ ctx.renderer.writeSuccess("Code index rebuilt");
739
888
  break;
740
889
  }
741
- case 'map': {
742
- ctx.renderer.writeInfo(' Rebuilding project map...');
890
+ case "map": {
891
+ ctx.renderer.writeInfo(" Rebuilding project map...");
743
892
  const { formatted } = rebuildProjectMap(process.cwd());
744
893
  ctx.renderer.writeLine(formatted);
745
- ctx.renderer.writeSuccess('Project map rebuilt');
894
+ ctx.renderer.writeSuccess("Project map rebuilt");
746
895
  break;
747
896
  }
748
- case 'show': {
897
+ case "show": {
749
898
  const { formatted, fromCache } = getOrBuildProjectMap(process.cwd());
750
899
  ctx.renderer.writeLine(formatted);
751
900
  if (fromCache) {
752
- ctx.renderer.writeDim(' (from cache)');
901
+ ctx.renderer.writeDim(" (from cache)");
753
902
  }
754
903
  break;
755
904
  }
756
- case 'status': {
905
+ case "status": {
757
906
  const { formatted: mapFormatted } = getOrBuildProjectMap(process.cwd());
758
907
  const { formatted: idxFormatted } = getOrBuildCodeIndex(process.cwd());
759
- ctx.renderer.writeLine(mapFormatted.split('\n').slice(0, 3).join('\n'));
908
+ ctx.renderer.writeLine(mapFormatted.split("\n").slice(0, 3).join("\n"));
760
909
  ctx.renderer.writeNewline();
761
- ctx.renderer.writeLine(idxFormatted.split('\n').slice(0, 3).join('\n'));
910
+ ctx.renderer.writeLine(idxFormatted.split("\n").slice(0, 3).join("\n"));
762
911
  break;
763
912
  }
764
913
  default:
765
- ctx.renderer.writeInfo(' Usage: /index [status|show|map|rebuild]');
914
+ ctx.renderer.writeInfo(" Usage: /index [status|show|map|rebuild]");
766
915
  }
767
916
  }
768
917
  function cmdProfile(args, ctx) {
@@ -775,20 +924,20 @@ function cmdProfile(args, ctx) {
775
924
  ctx.renderer.writeNewline();
776
925
  }
777
926
  else {
778
- ctx.renderer.writeDim(' User profile is empty.');
779
- ctx.renderer.writeDim(' It will be auto-populated as you work.');
927
+ ctx.renderer.writeDim(" User profile is empty.");
928
+ ctx.renderer.writeDim(" It will be auto-populated as you work.");
780
929
  }
781
930
  return;
782
931
  }
783
932
  const parts = args.trim().split(/\s+/);
784
933
  const subcmd = parts[0];
785
- const rest = parts.slice(1).join(' ');
786
- if (subcmd === 'name' && rest) {
787
- appendNote(`Имя пользователя: ${rest}`, 'Notes');
934
+ const rest = parts.slice(1).join(" ");
935
+ if (subcmd === "name" && rest) {
936
+ appendNote(`Имя пользователя: ${rest}`, "Notes");
788
937
  ctx.renderer.writeSuccess(` Name saved: ${theme.highlight(rest)}`);
789
938
  return;
790
939
  }
791
- if (subcmd === 'show') {
940
+ if (subcmd === "show") {
792
941
  const content = loadProfile();
793
942
  if (content) {
794
943
  ctx.renderer.writeNewline();
@@ -796,28 +945,28 @@ function cmdProfile(args, ctx) {
796
945
  ctx.renderer.writeNewline();
797
946
  }
798
947
  else {
799
- ctx.renderer.writeDim(' Profile is empty.');
948
+ ctx.renderer.writeDim(" Profile is empty.");
800
949
  }
801
950
  return;
802
951
  }
803
- appendNote(args.trim(), 'Notes');
804
- ctx.renderer.writeSuccess('Note added to user profile.');
952
+ appendNote(args.trim(), "Notes");
953
+ ctx.renderer.writeSuccess("Note added to user profile.");
805
954
  }
806
955
  function cmdExport(args, ctx) {
807
- const mode = args.trim() || 'normal';
956
+ const mode = args.trim() || "normal";
808
957
  const history = ctx.conversationHistory;
809
958
  if (history.length === 0) {
810
- ctx.renderer.writeDim(' No conversation to export.');
959
+ ctx.renderer.writeDim(" No conversation to export.");
811
960
  return;
812
961
  }
813
- let output = '';
814
- if (mode === 'debug') {
962
+ let output = "";
963
+ if (mode === "debug") {
815
964
  output = `# Session Debug Export\n`;
816
965
  output += `Model: ${ctx.modelLabel}\n`;
817
966
  output += `Messages: ${history.length}\n`;
818
967
  output += `---\n\n`;
819
968
  for (const msg of history) {
820
- const role = msg.role === 'user' ? '## User' : '## Assistant';
969
+ const role = msg.role === "user" ? "## User" : "## Assistant";
821
970
  output += `${role}\n\`\`\`\n${msg.content}\n\`\`\`\n\n`;
822
971
  }
823
972
  }
@@ -825,42 +974,42 @@ function cmdExport(args, ctx) {
825
974
  output = `# Session Export\n`;
826
975
  output += `Model: ${ctx.modelLabel}\n\n`;
827
976
  for (const msg of history) {
828
- const role = msg.role === 'user' ? '**You:**' : '**Assistant:**';
977
+ const role = msg.role === "user" ? "**You:**" : "**Assistant:**";
829
978
  output += `${role} ${msg.content}\n\n`;
830
979
  }
831
980
  }
832
981
  const filename = `.mma/export-${Date.now()}.md`;
833
- mkdirSync('.mma', { recursive: true });
834
- writeFileSync(filename, output, 'utf-8');
982
+ mkdirSync(".mma", { recursive: true });
983
+ writeFileSync(filename, output, "utf-8");
835
984
  const theme = getTheme();
836
985
  ctx.renderer.writeSuccess(` Exported to ${theme.filePath(filename)}`);
837
986
  }
838
987
  async function cmdTheme(args, ctx) {
839
988
  const theme = getTheme();
840
989
  const parts = args.trim().split(/\s+/);
841
- const subcmd = parts[0] || 'list';
842
- const name = parts.slice(1).join(' ');
990
+ const subcmd = parts[0] || "list";
991
+ const name = parts.slice(1).join(" ");
843
992
  switch (subcmd) {
844
- case 'list': {
993
+ case "list": {
845
994
  const allThemes = listThemes();
846
995
  const current = loadThemeConfig().name;
847
996
  ctx.renderer.writeInfo(` Available themes (${allThemes.length}):`);
848
997
  for (const t of allThemes) {
849
- const marker = t === current ? theme.success(' ← active') : '';
998
+ const marker = t === current ? theme.success(" в†ђ active") : "";
850
999
  ctx.renderer.writeLine(` ${theme.highlight(t)}${marker}`);
851
1000
  }
852
1001
  break;
853
1002
  }
854
- case 'show': {
1003
+ case "show": {
855
1004
  const config = loadThemeConfig();
856
1005
  ctx.renderer.writeInfo(` Current theme: ${theme.highlight(config.name)}`);
857
- ctx.renderer.writeLine(` ${theme.dim('Colors:')} ${theme.highlight(String(Object.keys(config.colors).length))} keys`);
858
- ctx.renderer.writeLine(` ${theme.dim('Icons:')} ${theme.highlight(String(Object.keys(config.icons).length))} keys`);
1006
+ ctx.renderer.writeLine(` ${theme.dim("Colors:")} ${theme.highlight(String(Object.keys(config.colors).length))} keys`);
1007
+ ctx.renderer.writeLine(` ${theme.dim("Icons:")} ${theme.highlight(String(Object.keys(config.icons).length))} keys`);
859
1008
  break;
860
1009
  }
861
- case 'create': {
1010
+ case "create": {
862
1011
  if (!name) {
863
- ctx.renderer.writeInfo(' Usage: /theme create <name>');
1012
+ ctx.renderer.writeInfo(" Usage: /theme create <name>");
864
1013
  break;
865
1014
  }
866
1015
  const current = loadThemeConfig();
@@ -869,15 +1018,19 @@ async function cmdTheme(args, ctx) {
869
1018
  ctx.renderer.writeSuccess(` Theme ${theme.highlight(name)} created (${theme.filePath(filePath)})`);
870
1019
  break;
871
1020
  }
872
- case 'install': {
1021
+ case "install": {
873
1022
  const [themeName, url] = parts.slice(1);
874
1023
  if (!themeName || !url) {
875
- ctx.renderer.writeInfo(' Usage: /theme install <name> <url>');
1024
+ ctx.renderer.writeInfo(" Usage: /theme install <name> <url>");
876
1025
  break;
877
1026
  }
878
1027
  ctx.renderer.writeDim(` Downloading from ${theme.link(url)}...`);
879
1028
  try {
880
- const resp = execSync(`curl -s "${url}"`, { encoding: 'utf-8', timeout: 10000, windowsHide: true });
1029
+ const resp = execSync(`curl -s "${url}"`, {
1030
+ encoding: "utf-8",
1031
+ timeout: 10000,
1032
+ windowsHide: true,
1033
+ });
881
1034
  JSON.parse(resp);
882
1035
  const filePath = installTheme(themeName, resp);
883
1036
  ctx.renderer.writeSuccess(` Theme ${theme.highlight(themeName)} installed (${theme.filePath(filePath)})`);
@@ -887,9 +1040,9 @@ async function cmdTheme(args, ctx) {
887
1040
  }
888
1041
  break;
889
1042
  }
890
- case 'remove': {
1043
+ case "remove": {
891
1044
  if (!name) {
892
- ctx.renderer.writeInfo(' Usage: /theme remove <name>');
1045
+ ctx.renderer.writeInfo(" Usage: /theme remove <name>");
893
1046
  break;
894
1047
  }
895
1048
  if (!removeTheme(name)) {