miii-agent 0.1.9 → 0.1.11
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/cli.js +209 -60
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -145,27 +145,64 @@ var init_client = __esm({
|
|
|
145
145
|
});
|
|
146
146
|
|
|
147
147
|
// src/tools/paths.ts
|
|
148
|
-
import { resolve, relative as relative2, isAbsolute, sep } from "path";
|
|
148
|
+
import { resolve, relative as relative2, isAbsolute, sep, join as join4 } from "path";
|
|
149
|
+
import { homedir as homedir3 } from "os";
|
|
150
|
+
function isUnder(parent, child) {
|
|
151
|
+
const rel = relative2(parent, child);
|
|
152
|
+
return rel === "" || !rel.startsWith(".." + sep) && rel !== ".." && !isAbsolute(rel);
|
|
153
|
+
}
|
|
149
154
|
function confinePath(p) {
|
|
150
155
|
if (typeof p !== "string" || p.length === 0) {
|
|
151
156
|
throw new Error("Path is required.");
|
|
152
157
|
}
|
|
153
158
|
const root = process.cwd();
|
|
154
159
|
const abs = resolve(root, p);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
throw new Error(`Path "${p}" is outside the working directory (${root}). Access denied.`);
|
|
160
|
+
if (isUnder(root, abs) || isUnder(SPILL_DIR, abs)) {
|
|
161
|
+
return abs;
|
|
158
162
|
}
|
|
159
|
-
|
|
163
|
+
throw new Error(`Path "${p}" is outside the working directory (${root}). Access denied.`);
|
|
160
164
|
}
|
|
165
|
+
var SPILL_DIR;
|
|
161
166
|
var init_paths = __esm({
|
|
162
167
|
"src/tools/paths.ts"() {
|
|
163
168
|
"use strict";
|
|
169
|
+
SPILL_DIR = resolve(join4(homedir3(), ".miii", "output"));
|
|
164
170
|
}
|
|
165
171
|
});
|
|
166
172
|
|
|
167
173
|
// src/tools/edit_file.ts
|
|
168
174
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
175
|
+
function similarity(a, b) {
|
|
176
|
+
const x = a.trim();
|
|
177
|
+
const y = b.trim();
|
|
178
|
+
if (!x && !y) return 1;
|
|
179
|
+
const len = Math.max(x.length, y.length);
|
|
180
|
+
if (len === 0) return 0;
|
|
181
|
+
let same = 0;
|
|
182
|
+
for (let i = 0; i < Math.min(x.length, y.length); i++) if (x[i] === y[i]) same++;
|
|
183
|
+
return same / len;
|
|
184
|
+
}
|
|
185
|
+
function nearMiss(src, old_str) {
|
|
186
|
+
const srcLines = src.split("\n");
|
|
187
|
+
const needle = old_str.split("\n").find((l) => l.trim()) ?? old_str;
|
|
188
|
+
let bestIdx = -1;
|
|
189
|
+
let bestScore = 0;
|
|
190
|
+
for (let i = 0; i < srcLines.length; i++) {
|
|
191
|
+
const s = similarity(srcLines[i], needle);
|
|
192
|
+
if (s > bestScore) {
|
|
193
|
+
bestScore = s;
|
|
194
|
+
bestIdx = i;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (bestIdx === -1 || bestScore < 0.4) return "";
|
|
198
|
+
const from = Math.max(0, bestIdx - 3);
|
|
199
|
+
const to = Math.min(srcLines.length, bestIdx + 4);
|
|
200
|
+
const width = String(to).length;
|
|
201
|
+
const ctx = srcLines.slice(from, to).map((l, i) => `${String(from + i + 1).padStart(width, " ")} ${l}`).join("\n");
|
|
202
|
+
return `
|
|
203
|
+
Closest text in file (lines ${from + 1}-${to}):
|
|
204
|
+
${ctx}`;
|
|
205
|
+
}
|
|
169
206
|
var edit_file;
|
|
170
207
|
var init_edit_file = __esm({
|
|
171
208
|
"src/tools/edit_file.ts"() {
|
|
@@ -173,29 +210,39 @@ var init_edit_file = __esm({
|
|
|
173
210
|
init_paths();
|
|
174
211
|
edit_file = {
|
|
175
212
|
name: "edit_file",
|
|
176
|
-
description: "Replace an exact string in a file. old_str must be unique.",
|
|
213
|
+
description: "Replace an exact string in a file. old_str must be unique unless replace_all is set. On no match, returns the closest text in the file.",
|
|
177
214
|
input_schema: {
|
|
178
215
|
type: "object",
|
|
179
216
|
properties: {
|
|
180
217
|
path: { type: "string", description: "File path" },
|
|
181
|
-
old_str: { type: "string", description: "Exact text to replace" },
|
|
182
|
-
new_str: { type: "string", description: "Replacement text" }
|
|
218
|
+
old_str: { type: "string", description: "Exact text to replace (whitespace-sensitive)" },
|
|
219
|
+
new_str: { type: "string", description: "Replacement text" },
|
|
220
|
+
replace_all: { type: "boolean", description: "Replace every occurrence instead of requiring uniqueness" }
|
|
183
221
|
},
|
|
184
222
|
required: ["path", "old_str", "new_str"]
|
|
185
223
|
},
|
|
186
|
-
handler: ({ path, old_str, new_str }) => {
|
|
224
|
+
handler: ({ path, old_str, new_str, replace_all }) => {
|
|
187
225
|
try {
|
|
226
|
+
if (old_str === new_str) {
|
|
227
|
+
return { content: `old_str and new_str are identical \u2014 nothing to change in ${path}.`, is_error: true };
|
|
228
|
+
}
|
|
188
229
|
const abs = confinePath(path);
|
|
189
230
|
const src = readFileSync3(abs, "utf-8");
|
|
190
231
|
const first = src.indexOf(old_str);
|
|
191
232
|
if (first === -1) {
|
|
192
|
-
return { content: `old_str not found in ${path}`, is_error: true };
|
|
233
|
+
return { content: `old_str not found in ${path}.${nearMiss(src, old_str)}`, is_error: true };
|
|
193
234
|
}
|
|
194
|
-
|
|
195
|
-
|
|
235
|
+
const all = replace_all === true;
|
|
236
|
+
if (!all && src.indexOf(old_str, first + 1) !== -1) {
|
|
237
|
+
return {
|
|
238
|
+
content: `old_str not unique in ${path}. Add surrounding context to disambiguate, or set replace_all.`,
|
|
239
|
+
is_error: true
|
|
240
|
+
};
|
|
196
241
|
}
|
|
197
|
-
|
|
198
|
-
|
|
242
|
+
const out = all ? src.split(old_str).join(new_str) : src.slice(0, first) + new_str + src.slice(first + old_str.length);
|
|
243
|
+
const n = all ? src.split(old_str).length - 1 : 1;
|
|
244
|
+
writeFileSync3(abs, out, "utf-8");
|
|
245
|
+
return { content: `Edited ${path}${all ? ` (${n} occurrences)` : ""}` };
|
|
199
246
|
} catch (err) {
|
|
200
247
|
return { content: err instanceof Error ? err.message : String(err), is_error: true };
|
|
201
248
|
}
|
|
@@ -206,6 +253,10 @@ var init_edit_file = __esm({
|
|
|
206
253
|
|
|
207
254
|
// src/tools/read_file.ts
|
|
208
255
|
import { readFileSync as readFileSync4 } from "fs";
|
|
256
|
+
function numbered(lines, start) {
|
|
257
|
+
const width = String(start + lines.length - 1).length;
|
|
258
|
+
return lines.map((l, i) => `${String(start + i).padStart(width, " ")} ${l}`).join("\n");
|
|
259
|
+
}
|
|
209
260
|
var read_file;
|
|
210
261
|
var init_read_file = __esm({
|
|
211
262
|
"src/tools/read_file.ts"() {
|
|
@@ -213,21 +264,40 @@ var init_read_file = __esm({
|
|
|
213
264
|
init_paths();
|
|
214
265
|
read_file = {
|
|
215
266
|
name: "read_file",
|
|
216
|
-
description: "Read
|
|
267
|
+
description: "Read file contents as UTF-8 text with line numbers. Use offset/limit to read a range of a large file instead of the whole thing.",
|
|
217
268
|
input_schema: {
|
|
218
269
|
type: "object",
|
|
219
270
|
properties: {
|
|
220
|
-
path: { type: "string", description: "File path" }
|
|
271
|
+
path: { type: "string", description: "File path" },
|
|
272
|
+
offset: { type: "number", description: "1-based line to start from (default 1)" },
|
|
273
|
+
limit: { type: "number", description: "Max lines to return (default all / capped)" }
|
|
221
274
|
},
|
|
222
275
|
required: ["path"]
|
|
223
276
|
},
|
|
224
|
-
handler: ({ path }) => {
|
|
277
|
+
handler: ({ path, offset, limit }) => {
|
|
225
278
|
try {
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
279
|
+
const MAX_CHARS = 2e5;
|
|
280
|
+
const buf = readFileSync4(confinePath(path));
|
|
281
|
+
if (buf.subarray(0, 8e3).includes(0)) {
|
|
282
|
+
return { content: `${path} looks binary (${buf.length} bytes); not reading as text.`, is_error: true };
|
|
283
|
+
}
|
|
284
|
+
const raw = buf.toString("utf-8").replace(/\r\n/g, "\n");
|
|
285
|
+
const allLines = raw.split("\n");
|
|
286
|
+
const total = allLines.length;
|
|
287
|
+
const start = Math.max(1, Math.floor(offset ?? 1));
|
|
288
|
+
const ranged = offset != null || limit != null;
|
|
289
|
+
const count = limit != null ? Math.max(0, Math.floor(limit)) : total;
|
|
290
|
+
const slice = allLines.slice(start - 1, start - 1 + count);
|
|
291
|
+
let body = numbered(slice, start);
|
|
292
|
+
if (body.length > MAX_CHARS) {
|
|
293
|
+
body = body.slice(0, MAX_CHARS) + `
|
|
294
|
+
[truncated: output exceeded ${MAX_CHARS} chars \u2014 use offset/limit]`;
|
|
295
|
+
}
|
|
296
|
+
if (ranged) {
|
|
297
|
+
const end = start - 1 + slice.length;
|
|
298
|
+
body += `
|
|
299
|
+
[showing lines ${start}-${end} of ${total}]`;
|
|
300
|
+
}
|
|
231
301
|
return { content: body };
|
|
232
302
|
} catch (err) {
|
|
233
303
|
return { content: err instanceof Error ? err.message : String(err), is_error: true };
|
|
@@ -270,12 +340,63 @@ var init_write_file = __esm({
|
|
|
270
340
|
}
|
|
271
341
|
});
|
|
272
342
|
|
|
343
|
+
// src/tools/spill.ts
|
|
344
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, rmSync as rmSync2, readdirSync as readdirSync3, statSync } from "fs";
|
|
345
|
+
import { join as join5 } from "path";
|
|
346
|
+
import { homedir as homedir4 } from "os";
|
|
347
|
+
import { randomBytes } from "crypto";
|
|
348
|
+
function ensureDir() {
|
|
349
|
+
mkdirSync4(OUTPUT_DIR, { recursive: true });
|
|
350
|
+
return OUTPUT_DIR;
|
|
351
|
+
}
|
|
352
|
+
function spillIfLarge(full, label = "output", budget = INLINE_BUDGET) {
|
|
353
|
+
if (full.length <= budget) return full;
|
|
354
|
+
const id = randomBytes(6).toString("hex");
|
|
355
|
+
const file = join5(ensureDir(), `${id}.txt`);
|
|
356
|
+
let path = file;
|
|
357
|
+
try {
|
|
358
|
+
writeFileSync5(file, full, "utf-8");
|
|
359
|
+
} catch {
|
|
360
|
+
path = "";
|
|
361
|
+
}
|
|
362
|
+
const head = Math.floor(budget * HEAD_FRACTION);
|
|
363
|
+
const tail = budget - head;
|
|
364
|
+
const totalLines = full.split("\n").length;
|
|
365
|
+
const preview = full.slice(0, head) + "\n\u2026\n" + full.slice(-tail);
|
|
366
|
+
const notice = path ? `[${label} truncated: ${totalLines} lines / ${full.length} bytes. Full output at ${path} \u2014 read it with read_file offset/limit to see the elided middle.]` : `[${label} truncated to ${budget} bytes; spill to disk failed, middle is lost.]`;
|
|
367
|
+
return `${preview}
|
|
368
|
+
${notice}`;
|
|
369
|
+
}
|
|
370
|
+
function cleanupSpill(maxAgeMs = 24 * 60 * 60 * 1e3) {
|
|
371
|
+
try {
|
|
372
|
+
const now = Date.now();
|
|
373
|
+
for (const name of readdirSync3(OUTPUT_DIR)) {
|
|
374
|
+
const f = join5(OUTPUT_DIR, name);
|
|
375
|
+
try {
|
|
376
|
+
if (now - statSync(f).mtimeMs > maxAgeMs) rmSync2(f, { force: true });
|
|
377
|
+
} catch {
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
} catch {
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
var OUTPUT_DIR, INLINE_BUDGET, HEAD_FRACTION;
|
|
384
|
+
var init_spill = __esm({
|
|
385
|
+
"src/tools/spill.ts"() {
|
|
386
|
+
"use strict";
|
|
387
|
+
OUTPUT_DIR = join5(homedir4(), ".miii", "output");
|
|
388
|
+
INLINE_BUDGET = 1e4;
|
|
389
|
+
HEAD_FRACTION = 0.3;
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
|
|
273
393
|
// src/tools/run_bash.ts
|
|
274
394
|
import { execa } from "execa";
|
|
275
395
|
var run_bash;
|
|
276
396
|
var init_run_bash = __esm({
|
|
277
397
|
"src/tools/run_bash.ts"() {
|
|
278
398
|
"use strict";
|
|
399
|
+
init_spill();
|
|
279
400
|
run_bash = {
|
|
280
401
|
name: "run_bash",
|
|
281
402
|
description: "Execute a shell command (bash on Unix, cmd on Windows). Returns stdout+stderr. Non-interactive only.",
|
|
@@ -300,10 +421,10 @@ var init_run_bash = __esm({
|
|
|
300
421
|
const out = [stdout, stderr].filter(Boolean).join("\n");
|
|
301
422
|
const is_error = exitCode !== 0;
|
|
302
423
|
const body = out || (is_error ? `(no output)` : "");
|
|
303
|
-
const content =
|
|
304
|
-
[exit ${exitCode}]
|
|
424
|
+
const content = `${spillIfLarge(body, "command output")}
|
|
425
|
+
[exit ${exitCode}]`;
|
|
305
426
|
return {
|
|
306
|
-
content
|
|
427
|
+
content,
|
|
307
428
|
is_error
|
|
308
429
|
};
|
|
309
430
|
} catch (err) {
|
|
@@ -329,7 +450,7 @@ var init_grep = __esm({
|
|
|
329
450
|
pattern: { type: "string", description: "Regex pattern" },
|
|
330
451
|
path: { type: "string", description: "Root path to search (default cwd)" },
|
|
331
452
|
glob: { type: "string", description: 'File glob filter, e.g. "*.ts"' },
|
|
332
|
-
case_insensitive: { type: "
|
|
453
|
+
case_insensitive: { type: "boolean", description: "Case-insensitive match" },
|
|
333
454
|
max_results: { type: "number", description: "Max matching lines (default 200)" }
|
|
334
455
|
},
|
|
335
456
|
required: ["pattern"]
|
|
@@ -377,8 +498,25 @@ var init_grep = __esm({
|
|
|
377
498
|
|
|
378
499
|
// src/tools/glob.ts
|
|
379
500
|
import { execa as execa3 } from "execa";
|
|
380
|
-
|
|
381
|
-
|
|
501
|
+
import { statSync as statSync2 } from "fs";
|
|
502
|
+
function byMtimeDesc(paths) {
|
|
503
|
+
const mtime = /* @__PURE__ */ new Map();
|
|
504
|
+
for (const p of paths) {
|
|
505
|
+
try {
|
|
506
|
+
mtime.set(p, statSync2(p).mtimeMs);
|
|
507
|
+
} catch {
|
|
508
|
+
mtime.set(p, 0);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return [...paths].sort((a, b) => (mtime.get(b) ?? 0) - (mtime.get(a) ?? 0));
|
|
512
|
+
}
|
|
513
|
+
function globToFindArgs(root, glob2) {
|
|
514
|
+
const stripped = glob2.replace(/^\*\*\//, "");
|
|
515
|
+
if (!stripped.includes("/")) {
|
|
516
|
+
return [root, "-type", "f", "-name", stripped];
|
|
517
|
+
}
|
|
518
|
+
const pathPat = "*/" + glob2.replace(/\*\*/g, "*");
|
|
519
|
+
return [root, "-type", "f", "-path", pathPat];
|
|
382
520
|
}
|
|
383
521
|
var glob;
|
|
384
522
|
var init_glob = __esm({
|
|
@@ -403,13 +541,10 @@ var init_glob = __esm({
|
|
|
403
541
|
reject: false,
|
|
404
542
|
timeout: 2e4
|
|
405
543
|
});
|
|
406
|
-
const tryFind = () => {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
timeout: 2e4
|
|
411
|
-
});
|
|
412
|
-
};
|
|
544
|
+
const tryFind = () => execa3("find", globToFindArgs(root, pattern), {
|
|
545
|
+
reject: false,
|
|
546
|
+
timeout: 2e4
|
|
547
|
+
});
|
|
413
548
|
try {
|
|
414
549
|
let res;
|
|
415
550
|
try {
|
|
@@ -420,8 +555,9 @@ var init_glob = __esm({
|
|
|
420
555
|
} catch {
|
|
421
556
|
res = await tryFind();
|
|
422
557
|
}
|
|
423
|
-
const
|
|
424
|
-
if (
|
|
558
|
+
const all = (res.stdout ?? "").split("\n").filter(Boolean);
|
|
559
|
+
if (all.length === 0) return { content: "No files matched." };
|
|
560
|
+
const lines = byMtimeDesc(all).slice(0, limit);
|
|
425
561
|
return { content: lines.join("\n") };
|
|
426
562
|
} catch (err) {
|
|
427
563
|
return { content: err instanceof Error ? err.message : String(err), is_error: true };
|
|
@@ -576,10 +712,14 @@ ${toolLines}
|
|
|
576
712
|
# Rules
|
|
577
713
|
- Always read a file before updating it. Never edit, overwrite, or create-over a file you have not read first this turn.
|
|
578
714
|
- Prefer editing existing files over creating new ones.
|
|
579
|
-
- For edit_file,
|
|
715
|
+
- For edit_file, make old_str unique by including surrounding context, or set replace_all to change every occurrence.
|
|
580
716
|
- Never invent file paths. Read, glob, or grep before editing.
|
|
581
717
|
- No filler, no pleasantries, no apologies.
|
|
582
718
|
|
|
719
|
+
# Context discipline
|
|
720
|
+
- read_file returns line numbers and accepts offset/limit. For large files, grep or glob to the relevant region first, then read only that range with offset/limit. Do not read a whole large file when you need a few functions \u2014 it wastes the context window.
|
|
721
|
+
- Reference code by the line numbers read_file returns.
|
|
722
|
+
|
|
583
723
|
# Testing and verification
|
|
584
724
|
- Always test the code after a change. Run the project's tests (e.g. npm test, pytest, go test) or the relevant script via run_bash before declaring a task done.
|
|
585
725
|
- If no test exists for the change, run the affected entry point via run_bash to verify it behaves correctly.
|
|
@@ -597,9 +737,9 @@ var init_system = __esm({
|
|
|
597
737
|
});
|
|
598
738
|
|
|
599
739
|
// src/permissions/policy.ts
|
|
600
|
-
import { readFileSync as readFileSync5, writeFileSync as
|
|
601
|
-
import { join as
|
|
602
|
-
import { homedir as
|
|
740
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync3, renameSync } from "fs";
|
|
741
|
+
import { join as join6 } from "path";
|
|
742
|
+
import { homedir as homedir5 } from "os";
|
|
603
743
|
function loadRules() {
|
|
604
744
|
if (!existsSync3(RULES_PATH)) return [];
|
|
605
745
|
try {
|
|
@@ -610,9 +750,9 @@ function loadRules() {
|
|
|
610
750
|
}
|
|
611
751
|
}
|
|
612
752
|
function saveRules(rules) {
|
|
613
|
-
|
|
753
|
+
mkdirSync5(RULES_DIR, { recursive: true });
|
|
614
754
|
const tmp = RULES_PATH + ".tmp";
|
|
615
|
-
|
|
755
|
+
writeFileSync6(tmp, JSON.stringify({ rules }, null, 2), "utf-8");
|
|
616
756
|
renameSync(tmp, RULES_PATH);
|
|
617
757
|
}
|
|
618
758
|
function addRule(tool, pattern) {
|
|
@@ -641,6 +781,7 @@ function matches(rule, toolName, subject) {
|
|
|
641
781
|
}
|
|
642
782
|
}
|
|
643
783
|
async function check(toolName, input, ctx) {
|
|
784
|
+
if (ALWAYS_ALLOW.has(toolName)) return "allow";
|
|
644
785
|
const subject = subjectFor(toolName, input);
|
|
645
786
|
const rules = loadRules();
|
|
646
787
|
if (rules.some((r) => matches(r, toolName, subject))) return "allow";
|
|
@@ -649,12 +790,13 @@ async function check(toolName, input, ctx) {
|
|
|
649
790
|
if (answer === "always") addRule(toolName, subject);
|
|
650
791
|
return "allow";
|
|
651
792
|
}
|
|
652
|
-
var RULES_DIR, RULES_PATH;
|
|
793
|
+
var RULES_DIR, RULES_PATH, ALWAYS_ALLOW;
|
|
653
794
|
var init_policy = __esm({
|
|
654
795
|
"src/permissions/policy.ts"() {
|
|
655
796
|
"use strict";
|
|
656
|
-
RULES_DIR =
|
|
657
|
-
RULES_PATH =
|
|
797
|
+
RULES_DIR = join6(homedir5(), ".miii");
|
|
798
|
+
RULES_PATH = join6(RULES_DIR, "permissions.json");
|
|
799
|
+
ALWAYS_ALLOW = /* @__PURE__ */ new Set(["read_file", "grep", "glob"]);
|
|
658
800
|
}
|
|
659
801
|
});
|
|
660
802
|
|
|
@@ -983,16 +1125,16 @@ var init_loop = __esm({
|
|
|
983
1125
|
});
|
|
984
1126
|
|
|
985
1127
|
// eval/runner.ts
|
|
986
|
-
import { mkdtempSync, writeFileSync as
|
|
987
|
-
import { dirname as dirname2, join as
|
|
1128
|
+
import { mkdtempSync, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, rmSync as rmSync3 } from "fs";
|
|
1129
|
+
import { dirname as dirname2, join as join7 } from "path";
|
|
988
1130
|
import { tmpdir } from "os";
|
|
989
1131
|
async function runScenario(model, s) {
|
|
990
|
-
const dir = mkdtempSync(
|
|
1132
|
+
const dir = mkdtempSync(join7(tmpdir(), "miii-eval-"));
|
|
991
1133
|
const prevCwd = process.cwd();
|
|
992
1134
|
for (const [rel, content] of Object.entries(s.files ?? {})) {
|
|
993
|
-
const abs =
|
|
994
|
-
|
|
995
|
-
|
|
1135
|
+
const abs = join7(dir, rel);
|
|
1136
|
+
mkdirSync6(dirname2(abs), { recursive: true });
|
|
1137
|
+
writeFileSync7(abs, content, "utf-8");
|
|
996
1138
|
}
|
|
997
1139
|
const r = {
|
|
998
1140
|
name: s.name,
|
|
@@ -1030,7 +1172,7 @@ async function runScenario(model, s) {
|
|
|
1030
1172
|
r.durationMs = Date.now() - start;
|
|
1031
1173
|
if (r.error) {
|
|
1032
1174
|
r.reason = `loop error: ${r.error}`;
|
|
1033
|
-
|
|
1175
|
+
rmSync3(dir, { recursive: true, force: true });
|
|
1034
1176
|
return r;
|
|
1035
1177
|
}
|
|
1036
1178
|
try {
|
|
@@ -1040,7 +1182,7 @@ async function runScenario(model, s) {
|
|
|
1040
1182
|
} catch (err) {
|
|
1041
1183
|
r.reason = `check threw: ${err instanceof Error ? err.message : String(err)}`;
|
|
1042
1184
|
}
|
|
1043
|
-
|
|
1185
|
+
rmSync3(dir, { recursive: true, force: true });
|
|
1044
1186
|
return r;
|
|
1045
1187
|
}
|
|
1046
1188
|
var autoYes;
|
|
@@ -1054,12 +1196,12 @@ var init_runner = __esm({
|
|
|
1054
1196
|
|
|
1055
1197
|
// eval/scenarios.ts
|
|
1056
1198
|
import { readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
|
|
1057
|
-
import { join as
|
|
1199
|
+
import { join as join8 } from "path";
|
|
1058
1200
|
var read, scenarios;
|
|
1059
1201
|
var init_scenarios = __esm({
|
|
1060
1202
|
"eval/scenarios.ts"() {
|
|
1061
1203
|
"use strict";
|
|
1062
|
-
read = (dir, f) => existsSync4(
|
|
1204
|
+
read = (dir, f) => existsSync4(join8(dir, f)) ? readFileSync6(join8(dir, f), "utf-8") : null;
|
|
1063
1205
|
scenarios = [
|
|
1064
1206
|
{
|
|
1065
1207
|
name: "edit-exact-string",
|
|
@@ -1208,7 +1350,7 @@ import { createElement } from "react";
|
|
|
1208
1350
|
init_client();
|
|
1209
1351
|
import { useState as useState4, useEffect as useEffect3 } from "react";
|
|
1210
1352
|
import { Box as Box10, Text as Text10, useApp } from "ink";
|
|
1211
|
-
import { homedir as
|
|
1353
|
+
import { homedir as homedir6 } from "os";
|
|
1212
1354
|
import { sep as sep2 } from "path";
|
|
1213
1355
|
|
|
1214
1356
|
// src/config.ts
|
|
@@ -2495,7 +2637,7 @@ async function checkForUpdate() {
|
|
|
2495
2637
|
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2496
2638
|
function App() {
|
|
2497
2639
|
const { exit } = useApp();
|
|
2498
|
-
const cwd = process.cwd().replace(
|
|
2640
|
+
const cwd = process.cwd().replace(homedir6(), "~").split(sep2).join("/");
|
|
2499
2641
|
const [cfg, setCfg] = useState4(loadConfig());
|
|
2500
2642
|
const [models, setModels] = useState4([]);
|
|
2501
2643
|
const [contexts, setContexts] = useState4({});
|
|
@@ -2572,7 +2714,7 @@ function App() {
|
|
|
2572
2714
|
})();
|
|
2573
2715
|
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingX: 1, children: [
|
|
2574
2716
|
/* @__PURE__ */ jsx10(WelcomeBlock, { model: cfg.model, activeCtx, effort, cwd, error: agent.error }),
|
|
2575
|
-
updateAvailable && /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { color: "yellow", children: `\u2191 update available: v${updateAvailable} \u2014 run:
|
|
2717
|
+
updateAvailable && /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { color: "yellow", children: `\u2191 update available: v${updateAvailable} \u2014 run: miii --update` }) }),
|
|
2576
2718
|
state === "loading" && !agent.error && /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "connecting to ollama\u2026" }) }),
|
|
2577
2719
|
agent.error && state !== "ready" && /* @__PURE__ */ jsx10(
|
|
2578
2720
|
ChatView,
|
|
@@ -2633,8 +2775,15 @@ function App() {
|
|
|
2633
2775
|
}
|
|
2634
2776
|
|
|
2635
2777
|
// src/cli.tsx
|
|
2778
|
+
init_spill();
|
|
2779
|
+
cleanupSpill();
|
|
2636
2780
|
var [, , cmd, ...rest] = process.argv;
|
|
2637
|
-
if (cmd === "
|
|
2781
|
+
if (cmd === "update" || cmd === "--update" || cmd === "-u") {
|
|
2782
|
+
const { spawnSync } = await import("child_process");
|
|
2783
|
+
console.log("Updating miii-agent\u2026");
|
|
2784
|
+
const r = spawnSync("npm", ["i", "-g", "miii-agent@latest"], { stdio: "inherit", shell: process.platform === "win32" });
|
|
2785
|
+
process.exit(r.status ?? 1);
|
|
2786
|
+
} else if (cmd === "doctor" || cmd === "eval") {
|
|
2638
2787
|
const { runEval: runEval2 } = await Promise.resolve().then(() => (init_run(), run_exports));
|
|
2639
2788
|
process.exit(await runEval2(rest));
|
|
2640
2789
|
} else {
|