min-agent 0.1.7 → 0.1.9
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.js +34 -2
- package/dist/cli.js +7 -1
- package/dist/confirm.js +12 -1
- package/dist/tools/bash.js +12 -0
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -14,6 +14,8 @@ import { DoomLoopDetector } from "./doom-loop.js";
|
|
|
14
14
|
import { scanProject, buildCodeSystemPrompt } from "./code-mode.js";
|
|
15
15
|
import { ThinkingBodySplitter, stripThinkingFromAssistantText } from "./assistant-stream.js";
|
|
16
16
|
import { printHeader, printDivider, printToolCall, printToolResult, printDone } from "./output.js";
|
|
17
|
+
import { killActiveProcesses } from "./tools/bash.js";
|
|
18
|
+
import { setConfirmReadline } from "./confirm.js";
|
|
17
19
|
import readline from "readline";
|
|
18
20
|
const MAX_STEPS = 30;
|
|
19
21
|
function dimStyle() {
|
|
@@ -142,10 +144,12 @@ export async function runChat(modelId, resumeSessionId) {
|
|
|
142
144
|
output: process.stdout,
|
|
143
145
|
prompt: "\x1b[36m> \x1b[0m",
|
|
144
146
|
});
|
|
147
|
+
setConfirmReadline(rl);
|
|
145
148
|
// Handle Ctrl+C: abort current generation, don't exit
|
|
146
149
|
let abortController = null;
|
|
147
150
|
process.on("SIGINT", () => {
|
|
148
151
|
if (abortController) {
|
|
152
|
+
killActiveProcesses();
|
|
149
153
|
abortController.abort();
|
|
150
154
|
abortController = null;
|
|
151
155
|
console.log("\n\x1b[90m(cancelled)\x1b[0m\n");
|
|
@@ -157,7 +161,20 @@ export async function runChat(modelId, resumeSessionId) {
|
|
|
157
161
|
rl.close();
|
|
158
162
|
}
|
|
159
163
|
});
|
|
160
|
-
|
|
164
|
+
// Listen for ESC key to cancel running agent task via keypress events
|
|
165
|
+
if (process.stdin.isTTY) {
|
|
166
|
+
readline.emitKeypressEvents(process.stdin, rl);
|
|
167
|
+
process.stdin.on("keypress", (_ch, key) => {
|
|
168
|
+
if (key && key.name === "escape" && abortController) {
|
|
169
|
+
killActiveProcesses();
|
|
170
|
+
abortController.abort();
|
|
171
|
+
abortController = null;
|
|
172
|
+
console.log("\n\x1b[90m(esc cancelled)\x1b[0m\n");
|
|
173
|
+
rl.prompt();
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
console.log("\x1b[90m输入消息开始对话,输入 /help 查看命令,Esc 取消运行,/exit 退出\x1b[0m\n");
|
|
161
178
|
rl.prompt();
|
|
162
179
|
// Multi-line paste detection: collect rapid successive lines
|
|
163
180
|
let pasteBuffer = [];
|
|
@@ -251,9 +268,11 @@ export async function runCode(modelId, resumeSessionId) {
|
|
|
251
268
|
output: process.stdout,
|
|
252
269
|
prompt: "\x1b[32m❯ \x1b[0m",
|
|
253
270
|
});
|
|
271
|
+
setConfirmReadline(rl);
|
|
254
272
|
let abortController = null;
|
|
255
273
|
process.on("SIGINT", () => {
|
|
256
274
|
if (abortController) {
|
|
275
|
+
killActiveProcesses();
|
|
257
276
|
abortController.abort();
|
|
258
277
|
abortController = null;
|
|
259
278
|
console.log("\n\x1b[90m(cancelled)\x1b[0m\n");
|
|
@@ -264,7 +283,20 @@ export async function runCode(modelId, resumeSessionId) {
|
|
|
264
283
|
rl.close();
|
|
265
284
|
}
|
|
266
285
|
});
|
|
267
|
-
|
|
286
|
+
// Listen for ESC key to cancel running agent task via keypress events
|
|
287
|
+
if (process.stdin.isTTY) {
|
|
288
|
+
readline.emitKeypressEvents(process.stdin, rl);
|
|
289
|
+
process.stdin.on("keypress", (_ch, key) => {
|
|
290
|
+
if (key && key.name === "escape" && abortController) {
|
|
291
|
+
killActiveProcesses();
|
|
292
|
+
abortController.abort();
|
|
293
|
+
abortController = null;
|
|
294
|
+
console.log("\n\x1b[90m(esc cancelled)\x1b[0m\n");
|
|
295
|
+
rl.prompt();
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
console.log("\x1b[90m输入任务开始编码,/help 查看命令,Esc 取消运行,Ctrl+C 中断\x1b[0m\n");
|
|
268
300
|
rl.prompt();
|
|
269
301
|
let pasteBuffer = [];
|
|
270
302
|
let pasteTimer = null;
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { discoverSkills, getSkills } from "./skills.js";
|
|
|
4
4
|
import { loadMemories, addMemory, deleteMemory, searchMemories } from "./memory.js";
|
|
5
5
|
import { runSetup, isConfigured, loadConfig, saveConfig, fetchModels, getConfigDir, getRulesFile } from "./config.js";
|
|
6
6
|
import { setAutoApprove } from "./confirm.js";
|
|
7
|
-
import { existsSync, writeFileSync, mkdirSync } from "fs";
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
8
8
|
import path from "path";
|
|
9
9
|
const rawArgs = process.argv.slice(2);
|
|
10
10
|
// Extract leading global flags only, so subcommands can still use "-y"
|
|
@@ -108,6 +108,12 @@ async function main() {
|
|
|
108
108
|
printUsage();
|
|
109
109
|
process.exit(0);
|
|
110
110
|
}
|
|
111
|
+
if (args[0] === "--version" || args[0] === "-v") {
|
|
112
|
+
const pkgPath = path.resolve(path.dirname(new URL(import.meta.url).pathname), "../package.json");
|
|
113
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
114
|
+
console.log(pkg.version);
|
|
115
|
+
process.exit(0);
|
|
116
|
+
}
|
|
111
117
|
const command = args[0];
|
|
112
118
|
switch (command) {
|
|
113
119
|
case "setup": {
|
package/dist/confirm.js
CHANGED
|
@@ -5,15 +5,24 @@ export function setAutoApprove(value) {
|
|
|
5
5
|
export function isAutoApprove() {
|
|
6
6
|
return autoApprove;
|
|
7
7
|
}
|
|
8
|
+
/** Optional readline interface to pause/resume during confirmation prompts. */
|
|
9
|
+
let _rl = null;
|
|
10
|
+
export function setConfirmReadline(rl) {
|
|
11
|
+
_rl = rl;
|
|
12
|
+
}
|
|
8
13
|
/** Ask user for confirmation. Returns true if approved. */
|
|
9
14
|
export async function confirm(message) {
|
|
10
15
|
if (autoApprove)
|
|
11
16
|
return true;
|
|
12
|
-
|
|
17
|
+
// Pause readline so it doesn't consume/echo the keystroke
|
|
18
|
+
_rl?.pause();
|
|
19
|
+
process.stdout.write(`\n\n\x1b[1;34m⚠ ${message} [y/N] \x1b[0m\n\n`);
|
|
13
20
|
return new Promise((resolve) => {
|
|
14
21
|
const wasRaw = process.stdin.isRaw;
|
|
15
22
|
if (process.stdin.isTTY)
|
|
16
23
|
process.stdin.setRawMode(true);
|
|
24
|
+
// Ensure stdin is flowing so we can receive data even after readline pause
|
|
25
|
+
process.stdin.resume();
|
|
17
26
|
const onData = (buf) => {
|
|
18
27
|
const ch = buf.toString();
|
|
19
28
|
process.stdin.removeListener("data", onData);
|
|
@@ -21,6 +30,8 @@ export async function confirm(message) {
|
|
|
21
30
|
process.stdin.setRawMode(wasRaw ?? false);
|
|
22
31
|
// Echo the character and newline
|
|
23
32
|
process.stdout.write(ch === "\r" || ch === "\n" ? "\n" : `${ch}\n`);
|
|
33
|
+
// Resume readline after confirmation
|
|
34
|
+
_rl?.resume();
|
|
24
35
|
const answer = ch.trim().toLowerCase();
|
|
25
36
|
resolve(answer === "y");
|
|
26
37
|
};
|
package/dist/tools/bash.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { tool, jsonSchema } from "ai";
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
3
|
import { confirm, isDangerousCommand, isAutoApprove } from "../confirm.js";
|
|
4
|
+
/** Track active child processes so they can be killed on abort (e.g. ESC). */
|
|
5
|
+
const activeProcesses = new Set();
|
|
6
|
+
/** Kill all active child processes spawned by the bash tool. */
|
|
7
|
+
export function killActiveProcesses() {
|
|
8
|
+
for (const proc of activeProcesses) {
|
|
9
|
+
killProcess(proc.pid);
|
|
10
|
+
}
|
|
11
|
+
activeProcesses.clear();
|
|
12
|
+
}
|
|
4
13
|
export const bashTool = tool({
|
|
5
14
|
description: "Run a shell command. Use this for system operations, running builds, tests, git commands, etc. The command runs in the current working directory. You SHOULD set a timeout based on how long you expect the command to take. If no timeout is set, the command runs until it finishes or the user manually interrupts (Ctrl+C).",
|
|
6
15
|
inputSchema: jsonSchema({
|
|
@@ -31,6 +40,7 @@ export const bashTool = tool({
|
|
|
31
40
|
detached: process.platform !== "win32",
|
|
32
41
|
env: { ...process.env, ...(isWin ? { PYTHONIOENCODING: "utf-8" } : {}) },
|
|
33
42
|
});
|
|
43
|
+
activeProcesses.add(proc);
|
|
34
44
|
proc.stdout?.on("data", (chunk) => chunks.push(chunk));
|
|
35
45
|
proc.stderr?.on("data", (chunk) => chunks.push(chunk));
|
|
36
46
|
// Timeout kill (only if timeout is specified)
|
|
@@ -52,6 +62,7 @@ export const bashTool = tool({
|
|
|
52
62
|
};
|
|
53
63
|
process.on("SIGINT", sigintHandler);
|
|
54
64
|
proc.on("close", (code) => {
|
|
65
|
+
activeProcesses.delete(proc);
|
|
55
66
|
process.removeListener("SIGINT", sigintHandler);
|
|
56
67
|
if (timer)
|
|
57
68
|
clearTimeout(timer);
|
|
@@ -66,6 +77,7 @@ export const bashTool = tool({
|
|
|
66
77
|
}
|
|
67
78
|
});
|
|
68
79
|
proc.on("error", (err) => {
|
|
80
|
+
activeProcesses.delete(proc);
|
|
69
81
|
process.removeListener("SIGINT", sigintHandler);
|
|
70
82
|
if (timer)
|
|
71
83
|
clearTimeout(timer);
|