minimal-agent 0.1.3 → 0.1.5
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/main.js +169 -81
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -4,6 +4,19 @@
|
|
|
4
4
|
import { render } from "ink";
|
|
5
5
|
import { createRequire } from "module";
|
|
6
6
|
|
|
7
|
+
// src/bootstrap/workingDir.ts
|
|
8
|
+
import { resolve } from "path";
|
|
9
|
+
var _workingDir = null;
|
|
10
|
+
function initWorkingDir() {
|
|
11
|
+
if (_workingDir !== null) return _workingDir;
|
|
12
|
+
const override = process.env.MINIMAL_AGENT_CWD;
|
|
13
|
+
_workingDir = resolve(override ?? process.cwd());
|
|
14
|
+
return _workingDir;
|
|
15
|
+
}
|
|
16
|
+
function getWorkingDir() {
|
|
17
|
+
return _workingDir ?? initWorkingDir();
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
// src/config/configFile.ts
|
|
8
21
|
import { chmod, mkdir, readFile, writeFile } from "fs/promises";
|
|
9
22
|
import { homedir } from "os";
|
|
@@ -111,16 +124,51 @@ async function loadProviderLayered() {
|
|
|
111
124
|
}
|
|
112
125
|
|
|
113
126
|
// src/context/persistContext.ts
|
|
114
|
-
import { mkdir as
|
|
127
|
+
import { mkdir as mkdir3, readFile as readFile2, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
128
|
+
import { dirname as dirname3 } from "path";
|
|
129
|
+
|
|
130
|
+
// src/context/sessionPath.ts
|
|
131
|
+
import { createHash } from "crypto";
|
|
132
|
+
import { mkdir as mkdir2, rename, stat } from "fs/promises";
|
|
115
133
|
import { homedir as homedir2 } from "os";
|
|
116
|
-
import { dirname as dirname2, join as join2 } from "path";
|
|
134
|
+
import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
135
|
+
function sessionFileFor(cwd) {
|
|
136
|
+
const normalized = resolve2(cwd).replace(/\\/g, "/").toLowerCase();
|
|
137
|
+
const sanitized = normalized.replace(/:/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
138
|
+
const hash = createHash("sha1").update(normalized).digest("hex").slice(0, 6);
|
|
139
|
+
return join2(
|
|
140
|
+
homedir2(),
|
|
141
|
+
".minimal-agent",
|
|
142
|
+
"sessions",
|
|
143
|
+
`${sanitized}-${hash}.json`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
async function migrateLegacyContext(cwd) {
|
|
147
|
+
const legacy = join2(homedir2(), ".minimal-agent", "last-context.json");
|
|
148
|
+
try {
|
|
149
|
+
await stat(legacy);
|
|
150
|
+
} catch {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const target = sessionFileFor(cwd);
|
|
154
|
+
try {
|
|
155
|
+
await mkdir2(dirname2(target), { recursive: true });
|
|
156
|
+
await rename(legacy, target);
|
|
157
|
+
} catch {
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/context/persistContext.ts
|
|
117
162
|
function getContextPath() {
|
|
118
|
-
|
|
163
|
+
if (process.env.MINIMAL_AGENT_CONTEXT_FILE) {
|
|
164
|
+
return process.env.MINIMAL_AGENT_CONTEXT_FILE;
|
|
165
|
+
}
|
|
166
|
+
return sessionFileFor(getWorkingDir());
|
|
119
167
|
}
|
|
120
|
-
async function loadContext() {
|
|
121
|
-
const
|
|
168
|
+
async function loadContext(file) {
|
|
169
|
+
const target = file ?? getContextPath();
|
|
122
170
|
try {
|
|
123
|
-
const raw = await readFile2(
|
|
171
|
+
const raw = await readFile2(target, "utf8");
|
|
124
172
|
const data = JSON.parse(raw);
|
|
125
173
|
if (!Array.isArray(data.messages)) return null;
|
|
126
174
|
return data.messages;
|
|
@@ -128,19 +176,19 @@ async function loadContext() {
|
|
|
128
176
|
return null;
|
|
129
177
|
}
|
|
130
178
|
}
|
|
131
|
-
async function saveContext(messages) {
|
|
132
|
-
const
|
|
179
|
+
async function saveContext(messages, file) {
|
|
180
|
+
const target = file ?? getContextPath();
|
|
133
181
|
try {
|
|
134
|
-
await
|
|
182
|
+
await mkdir3(dirname3(target), { recursive: true });
|
|
135
183
|
const data = { updatedAt: Date.now(), messages };
|
|
136
|
-
await writeFile2(
|
|
184
|
+
await writeFile2(target, JSON.stringify(data), "utf8");
|
|
137
185
|
} catch {
|
|
138
186
|
}
|
|
139
187
|
}
|
|
140
|
-
async function clearContext() {
|
|
141
|
-
const
|
|
188
|
+
async function clearContext(file) {
|
|
189
|
+
const target = file ?? getContextPath();
|
|
142
190
|
try {
|
|
143
|
-
await unlink(
|
|
191
|
+
await unlink(target);
|
|
144
192
|
} catch {
|
|
145
193
|
}
|
|
146
194
|
}
|
|
@@ -177,20 +225,20 @@ import { join as join4 } from "path";
|
|
|
177
225
|
|
|
178
226
|
// src/utils/packageRoot.ts
|
|
179
227
|
import { existsSync } from "fs";
|
|
180
|
-
import { dirname as
|
|
228
|
+
import { dirname as dirname4, resolve as resolve3 } from "path";
|
|
181
229
|
import { fileURLToPath } from "url";
|
|
182
230
|
var cache = /* @__PURE__ */ new Map();
|
|
183
231
|
var MAX_DEPTH = 8;
|
|
184
232
|
function findPackageRoot(metaUrl) {
|
|
185
233
|
const cached2 = cache.get(metaUrl);
|
|
186
234
|
if (cached2 !== void 0) return cached2;
|
|
187
|
-
let dir =
|
|
235
|
+
let dir = dirname4(fileURLToPath(metaUrl));
|
|
188
236
|
for (let i = 0; i < MAX_DEPTH; i++) {
|
|
189
|
-
if (existsSync(
|
|
237
|
+
if (existsSync(resolve3(dir, "package.json"))) {
|
|
190
238
|
cache.set(metaUrl, dir);
|
|
191
239
|
return dir;
|
|
192
240
|
}
|
|
193
|
-
const parent =
|
|
241
|
+
const parent = dirname4(dir);
|
|
194
242
|
if (parent === dir) break;
|
|
195
243
|
dir = parent;
|
|
196
244
|
}
|
|
@@ -249,10 +297,14 @@ function formatSkillHint(skills) {
|
|
|
249
297
|
const lines = [];
|
|
250
298
|
lines.push("# \u53EF\u7528\u6280\u80FD\uFF08/\u547D\u4EE4\uFF09");
|
|
251
299
|
lines.push("");
|
|
300
|
+
lines.push(`> Skills \u6839\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84\uFF1A\`${SKILLS_DIR}\``);
|
|
301
|
+
lines.push("");
|
|
252
302
|
lines.push(
|
|
253
303
|
"\u4EE5\u4E0B\u662F\u5F53\u524D\u53EF\u7528\u7684\u6280\u80FD\u3002**\u5F53\u7528\u6237\u610F\u56FE\u5339\u914D\u67D0\u4E2A\u6280\u80FD\u7684\u89E6\u53D1\u65F6\u673A\u65F6\uFF0C\u4F60\u5E94\u8BE5\u4E3B\u52A8\u4F7F\u7528\u5B83**\u3002"
|
|
254
304
|
);
|
|
255
|
-
lines.push(
|
|
305
|
+
lines.push(
|
|
306
|
+
`\u5728\u4F7F\u7528\u524D\uFF0C\u5148\u7528 Read \u5DE5\u5177\u8BFB\u53D6 \`${SKILLS_DIR}/{name}/SKILL.md\` \u4E86\u89E3\u5B8C\u6574\u6D41\u7A0B\u3002`
|
|
307
|
+
);
|
|
256
308
|
lines.push("");
|
|
257
309
|
for (const skill of skills) {
|
|
258
310
|
const type = skill.type ?? "prompt\uFF08\u5C55\u5F00\u4E3A\u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF0C\u6307\u5BFC\u6A21\u578B\u6267\u884C\u590D\u6742\u4EFB\u52A1\uFF09";
|
|
@@ -268,7 +320,7 @@ function formatSkillHint(skills) {
|
|
|
268
320
|
lines.push("");
|
|
269
321
|
lines.push("```");
|
|
270
322
|
lines.push("1. \u5728 T \u9636\u6BB5\u5224\u65AD\u7528\u6237\u610F\u56FE\u662F\u5426\u5339\u914D\u67D0\u4E2A\u6280\u80FD\u7684\u89E6\u53D1\u65F6\u673A");
|
|
271
|
-
lines.push(`2. \u5339\u914D \u2192 \u5728 A \u9636\u6BB5\u7528 Read \u5DE5\u5177\u8BFB\u53D6
|
|
323
|
+
lines.push(`2. \u5339\u914D \u2192 \u5728 A \u9636\u6BB5\u7528 Read \u5DE5\u5177\u8BFB\u53D6 ${SKILLS_DIR}/{name}/SKILL.md`);
|
|
272
324
|
lines.push("3. \u6309\u7167 SKILL.md \u7684 Quick Reference \u548C\u6D41\u7A0B\u6267\u884C");
|
|
273
325
|
lines.push("4. \u6280\u80FD\u811A\u672C\uFF08scripts/\uFF09\u4E0E SKILL.md \u4F4D\u4E8E\u540C\u4E00\u76EE\u5F55\u4E0B\uFF0C\u7528\u7EDD\u5BF9\u8DEF\u5F84\u6267\u884C");
|
|
274
326
|
lines.push("5. \u5982\u679C\u6280\u80FD\u7C7B\u578B\u662F prompt\uFF0C\u5C06\u5176\u5185\u5BB9\u4F5C\u4E3A\u989D\u5916\u4E0A\u4E0B\u6587\u6307\u5BFC\u540E\u7EED\u64CD\u4F5C");
|
|
@@ -563,7 +615,7 @@ async function call(input, signal) {
|
|
|
563
615
|
await new Promise((resolveP, rejectP) => {
|
|
564
616
|
const child = spawn(command, {
|
|
565
617
|
shell: true,
|
|
566
|
-
cwd:
|
|
618
|
+
cwd: getWorkingDir(),
|
|
567
619
|
signal: ac.signal,
|
|
568
620
|
env: process.env,
|
|
569
621
|
windowsHide: true
|
|
@@ -641,9 +693,9 @@ var bashTool = {
|
|
|
641
693
|
};
|
|
642
694
|
|
|
643
695
|
// src/tools/edit/edit.ts
|
|
644
|
-
import { readFile as readFile5, writeFile as writeFile3, mkdir as
|
|
696
|
+
import { readFile as readFile5, writeFile as writeFile3, mkdir as mkdir4 } from "fs/promises";
|
|
645
697
|
import { existsSync as existsSync2 } from "fs";
|
|
646
|
-
import { dirname as
|
|
698
|
+
import { dirname as dirname5, resolve as resolve4 } from "path";
|
|
647
699
|
import { z as z2 } from "zod";
|
|
648
700
|
var MAX_EDIT_FILE_SIZE_BYTES = 1024 * 1024 * 1024;
|
|
649
701
|
var inputSchema2 = z2.object({
|
|
@@ -676,7 +728,7 @@ function validatePath(filePath) {
|
|
|
676
728
|
return { ok: true };
|
|
677
729
|
}
|
|
678
730
|
async function call2(input) {
|
|
679
|
-
const filePath =
|
|
731
|
+
const filePath = resolve4(input.file_path);
|
|
680
732
|
const { old_string, new_string } = input;
|
|
681
733
|
const replaceAll = input.replace_all ?? false;
|
|
682
734
|
const pathCheck = validatePath(filePath);
|
|
@@ -686,7 +738,7 @@ async function call2(input) {
|
|
|
686
738
|
}
|
|
687
739
|
if (old_string === "" && !existsSync2(filePath)) {
|
|
688
740
|
try {
|
|
689
|
-
await
|
|
741
|
+
await mkdir4(dirname5(filePath), { recursive: true });
|
|
690
742
|
await writeFile3(filePath, new_string, "utf8");
|
|
691
743
|
return {
|
|
692
744
|
ok: true,
|
|
@@ -841,8 +893,8 @@ var editTool = {
|
|
|
841
893
|
};
|
|
842
894
|
|
|
843
895
|
// src/tools/glob/glob.ts
|
|
844
|
-
import { stat } from "fs/promises";
|
|
845
|
-
import { isAbsolute, resolve as
|
|
896
|
+
import { stat as stat2 } from "fs/promises";
|
|
897
|
+
import { isAbsolute, resolve as resolve5 } from "path";
|
|
846
898
|
import fg from "fast-glob";
|
|
847
899
|
import { z as z3 } from "zod";
|
|
848
900
|
var inputSchema3 = z3.object({
|
|
@@ -856,7 +908,7 @@ var description3 = `- Fast file pattern matching tool that works with any codeba
|
|
|
856
908
|
- Use this tool when you need to find files by name patterns
|
|
857
909
|
- When you need to do an open ended search that may require multiple rounds, prefer the Grep tool for content search`;
|
|
858
910
|
async function call3(input) {
|
|
859
|
-
const cwd = input.path ?
|
|
911
|
+
const cwd = input.path ? resolve5(input.path) : getWorkingDir();
|
|
860
912
|
const pattern = input.pattern.replace(/\\/g, "/");
|
|
861
913
|
let matches;
|
|
862
914
|
try {
|
|
@@ -877,9 +929,9 @@ async function call3(input) {
|
|
|
877
929
|
}
|
|
878
930
|
const withMtime = await Promise.all(
|
|
879
931
|
matches.map(async (rel) => {
|
|
880
|
-
const abs = isAbsolute(rel) ? rel :
|
|
932
|
+
const abs = isAbsolute(rel) ? rel : resolve5(cwd, rel);
|
|
881
933
|
try {
|
|
882
|
-
const st = await
|
|
934
|
+
const st = await stat2(abs);
|
|
883
935
|
return { path: rel, mtime: st.mtimeMs };
|
|
884
936
|
} catch {
|
|
885
937
|
return { path: rel, mtime: 0 };
|
|
@@ -913,13 +965,13 @@ var globTool = {
|
|
|
913
965
|
|
|
914
966
|
// src/tools/grep/grep.ts
|
|
915
967
|
import { spawn as spawn3 } from "child_process";
|
|
916
|
-
import { resolve as
|
|
968
|
+
import { resolve as resolve7 } from "path";
|
|
917
969
|
import { z as z4 } from "zod";
|
|
918
970
|
|
|
919
971
|
// src/tools/grep/rgPath.ts
|
|
920
972
|
import { spawn as spawn2 } from "child_process";
|
|
921
973
|
import { chmodSync, existsSync as existsSync3 } from "fs";
|
|
922
|
-
import { resolve as
|
|
974
|
+
import { resolve as resolve6 } from "path";
|
|
923
975
|
var cached;
|
|
924
976
|
async function resolveRgPath() {
|
|
925
977
|
if (cached !== void 0) return cached;
|
|
@@ -946,7 +998,7 @@ async function detect() {
|
|
|
946
998
|
function vendoredRgPath() {
|
|
947
999
|
try {
|
|
948
1000
|
const projectRoot = findPackageRoot(import.meta.url);
|
|
949
|
-
return
|
|
1001
|
+
return resolve6(projectRoot, "vendor", "ripgrep", subdir(), exeName());
|
|
950
1002
|
} catch {
|
|
951
1003
|
return null;
|
|
952
1004
|
}
|
|
@@ -1005,26 +1057,26 @@ function claudeCodeCandidates() {
|
|
|
1005
1057
|
const npmRoots = [];
|
|
1006
1058
|
if (platform === "win32") {
|
|
1007
1059
|
if (process.env.APPDATA) {
|
|
1008
|
-
npmRoots.push(
|
|
1060
|
+
npmRoots.push(resolve6(process.env.APPDATA, "npm", "node_modules"));
|
|
1009
1061
|
}
|
|
1010
1062
|
if (process.env.USERPROFILE) {
|
|
1011
1063
|
npmRoots.push(
|
|
1012
|
-
|
|
1064
|
+
resolve6(process.env.USERPROFILE, "AppData", "Roaming", "npm", "node_modules")
|
|
1013
1065
|
);
|
|
1014
1066
|
}
|
|
1015
1067
|
} else {
|
|
1016
1068
|
const home = process.env.HOME ?? "";
|
|
1017
1069
|
if (home) {
|
|
1018
|
-
npmRoots.push(
|
|
1019
|
-
npmRoots.push(
|
|
1020
|
-
npmRoots.push(
|
|
1070
|
+
npmRoots.push(resolve6(home, ".npm-global", "lib", "node_modules"));
|
|
1071
|
+
npmRoots.push(resolve6(home, ".npm", "lib", "node_modules"));
|
|
1072
|
+
npmRoots.push(resolve6(home, "node_modules"));
|
|
1021
1073
|
}
|
|
1022
1074
|
npmRoots.push("/usr/local/lib/node_modules");
|
|
1023
1075
|
npmRoots.push("/usr/lib/node_modules");
|
|
1024
1076
|
npmRoots.push("/opt/homebrew/lib/node_modules");
|
|
1025
1077
|
}
|
|
1026
1078
|
return npmRoots.map(
|
|
1027
|
-
(root) =>
|
|
1079
|
+
(root) => resolve6(root, "@anthropic-ai", "claude-code", "vendor", "ripgrep", subdir2, exe)
|
|
1028
1080
|
);
|
|
1029
1081
|
}
|
|
1030
1082
|
|
|
@@ -1070,7 +1122,7 @@ async function call4(input, signal) {
|
|
|
1070
1122
|
args.push("--max-columns-preview");
|
|
1071
1123
|
args.push("--sort", "modified");
|
|
1072
1124
|
args.push("-e", input.pattern);
|
|
1073
|
-
args.push(input.path ?
|
|
1125
|
+
args.push(input.path ? resolve7(input.path) : ".");
|
|
1074
1126
|
const rgPath = await resolveRgPath();
|
|
1075
1127
|
if (!rgPath) {
|
|
1076
1128
|
return {
|
|
@@ -1084,7 +1136,7 @@ async function call4(input, signal) {
|
|
|
1084
1136
|
try {
|
|
1085
1137
|
await new Promise((resolveP, rejectP) => {
|
|
1086
1138
|
const child = spawn3(rgPath, args, {
|
|
1087
|
-
cwd:
|
|
1139
|
+
cwd: getWorkingDir(),
|
|
1088
1140
|
signal,
|
|
1089
1141
|
windowsHide: true
|
|
1090
1142
|
});
|
|
@@ -1138,8 +1190,8 @@ var grepTool = {
|
|
|
1138
1190
|
};
|
|
1139
1191
|
|
|
1140
1192
|
// src/tools/read/read.ts
|
|
1141
|
-
import { readFile as readFile6, stat as
|
|
1142
|
-
import { resolve as
|
|
1193
|
+
import { readFile as readFile6, stat as stat3 } from "fs/promises";
|
|
1194
|
+
import { resolve as resolve8 } from "path";
|
|
1143
1195
|
import { z as z5 } from "zod";
|
|
1144
1196
|
var inputSchema5 = z5.object({
|
|
1145
1197
|
file_path: z5.string().min(1, "\u5FC5\u987B\u63D0\u4F9B file_path").describe("\u8981\u8BFB\u53D6\u7684\u6587\u4EF6\u8DEF\u5F84\uFF0C\u7EDD\u5BF9\u8DEF\u5F84\u4F18\u5148"),
|
|
@@ -1158,12 +1210,12 @@ Usage:
|
|
|
1158
1210
|
- This tool can only read text files, not directories. To read a directory, use the Glob tool.
|
|
1159
1211
|
- If you read a file that exists but has empty contents you will receive a warning in place of file contents.`;
|
|
1160
1212
|
async function call5(input) {
|
|
1161
|
-
const filePath =
|
|
1213
|
+
const filePath = resolve8(input.file_path);
|
|
1162
1214
|
const offset = input.offset ?? 1;
|
|
1163
1215
|
const limit = input.limit ?? MAX_LINES_TO_READ;
|
|
1164
1216
|
let st;
|
|
1165
1217
|
try {
|
|
1166
|
-
st = await
|
|
1218
|
+
st = await stat3(filePath);
|
|
1167
1219
|
} catch (e) {
|
|
1168
1220
|
return {
|
|
1169
1221
|
ok: false,
|
|
@@ -2028,8 +2080,8 @@ var webSearchTool = {
|
|
|
2028
2080
|
|
|
2029
2081
|
// src/tools/write/write.ts
|
|
2030
2082
|
import { existsSync as existsSync4 } from "fs";
|
|
2031
|
-
import { mkdir as
|
|
2032
|
-
import { dirname as
|
|
2083
|
+
import { mkdir as mkdir5, stat as stat4, writeFile as writeFile4 } from "fs/promises";
|
|
2084
|
+
import { dirname as dirname6, resolve as resolve9 } from "path";
|
|
2033
2085
|
import { z as z9 } from "zod";
|
|
2034
2086
|
var MAX_WRITE_SIZE_BYTES = 1024 * 1024 * 1024;
|
|
2035
2087
|
var inputSchema9 = z9.object({
|
|
@@ -2054,7 +2106,7 @@ function validatePath2(filePath) {
|
|
|
2054
2106
|
return { ok: true };
|
|
2055
2107
|
}
|
|
2056
2108
|
async function call9(input) {
|
|
2057
|
-
const filePath =
|
|
2109
|
+
const filePath = resolve9(input.file_path);
|
|
2058
2110
|
const pathCheck = validatePath2(filePath);
|
|
2059
2111
|
if (!pathCheck.ok) return pathCheck;
|
|
2060
2112
|
const contentSize = Buffer.byteLength(input.content, "utf8");
|
|
@@ -2065,12 +2117,12 @@ async function call9(input) {
|
|
|
2065
2117
|
};
|
|
2066
2118
|
}
|
|
2067
2119
|
try {
|
|
2068
|
-
await
|
|
2120
|
+
await mkdir5(dirname6(filePath), { recursive: true });
|
|
2069
2121
|
let originalSize = 0;
|
|
2070
2122
|
const fileExisted = existsSync4(filePath);
|
|
2071
2123
|
if (fileExisted) {
|
|
2072
2124
|
try {
|
|
2073
|
-
const st = await
|
|
2125
|
+
const st = await stat4(filePath);
|
|
2074
2126
|
originalSize = st.size;
|
|
2075
2127
|
} catch {
|
|
2076
2128
|
}
|
|
@@ -2910,6 +2962,8 @@ function MessageRow({ message }) {
|
|
|
2910
2962
|
|
|
2911
2963
|
// src/ui/StatusLine.tsx
|
|
2912
2964
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
2965
|
+
import { homedir as homedir4 } from "os";
|
|
2966
|
+
import { sep } from "path";
|
|
2913
2967
|
|
|
2914
2968
|
// src/llm/client.ts
|
|
2915
2969
|
async function* chat(args) {
|
|
@@ -3301,7 +3355,11 @@ function StatusLine({ provider, history }) {
|
|
|
3301
3355
|
const usage = useTokenUsage(history, provider);
|
|
3302
3356
|
const ratio = usage.tokens / usage.threshold;
|
|
3303
3357
|
const color = ratio >= 1 ? "red" : ratio >= 0.7 ? "yellow" : "green";
|
|
3358
|
+
const cwdDisplay = shortenPath(getWorkingDir());
|
|
3304
3359
|
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
3360
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "cwd " }),
|
|
3361
|
+
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: cwdDisplay }),
|
|
3362
|
+
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: " \xB7 " }),
|
|
3305
3363
|
/* @__PURE__ */ jsxs4(Text4, { color: "gray", children: [
|
|
3306
3364
|
provider.name,
|
|
3307
3365
|
"/",
|
|
@@ -3323,6 +3381,19 @@ function fmt(n) {
|
|
|
3323
3381
|
if (n < 1e6) return `${(n / 1e3).toFixed(1)}K`;
|
|
3324
3382
|
return `${(n / 1e6).toFixed(2)}M`;
|
|
3325
3383
|
}
|
|
3384
|
+
function shortenPath(abs) {
|
|
3385
|
+
const home = homedir4();
|
|
3386
|
+
let p = abs;
|
|
3387
|
+
if (home && (p === home || p.startsWith(home + sep))) {
|
|
3388
|
+
p = "~" + p.slice(home.length);
|
|
3389
|
+
}
|
|
3390
|
+
if (p.length <= 40) return p;
|
|
3391
|
+
const parts = p.split(/[\\/]/).filter(Boolean);
|
|
3392
|
+
if (parts.length <= 3) return p;
|
|
3393
|
+
const head = p.startsWith("~") ? "~" : parts[0];
|
|
3394
|
+
const tail = parts.slice(-2).join(sep);
|
|
3395
|
+
return `${head}${sep}\u2026${sep}${tail}`;
|
|
3396
|
+
}
|
|
3326
3397
|
|
|
3327
3398
|
// src/ui/ToolStatus.tsx
|
|
3328
3399
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
@@ -3409,9 +3480,12 @@ function findHeadCutpoint(messages, proposedCut) {
|
|
|
3409
3480
|
}
|
|
3410
3481
|
|
|
3411
3482
|
// src/context/reactiveCompact.ts
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3483
|
+
function createReactiveCompactState() {
|
|
3484
|
+
return { attempted: false };
|
|
3485
|
+
}
|
|
3486
|
+
var defaultState = createReactiveCompactState();
|
|
3487
|
+
function resetReactiveCompactState(state = defaultState) {
|
|
3488
|
+
state.attempted = false;
|
|
3415
3489
|
}
|
|
3416
3490
|
function isPromptTooLongError(error) {
|
|
3417
3491
|
const msg = errorMessage(error).toLowerCase();
|
|
@@ -3428,18 +3502,18 @@ function errorMessage(error) {
|
|
|
3428
3502
|
}
|
|
3429
3503
|
return String(error ?? "");
|
|
3430
3504
|
}
|
|
3431
|
-
async function reactiveCompactIfApplicable(messages, provider, error) {
|
|
3505
|
+
async function reactiveCompactIfApplicable(messages, provider, error, state = defaultState) {
|
|
3432
3506
|
if (!isPromptTooLongError(error)) {
|
|
3433
3507
|
return { recovered: false, messages, reason: "not a prompt-too-long error" };
|
|
3434
3508
|
}
|
|
3435
|
-
if (
|
|
3509
|
+
if (state.attempted) {
|
|
3436
3510
|
return {
|
|
3437
3511
|
recovered: false,
|
|
3438
3512
|
messages,
|
|
3439
3513
|
reason: "already attempted this session \u2014 use /new or /compact manually"
|
|
3440
3514
|
};
|
|
3441
3515
|
}
|
|
3442
|
-
|
|
3516
|
+
state.attempted = true;
|
|
3443
3517
|
try {
|
|
3444
3518
|
const r = await forceCompact(messages, provider);
|
|
3445
3519
|
return {
|
|
@@ -3474,17 +3548,19 @@ async function reactiveCompactIfApplicable(messages, provider, error) {
|
|
|
3474
3548
|
}
|
|
3475
3549
|
|
|
3476
3550
|
// src/context/microCompactLite.ts
|
|
3477
|
-
import { createHash } from "crypto";
|
|
3551
|
+
import { createHash as createHash2 } from "crypto";
|
|
3478
3552
|
var MAX_REPEAT_COUNT = 3;
|
|
3479
3553
|
var MAX_RESULT_SIZE = 4e3;
|
|
3480
3554
|
var HEAD_KEEP_CHARS = 2e3;
|
|
3481
3555
|
var TAIL_KEEP_CHARS = 1e3;
|
|
3482
3556
|
var MAX_KEEP_ROUNDS = 10;
|
|
3483
3557
|
var SHORT_CONTENT_THRESHOLD = 200;
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3558
|
+
function createMicroCompactState() {
|
|
3559
|
+
return { turn: 0, cache: /* @__PURE__ */ new Map() };
|
|
3560
|
+
}
|
|
3561
|
+
var defaultState2 = createMicroCompactState();
|
|
3562
|
+
function incrementTurn(state = defaultState2) {
|
|
3563
|
+
state.turn++;
|
|
3488
3564
|
}
|
|
3489
3565
|
var COMPRESSIBLE_TOOLS = /* @__PURE__ */ new Set([
|
|
3490
3566
|
"Grep",
|
|
@@ -3492,7 +3568,7 @@ var COMPRESSIBLE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
3492
3568
|
"WebFetch",
|
|
3493
3569
|
"Glob"
|
|
3494
3570
|
]);
|
|
3495
|
-
function microCompact(toolName, content) {
|
|
3571
|
+
function microCompact(toolName, content, state = defaultState2) {
|
|
3496
3572
|
if (!content) return content;
|
|
3497
3573
|
if (content.startsWith("Error:") || content.startsWith("\u9519\u8BEF")) {
|
|
3498
3574
|
return content;
|
|
@@ -3501,7 +3577,7 @@ function microCompact(toolName, content) {
|
|
|
3501
3577
|
return content;
|
|
3502
3578
|
}
|
|
3503
3579
|
const hash = sha1(content);
|
|
3504
|
-
const existing =
|
|
3580
|
+
const existing = state.cache.get(hash);
|
|
3505
3581
|
if (existing) {
|
|
3506
3582
|
existing.count++;
|
|
3507
3583
|
if (existing.count > MAX_REPEAT_COUNT) {
|
|
@@ -3509,24 +3585,24 @@ function microCompact(toolName, content) {
|
|
|
3509
3585
|
}
|
|
3510
3586
|
return content;
|
|
3511
3587
|
}
|
|
3512
|
-
|
|
3588
|
+
state.cache.set(hash, { count: 1, firstToolName: toolName, firstSeenTurn: state.turn });
|
|
3513
3589
|
if (content.length > MAX_RESULT_SIZE && COMPRESSIBLE_TOOLS.has(toolName)) {
|
|
3514
3590
|
return truncateContent(content);
|
|
3515
3591
|
}
|
|
3516
3592
|
return content;
|
|
3517
3593
|
}
|
|
3518
|
-
function expireOldEntries() {
|
|
3594
|
+
function expireOldEntries(state = defaultState2) {
|
|
3519
3595
|
let expired = 0;
|
|
3520
|
-
for (const [hash, entry] of
|
|
3521
|
-
if (
|
|
3522
|
-
|
|
3596
|
+
for (const [hash, entry] of state.cache) {
|
|
3597
|
+
if (state.turn - entry.firstSeenTurn > MAX_KEEP_ROUNDS) {
|
|
3598
|
+
state.cache.delete(hash);
|
|
3523
3599
|
expired++;
|
|
3524
3600
|
}
|
|
3525
3601
|
}
|
|
3526
3602
|
return expired;
|
|
3527
3603
|
}
|
|
3528
3604
|
function sha1(str) {
|
|
3529
|
-
return
|
|
3605
|
+
return createHash2("sha1").update(str).digest("hex");
|
|
3530
3606
|
}
|
|
3531
3607
|
function truncateContent(content) {
|
|
3532
3608
|
const omitted = content.length - HEAD_KEEP_CHARS - TAIL_KEEP_CHARS;
|
|
@@ -3539,15 +3615,15 @@ function truncateContent(content) {
|
|
|
3539
3615
|
|
|
3540
3616
|
// src/loop.ts
|
|
3541
3617
|
async function* runQuery(userInput, options) {
|
|
3542
|
-
const { provider, history, signal } = options;
|
|
3618
|
+
const { provider, history, signal, sessionState } = options;
|
|
3543
3619
|
const maxTurns = options.maxTurns ?? 50;
|
|
3544
3620
|
history.push({ role: "user", content: userInput });
|
|
3545
3621
|
let reactiveAttempted = false;
|
|
3546
3622
|
let turn = 0;
|
|
3547
3623
|
while (turn < maxTurns) {
|
|
3548
3624
|
turn++;
|
|
3549
|
-
incrementTurn();
|
|
3550
|
-
expireOldEntries();
|
|
3625
|
+
incrementTurn(sessionState?.microCompact);
|
|
3626
|
+
expireOldEntries(sessionState?.microCompact);
|
|
3551
3627
|
if (signal?.aborted) {
|
|
3552
3628
|
history.push({
|
|
3553
3629
|
role: "user",
|
|
@@ -3609,7 +3685,12 @@ async function* runQuery(userInput, options) {
|
|
|
3609
3685
|
if (isPromptTooLongError(e) && !reactiveAttempted) {
|
|
3610
3686
|
reactiveAttempted = true;
|
|
3611
3687
|
yield { type: "compact_start" };
|
|
3612
|
-
const result = await reactiveCompactIfApplicable(
|
|
3688
|
+
const result = await reactiveCompactIfApplicable(
|
|
3689
|
+
history,
|
|
3690
|
+
provider,
|
|
3691
|
+
e,
|
|
3692
|
+
sessionState?.reactive
|
|
3693
|
+
);
|
|
3613
3694
|
if (result.recovered) {
|
|
3614
3695
|
history.length = 0;
|
|
3615
3696
|
history.push(...result.messages);
|
|
@@ -3657,7 +3738,11 @@ async function* runQuery(userInput, options) {
|
|
|
3657
3738
|
};
|
|
3658
3739
|
const result = await executeTool(tc.function.name, tc.function.arguments, signal);
|
|
3659
3740
|
const rawContent = result.ok ? result.content : `Error: ${result.error}`;
|
|
3660
|
-
const content = microCompact(
|
|
3741
|
+
const content = microCompact(
|
|
3742
|
+
tc.function.name,
|
|
3743
|
+
rawContent,
|
|
3744
|
+
sessionState?.microCompact
|
|
3745
|
+
);
|
|
3661
3746
|
history.push({
|
|
3662
3747
|
role: "tool",
|
|
3663
3748
|
content,
|
|
@@ -3758,7 +3843,7 @@ function useChat(args) {
|
|
|
3758
3843
|
}, []);
|
|
3759
3844
|
const clearHistory = useCallback5(async () => {
|
|
3760
3845
|
if (isLoading) return;
|
|
3761
|
-
const newSystemPrompt = await buildFullSystemPrompt(
|
|
3846
|
+
const newSystemPrompt = await buildFullSystemPrompt(getWorkingDir(), ALL_TOOLS);
|
|
3762
3847
|
historyRef.current.length = 0;
|
|
3763
3848
|
historyRef.current.push({ role: "system", content: newSystemPrompt });
|
|
3764
3849
|
resetReactiveCompactState();
|
|
@@ -3935,7 +4020,7 @@ function Root({ initialProvider }) {
|
|
|
3935
4020
|
return /* @__PURE__ */ jsx7(App, { provider: phase.provider, initialHistory: phase.initialHistory });
|
|
3936
4021
|
}
|
|
3937
4022
|
async function buildInitialHistory() {
|
|
3938
|
-
const content = await buildFullSystemPrompt(
|
|
4023
|
+
const content = await buildFullSystemPrompt(getWorkingDir(), ALL_TOOLS);
|
|
3939
4024
|
const fresh = { role: "system", content };
|
|
3940
4025
|
const persisted = await loadContext();
|
|
3941
4026
|
if (!persisted || persisted.length === 0) return [fresh];
|
|
@@ -4057,13 +4142,13 @@ function handleEvent2(event, output, verbose) {
|
|
|
4057
4142
|
}
|
|
4058
4143
|
}
|
|
4059
4144
|
function readFromStdin() {
|
|
4060
|
-
return new Promise((
|
|
4145
|
+
return new Promise((resolve10) => {
|
|
4061
4146
|
let data = "";
|
|
4062
4147
|
let settled = false;
|
|
4063
4148
|
const timer = setTimeout(() => {
|
|
4064
4149
|
if (!settled) {
|
|
4065
4150
|
settled = true;
|
|
4066
|
-
|
|
4151
|
+
resolve10("");
|
|
4067
4152
|
}
|
|
4068
4153
|
}, STDIN_TIMEOUT_MS);
|
|
4069
4154
|
process.stdin.setEncoding("utf8");
|
|
@@ -4074,7 +4159,7 @@ function readFromStdin() {
|
|
|
4074
4159
|
if (!settled) {
|
|
4075
4160
|
clearTimeout(timer);
|
|
4076
4161
|
settled = true;
|
|
4077
|
-
|
|
4162
|
+
resolve10(data.trim());
|
|
4078
4163
|
}
|
|
4079
4164
|
}
|
|
4080
4165
|
process.stdin.on("data", onData);
|
|
@@ -4087,6 +4172,8 @@ import { jsx as jsx8 } from "react/jsx-runtime";
|
|
|
4087
4172
|
var require2 = createRequire(import.meta.url);
|
|
4088
4173
|
var pkg = require2("../package.json");
|
|
4089
4174
|
async function main() {
|
|
4175
|
+
initWorkingDir();
|
|
4176
|
+
await migrateLegacyContext(getWorkingDir());
|
|
4090
4177
|
const args = process.argv.slice(2);
|
|
4091
4178
|
if (args.includes("-h") || args.includes("--help")) {
|
|
4092
4179
|
printHelp();
|
|
@@ -4122,7 +4209,7 @@ ${e.message}
|
|
|
4122
4209
|
await waitUntilExit();
|
|
4123
4210
|
}
|
|
4124
4211
|
async function buildInitialHistory2() {
|
|
4125
|
-
const content = await buildFullSystemPrompt(
|
|
4212
|
+
const content = await buildFullSystemPrompt(getWorkingDir(), ALL_TOOLS);
|
|
4126
4213
|
const fresh = { role: "system", content };
|
|
4127
4214
|
const persisted = await loadContext();
|
|
4128
4215
|
if (!persisted || persisted.length === 0) return [fresh];
|
|
@@ -4143,8 +4230,9 @@ minimal-agent - \u8F7B\u91CF\u7EA7 AI \u7F16\u7A0B\u52A9\u624B
|
|
|
4143
4230
|
-h, --help \u663E\u793A\u5E2E\u52A9\u4FE1\u606F
|
|
4144
4231
|
|
|
4145
4232
|
\u4F1A\u8BDD\u8BB0\u5FC6:
|
|
4146
|
-
\u81EA\u52A8\u52A0\u8F7D / \u4FDD\u5B58
|
|
4147
|
-
|
|
4233
|
+
\u6309\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u9694\u79BB\uFF0C\u81EA\u52A8\u52A0\u8F7D / \u4FDD\u5B58\u5230
|
|
4234
|
+
~/.minimal-agent/sessions/<\u76EE\u5F55\u54C8\u5E0C>.json
|
|
4235
|
+
TUI \u4E0E -p \u5171\u4EAB\u540C\u4E00\u76EE\u5F55\u7684\u4E0A\u4E0B\u6587\uFF1BTUI \u4E2D\u8F93\u5165 /new \u4EC5\u6E05\u5F53\u524D\u76EE\u5F55\u7684\u4F1A\u8BDD
|
|
4148
4236
|
|
|
4149
4237
|
\u793A\u4F8B:
|
|
4150
4238
|
minimal-agent -p "\u5E2E\u6211\u5199\u4E00\u4E2A hello world"
|
package/package.json
CHANGED