miii-agent 0.1.25 → 0.1.26
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 +6 -0
- package/dist/cli.js +194 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://www.npmjs.com/package/miii-agent"><img src="https://img.shields.io/npm/v/miii-agent" alt="npm version"></a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/miii-agent"><img src="https://img.shields.io/npm/dt/miii-agent" alt="npm total downloads"></a>
|
|
11
12
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license"></a>
|
|
12
13
|
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen" alt="node version"></a>
|
|
13
14
|
<a href="https://ollama.com"><img src="https://img.shields.io/badge/powered%20by-Ollama-black" alt="powered by Ollama"></a>
|
|
@@ -68,6 +69,10 @@ It doesn't just chat, either — it decomposes the problem, calls tools, and che
|
|
|
68
69
|
miii doctor # grade every installed model
|
|
69
70
|
miii doctor qwen2.5-coder:7b # grade one
|
|
70
71
|
```
|
|
72
|
+
- **🖼️ Paste images** — copy a screenshot and hit `Ctrl+V` to attach it to your message, or paste an image file path. Great for "why does this UI look broken?" or reading an error screenshot. **Needs a vision-capable model** (`llava`, `llama3.2-vision`, `qwen2-vl`, …) — text-only models silently ignore the image.
|
|
73
|
+
```bash
|
|
74
|
+
ollama pull llava # or llama3.2-vision
|
|
75
|
+
```
|
|
71
76
|
- **💧 Lossless output spill** — that 50K-line test log won't get truncated and leave the model guessing. miii spills the full output to disk and lets the model page through it. Nothing is ever lost.
|
|
72
77
|
- **🔒 Permission-gated tools** — you approve what the agent can touch; "always" approvals persist. File tools are confined to your working directory.
|
|
73
78
|
- **📄 `MIII.md`** — drop one in your repo to teach miii your conventions, build/test commands, and do's & don'ts. Same idea as `CLAUDE.md`, read every turn.
|
|
@@ -96,6 +101,7 @@ File tools (`read_file`, `write_file`, `edit_file`) reject `../` traversal and a
|
|
|
96
101
|
|-----|--------|
|
|
97
102
|
| `Enter` | Send prompt |
|
|
98
103
|
| `@filename` | Attach file to context |
|
|
104
|
+
| `Ctrl+V` | Paste clipboard image (needs a vision model) |
|
|
99
105
|
| `/models` | Switch active model |
|
|
100
106
|
| `/clear` | Reset conversation |
|
|
101
107
|
| `Esc` | Stop generation or tool run |
|
package/dist/cli.js
CHANGED
|
@@ -129,6 +129,15 @@ function isConnectionError(err) {
|
|
|
129
129
|
const msg = err instanceof Error ? err.message : String(err);
|
|
130
130
|
return msg.includes("ECONNREFUSED") || msg.includes("fetch failed") || msg.includes("connect");
|
|
131
131
|
}
|
|
132
|
+
function isNoVisionError(err) {
|
|
133
|
+
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
134
|
+
return msg.includes("does not support image") || msg.includes("image input");
|
|
135
|
+
}
|
|
136
|
+
function noVisionError(model) {
|
|
137
|
+
return new Error(
|
|
138
|
+
`"${model}" can't read images \u2014 it has no vision support. Switch to a vision-capable model (e.g. llava, llama3.2-vision, qwen2-vl) with /models, then resend. Pull one with: ollama pull llava`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
132
141
|
async function listModels(entry) {
|
|
133
142
|
try {
|
|
134
143
|
const { models } = await makeClient(entry).list();
|
|
@@ -208,6 +217,7 @@ async function* chat(entry, model, messages, tools, opts) {
|
|
|
208
217
|
}
|
|
209
218
|
} catch (err) {
|
|
210
219
|
if (signal?.aborted) return;
|
|
220
|
+
if (isNoVisionError(err)) throw noVisionError(model);
|
|
211
221
|
if (isConnectionError(err)) {
|
|
212
222
|
throw new Error(NOT_RUNNING);
|
|
213
223
|
}
|
|
@@ -233,6 +243,7 @@ async function* chat(entry, model, messages, tools, opts) {
|
|
|
233
243
|
}
|
|
234
244
|
} catch (err) {
|
|
235
245
|
if (opts?.signal?.aborted) return;
|
|
246
|
+
if (isNoVisionError(err)) throw noVisionError(model);
|
|
236
247
|
if (isConnectionError(err)) {
|
|
237
248
|
throw new Error(NOT_RUNNING);
|
|
238
249
|
}
|
|
@@ -1429,7 +1440,9 @@ function toOllamaMessages(history, system) {
|
|
|
1429
1440
|
const out = [{ role: "system", content: system }];
|
|
1430
1441
|
for (const msg of history) {
|
|
1431
1442
|
if (typeof msg.content === "string") {
|
|
1432
|
-
|
|
1443
|
+
const om = { role: msg.role === "system" ? "system" : msg.role, content: msg.content };
|
|
1444
|
+
if (msg.role === "user" && msg.images && msg.images.length > 0) om.images = msg.images;
|
|
1445
|
+
out.push(om);
|
|
1433
1446
|
continue;
|
|
1434
1447
|
}
|
|
1435
1448
|
if (msg.role === "assistant") {
|
|
@@ -1615,7 +1628,11 @@ async function* runAgent(opts) {
|
|
|
1615
1628
|
const effort = EFFORT_OPTIONS[loadConfig().effort ?? "medium"];
|
|
1616
1629
|
const history = [
|
|
1617
1630
|
...opts.history,
|
|
1618
|
-
{
|
|
1631
|
+
{
|
|
1632
|
+
role: "user",
|
|
1633
|
+
content: opts.userText,
|
|
1634
|
+
...opts.images && opts.images.length > 0 ? { images: opts.images } : {}
|
|
1635
|
+
}
|
|
1619
1636
|
];
|
|
1620
1637
|
let promptTokens = 0;
|
|
1621
1638
|
let evalTokens = 0;
|
|
@@ -1833,14 +1850,14 @@ var init_loop = __esm({
|
|
|
1833
1850
|
});
|
|
1834
1851
|
|
|
1835
1852
|
// eval/runner.ts
|
|
1836
|
-
import { mkdtempSync, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, rmSync as
|
|
1837
|
-
import { dirname as dirname3, join as
|
|
1838
|
-
import { tmpdir } from "os";
|
|
1853
|
+
import { mkdtempSync, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, rmSync as rmSync4 } from "fs";
|
|
1854
|
+
import { dirname as dirname3, join as join10 } from "path";
|
|
1855
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
1839
1856
|
async function runScenario(model, s) {
|
|
1840
|
-
const dir = mkdtempSync(
|
|
1857
|
+
const dir = mkdtempSync(join10(tmpdir2(), "miii-eval-"));
|
|
1841
1858
|
const prevCwd = process.cwd();
|
|
1842
1859
|
for (const [rel, content] of Object.entries(s.files ?? {})) {
|
|
1843
|
-
const abs =
|
|
1860
|
+
const abs = join10(dir, rel);
|
|
1844
1861
|
mkdirSync6(dirname3(abs), { recursive: true });
|
|
1845
1862
|
writeFileSync7(abs, content, "utf-8");
|
|
1846
1863
|
}
|
|
@@ -1880,7 +1897,7 @@ async function runScenario(model, s) {
|
|
|
1880
1897
|
r.durationMs = Date.now() - start;
|
|
1881
1898
|
if (r.error) {
|
|
1882
1899
|
r.reason = `loop error: ${r.error}`;
|
|
1883
|
-
|
|
1900
|
+
rmSync4(dir, { recursive: true, force: true });
|
|
1884
1901
|
return r;
|
|
1885
1902
|
}
|
|
1886
1903
|
try {
|
|
@@ -1890,7 +1907,7 @@ async function runScenario(model, s) {
|
|
|
1890
1907
|
} catch (err) {
|
|
1891
1908
|
r.reason = `check threw: ${err instanceof Error ? err.message : String(err)}`;
|
|
1892
1909
|
}
|
|
1893
|
-
|
|
1910
|
+
rmSync4(dir, { recursive: true, force: true });
|
|
1894
1911
|
return r;
|
|
1895
1912
|
}
|
|
1896
1913
|
var autoYes;
|
|
@@ -1903,13 +1920,13 @@ var init_runner = __esm({
|
|
|
1903
1920
|
});
|
|
1904
1921
|
|
|
1905
1922
|
// eval/scenarios.ts
|
|
1906
|
-
import { readFileSync as
|
|
1907
|
-
import { join as
|
|
1923
|
+
import { readFileSync as readFileSync9, existsSync as existsSync8 } from "fs";
|
|
1924
|
+
import { join as join11 } from "path";
|
|
1908
1925
|
var read, scenarios;
|
|
1909
1926
|
var init_scenarios = __esm({
|
|
1910
1927
|
"eval/scenarios.ts"() {
|
|
1911
1928
|
"use strict";
|
|
1912
|
-
read = (dir, f) =>
|
|
1929
|
+
read = (dir, f) => existsSync8(join11(dir, f)) ? readFileSync9(join11(dir, f), "utf-8") : null;
|
|
1913
1930
|
scenarios = [
|
|
1914
1931
|
{
|
|
1915
1932
|
name: "edit-exact-string",
|
|
@@ -2059,7 +2076,7 @@ init_client();
|
|
|
2059
2076
|
init_config();
|
|
2060
2077
|
import { useState as useState5, useEffect as useEffect4, useRef as useRef2 } from "react";
|
|
2061
2078
|
import { Box as Box13, Text as Text13, useApp } from "ink";
|
|
2062
|
-
import { homedir as
|
|
2079
|
+
import { homedir as homedir7 } from "os";
|
|
2063
2080
|
import { sep as sep2 } from "path";
|
|
2064
2081
|
|
|
2065
2082
|
// src/ui/WelcomeBlock.tsx
|
|
@@ -2346,7 +2363,8 @@ function persistSession(id, messages, title) {
|
|
|
2346
2363
|
};
|
|
2347
2364
|
const lines = [JSON.stringify({ type: "meta", ...meta })];
|
|
2348
2365
|
for (const message of messages) {
|
|
2349
|
-
|
|
2366
|
+
const { images: _img, ...rest } = message;
|
|
2367
|
+
lines.push(JSON.stringify({ type: "message", message: rest }));
|
|
2350
2368
|
}
|
|
2351
2369
|
writeFileSync2(sessionPath(id), lines.join("\n") + "\n", "utf-8");
|
|
2352
2370
|
}
|
|
@@ -3240,6 +3258,7 @@ var EMPTY_STATE_HINTS = [
|
|
|
3240
3258
|
"\u2022 /models \u2014 switch model or effort",
|
|
3241
3259
|
"\u2022 /new \u2014 start a new chat",
|
|
3242
3260
|
"\u2022 /sessions \u2014 view saved chats",
|
|
3261
|
+
"\u2022 ctrl+v \u2014 paste an image (needs a vision model)",
|
|
3243
3262
|
"\u2022 ctrl+t \u2014 toggle thinking"
|
|
3244
3263
|
];
|
|
3245
3264
|
var EMPTY_STATE_TITLE = "Ask anything, or try:";
|
|
@@ -3685,7 +3704,7 @@ function useAgentRunner(model, activeCtx) {
|
|
|
3685
3704
|
setPendingPermission(null);
|
|
3686
3705
|
req.resolve(answers[cursor]);
|
|
3687
3706
|
}
|
|
3688
|
-
async function sendMessage(text) {
|
|
3707
|
+
async function sendMessage(text, images) {
|
|
3689
3708
|
if (busyRef.current || !model) return;
|
|
3690
3709
|
busyRef.current = true;
|
|
3691
3710
|
setBusy(true);
|
|
@@ -3743,6 +3762,7 @@ function useAgentRunner(model, activeCtx) {
|
|
|
3743
3762
|
cwd: process.cwd(),
|
|
3744
3763
|
history: agentHistory,
|
|
3745
3764
|
userText: text,
|
|
3765
|
+
images,
|
|
3746
3766
|
permissions: { ask: askPermission },
|
|
3747
3767
|
signal: controller.signal,
|
|
3748
3768
|
num_ctx: activeCtx ?? void 0
|
|
@@ -3872,16 +3892,141 @@ function useAgentRunner(model, activeCtx) {
|
|
|
3872
3892
|
}
|
|
3873
3893
|
|
|
3874
3894
|
// src/ui/hooks/useKeyboard.ts
|
|
3875
|
-
|
|
3895
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
|
|
3896
|
+
import { basename, join as join9 } from "path";
|
|
3897
|
+
import { homedir as homedir6 } from "os";
|
|
3876
3898
|
import { useInput, useStdout } from "ink";
|
|
3899
|
+
|
|
3900
|
+
// src/ui/clipboard.ts
|
|
3901
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
3902
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, rmSync as rmSync3 } from "fs";
|
|
3903
|
+
import { join as join8 } from "path";
|
|
3904
|
+
import { tmpdir } from "os";
|
|
3905
|
+
function safeRm(p) {
|
|
3906
|
+
try {
|
|
3907
|
+
rmSync3(p, { force: true });
|
|
3908
|
+
} catch {
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3911
|
+
function consume(p) {
|
|
3912
|
+
try {
|
|
3913
|
+
const b64 = readFileSync7(p).toString("base64");
|
|
3914
|
+
return b64.length > 0 ? b64 : null;
|
|
3915
|
+
} catch {
|
|
3916
|
+
return null;
|
|
3917
|
+
} finally {
|
|
3918
|
+
safeRm(p);
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
function readMac(out) {
|
|
3922
|
+
try {
|
|
3923
|
+
execFileSync2("pngpaste", [out], { stdio: "ignore" });
|
|
3924
|
+
if (existsSync6(out)) return consume(out);
|
|
3925
|
+
} catch {
|
|
3926
|
+
}
|
|
3927
|
+
const png = "\xABclass PNGf\xBB";
|
|
3928
|
+
const script = [
|
|
3929
|
+
"try",
|
|
3930
|
+
`set f to open for access (POSIX file "${out}") with write permission`,
|
|
3931
|
+
`set theData to (the clipboard as ${png})`,
|
|
3932
|
+
"write theData to f",
|
|
3933
|
+
"close access f",
|
|
3934
|
+
"on error",
|
|
3935
|
+
"try",
|
|
3936
|
+
"close access f",
|
|
3937
|
+
"end try",
|
|
3938
|
+
'return "NOIMG"',
|
|
3939
|
+
"end try"
|
|
3940
|
+
];
|
|
3941
|
+
try {
|
|
3942
|
+
const res = execFileSync2("osascript", script.flatMap((s) => ["-e", s]), { encoding: "utf8" });
|
|
3943
|
+
if (res.includes("NOIMG")) {
|
|
3944
|
+
safeRm(out);
|
|
3945
|
+
return null;
|
|
3946
|
+
}
|
|
3947
|
+
if (existsSync6(out)) return consume(out);
|
|
3948
|
+
} catch {
|
|
3949
|
+
}
|
|
3950
|
+
safeRm(out);
|
|
3951
|
+
return null;
|
|
3952
|
+
}
|
|
3953
|
+
function readLinux(out) {
|
|
3954
|
+
for (const [cmd2, args2] of [
|
|
3955
|
+
["wl-paste", ["--type", "image/png"]],
|
|
3956
|
+
["xclip", ["-selection", "clipboard", "-t", "image/png", "-o"]]
|
|
3957
|
+
]) {
|
|
3958
|
+
try {
|
|
3959
|
+
const buf = execFileSync2(cmd2, args2, { maxBuffer: 64 * 1024 * 1024 });
|
|
3960
|
+
if (buf.length > 0) return buf.toString("base64");
|
|
3961
|
+
} catch {
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
return null;
|
|
3965
|
+
}
|
|
3966
|
+
function readWindows(out) {
|
|
3967
|
+
const ps = [
|
|
3968
|
+
"Add-Type -AssemblyName System.Windows.Forms,System.Drawing;",
|
|
3969
|
+
"$img = [System.Windows.Forms.Clipboard]::GetImage();",
|
|
3970
|
+
// Single-quoted PS string → backslashes are literal, no escaping needed.
|
|
3971
|
+
`if ($img -ne $null) { $img.Save('${out}', [System.Drawing.Imaging.ImageFormat]::Png); 'OK' } else { 'NOIMG' }`
|
|
3972
|
+
].join(" ");
|
|
3973
|
+
try {
|
|
3974
|
+
const res = execFileSync2(
|
|
3975
|
+
"powershell",
|
|
3976
|
+
["-NoProfile", "-NonInteractive", "-STA", "-Command", ps],
|
|
3977
|
+
{ encoding: "utf8" }
|
|
3978
|
+
);
|
|
3979
|
+
if (res.includes("NOIMG")) {
|
|
3980
|
+
safeRm(out);
|
|
3981
|
+
return null;
|
|
3982
|
+
}
|
|
3983
|
+
if (existsSync6(out)) return consume(out);
|
|
3984
|
+
} catch {
|
|
3985
|
+
}
|
|
3986
|
+
safeRm(out);
|
|
3987
|
+
return null;
|
|
3988
|
+
}
|
|
3989
|
+
function readClipboardImage() {
|
|
3990
|
+
const out = join8(tmpdir(), `miii-clip-${Date.now()}-${Math.random().toString(36).slice(2)}.png`);
|
|
3991
|
+
if (process.platform === "darwin") return readMac(out);
|
|
3992
|
+
if (process.platform === "linux") return readLinux(out);
|
|
3993
|
+
if (process.platform === "win32") return readWindows(out);
|
|
3994
|
+
return null;
|
|
3995
|
+
}
|
|
3996
|
+
|
|
3997
|
+
// src/ui/hooks/useKeyboard.ts
|
|
3998
|
+
init_config();
|
|
3877
3999
|
var EFFORTS = ["low", "medium", "high"];
|
|
3878
4000
|
var PASTE_CHIP_LINES = 4;
|
|
3879
4001
|
var PASTE_CHIP_CHARS = 200;
|
|
3880
4002
|
var pasteStore = /* @__PURE__ */ new Map();
|
|
3881
4003
|
var pasteCounter = 0;
|
|
4004
|
+
var imageStore = /* @__PURE__ */ new Map();
|
|
4005
|
+
var imageCounter = 0;
|
|
4006
|
+
var IMAGE_EXT_RE = /\.(png|jpe?g|webp|gif|bmp)$/i;
|
|
3882
4007
|
function clearPasteStore() {
|
|
3883
4008
|
pasteStore.clear();
|
|
3884
4009
|
pasteCounter = 0;
|
|
4010
|
+
imageStore.clear();
|
|
4011
|
+
imageCounter = 0;
|
|
4012
|
+
}
|
|
4013
|
+
function tryImagePaste(cleaned) {
|
|
4014
|
+
let p = cleaned.trim();
|
|
4015
|
+
if (p.startsWith('"') && p.endsWith('"') || p.startsWith("'") && p.endsWith("'")) {
|
|
4016
|
+
p = p.slice(1, -1);
|
|
4017
|
+
}
|
|
4018
|
+
p = p.replace(/\\ /g, " ");
|
|
4019
|
+
if (p.includes("\n") || !IMAGE_EXT_RE.test(p)) return null;
|
|
4020
|
+
if (p.startsWith("~/")) p = join9(homedir6(), p.slice(2));
|
|
4021
|
+
if (!existsSync7(p)) return null;
|
|
4022
|
+
try {
|
|
4023
|
+
const b64 = readFileSync8(p).toString("base64");
|
|
4024
|
+
const chip = `[Image #${++imageCounter} \xB7 ${basename(p)}]`;
|
|
4025
|
+
imageStore.set(chip, b64);
|
|
4026
|
+
return chip;
|
|
4027
|
+
} catch {
|
|
4028
|
+
return null;
|
|
4029
|
+
}
|
|
3885
4030
|
}
|
|
3886
4031
|
var inputHistory = [];
|
|
3887
4032
|
var historyIndex = -1;
|
|
@@ -3902,6 +4047,8 @@ function stripControls(chunk) {
|
|
|
3902
4047
|
function sanitizePaste(chunk) {
|
|
3903
4048
|
if (chunk.length <= 1) return chunk;
|
|
3904
4049
|
const cleaned = stripControls(chunk).replace(/\r/g, "");
|
|
4050
|
+
const imageChip = tryImagePaste(cleaned);
|
|
4051
|
+
if (imageChip) return imageChip;
|
|
3905
4052
|
const lines = cleaned.split("\n").length;
|
|
3906
4053
|
if (lines > PASTE_CHIP_LINES || cleaned.length > PASTE_CHIP_CHARS) {
|
|
3907
4054
|
const chip = `[Pasted #${++pasteCounter} \xB7 ${lines} line${lines === 1 ? "" : "s"}]`;
|
|
@@ -4136,6 +4283,19 @@ function useKeyboard(opts) {
|
|
|
4136
4283
|
}
|
|
4137
4284
|
if (state === "ready") {
|
|
4138
4285
|
if (busyRef.current) return;
|
|
4286
|
+
if (key.ctrl && char === "v") {
|
|
4287
|
+
const b64 = readClipboardImage();
|
|
4288
|
+
if (!b64) {
|
|
4289
|
+
setNotice("no image in clipboard");
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
4292
|
+
const chip = `[Image #${++imageCounter} \xB7 clipboard]`;
|
|
4293
|
+
imageStore.set(chip, b64);
|
|
4294
|
+
historyIndex = -1;
|
|
4295
|
+
setInput((s) => s.slice(0, caret) + chip + s.slice(caret));
|
|
4296
|
+
setCaret((i) => i + chip.length);
|
|
4297
|
+
return;
|
|
4298
|
+
}
|
|
4139
4299
|
const paletteOpen = input.startsWith("/");
|
|
4140
4300
|
const matches2 = paletteOpen ? filteredCommands(input) : [];
|
|
4141
4301
|
const mention = !paletteOpen ? parseMention(input) : null;
|
|
@@ -4248,8 +4408,16 @@ function useKeyboard(opts) {
|
|
|
4248
4408
|
}
|
|
4249
4409
|
} else if (trimmed) {
|
|
4250
4410
|
setNotice(null);
|
|
4251
|
-
const
|
|
4252
|
-
|
|
4411
|
+
const images = [];
|
|
4412
|
+
let textPart = trimmed;
|
|
4413
|
+
for (const [chip, b64] of imageStore) {
|
|
4414
|
+
if (textPart.includes(chip)) {
|
|
4415
|
+
images.push(b64);
|
|
4416
|
+
textPart = textPart.split(chip).join("").trim();
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
const message = expandPastes(textPart) || "Describe the attached image.";
|
|
4420
|
+
sendMessage(message, images.length ? images : void 0);
|
|
4253
4421
|
}
|
|
4254
4422
|
clearPasteStore();
|
|
4255
4423
|
setInput(() => "");
|
|
@@ -4283,8 +4451,14 @@ function useKeyboard(opts) {
|
|
|
4283
4451
|
for (const chip of pasteStore.keys()) {
|
|
4284
4452
|
if (before.endsWith(chip) && chip.length > match.length) match = chip;
|
|
4285
4453
|
}
|
|
4454
|
+
for (const chip of imageStore.keys()) {
|
|
4455
|
+
if (before.endsWith(chip) && chip.length > match.length) match = chip;
|
|
4456
|
+
}
|
|
4286
4457
|
const cut = match ? match.length : 1;
|
|
4287
|
-
if (match)
|
|
4458
|
+
if (match) {
|
|
4459
|
+
pasteStore.delete(match);
|
|
4460
|
+
imageStore.delete(match);
|
|
4461
|
+
}
|
|
4288
4462
|
setInput((s) => s.slice(0, caret - cut) + s.slice(caret));
|
|
4289
4463
|
setCaret((i) => Math.max(0, i - cut));
|
|
4290
4464
|
} else if (char && !key.ctrl && !key.meta && !key.tab) {
|
|
@@ -4340,7 +4514,7 @@ async function checkForUpdate() {
|
|
|
4340
4514
|
import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4341
4515
|
function App() {
|
|
4342
4516
|
const { exit } = useApp();
|
|
4343
|
-
const cwd = process.cwd().replace(
|
|
4517
|
+
const cwd = process.cwd().replace(homedir7(), "~").split(sep2).join("/");
|
|
4344
4518
|
const [cfg, setCfg] = useState5(loadConfig());
|
|
4345
4519
|
const [models, setModels] = useState5([]);
|
|
4346
4520
|
const [contexts, setContexts] = useState5(() => cfg.modelContexts ?? {});
|
package/package.json
CHANGED