min-agent 0.3.0 → 0.4.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/README.md +111 -28
- package/dist/agent.js +1119 -256
- package/dist/cli/commands/chat.js +10 -0
- package/dist/cli/commands/exec.js +32 -0
- package/dist/cli/commands/history.js +58 -0
- package/dist/cli/commands/index.js +224 -0
- package/dist/cli/commands/init.js +18 -0
- package/dist/cli/commands/mcp.js +173 -0
- package/dist/cli/commands/memory.js +69 -0
- package/dist/cli/commands/models.js +21 -0
- package/dist/cli/commands/permission.js +12 -0
- package/dist/cli/commands/rules.js +33 -0
- package/dist/cli/commands/sandbox.js +13 -0
- package/dist/cli/commands/serve.js +9 -0
- package/dist/cli/commands/setup.js +4 -0
- package/dist/cli/commands/shared.js +16 -0
- package/dist/cli/commands/skills.js +119 -0
- package/dist/cli/commands/update.js +7 -0
- package/dist/cli/commands/write-config.js +30 -0
- package/dist/cli/errors.js +36 -0
- package/dist/cli/exec-prompt.js +26 -0
- package/dist/cli/option-helpers.js +53 -0
- package/dist/cli/program.js +180 -0
- package/dist/cli.js +5 -888
- package/dist/code-mode.js +32 -14
- package/dist/compaction.js +347 -160
- package/dist/config.js +119 -10
- package/dist/confirm.js +56 -9
- package/dist/context-window.js +107 -39
- package/dist/doom-loop.js +264 -29
- package/dist/fetch-timeout.js +152 -0
- package/dist/http-approvals.js +60 -0
- package/dist/instructions.js +21 -0
- package/dist/logger.js +33 -4
- package/dist/markdown.js +37 -11
- package/dist/mcp.js +328 -30
- package/dist/memory.js +97 -56
- package/dist/output.js +7 -5
- package/dist/permission-cli.js +43 -0
- package/dist/plugins.js +46 -8
- package/dist/pricing.js +4 -4
- package/dist/provider.js +23 -6
- package/dist/question-format.js +60 -0
- package/dist/sandbox-cli.js +82 -0
- package/dist/sandbox.js +403 -0
- package/dist/save-throttle.js +45 -0
- package/dist/serve/common.js +404 -0
- package/dist/serve/routes-chat.js +347 -0
- package/dist/serve/routes-mcp.js +212 -0
- package/dist/serve/routes-memory.js +66 -0
- package/dist/serve/routes-meta.js +205 -0
- package/dist/serve/routes-sessions.js +61 -0
- package/dist/serve/routes-skills.js +70 -0
- package/dist/serve.js +33 -883
- package/dist/sessions.js +53 -9
- package/dist/skills.js +82 -18
- package/dist/title-gen.js +8 -2
- package/dist/token-display.js +36 -0
- package/dist/tool-display.js +5 -0
- package/dist/tool-output.js +1 -3
- package/dist/tools/apply_patch.js +85 -11
- package/dist/tools/atomic-file.js +35 -0
- package/dist/tools/backend.js +2 -2
- package/dist/tools/bash.js +57 -19
- package/dist/tools/code_search.js +7 -1
- package/dist/tools/edit.js +11 -10
- package/dist/tools/explore.js +74 -14
- package/dist/tools/glob.js +4 -0
- package/dist/tools/grep.js +17 -10
- package/dist/tools/index.js +6 -21
- package/dist/tools/question.js +28 -9
- package/dist/tools/read.js +6 -4
- package/dist/tools/search-searxng.js +223 -0
- package/dist/tools/search-serper.js +189 -0
- package/dist/tools/task.js +84 -30
- package/dist/tools/todo.js +120 -19
- package/dist/tools/web_fetch.js +11 -3
- package/dist/tools/web_search.js +66 -556
- package/dist/tools/write.js +23 -6
- package/dist/tui/App.js +63 -14
- package/dist/tui/ConfirmBar.js +45 -13
- package/dist/tui/InputBar.js +150 -35
- package/dist/tui/MessageList.js +266 -125
- package/dist/tui/ModelPicker.js +8 -3
- package/dist/tui/QuestionBar.js +51 -19
- package/dist/tui/SessionPicker.js +79 -0
- package/dist/tui/StatusBar.js +8 -14
- package/dist/tui/agent-runner.js +142 -22
- package/dist/tui/caret-pos.js +48 -5
- package/dist/tui/caret.js +1 -1
- package/dist/tui/click-count.js +13 -0
- package/dist/tui/drag-state.js +8 -3
- package/dist/tui/hydrate.js +129 -0
- package/dist/tui/index.js +42 -13
- package/dist/tui/input-history.js +92 -11
- package/dist/tui/layout.js +75 -4
- package/dist/tui/prompt-queue.js +24 -0
- package/dist/tui/selection.js +113 -21
- package/dist/tui/session-switch.js +28 -0
- package/dist/tui/slash-commands.js +22 -6
- package/dist/tui/slash-handler.js +233 -58
- package/dist/tui/text-width.js +38 -16
- package/dist/tui/token-info.js +7 -0
- package/dist/tui/tool-children.js +19 -0
- package/dist/tui/undo-stack.js +1 -1
- package/dist/tui/use-sgr-mouse.js +3 -1
- package/dist/tui-chat.js +276 -40
- package/dist/updater.js +88 -29
- package/dist/xml-search.js +194 -0
- package/docs/API.md +257 -25
- package/docs/superpowers/plans/2026-08-20-tui-completeness.md +873 -0
- package/docs/superpowers/plans/2026-08-20-unified-tui-default.md +631 -0
- package/docs/superpowers/specs/2026-08-20-config-http-alignment-design.md +47 -0
- package/docs/superpowers/specs/2026-08-20-mcp-plugins-alignment-design.md +37 -0
- package/docs/superpowers/specs/2026-08-20-sandbox-permissions-design.md +68 -0
- package/docs/superpowers/specs/2026-08-20-tui-completeness-design.md +273 -0
- package/docs/superpowers/specs/2026-08-20-unified-tui-default-design.md +165 -0
- package/package.json +6 -1
- package/skills/self-config/SKILL.md +90 -0
- package/skills/self-config/reference.md +149 -0
package/dist/cli.js
CHANGED
|
@@ -1,895 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { discoverSkills } from "./skills.js";
|
|
4
|
-
import { loadMemories, addMemory, deleteMemory, searchMemories } from "./memory.js";
|
|
5
|
-
import { runSetup, isConfigured, loadConfig, fetchModels, getConfigDir, getRulesFile, getActiveProvider } from "./config.js";
|
|
6
|
-
import { setAutoApprove } from "./confirm.js";
|
|
7
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
8
|
-
import path from "path";
|
|
9
|
-
import os from "os";
|
|
10
|
-
import { fileURLToPath } from "url";
|
|
1
|
+
import { runProgram } from "./cli/program.js";
|
|
2
|
+
import { registerCommands } from "./cli/commands/index.js";
|
|
11
3
|
import { initLogger, log } from "./logger.js";
|
|
12
|
-
const rawArgs = process.argv.slice(2);
|
|
13
|
-
// Extract leading global flags only, so subcommands can still use "-y"
|
|
14
|
-
const args = [...rawArgs];
|
|
15
|
-
let hasYes = false;
|
|
16
|
-
while (args[0] === "--yes" || args[0] === "-y") {
|
|
17
|
-
hasYes = true;
|
|
18
|
-
args.shift();
|
|
19
|
-
}
|
|
20
|
-
if (hasYes)
|
|
21
|
-
setAutoApprove(true);
|
|
22
|
-
/** Parse common flags in any position; unknown args become positionals. */
|
|
23
|
-
function parseFlags(argv) {
|
|
24
|
-
let model;
|
|
25
|
-
let provider;
|
|
26
|
-
let resume;
|
|
27
|
-
const images = [];
|
|
28
|
-
let port;
|
|
29
|
-
let host;
|
|
30
|
-
const positionals = [];
|
|
31
|
-
for (let i = 0; i < argv.length; i++) {
|
|
32
|
-
const a = argv[i];
|
|
33
|
-
const take = () => (argv[i + 1] ? argv[++i] : "");
|
|
34
|
-
if ((a === "--model" || a === "-m") && argv[i + 1]) {
|
|
35
|
-
model = take();
|
|
36
|
-
}
|
|
37
|
-
else if (a === "--provider" && argv[i + 1]) {
|
|
38
|
-
provider = take();
|
|
39
|
-
}
|
|
40
|
-
else if (a === "--resume" && argv[i + 1]) {
|
|
41
|
-
resume = take();
|
|
42
|
-
}
|
|
43
|
-
else if ((a === "--image" || a === "-i") && argv[i + 1]) {
|
|
44
|
-
images.push(take());
|
|
45
|
-
}
|
|
46
|
-
else if ((a === "--port" || a === "-p") && argv[i + 1]) {
|
|
47
|
-
port = parseInt(take(), 10);
|
|
48
|
-
}
|
|
49
|
-
else if (a === "--host" && argv[i + 1]) {
|
|
50
|
-
host = take();
|
|
51
|
-
}
|
|
52
|
-
else if (a === "--yes" || a === "-y") {
|
|
53
|
-
setAutoApprove(true);
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
positionals.push(a);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return { model, provider, resume, images, port, host, positionals };
|
|
60
|
-
}
|
|
61
|
-
function printUsage() {
|
|
62
|
-
console.log(`
|
|
63
|
-
min-agent - Minimal AI coding agent
|
|
64
|
-
|
|
65
|
-
Usage:
|
|
66
|
-
min-agent init Initialize .min-agent/ in current directory
|
|
67
|
-
min-agent chat <message> Send a message to the agent
|
|
68
|
-
min-agent chat Start interactive multi-turn chat
|
|
69
|
-
min-agent chat --resume <id> Resume a previous session
|
|
70
|
-
min-agent code AI coding mode (project-aware)
|
|
71
|
-
min-agent setup Configure providers (interactive)
|
|
72
|
-
min-agent models List available models
|
|
73
|
-
min-agent history List saved sessions
|
|
74
|
-
min-agent history delete <id> Delete a saved session
|
|
75
|
-
min-agent history rename <id> <t> Rename a saved session
|
|
76
|
-
min-agent history export <id> [-o <file>] Export a session as JSON
|
|
77
|
-
min-agent update Check for updates and upgrade
|
|
78
|
-
min-agent rules Show loaded instruction rules
|
|
79
|
-
min-agent rules edit Edit global rules file
|
|
80
|
-
min-agent memory List all memories
|
|
81
|
-
min-agent memory add <text> Add a memory manually
|
|
82
|
-
min-agent memory search <query> Search memories
|
|
83
|
-
min-agent memory delete <index> Delete a memory by number
|
|
84
|
-
min-agent mcp add <name> <cmd> Add a local MCP server (stdio)
|
|
85
|
-
min-agent mcp add <name> --url <url> [--sse] [--streamable-http] [--token <t>] Add remote MCP (HTTP)
|
|
86
|
-
min-agent mcp remove <name> Remove an MCP server
|
|
87
|
-
min-agent mcp list List configured MCP servers
|
|
88
|
-
min-agent mcp check Check MCP server availability
|
|
89
|
-
min-agent skills list List available skills
|
|
90
|
-
min-agent skills info <name> Show skill details
|
|
91
|
-
min-agent skills enable|disable <name...> [--project] Toggle skills
|
|
92
|
-
min-agent serve [--host H] [--port P] HTTP API (see docs/API.md)
|
|
93
|
-
|
|
94
|
-
Options:
|
|
95
|
-
--model, -m <model> Override model for this request
|
|
96
|
-
--provider <name> Use a specific configured provider
|
|
97
|
-
--image, -i <path> Attach an image (can be used multiple times)
|
|
98
|
-
--yes, -y Auto-approve all confirmations (dangerous commands, file overwrites)
|
|
99
|
-
|
|
100
|
-
Permission modes (set in ~/.min-agent/config.json):
|
|
101
|
-
"permission": "ask" Prompt before dangerous operations (default)
|
|
102
|
-
"permission": "allow-all" Auto-approve everything (same as --yes permanently)
|
|
103
|
-
|
|
104
|
-
Rules (loaded as system instructions):
|
|
105
|
-
Global: ~/.min-agent/rules.md
|
|
106
|
-
Project: ./AGENTS.md or ./RULES.md or ./.min-agent/AGENTS.md
|
|
107
|
-
Config: "instructions" array in ~/.min-agent/config.json
|
|
108
|
-
|
|
109
|
-
Examples:
|
|
110
|
-
min-agent setup
|
|
111
|
-
min-agent chat "hello"
|
|
112
|
-
min-agent chat
|
|
113
|
-
min-agent serve --port 8787
|
|
114
|
-
min-agent rules edit
|
|
115
|
-
min-agent mcp add filesystem npx -y @modelcontextprotocol/server-filesystem /tmp
|
|
116
|
-
min-agent mcp add remote --url https://example.com/mcp --token "$TOKEN"
|
|
117
|
-
`);
|
|
118
|
-
}
|
|
119
|
-
function parseMcpAddArgs(argv) {
|
|
120
|
-
let skipCheck = false;
|
|
121
|
-
let url;
|
|
122
|
-
let token;
|
|
123
|
-
let sse = false;
|
|
124
|
-
let streamableHttp = false;
|
|
125
|
-
let project = false;
|
|
126
|
-
let timeout;
|
|
127
|
-
const environment = {};
|
|
128
|
-
const cmd = [];
|
|
129
|
-
for (let i = 0; i < argv.length; i++) {
|
|
130
|
-
const a = argv[i];
|
|
131
|
-
if (a === "--skip-check") {
|
|
132
|
-
skipCheck = true;
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
if (a === "--sse") {
|
|
136
|
-
sse = true;
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
if (a === "--streamable-http") {
|
|
140
|
-
streamableHttp = true;
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (a === "--project") {
|
|
144
|
-
project = true;
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
if (a === "--url" && argv[i + 1]) {
|
|
148
|
-
url = argv[++i];
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
if (a === "--token" && argv[i + 1]) {
|
|
152
|
-
token = argv[++i];
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
if (a === "--timeout" && argv[i + 1]) {
|
|
156
|
-
const value = parseInt(argv[++i], 10);
|
|
157
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
158
|
-
console.error(`Invalid --timeout value: ${argv[i]} (must be a positive number of ms)`);
|
|
159
|
-
process.exit(1);
|
|
160
|
-
}
|
|
161
|
-
timeout = value;
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
if (a === "--env") {
|
|
165
|
-
const kv = argv[i + 1];
|
|
166
|
-
if (!kv) {
|
|
167
|
-
console.error("Invalid --env: missing KEY=VALUE");
|
|
168
|
-
process.exit(1);
|
|
169
|
-
}
|
|
170
|
-
const eq = kv.indexOf("=");
|
|
171
|
-
if (eq <= 0) {
|
|
172
|
-
console.error(`Invalid --env format: ${kv} (use KEY=VALUE)`);
|
|
173
|
-
process.exit(1);
|
|
174
|
-
}
|
|
175
|
-
i++;
|
|
176
|
-
environment[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
cmd.push(a);
|
|
180
|
-
}
|
|
181
|
-
return { skipCheck, url, token, sse, streamableHttp, project, timeout, environment, cmd };
|
|
182
|
-
}
|
|
183
|
-
/** One-line, length-capped text for list output. */
|
|
184
|
-
function truncate(text, max) {
|
|
185
|
-
const flat = text.replace(/\s+/g, " ").trim();
|
|
186
|
-
return flat.length <= max ? flat : `${flat.slice(0, max - 1)}…`;
|
|
187
|
-
}
|
|
188
|
-
function ensureProjectDefaults() {
|
|
189
|
-
const projectConfigDir = path.join(process.cwd(), ".min-agent");
|
|
190
|
-
const projectSkillsDir = path.join(projectConfigDir, "skills");
|
|
191
|
-
const projectMcpFile = path.join(projectConfigDir, "mcp.json");
|
|
192
|
-
if (!existsSync(projectSkillsDir)) {
|
|
193
|
-
mkdirSync(projectSkillsDir, { recursive: true });
|
|
194
|
-
}
|
|
195
|
-
if (!existsSync(projectMcpFile)) {
|
|
196
|
-
mkdirSync(projectConfigDir, { recursive: true });
|
|
197
|
-
writeFileSync(projectMcpFile, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
4
|
async function main() {
|
|
201
|
-
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
202
|
-
printUsage();
|
|
203
|
-
process.exit(0);
|
|
204
|
-
}
|
|
205
|
-
if (args[0] === "--version" || args[0] === "-v") {
|
|
206
|
-
const pkgPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../package.json");
|
|
207
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
208
|
-
console.log(pkg.version);
|
|
209
|
-
process.exit(0);
|
|
210
|
-
}
|
|
211
|
-
const command = args[0];
|
|
212
5
|
initLogger();
|
|
213
|
-
log("info",
|
|
214
|
-
|
|
215
|
-
case "setup": {
|
|
216
|
-
await runSetup();
|
|
217
|
-
break;
|
|
218
|
-
}
|
|
219
|
-
case "models": {
|
|
220
|
-
const flags = parseFlags(args.slice(1));
|
|
221
|
-
const config = loadConfig();
|
|
222
|
-
const provider = flags.provider
|
|
223
|
-
? config.providers?.find((p) => p.name === flags.provider)
|
|
224
|
-
: getActiveProvider(config);
|
|
225
|
-
if (!provider?.baseURL || !provider?.apiKey) {
|
|
226
|
-
console.error("Not configured. Run: min-agent setup");
|
|
227
|
-
process.exit(1);
|
|
228
|
-
}
|
|
229
|
-
console.log(`Fetching models (${provider.name ?? "default"})...`);
|
|
230
|
-
const models = await fetchModels(provider.baseURL, provider.apiKey);
|
|
231
|
-
if (models.length === 0) {
|
|
232
|
-
console.log("No models found or unable to fetch model list.");
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
console.log(`\nAvailable models (${models.length}):`);
|
|
236
|
-
for (const m of models) {
|
|
237
|
-
const marker = m === provider.defaultModel ? " ← default" : "";
|
|
238
|
-
console.log(` ${m}${marker}`);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
break;
|
|
242
|
-
}
|
|
243
|
-
case "chat": {
|
|
244
|
-
if (!isConfigured()) {
|
|
245
|
-
console.error("Not configured. Run: min-agent setup");
|
|
246
|
-
process.exit(1);
|
|
247
|
-
}
|
|
248
|
-
const { model: modelOverride, provider: providerOverride, resume: resumeId, images, positionals } = parseFlags(args.slice(1));
|
|
249
|
-
const message = positionals.join(" ");
|
|
250
|
-
if (resumeId && message) {
|
|
251
|
-
console.error("--resume <id> cannot be combined with a message; run `min-agent chat --resume <id>` to continue that session");
|
|
252
|
-
process.exit(1);
|
|
253
|
-
}
|
|
254
|
-
if (!message) {
|
|
255
|
-
const { runTui } = await import("./tui-chat.js");
|
|
256
|
-
await runTui({ modelId: modelOverride, providerName: providerOverride, resumeSessionId: resumeId, mode: "chat", images });
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
await runAgent(message, modelOverride, images.length > 0 ? images : undefined, providerOverride);
|
|
260
|
-
}
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
263
|
-
case "code": {
|
|
264
|
-
if (!isConfigured()) {
|
|
265
|
-
console.error("Not configured. Run: min-agent setup");
|
|
266
|
-
process.exit(1);
|
|
267
|
-
}
|
|
268
|
-
const { model: modelOverride, provider: providerOverride, resume: resumeId } = parseFlags(args.slice(1));
|
|
269
|
-
const { runTui } = await import("./tui-chat.js");
|
|
270
|
-
await runTui({ modelId: modelOverride, providerName: providerOverride, resumeSessionId: resumeId, mode: "code" });
|
|
271
|
-
break;
|
|
272
|
-
}
|
|
273
|
-
case "serve": {
|
|
274
|
-
if (!isConfigured()) {
|
|
275
|
-
console.error("Not configured. Run: min-agent setup");
|
|
276
|
-
process.exit(1);
|
|
277
|
-
}
|
|
278
|
-
const { port: servePort, host: serveHost } = parseFlags(args.slice(1));
|
|
279
|
-
const { runServe } = await import("./serve.js");
|
|
280
|
-
await runServe({ port: servePort, host: serveHost });
|
|
281
|
-
break;
|
|
282
|
-
}
|
|
283
|
-
case "mcp": {
|
|
284
|
-
const subcommand = args[1];
|
|
285
|
-
if (subcommand === "-h" || subcommand === "--help" || !subcommand) {
|
|
286
|
-
console.log(`Usage: min-agent mcp <command>
|
|
287
|
-
|
|
288
|
-
Commands:
|
|
289
|
-
list List configured MCP servers (name + status + scope)
|
|
290
|
-
info <name> Show details of a server
|
|
291
|
-
add <name> ... Add a new server (stdio or remote)
|
|
292
|
-
remove <name> [--project] Remove a server
|
|
293
|
-
enable <name...> [--project] Enable a disabled server
|
|
294
|
-
disable <name...> [--project] Disable a server
|
|
295
|
-
check Test connectivity of all servers
|
|
296
|
-
|
|
297
|
-
Add flags:
|
|
298
|
-
--project Write to .min-agent/mcp.json (project scope)
|
|
299
|
-
--url <url> Remote server URL
|
|
300
|
-
--sse | --streamable-http Remote transport mode (default: auto)
|
|
301
|
-
--token <t> Bearer token for remote servers
|
|
302
|
-
--env KEY=VALUE Extra env for stdio servers (repeatable)
|
|
303
|
-
--timeout <ms> Connection/call timeout
|
|
304
|
-
--skip-check Skip validation before saving`);
|
|
305
|
-
process.exit(0);
|
|
306
|
-
}
|
|
307
|
-
switch (subcommand) {
|
|
308
|
-
case "add": {
|
|
309
|
-
const name = args[2];
|
|
310
|
-
if (!name) {
|
|
311
|
-
console.error("Usage: min-agent mcp add <name> [--project] [--skip-check] <command...>");
|
|
312
|
-
console.error(" or: min-agent mcp add <name> --url <https://host/mcp> [--sse|--streamable-http] [--token <bearer>] [--project] [--skip-check]");
|
|
313
|
-
process.exit(1);
|
|
314
|
-
}
|
|
315
|
-
const { skipCheck, url, token, sse, streamableHttp, project, timeout, environment, cmd } = parseMcpAddArgs(args.slice(3));
|
|
316
|
-
const scope = project ? "project" : "global";
|
|
317
|
-
if (sse && streamableHttp) {
|
|
318
|
-
console.error("--sse and --streamable-http are mutually exclusive");
|
|
319
|
-
process.exit(1);
|
|
320
|
-
}
|
|
321
|
-
let entry;
|
|
322
|
-
if (url?.trim()) {
|
|
323
|
-
entry = {
|
|
324
|
-
url: url.trim(),
|
|
325
|
-
enabled: true,
|
|
326
|
-
remoteTransport: streamableHttp ? "streamable-http" : sse ? "sse" : "auto",
|
|
327
|
-
...(timeout ? { timeout } : {}),
|
|
328
|
-
};
|
|
329
|
-
if (token?.trim())
|
|
330
|
-
entry.token = token.trim();
|
|
331
|
-
}
|
|
332
|
-
else if (cmd.length > 0) {
|
|
333
|
-
entry = { command: cmd, enabled: true, ...(timeout ? { timeout } : {}) };
|
|
334
|
-
}
|
|
335
|
-
else {
|
|
336
|
-
console.error("Usage: min-agent mcp add <name> [--project] [--skip-check] <command...>");
|
|
337
|
-
console.error(" or: min-agent mcp add <name> --url <https://host/mcp> [--sse|--streamable-http] [--token <bearer>] [--project] [--skip-check]");
|
|
338
|
-
process.exit(1);
|
|
339
|
-
}
|
|
340
|
-
if (Object.keys(environment).length > 0)
|
|
341
|
-
entry.environment = environment;
|
|
342
|
-
let detectedTools = 0;
|
|
343
|
-
if (!skipCheck) {
|
|
344
|
-
console.log(`Validating MCP server "${name}"...`);
|
|
345
|
-
const checkResult = await checkMcpServer(name, entry);
|
|
346
|
-
if (!checkResult.ok) {
|
|
347
|
-
console.error(`MCP server "${name}" validation failed: ${checkResult.error ?? "unknown error"}`);
|
|
348
|
-
process.exit(1);
|
|
349
|
-
}
|
|
350
|
-
detectedTools = checkResult.toolCount;
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
console.log(`Skipping MCP validation for "${name}" (--skip-check).`);
|
|
354
|
-
}
|
|
355
|
-
const config = loadMcpConfigFile(scope);
|
|
356
|
-
config.mcpServers[name] = entry;
|
|
357
|
-
saveMcpConfig(config, scope);
|
|
358
|
-
const toolsSuffix = skipCheck ? "" : ` (${detectedTools} tools detected)`;
|
|
359
|
-
const scopeSuffix = project ? " [project]" : "";
|
|
360
|
-
console.log(`✓ MCP server "${name}" added${scopeSuffix}: ${formatMcpServerBinding(entry)}${toolsSuffix}`);
|
|
361
|
-
break;
|
|
362
|
-
}
|
|
363
|
-
case "remove": {
|
|
364
|
-
const project = args.includes("--project");
|
|
365
|
-
const scope = project ? "project" : "global";
|
|
366
|
-
const name = args.slice(2).find((a) => a !== "--project");
|
|
367
|
-
if (!name) {
|
|
368
|
-
console.error("Usage: min-agent mcp remove <name> [--project]");
|
|
369
|
-
process.exit(1);
|
|
370
|
-
}
|
|
371
|
-
const config = loadMcpConfigFile(scope);
|
|
372
|
-
if (!config.mcpServers[name]) {
|
|
373
|
-
const elsewhere = loadMcpConfigEntries().find((e) => e.name === name);
|
|
374
|
-
console.error(elsewhere
|
|
375
|
-
? `MCP server "${name}" is defined in ${elsewhere.scope} scope, not ${scope}.`
|
|
376
|
-
: `MCP server "${name}" not found`);
|
|
377
|
-
process.exit(1);
|
|
378
|
-
}
|
|
379
|
-
delete config.mcpServers[name];
|
|
380
|
-
saveMcpConfig(config, scope);
|
|
381
|
-
console.log(`✓ MCP server "${name}" removed${project ? " [project]" : ""}`);
|
|
382
|
-
break;
|
|
383
|
-
}
|
|
384
|
-
case "list": {
|
|
385
|
-
const entries = loadMcpConfigEntries();
|
|
386
|
-
if (entries.length === 0) {
|
|
387
|
-
console.log("No MCP servers configured.");
|
|
388
|
-
console.log("Add one with: min-agent mcp add <name> <command...>");
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
console.log("MCP Servers:");
|
|
392
|
-
for (const { name, config, scope } of entries) {
|
|
393
|
-
const status = config.enabled === false ? "\x1b[90mdisabled\x1b[0m" : "\x1b[32menabled\x1b[0m";
|
|
394
|
-
const scopeMark = scope === "project" ? " \x1b[90m[project]\x1b[0m" : "";
|
|
395
|
-
console.log(` ${name} ${status}${scopeMark}`);
|
|
396
|
-
}
|
|
397
|
-
console.log("\nUse: min-agent mcp info <name> for details");
|
|
398
|
-
}
|
|
399
|
-
break;
|
|
400
|
-
}
|
|
401
|
-
case "info": {
|
|
402
|
-
const name = args[2];
|
|
403
|
-
if (!name) {
|
|
404
|
-
console.error("Usage: min-agent mcp info <name>");
|
|
405
|
-
process.exit(1);
|
|
406
|
-
}
|
|
407
|
-
const found = loadMcpConfigEntries().find((e) => e.name === name);
|
|
408
|
-
if (!found) {
|
|
409
|
-
console.error(`MCP server "${name}" not found`);
|
|
410
|
-
process.exit(1);
|
|
411
|
-
}
|
|
412
|
-
const cfg = found.config;
|
|
413
|
-
console.log(`Name: ${name}`);
|
|
414
|
-
console.log(`Scope: ${found.scope}`);
|
|
415
|
-
console.log(`Status: ${cfg.enabled === false ? "disabled" : "enabled"}`);
|
|
416
|
-
console.log(`Binding: ${formatMcpServerBinding(cfg)}`);
|
|
417
|
-
if (cfg.command)
|
|
418
|
-
console.log(`Command: ${Array.isArray(cfg.command) ? cfg.command.join(" ") : `${cfg.command} ${(cfg.args ?? []).join(" ")}`.trim()}`);
|
|
419
|
-
if (cfg.url) {
|
|
420
|
-
console.log(`URL: ${cfg.url}`);
|
|
421
|
-
if (cfg.remoteTransport)
|
|
422
|
-
console.log(`Transport: ${cfg.remoteTransport}`);
|
|
423
|
-
if (cfg.oauth === false)
|
|
424
|
-
console.log(`OAuth: disabled`);
|
|
425
|
-
else if (cfg.oauth)
|
|
426
|
-
console.log(`OAuth: enabled${cfg.oauth.clientId ? ` (clientId set)` : ""}`);
|
|
427
|
-
}
|
|
428
|
-
if (cfg.headers)
|
|
429
|
-
console.log(`Headers: ${Object.keys(cfg.headers).join(", ")} (values hidden)`);
|
|
430
|
-
if (cfg.token)
|
|
431
|
-
console.log(`Token: ***`);
|
|
432
|
-
if (cfg.environment)
|
|
433
|
-
console.log(`Env: ${Object.keys(cfg.environment).join(", ")}`);
|
|
434
|
-
if (cfg.timeout)
|
|
435
|
-
console.log(`Timeout: ${cfg.timeout}ms`);
|
|
436
|
-
if (cfg.connectTimeout)
|
|
437
|
-
console.log(`ConnectTimeout: ${cfg.connectTimeout}ms`);
|
|
438
|
-
if (cfg.callTimeout)
|
|
439
|
-
console.log(`CallTimeout: ${cfg.callTimeout}ms`);
|
|
440
|
-
break;
|
|
441
|
-
}
|
|
442
|
-
case "check": {
|
|
443
|
-
const results = await checkAllMcpServers();
|
|
444
|
-
if (results.length === 0) {
|
|
445
|
-
console.log("No MCP servers configured.");
|
|
446
|
-
console.log("Add one with: min-agent mcp add <name> <command...> or --url <https://...>");
|
|
447
|
-
break;
|
|
448
|
-
}
|
|
449
|
-
let failed = 0;
|
|
450
|
-
console.log("MCP Check Results:");
|
|
451
|
-
for (const result of results) {
|
|
452
|
-
if (!result.enabled) {
|
|
453
|
-
console.log(` ${result.name}: skipped (disabled)`);
|
|
454
|
-
continue;
|
|
455
|
-
}
|
|
456
|
-
if (result.ok) {
|
|
457
|
-
console.log(` ${result.name}: ok (${result.toolCount} tools)`);
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
failed++;
|
|
461
|
-
console.log(` ${result.name}: failed${result.error ? ` - ${result.error}` : ""}`);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
if (failed > 0) {
|
|
465
|
-
console.error(`\n${failed} MCP server(s) failed.`);
|
|
466
|
-
process.exit(1);
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
console.log("\nAll enabled MCP servers are available.");
|
|
470
|
-
}
|
|
471
|
-
break;
|
|
472
|
-
}
|
|
473
|
-
case "enable": {
|
|
474
|
-
const project = args.includes("--project");
|
|
475
|
-
const scope = project ? "project" : "global";
|
|
476
|
-
const names = args.slice(2).filter((a) => a !== "--project");
|
|
477
|
-
if (names.length === 0) {
|
|
478
|
-
console.error("Usage: min-agent mcp enable <name...> [--project]");
|
|
479
|
-
process.exit(1);
|
|
480
|
-
}
|
|
481
|
-
const config = loadMcpConfigFile(scope);
|
|
482
|
-
for (const name of names) {
|
|
483
|
-
if (!config.mcpServers[name]) {
|
|
484
|
-
console.error(`MCP server "${name}" not found${project ? " in project scope" : ""}`);
|
|
485
|
-
continue;
|
|
486
|
-
}
|
|
487
|
-
config.mcpServers[name].enabled = true;
|
|
488
|
-
console.log(`✓ MCP server "${name}" enabled${project ? " [project]" : ""}`);
|
|
489
|
-
}
|
|
490
|
-
saveMcpConfig(config, scope);
|
|
491
|
-
break;
|
|
492
|
-
}
|
|
493
|
-
case "disable": {
|
|
494
|
-
const project = args.includes("--project");
|
|
495
|
-
const scope = project ? "project" : "global";
|
|
496
|
-
const names = args.slice(2).filter((a) => a !== "--project");
|
|
497
|
-
if (names.length === 0) {
|
|
498
|
-
console.error("Usage: min-agent mcp disable <name...> [--project]");
|
|
499
|
-
process.exit(1);
|
|
500
|
-
}
|
|
501
|
-
const config = loadMcpConfigFile(scope);
|
|
502
|
-
for (const name of names) {
|
|
503
|
-
if (!config.mcpServers[name]) {
|
|
504
|
-
console.error(`MCP server "${name}" not found${project ? " in project scope" : ""}`);
|
|
505
|
-
continue;
|
|
506
|
-
}
|
|
507
|
-
config.mcpServers[name].enabled = false;
|
|
508
|
-
console.log(`✓ MCP server "${name}" disabled${project ? " [project]" : ""}`);
|
|
509
|
-
}
|
|
510
|
-
saveMcpConfig(config, scope);
|
|
511
|
-
break;
|
|
512
|
-
}
|
|
513
|
-
default:
|
|
514
|
-
console.error("Usage: min-agent mcp -h");
|
|
515
|
-
process.exit(1);
|
|
516
|
-
}
|
|
517
|
-
break;
|
|
518
|
-
}
|
|
519
|
-
case "history": {
|
|
520
|
-
const subcommand = args[1];
|
|
521
|
-
if (subcommand === "delete") {
|
|
522
|
-
const id = args[2];
|
|
523
|
-
if (!id) {
|
|
524
|
-
console.error("Usage: min-agent history delete <id>");
|
|
525
|
-
process.exit(1);
|
|
526
|
-
}
|
|
527
|
-
const { deleteSession } = await import("./sessions.js");
|
|
528
|
-
if (deleteSession(id)) {
|
|
529
|
-
console.log(`✓ 会话 ${id} 已删除`);
|
|
530
|
-
}
|
|
531
|
-
else {
|
|
532
|
-
console.error(`会话 ${id} 不存在`);
|
|
533
|
-
process.exit(1);
|
|
534
|
-
}
|
|
535
|
-
break;
|
|
536
|
-
}
|
|
537
|
-
if (subcommand === "rename") {
|
|
538
|
-
const id = args[2];
|
|
539
|
-
const title = args.slice(3).join(" ");
|
|
540
|
-
if (!id || !title) {
|
|
541
|
-
console.error("Usage: min-agent history rename <id> <title>");
|
|
542
|
-
process.exit(1);
|
|
543
|
-
}
|
|
544
|
-
const { renameSession } = await import("./sessions.js");
|
|
545
|
-
if (renameSession(id, title)) {
|
|
546
|
-
console.log(`✓ 会话 ${id} 已重命名为: ${title}`);
|
|
547
|
-
}
|
|
548
|
-
else {
|
|
549
|
-
console.error(`会话 ${id} 不存在`);
|
|
550
|
-
process.exit(1);
|
|
551
|
-
}
|
|
552
|
-
break;
|
|
553
|
-
}
|
|
554
|
-
if (subcommand === "export") {
|
|
555
|
-
const id = args[2];
|
|
556
|
-
if (!id) {
|
|
557
|
-
console.error("Usage: min-agent history export <id> [-o <file>]");
|
|
558
|
-
process.exit(1);
|
|
559
|
-
}
|
|
560
|
-
const { loadSession } = await import("./sessions.js");
|
|
561
|
-
const session = loadSession(id);
|
|
562
|
-
if (!session) {
|
|
563
|
-
console.error(`会话 ${id} 不存在`);
|
|
564
|
-
process.exit(1);
|
|
565
|
-
}
|
|
566
|
-
const json = JSON.stringify(session, null, 2);
|
|
567
|
-
const outIdx = args.indexOf("-o");
|
|
568
|
-
const outFile = outIdx !== -1 ? args[outIdx + 1] : undefined;
|
|
569
|
-
if (outFile) {
|
|
570
|
-
try {
|
|
571
|
-
writeFileSync(outFile, json, "utf-8");
|
|
572
|
-
console.log(`✓ 会话 ${id} 已导出到 ${outFile}`);
|
|
573
|
-
}
|
|
574
|
-
catch (err) {
|
|
575
|
-
console.error(`导出失败: ${err.message}`);
|
|
576
|
-
process.exit(1);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
else {
|
|
580
|
-
if (outIdx !== -1) {
|
|
581
|
-
console.error("Usage: min-agent history export <id> [-o <file>](-o 后缺少文件路径)");
|
|
582
|
-
process.exit(1);
|
|
583
|
-
}
|
|
584
|
-
console.log(json);
|
|
585
|
-
}
|
|
586
|
-
break;
|
|
587
|
-
}
|
|
588
|
-
const { listSessions } = await import("./sessions.js");
|
|
589
|
-
const sessions = listSessions();
|
|
590
|
-
if (sessions.length === 0) {
|
|
591
|
-
console.log("No saved sessions.");
|
|
592
|
-
console.log("Sessions are auto-saved when you exit interactive chat.");
|
|
593
|
-
}
|
|
594
|
-
else {
|
|
595
|
-
console.log(`Sessions (${sessions.length}):`);
|
|
596
|
-
for (const s of sessions.slice(0, 20)) {
|
|
597
|
-
const date = s.updated.split("T")[0];
|
|
598
|
-
console.log(` ${s.id} ${date} ${s.title} (${s.messageCount} msgs)`);
|
|
599
|
-
}
|
|
600
|
-
console.log("\nResume with: min-agent chat --resume <id>");
|
|
601
|
-
}
|
|
602
|
-
break;
|
|
603
|
-
}
|
|
604
|
-
case "memory": {
|
|
605
|
-
const subcommand = args[1];
|
|
606
|
-
switch (subcommand) {
|
|
607
|
-
case "add": {
|
|
608
|
-
const text = args.slice(2).join(" ");
|
|
609
|
-
if (!text) {
|
|
610
|
-
console.error("Usage: min-agent memory add <text>");
|
|
611
|
-
process.exit(1);
|
|
612
|
-
}
|
|
613
|
-
addMemory(text);
|
|
614
|
-
console.log(`✓ Memory saved: "${text}"`);
|
|
615
|
-
break;
|
|
616
|
-
}
|
|
617
|
-
case "search": {
|
|
618
|
-
const query = args.slice(2).join(" ");
|
|
619
|
-
if (!query) {
|
|
620
|
-
console.error("Usage: min-agent memory search <query>");
|
|
621
|
-
process.exit(1);
|
|
622
|
-
}
|
|
623
|
-
const results = searchMemories(query);
|
|
624
|
-
if (results.length === 0) {
|
|
625
|
-
console.log(`No memories matching "${query}"`);
|
|
626
|
-
}
|
|
627
|
-
else {
|
|
628
|
-
console.log(`Found ${results.length} memory(s):`);
|
|
629
|
-
for (const m of results) {
|
|
630
|
-
const tags = m.tags.length > 0 ? ` [${m.tags.join(", ")}]` : "";
|
|
631
|
-
console.log(` #${m.index + 1}: ${m.content}${tags}`);
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
break;
|
|
635
|
-
}
|
|
636
|
-
case "delete": {
|
|
637
|
-
const idx = parseInt(args[2]);
|
|
638
|
-
if (isNaN(idx)) {
|
|
639
|
-
console.error("Usage: min-agent memory delete <number>");
|
|
640
|
-
process.exit(1);
|
|
641
|
-
}
|
|
642
|
-
if (deleteMemory(idx - 1)) {
|
|
643
|
-
console.log(`✓ Memory #${idx} deleted`);
|
|
644
|
-
}
|
|
645
|
-
else {
|
|
646
|
-
console.error(`Memory #${idx} not found`);
|
|
647
|
-
}
|
|
648
|
-
break;
|
|
649
|
-
}
|
|
650
|
-
default: {
|
|
651
|
-
// List all memories
|
|
652
|
-
const memories = loadMemories();
|
|
653
|
-
if (memories.length === 0) {
|
|
654
|
-
console.log("No memories stored.");
|
|
655
|
-
console.log("The agent will automatically save memories during conversations.");
|
|
656
|
-
console.log("Or add manually: min-agent memory add \"prefer TypeScript over JavaScript\"");
|
|
657
|
-
}
|
|
658
|
-
else {
|
|
659
|
-
console.log(`Memories (${memories.length}):`);
|
|
660
|
-
for (let i = 0; i < memories.length; i++) {
|
|
661
|
-
const m = memories[i];
|
|
662
|
-
const tags = m.tags.length > 0 ? ` [${m.tags.join(", ")}]` : "";
|
|
663
|
-
const date = m.created.split("T")[0];
|
|
664
|
-
console.log(` #${i + 1}: ${m.content}${tags} (${date})`);
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
break;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
break;
|
|
671
|
-
}
|
|
672
|
-
case "skills": {
|
|
673
|
-
const subcommand = args[1];
|
|
674
|
-
if (subcommand === "-h" || subcommand === "--help" || !subcommand) {
|
|
675
|
-
console.log(`Usage: min-agent skills <command>
|
|
676
|
-
|
|
677
|
-
Commands:
|
|
678
|
-
list List skills (name + status + description)
|
|
679
|
-
info <name> Show details of a skill
|
|
680
|
-
new <name> Create a new skill scaffold
|
|
681
|
-
enable <name...> Enable disabled skills
|
|
682
|
-
disable <name...> Disable skills
|
|
683
|
-
|
|
684
|
-
Options (enable/disable):
|
|
685
|
-
--project Apply to this project only (.min-agent/config.json)
|
|
686
|
-
--global Apply to every project (default, ~/.min-agent/config.json)
|
|
687
|
-
|
|
688
|
-
Options (list/info):
|
|
689
|
-
--json Machine-readable JSON output
|
|
690
|
-
|
|
691
|
-
Options (new):
|
|
692
|
-
--global Create in ~/.agents/skills/ instead of .min-agent/skills/
|
|
693
|
-
|
|
694
|
-
Project scope wins over global, so a skill disabled globally can be
|
|
695
|
-
re-enabled for one repo with: min-agent skills enable <name> --project
|
|
696
|
-
|
|
697
|
-
Skills are discovered in (later paths override earlier ones):
|
|
698
|
-
~/.agents/skills/ .min-agent/skills/ .agents/skills/
|
|
699
|
-
.opencode/skills/ .claude/skills/`);
|
|
700
|
-
process.exit(0);
|
|
701
|
-
}
|
|
702
|
-
switch (subcommand) {
|
|
703
|
-
case "info": {
|
|
704
|
-
const flags = args.slice(2);
|
|
705
|
-
const name = flags.find((a) => !a.startsWith("-"));
|
|
706
|
-
if (!name) {
|
|
707
|
-
console.error("Usage: min-agent skills info <name> [--json]");
|
|
708
|
-
process.exit(1);
|
|
709
|
-
}
|
|
710
|
-
discoverSkills({ silent: true });
|
|
711
|
-
const { getSkill, getAllSkills } = await import("./skills.js");
|
|
712
|
-
const skill = getSkill(name);
|
|
713
|
-
if (!skill) {
|
|
714
|
-
const names = getAllSkills().map((s) => s.name);
|
|
715
|
-
console.error(`Skill "${name}" not found.${names.length ? ` Available: ${names.join(", ")}` : " No skills discovered."}`);
|
|
716
|
-
process.exit(1);
|
|
717
|
-
}
|
|
718
|
-
if (flags.includes("--json")) {
|
|
719
|
-
console.log(JSON.stringify(skill, null, 2));
|
|
720
|
-
break;
|
|
721
|
-
}
|
|
722
|
-
console.log(`Name: ${skill.name}`);
|
|
723
|
-
console.log(`Status: ${skill.enabled ? "enabled" : `disabled (${skill.disabledScope} scope)`}`);
|
|
724
|
-
if (skill.version)
|
|
725
|
-
console.log(`Version: ${skill.version}`);
|
|
726
|
-
console.log(`Description: ${skill.description}`);
|
|
727
|
-
console.log(`Location: ${skill.location}`);
|
|
728
|
-
if (skill.allowedTools.length > 0)
|
|
729
|
-
console.log(`Tools: ${skill.allowedTools.join(", ")}`);
|
|
730
|
-
if (skill.requiredBins.length > 0)
|
|
731
|
-
console.log(`Requires: ${skill.requiredBins.join(", ")}`);
|
|
732
|
-
if (skill.content.trim()) {
|
|
733
|
-
console.log("\n--- Content ---\n");
|
|
734
|
-
console.log(skill.content.trim());
|
|
735
|
-
}
|
|
736
|
-
break;
|
|
737
|
-
}
|
|
738
|
-
case "enable":
|
|
739
|
-
case "disable": {
|
|
740
|
-
const flags = args.slice(2);
|
|
741
|
-
const scope = flags.includes("--project") ? "project" : "global";
|
|
742
|
-
const names = flags.filter((a) => a !== "--project" && a !== "--global");
|
|
743
|
-
const unknownFlag = flags.find((a) => a.startsWith("-") && a !== "--project" && a !== "--global");
|
|
744
|
-
if (unknownFlag) {
|
|
745
|
-
console.error(`Unknown option: ${unknownFlag} (use --project or --global)`);
|
|
746
|
-
process.exit(1);
|
|
747
|
-
}
|
|
748
|
-
if (names.length === 0) {
|
|
749
|
-
console.error(`Usage: min-agent skills ${subcommand} <name...> [--project]`);
|
|
750
|
-
process.exit(1);
|
|
751
|
-
}
|
|
752
|
-
discoverSkills({ silent: true });
|
|
753
|
-
const { getAllSkills, setSkillEnabled, getStaleDisabledSkills } = await import("./skills.js");
|
|
754
|
-
const known = new Set(getAllSkills().map((s) => s.name));
|
|
755
|
-
const stale = new Set(getStaleDisabledSkills());
|
|
756
|
-
// `enable` must accept stale names so they can be cleaned up from config;
|
|
757
|
-
// `disable` on an unknown name is always an error.
|
|
758
|
-
const unknown = names.filter((n) => !known.has(n) && !(subcommand === "enable" && stale.has(n)));
|
|
759
|
-
if (unknown.length > 0) {
|
|
760
|
-
console.error(`Skill${unknown.length > 1 ? "s" : ""} not found: ${unknown.join(", ")}`);
|
|
761
|
-
console.error(known.size ? `Available: ${[...known].join(", ")}` : "No skills discovered.");
|
|
762
|
-
process.exit(1);
|
|
763
|
-
}
|
|
764
|
-
for (const result of setSkillEnabled(names, subcommand === "enable", scope)) {
|
|
765
|
-
console.log(`✓ Skill "${result.name}" ${subcommand}d (${scope} scope)`);
|
|
766
|
-
if (result.blockedBy) {
|
|
767
|
-
console.log(`\x1b[33m ⚠ still ${result.enabled ? "enabled" : "disabled"} — overridden by ${result.blockedBy} scope\x1b[0m`);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
break;
|
|
771
|
-
}
|
|
772
|
-
case "new": {
|
|
773
|
-
const flags = args.slice(2);
|
|
774
|
-
const name = flags.find((a) => !a.startsWith("-"));
|
|
775
|
-
if (!name || !/^[\w-]+$/.test(name)) {
|
|
776
|
-
console.error("Usage: min-agent skills new <name> [--global] (name: letters, digits, _ or -)");
|
|
777
|
-
process.exit(1);
|
|
778
|
-
}
|
|
779
|
-
const global = flags.includes("--global");
|
|
780
|
-
const root = global ? path.join(os.homedir(), ".agents", "skills") : path.join(process.cwd(), ".min-agent", "skills");
|
|
781
|
-
const dir = path.join(root, name);
|
|
782
|
-
if (existsSync(path.join(dir, "SKILL.md"))) {
|
|
783
|
-
console.error(`Skill "${name}" already exists at ${dir}`);
|
|
784
|
-
process.exit(1);
|
|
785
|
-
}
|
|
786
|
-
mkdirSync(dir, { recursive: true });
|
|
787
|
-
writeFileSync(path.join(dir, "SKILL.md"), `---\nname: ${name}\ndescription: What this skill does\n# version: 1.0.0\n# allowed-tools: [read, grep]\n# metadata:\n# requires:\n# bins: ["some-cli"]\n---\n\n# ${name}\n\nWrite the skill instructions here.\n`, "utf-8");
|
|
788
|
-
console.log(`✓ Created skill "${name}" at ${path.join(dir, "SKILL.md")}`);
|
|
789
|
-
break;
|
|
790
|
-
}
|
|
791
|
-
case "list": {
|
|
792
|
-
const json = args.includes("--json");
|
|
793
|
-
discoverSkills({ silent: true });
|
|
794
|
-
const { getAllSkills, getStaleDisabledSkills } = await import("./skills.js");
|
|
795
|
-
const all = getAllSkills().sort((a, b) => a.name.localeCompare(b.name));
|
|
796
|
-
const stale = getStaleDisabledSkills();
|
|
797
|
-
if (json) {
|
|
798
|
-
console.log(JSON.stringify({
|
|
799
|
-
count: all.length,
|
|
800
|
-
enabled_count: all.filter((s) => s.enabled).length,
|
|
801
|
-
stale_disabled: stale,
|
|
802
|
-
skills: all.map((s) => ({
|
|
803
|
-
name: s.name,
|
|
804
|
-
description: s.description,
|
|
805
|
-
location: s.location,
|
|
806
|
-
enabled: s.enabled,
|
|
807
|
-
...(s.disabledScope ? { disabled_scope: s.disabledScope } : {}),
|
|
808
|
-
...(s.version ? { version: s.version } : {}),
|
|
809
|
-
...(s.allowedTools.length ? { allowed_tools: s.allowedTools } : {}),
|
|
810
|
-
...(s.requiredBins.length ? { requires_bins: s.requiredBins } : {}),
|
|
811
|
-
})),
|
|
812
|
-
}, null, 2));
|
|
813
|
-
break;
|
|
814
|
-
}
|
|
815
|
-
if (all.length === 0 && stale.length === 0) {
|
|
816
|
-
console.log("No skills found. Add SKILL.md files in .min-agent/skills/<name>/");
|
|
817
|
-
break;
|
|
818
|
-
}
|
|
819
|
-
console.log(`Skills (${all.filter((s) => s.enabled).length} enabled, ${all.filter((s) => !s.enabled).length} disabled):`);
|
|
820
|
-
for (const skill of all) {
|
|
821
|
-
const status = skill.enabled
|
|
822
|
-
? "\x1b[32menabled \x1b[0m"
|
|
823
|
-
: `\x1b[90mdisabled(${skill.disabledScope === "project" ? "proj" : "glob"})\x1b[0m`;
|
|
824
|
-
console.log(` ${skill.name} ${status} \x1b[90m${truncate(skill.description, 60)}\x1b[0m`);
|
|
825
|
-
console.log(` \x1b[90m${skill.location}\x1b[0m`);
|
|
826
|
-
}
|
|
827
|
-
if (stale.length > 0) {
|
|
828
|
-
console.log(`\n\x1b[33m⚠ disabledSkills/enabledSkills entries matching no skill: ${stale.join(", ")}\x1b[0m`);
|
|
829
|
-
console.log(`\x1b[33m Remove them with: min-agent skills enable ${stale.join(" ")} (add --project if the entry is project-scoped)\x1b[0m`);
|
|
830
|
-
}
|
|
831
|
-
console.log("\nUse: min-agent skills info <name> for details");
|
|
832
|
-
break;
|
|
833
|
-
}
|
|
834
|
-
default:
|
|
835
|
-
console.error(`Unknown skills command: ${subcommand}`);
|
|
836
|
-
console.error("Available: list, info, new, enable, disable (see: min-agent skills -h)");
|
|
837
|
-
process.exit(1);
|
|
838
|
-
}
|
|
839
|
-
break;
|
|
840
|
-
}
|
|
841
|
-
case "update": {
|
|
842
|
-
const { runUpdate } = await import("./updater.js");
|
|
843
|
-
await runUpdate();
|
|
844
|
-
break;
|
|
845
|
-
}
|
|
846
|
-
case "init": {
|
|
847
|
-
ensureProjectDefaults();
|
|
848
|
-
console.log(`✓ Initialized .min-agent/ in ${process.cwd()}`);
|
|
849
|
-
break;
|
|
850
|
-
}
|
|
851
|
-
case "rules": {
|
|
852
|
-
const subcommand = args[1];
|
|
853
|
-
if (subcommand === "edit") {
|
|
854
|
-
const rulesFile = getRulesFile();
|
|
855
|
-
if (!existsSync(rulesFile)) {
|
|
856
|
-
mkdirSync(getConfigDir(), { recursive: true });
|
|
857
|
-
writeFileSync(rulesFile, `# Global Agent Rules\n\n<!-- Add your custom instructions here. They will be included in every conversation. -->\n`, "utf-8");
|
|
858
|
-
}
|
|
859
|
-
const editor = process.env.EDITOR || "vi";
|
|
860
|
-
const { execSync } = await import("child_process");
|
|
861
|
-
execSync(`${editor} "${rulesFile}"`, { stdio: "inherit" });
|
|
862
|
-
}
|
|
863
|
-
else {
|
|
864
|
-
const { loadInstructions } = await import("./instructions.js");
|
|
865
|
-
const instructions = await loadInstructions();
|
|
866
|
-
if (instructions.length === 0) {
|
|
867
|
-
console.log("No instruction rules loaded.");
|
|
868
|
-
console.log("");
|
|
869
|
-
console.log("Add rules by creating:");
|
|
870
|
-
console.log(` Global: ${getRulesFile()}`);
|
|
871
|
-
console.log(" Project: ./AGENTS.md or ./RULES.md");
|
|
872
|
-
console.log("");
|
|
873
|
-
console.log("Or add paths/URLs in ~/.min-agent/config.json:");
|
|
874
|
-
console.log(' { "instructions": ["./docs/rules.md", "https://..."] }');
|
|
875
|
-
}
|
|
876
|
-
else {
|
|
877
|
-
console.log(`Loaded ${instructions.length} instruction source(s):\n`);
|
|
878
|
-
for (const inst of instructions) {
|
|
879
|
-
const firstLine = inst.split("\n")[0];
|
|
880
|
-
console.log(` ${firstLine}`);
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
break;
|
|
885
|
-
}
|
|
886
|
-
default:
|
|
887
|
-
console.error(`Unknown command: ${command}`);
|
|
888
|
-
printUsage();
|
|
889
|
-
process.exit(1);
|
|
890
|
-
}
|
|
6
|
+
log("info", "min-agent started");
|
|
7
|
+
await runProgram(process.argv.slice(2), registerCommands);
|
|
891
8
|
}
|
|
892
9
|
main().catch((err) => {
|
|
893
10
|
console.error(`\x1b[31mFatal error: ${err.message}\x1b[0m`);
|
|
894
|
-
process.
|
|
11
|
+
process.exitCode = 1;
|
|
895
12
|
});
|