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.
- package/dist/agent/context.d.ts +3 -6
- package/dist/agent/context.d.ts.map +1 -1
- package/dist/agent/context.js +8 -41
- package/dist/agent/context.js.map +1 -1
- package/dist/agent/error-escalation.d.ts +6 -6
- package/dist/agent/error-escalation.d.ts.map +1 -1
- package/dist/agent/error-escalation.js +67 -34
- package/dist/agent/error-escalation.js.map +1 -1
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +14 -79
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/memory.d.ts +0 -29
- package/dist/agent/memory.d.ts.map +1 -1
- package/dist/agent/memory.js +1 -78
- package/dist/agent/memory.js.map +1 -1
- package/dist/agent/modes/registry.d.ts +1 -1
- package/dist/agent/modes/registry.d.ts.map +1 -1
- package/dist/agent/modes/registry.js +26 -27
- package/dist/agent/modes/registry.js.map +1 -1
- package/dist/agent/modes/types.d.ts +0 -5
- package/dist/agent/modes/types.d.ts.map +1 -1
- package/dist/agent/system-prompt.d.ts +1 -1
- package/dist/agent/system-prompt.d.ts.map +1 -1
- package/dist/agent/system-prompt.js +95 -147
- package/dist/agent/system-prompt.js.map +1 -1
- package/dist/agent/tool-executor.d.ts +2 -6
- package/dist/agent/tool-executor.d.ts.map +1 -1
- package/dist/agent/tool-executor.js +79 -65
- package/dist/agent/tool-executor.js.map +1 -1
- package/dist/agent/user-profile.d.ts +1 -21
- package/dist/agent/user-profile.d.ts.map +1 -1
- package/dist/agent/user-profile.js +41 -174
- package/dist/agent/user-profile.js.map +1 -1
- package/dist/agent/write-enforcer.d.ts +3 -7
- package/dist/agent/write-enforcer.d.ts.map +1 -1
- package/dist/agent/write-enforcer.js +7 -71
- package/dist/agent/write-enforcer.js.map +1 -1
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +2 -25
- package/dist/cli/config.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +102 -139
- package/dist/cli.js.map +1 -1
- package/dist/config/config.d.ts +15 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +4 -38
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +0 -2
- package/dist/config/types.d.ts.map +1 -1
- package/dist/logger.d.ts +10 -3
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +89 -38
- package/dist/logger.js.map +1 -1
- package/dist/repl/commands.d.ts +16 -16
- package/dist/repl/commands.d.ts.map +1 -1
- package/dist/repl/commands.js +474 -321
- package/dist/repl/commands.js.map +1 -1
- package/dist/repl/index.d.ts +4 -4
- package/dist/repl/index.d.ts.map +1 -1
- package/dist/repl/index.js +60 -56
- package/dist/repl/index.js.map +1 -1
- package/dist/session/store.d.ts +4 -7
- package/dist/session/store.d.ts.map +1 -1
- package/dist/session/store.js +35 -71
- package/dist/session/store.js.map +1 -1
- package/dist/setup-wizard.d.ts.map +1 -1
- package/dist/setup-wizard.js +5 -23
- package/dist/setup-wizard.js.map +1 -1
- package/dist/styling/engine.d.ts +7 -12
- package/dist/styling/engine.d.ts.map +1 -1
- package/dist/styling/engine.js +85 -134
- package/dist/styling/engine.js.map +1 -1
- package/dist/styling/index.d.ts +2 -2
- package/dist/styling/index.d.ts.map +1 -1
- package/dist/styling/index.js +0 -1
- package/dist/styling/index.js.map +1 -1
- package/dist/styling/loader.d.ts +2 -2
- package/dist/styling/loader.d.ts.map +1 -1
- package/dist/styling/loader.js +99 -47
- package/dist/styling/loader.js.map +1 -1
- package/dist/subagent/chat.d.ts +19 -0
- package/dist/subagent/chat.d.ts.map +1 -0
- package/dist/subagent/chat.js +279 -0
- package/dist/subagent/chat.js.map +1 -0
- package/dist/subagent/context.d.ts +9 -0
- package/dist/subagent/context.d.ts.map +1 -0
- package/dist/subagent/context.js +75 -0
- package/dist/subagent/context.js.map +1 -0
- package/dist/subagent/definition.d.ts +7 -0
- package/dist/subagent/definition.d.ts.map +1 -0
- package/dist/subagent/definition.js +26 -0
- package/dist/subagent/definition.js.map +1 -0
- package/dist/subagent/errors.d.ts +24 -0
- package/dist/subagent/errors.d.ts.map +1 -0
- package/dist/subagent/errors.js +85 -0
- package/dist/subagent/errors.js.map +1 -0
- package/dist/subagent/validation.d.ts +17 -0
- package/dist/subagent/validation.d.ts.map +1 -0
- package/dist/subagent/validation.js +83 -0
- package/dist/subagent/validation.js.map +1 -0
- package/dist/tools/hooks.d.ts +2 -0
- package/dist/tools/hooks.d.ts.map +1 -1
- package/dist/tools/hooks.js +118 -16
- package/dist/tools/hooks.js.map +1 -1
- package/dist/tools/parse-tool-call.d.ts.map +1 -1
- package/dist/tools/parse-tool-call.js +16 -14
- package/dist/tools/parse-tool-call.js.map +1 -1
- package/dist/tools/registry.d.ts +5 -3
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +47 -28
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/subagent.d.ts +4 -57
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +112 -556
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/types.d.ts +2 -10
- package/dist/tools/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/repl/commands.js
CHANGED
|
@@ -1,72 +1,106 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync } from
|
|
2
|
-
import { join, basename } from
|
|
3
|
-
import { globSync } from
|
|
4
|
-
import { execSync } from
|
|
5
|
-
import { executeBash } from
|
|
6
|
-
import { runAgentLoop } from
|
|
7
|
-
import { loadMemory, saveMemory, searchMemory, loadAllMemory, ensureMemoryDir, removeMemoryEntry, exportMemory } from
|
|
8
|
-
import { loadProfile, appendNote } from
|
|
9
|
-
import { listSkillsBySource } from
|
|
10
|
-
import { listSessions, loadSession, createNamedSession, createAutoSession, formatSessionList } from
|
|
11
|
-
import { rebuildCodeIndex, rebuildProjectMap, getOrBuildCodeIndex, getOrBuildProjectMap } from
|
|
12
|
-
import { getTheme, listThemes, switchTheme } from
|
|
13
|
-
import { loadThemeConfig, installTheme, removeTheme } from
|
|
14
|
-
import { loadConfig, saveConfig } from
|
|
15
|
-
import { logInfo } from
|
|
16
|
-
|
|
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:
|
|
22
|
-
{ name:
|
|
23
|
-
{ name:
|
|
24
|
-
{ name:
|
|
25
|
-
{ name:
|
|
26
|
-
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{ name:
|
|
32
|
-
{ name:
|
|
33
|
-
{ name:
|
|
34
|
-
{ name:
|
|
35
|
-
{ name:
|
|
36
|
-
{ name:
|
|
37
|
-
{ name:
|
|
38
|
-
{ name:
|
|
39
|
-
{ name:
|
|
40
|
-
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{
|
|
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 ?
|
|
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(), {
|
|
60
|
-
|
|
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(
|
|
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(
|
|
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(`
|
|
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 ||
|
|
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:
|
|
89
|
-
{ cmd:
|
|
90
|
-
{
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
{ cmd:
|
|
95
|
-
{ cmd:
|
|
96
|
-
{ cmd:
|
|
97
|
-
{ cmd:
|
|
98
|
-
{
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
{ cmd:
|
|
103
|
-
{ cmd:
|
|
104
|
-
{ cmd:
|
|
105
|
-
{ cmd:
|
|
106
|
-
{ cmd:
|
|
107
|
-
{ cmd:
|
|
108
|
-
{ cmd:
|
|
109
|
-
{ cmd:
|
|
110
|
-
{ cmd:
|
|
111
|
-
{ cmd:
|
|
112
|
-
{ cmd:
|
|
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(
|
|
153
|
+
ctx.renderer.writeLine(` ${theme.highlight("/ commands")}`);
|
|
115
154
|
for (const entry of slashHelp) {
|
|
116
|
-
ctx.renderer.writeLine(` ${formatHelpCmd(theme, entry.cmd)} ${theme.dim(
|
|
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:
|
|
121
|
-
{
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
{ cmd:
|
|
126
|
-
{ cmd:
|
|
127
|
-
{ cmd:
|
|
128
|
-
{
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
{ cmd:
|
|
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(
|
|
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(
|
|
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,
|
|
149
|
-
const lines = content.split(
|
|
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() {
|
|
166
|
-
|
|
167
|
-
|
|
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(
|
|
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(
|
|
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() ===
|
|
233
|
+
if (line.trim() === "EOF") {
|
|
179
234
|
const theme = getTheme();
|
|
180
235
|
try {
|
|
181
|
-
writeFileSync(_currentWritePath, _writeBuffer.join(
|
|
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(
|
|
198
|
-
ctx.renderer.writeInfo(
|
|
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(
|
|
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,
|
|
210
|
-
const lines = content.split(
|
|
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(
|
|
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 = [
|
|
227
|
-
|
|
228
|
-
|
|
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(
|
|
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:
|
|
245
|
-
const allLines = result.trim().split(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
362
|
+
case "show": {
|
|
301
363
|
if (!type) {
|
|
302
|
-
ctx.renderer.writeInfo(
|
|
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
|
|
373
|
+
case "add": {
|
|
312
374
|
if (!type || !text) {
|
|
313
|
-
ctx.renderer.writeInfo(
|
|
314
|
-
ctx.renderer.writeInfo(
|
|
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
|
|
383
|
+
case "search": {
|
|
322
384
|
if (!type) {
|
|
323
|
-
ctx.renderer.writeInfo(
|
|
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(
|
|
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
|
|
399
|
+
case "remove": {
|
|
338
400
|
if (!type) {
|
|
339
|
-
ctx.renderer.writeInfo(
|
|
340
|
-
ctx.renderer.writeInfo(
|
|
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(
|
|
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(
|
|
415
|
+
ctx.renderer.writeError(" Could not remove entry (line number out of range?)");
|
|
354
416
|
}
|
|
355
417
|
break;
|
|
356
418
|
}
|
|
357
|
-
case
|
|
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(
|
|
425
|
+
ctx.renderer.writeDim(" No memory to export");
|
|
364
426
|
}
|
|
365
427
|
break;
|
|
366
428
|
}
|
|
367
429
|
default: {
|
|
368
|
-
ctx.renderer.writeInfo(
|
|
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() ||
|
|
435
|
+
const subcmd = args.trim() || "list";
|
|
374
436
|
const theme = getTheme();
|
|
375
437
|
switch (subcmd) {
|
|
376
|
-
case
|
|
438
|
+
case "list": {
|
|
377
439
|
const bySource = listSkillsBySource(process.cwd());
|
|
378
|
-
const total = bySource.builtin.length +
|
|
440
|
+
const total = bySource.builtin.length +
|
|
441
|
+
bySource.project.length +
|
|
442
|
+
bySource.global.length;
|
|
379
443
|
if (total === 0) {
|
|
380
|
-
ctx.renderer.writeDim(
|
|
381
|
-
ctx.renderer.writeDim(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
464
|
+
ctx.renderer.writeDim(` ${theme.dim("Reference:")} ${theme.symbolName("@skill-name")}`);
|
|
401
465
|
break;
|
|
402
466
|
}
|
|
403
467
|
default:
|
|
404
|
-
ctx.renderer.writeInfo(
|
|
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: {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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:
|
|
436
|
-
{ key:
|
|
437
|
-
{ key:
|
|
438
|
-
{ key:
|
|
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(
|
|
450
|
-
const shortDesc = desc.length > 55 ? desc.slice(0, 52) +
|
|
451
|
-
ctx.renderer.writeLine(` ${cat.color(d.function.name)} ${theme.dim(
|
|
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(
|
|
465
|
-
ctx.renderer.writeInfo(
|
|
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 !==
|
|
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(
|
|
483
|
-
const tag = msg.role ===
|
|
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(
|
|
554
|
+
ctx.renderer.writeDim(" (message truncated, full content loaded)");
|
|
487
555
|
}
|
|
488
|
-
ctx.renderer.writeDim(
|
|
556
|
+
ctx.renderer.writeDim("в”Ђ".repeat(40));
|
|
489
557
|
ctx.renderer.writeNewline();
|
|
490
|
-
ctx.history.push({
|
|
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 ===
|
|
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 !==
|
|
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 !==
|
|
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({
|
|
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(
|
|
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(
|
|
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(
|
|
598
|
+
ctx.renderer.writeInfo(" Context cleared. Starting new session.");
|
|
525
599
|
}
|
|
526
600
|
function cmdStop(_args, ctx) {
|
|
527
|
-
ctx.renderer.writeDim(
|
|
601
|
+
ctx.renderer.writeDim(" Stopping agent loop...");
|
|
528
602
|
ctx.renderer.destroy();
|
|
529
603
|
}
|
|
530
604
|
const AUTO_HEADERS = new Set([
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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(
|
|
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(
|
|
624
|
+
return result.join("\n").trim();
|
|
547
625
|
}
|
|
548
626
|
export function getGitInfo() {
|
|
549
627
|
try {
|
|
550
|
-
const remote = execSync(
|
|
551
|
-
|
|
552
|
-
|
|
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(
|
|
564
|
-
const agentsPath = join(cwd,
|
|
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,
|
|
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,
|
|
668
|
+
const pkgPath = join(cwd, "package.json");
|
|
579
669
|
if (existsSync(pkgPath)) {
|
|
580
670
|
try {
|
|
581
|
-
const pkg = JSON.parse(readFileSync(pkgPath,
|
|
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 {
|
|
677
|
+
catch {
|
|
678
|
+
/* ignore */
|
|
679
|
+
}
|
|
588
680
|
}
|
|
589
681
|
const langCounts = {};
|
|
590
|
-
const exts = [
|
|
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 {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
if (existsSync(join(cwd,
|
|
602
|
-
stack +=
|
|
603
|
-
if (existsSync(join(cwd,
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
stack +=
|
|
611
|
-
if (existsSync(join(cwd,
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
stack +=
|
|
617
|
-
if (existsSync(join(cwd,
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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 +=
|
|
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 +=
|
|
759
|
+
agentsContent += "## Tech Stack\n\n";
|
|
637
760
|
if (stack)
|
|
638
|
-
agentsContent += `- **Framework/Tools**: ${stack.replace(/, $/,
|
|
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 +=
|
|
765
|
+
agentsContent += "\n";
|
|
643
766
|
}
|
|
644
767
|
if (deps.length > 0 || devDeps.length > 0) {
|
|
645
|
-
agentsContent +=
|
|
768
|
+
agentsContent += "## Key Dependencies\n\n";
|
|
646
769
|
if (deps.length > 0) {
|
|
647
|
-
const core = deps.filter(d => !d.startsWith(
|
|
648
|
-
agentsContent += `- Runtime: ${core.join(
|
|
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
|
|
652
|
-
|
|
774
|
+
const tools = devDeps
|
|
775
|
+
.filter((d) => !d.startsWith("@types/"))
|
|
776
|
+
.slice(0, 10);
|
|
777
|
+
agentsContent += `- Dev: ${tools.join(", ")}\n`;
|
|
653
778
|
}
|
|
654
|
-
agentsContent +=
|
|
779
|
+
agentsContent += "\n";
|
|
655
780
|
}
|
|
656
781
|
if (buildCmd || testCmd || devCmd) {
|
|
657
|
-
agentsContent +=
|
|
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,
|
|
793
|
+
if (existsSync(join(cwd, ".gitignore"))) {
|
|
669
794
|
try {
|
|
670
|
-
const gitignore = readFileSync(join(cwd,
|
|
671
|
-
const ignores = gitignore
|
|
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(
|
|
801
|
+
agentsContent += `- Ignored paths: \`${ignores.join("`, `")}\`\n`;
|
|
674
802
|
}
|
|
675
803
|
}
|
|
676
|
-
catch {
|
|
804
|
+
catch {
|
|
805
|
+
/* ignore */
|
|
806
|
+
}
|
|
677
807
|
}
|
|
678
|
-
agentsContent +=
|
|
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(
|
|
812
|
+
.filter((f) => !f.name.startsWith(".") &&
|
|
813
|
+
f.name !== "node_modules" &&
|
|
814
|
+
f.name !== "dist")
|
|
683
815
|
.slice(0, 20);
|
|
684
|
-
agentsContent +=
|
|
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 +=
|
|
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 +
|
|
827
|
+
agentsContent += preservedContent + "\n\n";
|
|
694
828
|
}
|
|
695
829
|
try {
|
|
696
|
-
writeFileSync(agentsPath, agentsContent,
|
|
697
|
-
const verb = existed ?
|
|
698
|
-
ctx.renderer.writeSuccess(` ${verb} ${theme.filePath(
|
|
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(
|
|
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([
|
|
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(
|
|
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() ||
|
|
881
|
+
const subcmd = args.trim() || "status";
|
|
733
882
|
switch (subcmd) {
|
|
734
|
-
case
|
|
735
|
-
ctx.renderer.writeInfo(
|
|
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(
|
|
887
|
+
ctx.renderer.writeSuccess("Code index rebuilt");
|
|
739
888
|
break;
|
|
740
889
|
}
|
|
741
|
-
case
|
|
742
|
-
ctx.renderer.writeInfo(
|
|
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(
|
|
894
|
+
ctx.renderer.writeSuccess("Project map rebuilt");
|
|
746
895
|
break;
|
|
747
896
|
}
|
|
748
|
-
case
|
|
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(
|
|
901
|
+
ctx.renderer.writeDim(" (from cache)");
|
|
753
902
|
}
|
|
754
903
|
break;
|
|
755
904
|
}
|
|
756
|
-
case
|
|
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(
|
|
908
|
+
ctx.renderer.writeLine(mapFormatted.split("\n").slice(0, 3).join("\n"));
|
|
760
909
|
ctx.renderer.writeNewline();
|
|
761
|
-
ctx.renderer.writeLine(idxFormatted.split(
|
|
910
|
+
ctx.renderer.writeLine(idxFormatted.split("\n").slice(0, 3).join("\n"));
|
|
762
911
|
break;
|
|
763
912
|
}
|
|
764
913
|
default:
|
|
765
|
-
ctx.renderer.writeInfo(
|
|
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(
|
|
779
|
-
ctx.renderer.writeDim(
|
|
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 ===
|
|
787
|
-
appendNote(
|
|
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 ===
|
|
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(
|
|
948
|
+
ctx.renderer.writeDim(" Profile is empty.");
|
|
800
949
|
}
|
|
801
950
|
return;
|
|
802
951
|
}
|
|
803
|
-
appendNote(args.trim(),
|
|
804
|
-
ctx.renderer.writeSuccess(
|
|
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() ||
|
|
956
|
+
const mode = args.trim() || "normal";
|
|
808
957
|
const history = ctx.conversationHistory;
|
|
809
958
|
if (history.length === 0) {
|
|
810
|
-
ctx.renderer.writeDim(
|
|
959
|
+
ctx.renderer.writeDim(" No conversation to export.");
|
|
811
960
|
return;
|
|
812
961
|
}
|
|
813
|
-
let output =
|
|
814
|
-
if (mode ===
|
|
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 ===
|
|
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 ===
|
|
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(
|
|
834
|
-
writeFileSync(filename, output,
|
|
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] ||
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
858
|
-
ctx.renderer.writeLine(` ${theme.dim(
|
|
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
|
|
1010
|
+
case "create": {
|
|
862
1011
|
if (!name) {
|
|
863
|
-
ctx.renderer.writeInfo(
|
|
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
|
|
1021
|
+
case "install": {
|
|
873
1022
|
const [themeName, url] = parts.slice(1);
|
|
874
1023
|
if (!themeName || !url) {
|
|
875
|
-
ctx.renderer.writeInfo(
|
|
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}"`, {
|
|
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
|
|
1043
|
+
case "remove": {
|
|
891
1044
|
if (!name) {
|
|
892
|
-
ctx.renderer.writeInfo(
|
|
1045
|
+
ctx.renderer.writeInfo(" Usage: /theme remove <name>");
|
|
893
1046
|
break;
|
|
894
1047
|
}
|
|
895
1048
|
if (!removeTheme(name)) {
|