@adyingdeath/meatproxy 1.0.0 → 1.1.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 +6 -4
- package/dist/index.js +586 -142
- package/dist/index.js.map +1 -1
- package/package.json +52 -44
package/README.md
CHANGED
|
@@ -14,10 +14,10 @@ It is a small bridge between your clipboard and chatbots: paste the prompt into
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
16
16
|
```sh
|
|
17
|
-
npm install -g meatproxy
|
|
17
|
+
npm install -g @adyingdeath/meatproxy
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
This provides the `mtp` command
|
|
20
|
+
This provides the `mtp` command.
|
|
21
21
|
|
|
22
22
|
## Usage
|
|
23
23
|
|
|
@@ -31,18 +31,20 @@ mtp init
|
|
|
31
31
|
mtp
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
Run `mtp --help` to see the workflow again at any time.
|
|
35
|
+
|
|
34
36
|
`mtp init` copies the prompt and prints what to do next. `mtp` reads the clipboard, extracts the snippet, runs it, prints the output, and copies that output back to the clipboard.
|
|
35
37
|
|
|
36
38
|
The snippet runs with the same access as your terminal, so only paste replies you trust.
|
|
37
39
|
|
|
38
40
|
## Tools
|
|
39
41
|
|
|
40
|
-
The snippet gets a `tools` object with four methods: `read`, `write`, `edit` and `pwsh`, enough to inspect files, change them, and run PowerShell commands.
|
|
42
|
+
The snippet gets a `tools` object with four methods: `read`, `write`, `edit` and `pwsh`, enough to inspect files, change them, and run PowerShell commands. Each call prints a small card with its result, and `edit` also shows a diff of the change.
|
|
41
43
|
|
|
42
44
|
## Development
|
|
43
45
|
|
|
44
46
|
```sh
|
|
45
47
|
npm install
|
|
46
48
|
npm run build # bundles to dist/
|
|
47
|
-
npm run dev
|
|
49
|
+
npm run dev # runs src/index.ts directly
|
|
48
50
|
```
|
package/dist/index.js
CHANGED
|
@@ -3,41 +3,408 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
|
-
// src/core.ts
|
|
6
|
+
// src/runner/core.ts
|
|
7
7
|
import clipboard from "clipboardy";
|
|
8
8
|
|
|
9
|
-
// src/console.ts
|
|
9
|
+
// src/runner/console.ts
|
|
10
10
|
import { format } from "util";
|
|
11
|
-
var
|
|
11
|
+
var Console = class {
|
|
12
12
|
/** Everything written so far, one entry per console call. */
|
|
13
13
|
lines = [];
|
|
14
14
|
log(...args) {
|
|
15
|
-
console.log(...args);
|
|
16
15
|
this.lines.push(format(...args));
|
|
17
16
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/tools/index.ts
|
|
20
|
+
import { spawnSync } from "child_process";
|
|
21
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
22
|
+
import path from "path";
|
|
23
|
+
|
|
24
|
+
// src/tools/types.ts
|
|
25
|
+
function isRecord(value) {
|
|
26
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27
|
+
}
|
|
28
|
+
function isNonEmptyString(value) {
|
|
29
|
+
return typeof value === "string" && value.length > 0;
|
|
30
|
+
}
|
|
31
|
+
function isOptionalBoolean(value) {
|
|
32
|
+
return value === void 0 || typeof value === "boolean";
|
|
33
|
+
}
|
|
34
|
+
function isOptionalString(value) {
|
|
35
|
+
return value === void 0 || typeof value === "string";
|
|
36
|
+
}
|
|
37
|
+
function isOptionalPositiveInteger(value) {
|
|
38
|
+
return value === void 0 || typeof value === "number" && Number.isInteger(value) && value >= 1;
|
|
39
|
+
}
|
|
40
|
+
function isReadParams(value) {
|
|
41
|
+
return isRecord(value) && isNonEmptyString(value.file_path) && isOptionalPositiveInteger(value.offset) && isOptionalPositiveInteger(value.limit);
|
|
42
|
+
}
|
|
43
|
+
function isWriteParams(value) {
|
|
44
|
+
return isRecord(value) && isNonEmptyString(value.file_path) && typeof value.content === "string";
|
|
45
|
+
}
|
|
46
|
+
function isEditParams(value) {
|
|
47
|
+
return isRecord(value) && isNonEmptyString(value.file_path) && typeof value.old_string === "string" && typeof value.new_string === "string" && isOptionalBoolean(value.replace_all);
|
|
48
|
+
}
|
|
49
|
+
function isPwshParams(value) {
|
|
50
|
+
return isRecord(value) && typeof value.command === "string" && isOptionalString(value.cwd) && isOptionalPositiveInteger(value.timeoutMs);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/tools/tool-cards.ts
|
|
54
|
+
import chalk from "chalk";
|
|
55
|
+
import terminalSize from "terminal-size";
|
|
56
|
+
|
|
57
|
+
// src/tools/card-diff.ts
|
|
58
|
+
import { diffLines } from "diff";
|
|
59
|
+
function splitChangeValue(value) {
|
|
60
|
+
const parts = value.split("\n");
|
|
61
|
+
if (parts.length > 0 && parts[parts.length - 1] === "") parts.pop();
|
|
62
|
+
return parts;
|
|
63
|
+
}
|
|
64
|
+
function toDiffLines(original, updated) {
|
|
65
|
+
const changes = diffLines(original, updated, { stripTrailingCr: true });
|
|
66
|
+
const rows = [];
|
|
67
|
+
let oldLine = 1;
|
|
68
|
+
let newLine = 1;
|
|
69
|
+
for (const change of changes) {
|
|
70
|
+
for (const text of splitChangeValue(change.value)) {
|
|
71
|
+
if (change.added) {
|
|
72
|
+
rows.push({ kind: "add", newLine: newLine++, text });
|
|
73
|
+
} else if (change.removed) {
|
|
74
|
+
rows.push({ kind: "del", oldLine: oldLine++, text });
|
|
75
|
+
} else {
|
|
76
|
+
rows.push({ kind: "ctx", oldLine: oldLine++, newLine: newLine++, text });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
21
79
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
80
|
+
return rows;
|
|
81
|
+
}
|
|
82
|
+
function hunkRows(lines, context) {
|
|
83
|
+
const keep = new Array(lines.length).fill(false);
|
|
84
|
+
for (let i = 0; i < lines.length; i++) {
|
|
85
|
+
const current = lines[i];
|
|
86
|
+
if (!current || current.kind === "ctx") continue;
|
|
87
|
+
const from = Math.max(0, i - context);
|
|
88
|
+
const to = Math.min(lines.length - 1, i + context);
|
|
89
|
+
for (let j = from; j <= to; j++) keep[j] = true;
|
|
25
90
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
91
|
+
const rows = [];
|
|
92
|
+
for (let i = 0; i < lines.length; ) {
|
|
93
|
+
if (keep[i]) {
|
|
94
|
+
const kept = lines[i];
|
|
95
|
+
if (kept) rows.push(kept);
|
|
96
|
+
i++;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
let j = i;
|
|
100
|
+
while (j < lines.length && !keep[j]) j++;
|
|
101
|
+
const skipped = j - i;
|
|
102
|
+
rows.push({
|
|
103
|
+
kind: "gap",
|
|
104
|
+
text: "... " + skipped + " unchanged line" + (skipped === 1 ? "" : "s")
|
|
105
|
+
});
|
|
106
|
+
i = j;
|
|
29
107
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
108
|
+
return rows;
|
|
109
|
+
}
|
|
110
|
+
function groupDiffHunks(lines, context = 2) {
|
|
111
|
+
const all = hunkRows(lines, context);
|
|
112
|
+
let start = 0;
|
|
113
|
+
let end = all.length;
|
|
114
|
+
while (start < end && all[start]?.kind === "gap") start++;
|
|
115
|
+
while (end > start && all[end - 1]?.kind === "gap") end--;
|
|
116
|
+
const trimmed = all.slice(start, end);
|
|
117
|
+
if (trimmed.length === 0) return [];
|
|
118
|
+
const hunks = [];
|
|
119
|
+
let pendingGap;
|
|
120
|
+
let cur = [];
|
|
121
|
+
for (const row of trimmed) {
|
|
122
|
+
if (row.kind === "gap") {
|
|
123
|
+
if (cur.length > 0) {
|
|
124
|
+
hunks.push({ gap: pendingGap, rows: cur });
|
|
125
|
+
cur = [];
|
|
126
|
+
}
|
|
127
|
+
pendingGap = row;
|
|
128
|
+
} else {
|
|
129
|
+
cur.push(row);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (cur.length > 0) hunks.push({ gap: pendingGap, rows: cur });
|
|
133
|
+
return hunks;
|
|
134
|
+
}
|
|
135
|
+
function toPairs(rows) {
|
|
136
|
+
const pairs = [];
|
|
137
|
+
for (let i = 0; i < rows.length; ) {
|
|
138
|
+
const row = rows[i];
|
|
139
|
+
if (!row) {
|
|
140
|
+
i++;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (row.kind === "gap") {
|
|
144
|
+
pairs.push({ kind: "gap", text: row.text });
|
|
145
|
+
i++;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (row.kind === "ctx") {
|
|
149
|
+
pairs.push({ kind: "pair", left: row, right: row });
|
|
150
|
+
i++;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const lefts = [];
|
|
154
|
+
const rights = [];
|
|
155
|
+
while (i < rows.length) {
|
|
156
|
+
const next = rows[i];
|
|
157
|
+
if (!next || next.kind === "ctx" || next.kind === "gap") break;
|
|
158
|
+
if (next.kind === "del") lefts.push(next);
|
|
159
|
+
else rights.push(next);
|
|
160
|
+
i++;
|
|
161
|
+
}
|
|
162
|
+
const count = Math.max(lefts.length, rights.length);
|
|
163
|
+
for (let k = 0; k < count; k++) {
|
|
164
|
+
pairs.push({ kind: "pair", left: lefts[k], right: rights[k] });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return pairs;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/tools/tool-cards.ts
|
|
171
|
+
function width() {
|
|
172
|
+
let cols = FALLBACK_WIDTH;
|
|
173
|
+
try {
|
|
174
|
+
const size = terminalSize();
|
|
175
|
+
if (Number.isFinite(size.columns) && size.columns > 0) cols = size.columns;
|
|
176
|
+
} catch {
|
|
177
|
+
}
|
|
178
|
+
if (cols <= 64) return cols;
|
|
179
|
+
if (cols <= 74) return 64;
|
|
180
|
+
return cols - 10;
|
|
181
|
+
}
|
|
182
|
+
function truncate(text, maxLen) {
|
|
183
|
+
if (text.length <= maxLen) return text;
|
|
184
|
+
if (maxLen <= 0) return "";
|
|
185
|
+
if (maxLen <= 3) return ".".repeat(maxLen);
|
|
186
|
+
return text.slice(0, maxLen - 3) + "...";
|
|
187
|
+
}
|
|
188
|
+
var PAD = " ";
|
|
189
|
+
var FALLBACK_WIDTH = 80;
|
|
190
|
+
var SPLIT_MIN_WIDTH = 80;
|
|
191
|
+
var INNER_WIDTH = width() - PAD.length * 2;
|
|
192
|
+
var S = {
|
|
193
|
+
header: (t) => chalk.bgHex("#fcebdf").hex("#783f09")(t),
|
|
194
|
+
headerContent: (t) => chalk.bgHex("#fcebdf").hex("#34251a")(t),
|
|
195
|
+
body: (t) => chalk.bgHex("#f5f1ef").hex("#34251a")(t),
|
|
196
|
+
ok: (t) => chalk.bgHex("#f5f1ef").hex("#005E19")(t),
|
|
197
|
+
fail: (t) => chalk.bgHex("#f5f1ef").hex("#94020d")(t),
|
|
198
|
+
warn: (t) => chalk.bgHex("#f5f1ef").hex("#595101")(t),
|
|
199
|
+
del: (t) => chalk.bgHex("#f5f1ef").hex("#94020d")(t),
|
|
200
|
+
add: (t) => chalk.bgHex("#f5f1ef").hex("#005E19")(t)
|
|
201
|
+
};
|
|
202
|
+
var ToolCards = class _ToolCards {
|
|
203
|
+
constructor() {
|
|
204
|
+
}
|
|
205
|
+
/** Total width of tools cards. */
|
|
206
|
+
static width() {
|
|
207
|
+
let cols = FALLBACK_WIDTH;
|
|
208
|
+
try {
|
|
209
|
+
const size = terminalSize();
|
|
210
|
+
if (Number.isFinite(size.columns) && size.columns > 0) cols = size.columns;
|
|
211
|
+
} catch {
|
|
212
|
+
}
|
|
213
|
+
if (cols <= 64) return cols;
|
|
214
|
+
if (cols <= 74) return 64;
|
|
215
|
+
return cols - 10;
|
|
216
|
+
}
|
|
217
|
+
/** Keep maxLines of text, every lines truncated by maxLen */
|
|
218
|
+
static blockLines(text, maxLines, maxLen) {
|
|
219
|
+
const parts = text.split("\n");
|
|
220
|
+
const shown = parts.slice(0, maxLines);
|
|
221
|
+
return shown.map((raw) => {
|
|
222
|
+
return truncate(raw.replace(/\r$/, ""), Math.max(0, maxLen));
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
static line(parts, padStyle = S.body) {
|
|
226
|
+
let used = 0;
|
|
227
|
+
let lastStyle = padStyle;
|
|
228
|
+
const chunks = [];
|
|
229
|
+
for (const part of parts) {
|
|
230
|
+
if (used >= INNER_WIDTH) break;
|
|
231
|
+
const room = INNER_WIDTH - used;
|
|
232
|
+
const text = part.text.length > room ? part.text.slice(0, room - 3) + "..." : part.text;
|
|
233
|
+
if (text.length === 0) continue;
|
|
234
|
+
chunks.push(part.style(text));
|
|
235
|
+
used += text.length;
|
|
236
|
+
lastStyle = part.style;
|
|
237
|
+
}
|
|
238
|
+
let padding = "";
|
|
239
|
+
if (used < INNER_WIDTH) {
|
|
240
|
+
padding = lastStyle(" ".repeat(INNER_WIDTH - used));
|
|
241
|
+
}
|
|
242
|
+
console.log(padStyle(PAD) + chunks.join("") + padding + padStyle(PAD));
|
|
243
|
+
}
|
|
244
|
+
// Cards for different tools.
|
|
245
|
+
static read(opts) {
|
|
246
|
+
this.line([{
|
|
247
|
+
text: "read ",
|
|
248
|
+
style: S.header
|
|
249
|
+
}, {
|
|
250
|
+
text: opts.filePath,
|
|
251
|
+
style: S.headerContent
|
|
252
|
+
}], S.header);
|
|
253
|
+
if (opts.ok) {
|
|
254
|
+
this.line([{ text: "success", style: S.ok }], S.body);
|
|
255
|
+
} else {
|
|
256
|
+
this.line([{ text: "failed", style: S.fail }], S.body);
|
|
257
|
+
}
|
|
258
|
+
console.log();
|
|
259
|
+
}
|
|
260
|
+
static write(opts) {
|
|
261
|
+
const BLOCK_MAX_LINES = 2;
|
|
262
|
+
this.line([{
|
|
263
|
+
text: "write ",
|
|
264
|
+
style: S.header
|
|
265
|
+
}, {
|
|
266
|
+
text: opts.filePath,
|
|
267
|
+
style: S.headerContent
|
|
268
|
+
}], S.header);
|
|
269
|
+
if (opts.content) {
|
|
270
|
+
for (const text of _ToolCards.blockLines(opts.content, BLOCK_MAX_LINES, INNER_WIDTH)) {
|
|
271
|
+
this.line([{ text, style: S.body }], S.body);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (opts.ok) {
|
|
275
|
+
this.line([{ text: "success", style: S.ok }], S.body);
|
|
276
|
+
} else {
|
|
277
|
+
this.line([{ text: "failed", style: S.fail }], S.body);
|
|
278
|
+
}
|
|
279
|
+
console.log();
|
|
280
|
+
}
|
|
281
|
+
static edit(opts) {
|
|
282
|
+
_ToolCards.line([{
|
|
283
|
+
text: "edit ",
|
|
284
|
+
style: S.header
|
|
285
|
+
}, {
|
|
286
|
+
text: opts.filePath,
|
|
287
|
+
style: S.headerContent
|
|
288
|
+
}], S.header);
|
|
289
|
+
if (opts.ok && opts.diff && opts.diff.length > 0) {
|
|
290
|
+
const MAX_HUNKS = 3;
|
|
291
|
+
const MAX_ROWS = 10;
|
|
292
|
+
const hunks = groupDiffHunks(opts.diff);
|
|
293
|
+
const moreHunks = hunks.length - MAX_HUNKS;
|
|
294
|
+
const shownHunks = moreHunks > 0 ? hunks.slice(0, MAX_HUNKS) : hunks;
|
|
295
|
+
const renderSingle = (rows) => {
|
|
296
|
+
const capped = _ToolCards.capRows(rows, MAX_ROWS);
|
|
297
|
+
let gutter = 1;
|
|
298
|
+
for (const row of capped) {
|
|
299
|
+
if (row.kind === "gap") continue;
|
|
300
|
+
gutter = Math.max(
|
|
301
|
+
gutter,
|
|
302
|
+
String(row.oldLine ?? row.newLine ?? 0).length
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
const textMax = Math.max(4, INNER_WIDTH - 2 - gutter - 1);
|
|
306
|
+
for (const row of capped) {
|
|
307
|
+
if (row.kind === "gap") {
|
|
308
|
+
_ToolCards.line([{ text: row.text, style: S.body }], S.body);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const [sign, ln, style] = row.kind === "add" ? ["+", row.newLine, S.add] : row.kind === "del" ? ["-", row.oldLine, S.del] : [" ", "", S.body];
|
|
312
|
+
const text = truncate(row.text.replace(/\r$/, ""), textMax);
|
|
313
|
+
_ToolCards.line([{ text: sign + String(ln ?? "").padStart(gutter) + " " + text, style }], S.body);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
const renderSplit = (rows) => {
|
|
317
|
+
const capped = _ToolCards.capPairs(toPairs(rows), MAX_ROWS);
|
|
318
|
+
const SEP = " \u2502 ";
|
|
319
|
+
const cellTotal = INNER_WIDTH - SEP.length;
|
|
320
|
+
const leftW = Math.floor(cellTotal / 2);
|
|
321
|
+
const rightW = cellTotal - leftW;
|
|
322
|
+
let leftGutter = 1;
|
|
323
|
+
let rightGutter = 1;
|
|
324
|
+
for (const pair of capped) {
|
|
325
|
+
if (pair.kind === "gap") continue;
|
|
326
|
+
leftGutter = Math.max(leftGutter, String(pair.left?.oldLine ?? 0).length);
|
|
327
|
+
rightGutter = Math.max(rightGutter, String(pair.right?.newLine ?? 0).length);
|
|
328
|
+
}
|
|
329
|
+
const leftMax = Math.max(4, leftW - 2 - leftGutter);
|
|
330
|
+
const rightMax = Math.max(4, rightW - 2 - rightGutter);
|
|
331
|
+
const cell = (line, side, cellW, gutterW, maxLen) => {
|
|
332
|
+
if (!line) return { text: "\u2591".repeat(cellW), style: S.body };
|
|
333
|
+
const ln = side === "old" ? line.oldLine : line.newLine;
|
|
334
|
+
const [sign, style] = line.kind === "add" ? ["+", S.add] : line.kind === "del" ? ["-", S.del] : [" ", S.body];
|
|
335
|
+
const text = truncate(line.text.replace(/\r$/, ""), maxLen);
|
|
336
|
+
return { text: (sign + String(ln ?? "").padStart(gutterW) + " " + text).padEnd(cellW), style };
|
|
337
|
+
};
|
|
338
|
+
for (const pair of capped) {
|
|
339
|
+
if (pair.kind === "gap") {
|
|
340
|
+
_ToolCards.line([{ text: pair.text, style: S.body }], S.body);
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
_ToolCards.line([
|
|
344
|
+
cell(pair.left, "old", leftW, leftGutter, leftMax),
|
|
345
|
+
{ text: SEP, style: S.body },
|
|
346
|
+
cell(pair.right, "new", rightW, rightGutter, rightMax)
|
|
347
|
+
], S.body);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
shownHunks.forEach((hunk, index) => {
|
|
351
|
+
if (index > 0) {
|
|
352
|
+
const gapText = hunk.gap?.text ?? "...";
|
|
353
|
+
_ToolCards.line([{ text: gapText, style: S.body }], S.body);
|
|
354
|
+
}
|
|
355
|
+
if (INNER_WIDTH < SPLIT_MIN_WIDTH) {
|
|
356
|
+
renderSingle(hunk.rows);
|
|
357
|
+
} else {
|
|
358
|
+
renderSplit(hunk.rows);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
if (moreHunks > 0) {
|
|
362
|
+
_ToolCards.line([{ text: "... " + moreHunks + " more hunk" + (moreHunks === 1 ? "" : "s"), style: S.body }], S.body);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (opts.ok) {
|
|
366
|
+
_ToolCards.line([{ text: "success", style: S.ok }], S.body);
|
|
367
|
+
} else {
|
|
368
|
+
_ToolCards.line([{ text: "failed", style: S.fail }], S.body);
|
|
369
|
+
}
|
|
370
|
+
console.log();
|
|
371
|
+
}
|
|
372
|
+
static pwsh(opts) {
|
|
373
|
+
_ToolCards.line([{
|
|
374
|
+
text: "pwsh ",
|
|
375
|
+
style: S.header
|
|
376
|
+
}, {
|
|
377
|
+
text: opts.command.replace(/\r?\n/g, " "),
|
|
378
|
+
style: S.headerContent
|
|
379
|
+
}], S.header);
|
|
380
|
+
if (!opts.ok) {
|
|
381
|
+
_ToolCards.line([{ text: "failed", style: S.fail }], S.body);
|
|
382
|
+
} else {
|
|
383
|
+
if (opts.exitCode === 0) {
|
|
384
|
+
_ToolCards.line([{ text: "success", style: S.ok }], S.body);
|
|
385
|
+
} else {
|
|
386
|
+
_ToolCards.line([{ text: "exit " + opts.exitCode, style: S.warn }], S.body);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
console.log();
|
|
390
|
+
}
|
|
391
|
+
static capRows(rows, maxRows) {
|
|
392
|
+
if (rows.length <= maxRows) return rows;
|
|
393
|
+
const shown = rows.slice(0, maxRows - 1);
|
|
394
|
+
const hidden = rows.length - shown.length;
|
|
395
|
+
return [...shown, { kind: "gap", text: "... " + hidden + " more line" + (hidden === 1 ? "" : "s") }];
|
|
396
|
+
}
|
|
397
|
+
static capPairs(pairs, maxRows) {
|
|
398
|
+
if (pairs.length <= maxRows) return pairs;
|
|
399
|
+
const shown = pairs.slice(0, maxRows - 1);
|
|
400
|
+
const hidden = pairs.length - shown.length;
|
|
401
|
+
return [...shown, { kind: "gap", text: "... " + hidden + " more line" + (hidden === 1 ? "" : "s") }];
|
|
33
402
|
}
|
|
34
403
|
};
|
|
35
404
|
|
|
36
|
-
// src/tools.ts
|
|
37
|
-
import { spawnSync } from "child_process";
|
|
38
|
-
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
39
|
-
import path from "path";
|
|
405
|
+
// src/tools/index.ts
|
|
40
406
|
var DEFAULT_PWSH_TIMEOUT = 12e4;
|
|
407
|
+
var DEFAULT_READ_LIMIT = 2e3;
|
|
41
408
|
var MAX_PWSH_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
42
409
|
var PWSH_PRELUDE = [
|
|
43
410
|
"$ProgressPreference = 'SilentlyContinue'",
|
|
@@ -63,60 +430,61 @@ function shellCandidates() {
|
|
|
63
430
|
function describeError(error) {
|
|
64
431
|
return error instanceof Error ? error.message : String(error);
|
|
65
432
|
}
|
|
66
|
-
function requirePath(value, method) {
|
|
67
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
68
|
-
throw new TypeError(`${method} failed: file_path must be a non-empty string.`);
|
|
69
|
-
}
|
|
70
|
-
return value;
|
|
71
|
-
}
|
|
72
|
-
function requireText(value, method, field) {
|
|
73
|
-
if (typeof value !== "string") {
|
|
74
|
-
throw new TypeError(`${method} failed: ${field} must be a string.`);
|
|
75
|
-
}
|
|
76
|
-
return value;
|
|
77
|
-
}
|
|
78
|
-
function countOccurrences(text, search) {
|
|
79
|
-
let count = 0;
|
|
80
|
-
let index = text.indexOf(search);
|
|
81
|
-
while (index !== -1) {
|
|
82
|
-
count += 1;
|
|
83
|
-
index = text.indexOf(search, index + search.length);
|
|
84
|
-
}
|
|
85
|
-
return count;
|
|
86
|
-
}
|
|
87
|
-
function replaceFirst(text, search, replacement) {
|
|
88
|
-
const index = text.indexOf(search);
|
|
89
|
-
if (index === -1) {
|
|
90
|
-
return text;
|
|
91
|
-
}
|
|
92
|
-
return text.slice(0, index) + replacement + text.slice(index + search.length);
|
|
93
|
-
}
|
|
94
433
|
var Tools = class {
|
|
95
434
|
root;
|
|
96
435
|
constructor(root = process.cwd()) {
|
|
97
436
|
this.root = path.resolve(root);
|
|
98
437
|
}
|
|
99
|
-
/** Read a UTF-8 text file and return
|
|
100
|
-
read(
|
|
101
|
-
|
|
438
|
+
/** Read a UTF-8 text file and return line-numbered content. */
|
|
439
|
+
read(params) {
|
|
440
|
+
if (!isReadParams(params)) {
|
|
441
|
+
ToolCards.read({ filePath: "", ok: false });
|
|
442
|
+
throw new Error("Read failed: invalid parameters.");
|
|
443
|
+
}
|
|
444
|
+
const filePath = params.file_path;
|
|
102
445
|
const target = this.resolve(filePath);
|
|
446
|
+
let content;
|
|
103
447
|
try {
|
|
104
|
-
|
|
448
|
+
content = readFileSync(target, "utf8");
|
|
105
449
|
} catch (error) {
|
|
450
|
+
ToolCards.read({ filePath, ok: false });
|
|
106
451
|
throw new Error(`Read failed for ${filePath}: ${describeError(error)}`);
|
|
107
452
|
}
|
|
453
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
454
|
+
const allLines = normalized.split("\n");
|
|
455
|
+
if (allLines.length > 0 && allLines[allLines.length - 1] === "") {
|
|
456
|
+
allLines.pop();
|
|
457
|
+
}
|
|
458
|
+
const offset = (params.offset ?? 1) - 1;
|
|
459
|
+
const limit = params.limit ?? DEFAULT_READ_LIMIT;
|
|
460
|
+
ToolCards.read({ filePath, ok: true });
|
|
461
|
+
return {
|
|
462
|
+
path: filePath,
|
|
463
|
+
offset: offset + 1,
|
|
464
|
+
// 1-based
|
|
465
|
+
lines: allLines.slice(offset, offset + limit).map((text, index) => ({
|
|
466
|
+
number: offset + index + 1,
|
|
467
|
+
text
|
|
468
|
+
})),
|
|
469
|
+
totalLines: allLines.length
|
|
470
|
+
};
|
|
108
471
|
}
|
|
109
472
|
/** Create or fully overwrite a UTF-8 text file, creating parent directories. */
|
|
110
|
-
write(
|
|
111
|
-
|
|
112
|
-
|
|
473
|
+
write(params) {
|
|
474
|
+
if (!isWriteParams(params)) {
|
|
475
|
+
ToolCards.write({ filePath: "", content: "", ok: false });
|
|
476
|
+
throw new Error("Write failed: invalid parameters.");
|
|
477
|
+
}
|
|
478
|
+
const filePath = params.file_path;
|
|
113
479
|
const target = this.resolve(filePath);
|
|
114
480
|
try {
|
|
115
481
|
mkdirSync(path.dirname(target), { recursive: true });
|
|
116
|
-
writeFileSync(target, content, "utf8");
|
|
482
|
+
writeFileSync(target, params.content, "utf8");
|
|
117
483
|
} catch (error) {
|
|
484
|
+
ToolCards.write({ filePath, content: params.content, ok: false });
|
|
118
485
|
throw new Error(`Write failed for ${filePath}: ${describeError(error)}`);
|
|
119
486
|
}
|
|
487
|
+
ToolCards.write({ filePath, content: params.content, ok: true });
|
|
120
488
|
}
|
|
121
489
|
/**
|
|
122
490
|
* Replace literal text in a file.
|
|
@@ -124,39 +492,65 @@ var Tools = class {
|
|
|
124
492
|
* `old_string` has to be unique unless `replace_all` is true, and an empty
|
|
125
493
|
* `old_string` is rejected because it would match everywhere.
|
|
126
494
|
*/
|
|
127
|
-
edit(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
495
|
+
edit(params) {
|
|
496
|
+
if (!isEditParams(params)) {
|
|
497
|
+
ToolCards.edit({ filePath: "", replaceAll: false, ok: false });
|
|
498
|
+
throw new Error("Edit failed: invalid parameters.");
|
|
499
|
+
}
|
|
500
|
+
const filePath = params.file_path;
|
|
501
|
+
const oldText = params.old_string;
|
|
502
|
+
const newText = params.new_string;
|
|
503
|
+
const replaceAll = params.replace_all === true;
|
|
131
504
|
if (oldText.length === 0) {
|
|
505
|
+
ToolCards.edit({ filePath, replaceAll, ok: false });
|
|
132
506
|
throw new Error("Edit failed: old_string must not be empty.");
|
|
133
507
|
}
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
508
|
+
const target = this.resolve(filePath);
|
|
509
|
+
let original;
|
|
510
|
+
try {
|
|
511
|
+
original = readFileSync(target, "utf8");
|
|
512
|
+
} catch (error) {
|
|
513
|
+
ToolCards.edit({ filePath, replaceAll, ok: false });
|
|
514
|
+
throw new Error(`Edit failed for ${filePath}: ${describeError(error)}`);
|
|
515
|
+
}
|
|
516
|
+
const eol = original.includes("\r\n") ? "\r\n" : "\n";
|
|
517
|
+
const lf = (text) => text.replace(/\r\n/g, "\n");
|
|
518
|
+
const lfOriginal = lf(original);
|
|
519
|
+
const lfOldText = lf(oldText);
|
|
520
|
+
const firstIndex = lfOriginal.indexOf(lfOldText);
|
|
521
|
+
if (firstIndex === -1) {
|
|
522
|
+
ToolCards.edit({ filePath, replaceAll, ok: false });
|
|
137
523
|
throw new Error(`Edit failed for ${filePath}: old_string was not found.`);
|
|
138
524
|
}
|
|
139
|
-
|
|
525
|
+
const secondIndex = lfOriginal.indexOf(lfOldText, firstIndex + lfOldText.length);
|
|
526
|
+
if (secondIndex !== -1 && !replaceAll) {
|
|
527
|
+
ToolCards.edit({ filePath, replaceAll, ok: false });
|
|
140
528
|
throw new Error(
|
|
141
|
-
`Edit failed for ${filePath}: old_string appears
|
|
529
|
+
`Edit failed for ${filePath}: old_string appears multiple times. Make it unique or pass replace_all = true.`
|
|
142
530
|
);
|
|
143
531
|
}
|
|
144
|
-
const updated =
|
|
145
|
-
|
|
532
|
+
const updated = lfOriginal.replaceAll(lfOldText, () => lf(newText)).replace(/\n/g, eol);
|
|
533
|
+
try {
|
|
534
|
+
writeFileSync(target, updated, "utf8");
|
|
535
|
+
} catch (error) {
|
|
536
|
+
ToolCards.edit({ filePath, replaceAll, ok: false });
|
|
537
|
+
throw new Error(`Edit failed for ${filePath}: ${describeError(error)}`);
|
|
538
|
+
}
|
|
539
|
+
const diff = toDiffLines(original, updated);
|
|
540
|
+
ToolCards.edit({ filePath, replaceAll, ok: true, diff });
|
|
146
541
|
}
|
|
147
542
|
/**
|
|
148
543
|
* Run a PowerShell command synchronously and return its output.
|
|
149
|
-
*
|
|
150
|
-
* A non-zero `exitCode` is returned instead of thrown, so the code can inspect
|
|
151
|
-
* `stdout` and `stderr` itself. The command runs with `pwsh` when it is
|
|
152
|
-
* available and falls back to `powershell.exe` on Windows.
|
|
153
544
|
*/
|
|
154
|
-
pwsh(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
545
|
+
pwsh(params) {
|
|
546
|
+
if (!isPwshParams(params)) {
|
|
547
|
+
ToolCards.pwsh({ command: "", exitCode: null, ok: false });
|
|
548
|
+
throw new Error("Pwsh failed: invalid parameters.");
|
|
549
|
+
}
|
|
550
|
+
const command = params.command;
|
|
551
|
+
const timeout = params.timeoutMs ?? DEFAULT_PWSH_TIMEOUT;
|
|
552
|
+
const cwd = params.cwd ? this.resolve(params.cwd) : this.root;
|
|
553
|
+
const encoded = Buffer.from(PWSH_PRELUDE + "\n" + command, "utf16le").toString("base64");
|
|
160
554
|
for (const shell of shellCandidates()) {
|
|
161
555
|
const result = spawnSync(
|
|
162
556
|
shell,
|
|
@@ -171,20 +565,21 @@ ${command}`, "utf16le").toString("base64");
|
|
|
171
565
|
);
|
|
172
566
|
if (result.error) {
|
|
173
567
|
const code = result.error.code;
|
|
174
|
-
if (code === "ENOENT")
|
|
175
|
-
|
|
176
|
-
}
|
|
568
|
+
if (code === "ENOENT") continue;
|
|
569
|
+
ToolCards.pwsh({ command, exitCode: null, ok: false });
|
|
177
570
|
if (code === "ETIMEDOUT") {
|
|
178
|
-
throw new Error(
|
|
571
|
+
throw new Error("Pwsh timed out after " + timeout + " ms.");
|
|
179
572
|
}
|
|
180
|
-
throw new Error(
|
|
573
|
+
throw new Error("Pwsh failed: " + result.error.message);
|
|
181
574
|
}
|
|
575
|
+
ToolCards.pwsh({ command, exitCode: result.status, ok: true });
|
|
182
576
|
return {
|
|
183
577
|
stdout: result.stdout,
|
|
184
578
|
stderr: decodePowerShellStderr(result.stderr),
|
|
185
579
|
exitCode: result.status
|
|
186
580
|
};
|
|
187
581
|
}
|
|
582
|
+
ToolCards.pwsh({ command, exitCode: null, ok: false });
|
|
188
583
|
throw new Error("Pwsh failed: PowerShell is not available on this machine.");
|
|
189
584
|
}
|
|
190
585
|
resolve(filePath) {
|
|
@@ -192,9 +587,12 @@ ${command}`, "utf16le").toString("base64");
|
|
|
192
587
|
}
|
|
193
588
|
};
|
|
194
589
|
|
|
195
|
-
// src/core.ts
|
|
590
|
+
// src/runner/core.ts
|
|
591
|
+
import chalk2 from "chalk";
|
|
196
592
|
var HEAD_MARKER = "<<<<<<< MEATPROXY";
|
|
197
593
|
var TAIL_MARKER = ">>>>>>> MEATPROXY";
|
|
594
|
+
var OUTPUT_HEAD_MARKER = "<<<<<<< MEATPROXY OUTPUT\n";
|
|
595
|
+
var OUTPUT_TAIL_MARKER = "\n>>>>>>> MEATPROXY OUTPUT\n\n";
|
|
198
596
|
var INVALID_MARKER_MESSAGE = `Error: Invalid format.
|
|
199
597
|
Expected format:
|
|
200
598
|
${HEAD_MARKER}
|
|
@@ -213,33 +611,21 @@ function extractCodeBlock(text) {
|
|
|
213
611
|
}
|
|
214
612
|
});
|
|
215
613
|
if (heads.length === 0 && tails.length === 0) {
|
|
216
|
-
|
|
217
|
-
return "";
|
|
614
|
+
return { status: "error", message: INVALID_MARKER_MESSAGE };
|
|
218
615
|
}
|
|
219
616
|
if (heads.length > 1 || tails.length > 1) {
|
|
220
|
-
|
|
221
|
-
return "";
|
|
617
|
+
return { status: "error", message: "Error: Found more than one pair of MEATPROXY markers." };
|
|
222
618
|
}
|
|
223
619
|
const head = heads[0];
|
|
224
620
|
const tail = tails[0];
|
|
225
|
-
if (head === void 0) {
|
|
226
|
-
|
|
227
|
-
return "";
|
|
228
|
-
}
|
|
229
|
-
if (tail === void 0) {
|
|
230
|
-
console.log(INVALID_MARKER_MESSAGE);
|
|
231
|
-
return "";
|
|
232
|
-
}
|
|
233
|
-
if (tail < head) {
|
|
234
|
-
console.log(INVALID_MARKER_MESSAGE);
|
|
235
|
-
return "";
|
|
621
|
+
if (head === void 0 || tail === void 0 || tail < head) {
|
|
622
|
+
return { status: "error", message: INVALID_MARKER_MESSAGE };
|
|
236
623
|
}
|
|
237
624
|
const code = lines.slice(head + 1, tail).join("\n").trim();
|
|
238
625
|
if (code.length === 0) {
|
|
239
|
-
|
|
240
|
-
return "";
|
|
626
|
+
return { status: "empty" };
|
|
241
627
|
}
|
|
242
|
-
return code;
|
|
628
|
+
return { status: "ok", code };
|
|
243
629
|
}
|
|
244
630
|
var AsyncFunction = Object.getPrototypeOf(async function() {
|
|
245
631
|
}).constructor;
|
|
@@ -248,28 +634,35 @@ async function run() {
|
|
|
248
634
|
try {
|
|
249
635
|
text = await clipboard.read();
|
|
250
636
|
} catch (error) {
|
|
251
|
-
console.log("could not read the clipboard");
|
|
637
|
+
console.log(chalk2.red("could not read the clipboard"));
|
|
252
638
|
return;
|
|
253
639
|
}
|
|
254
|
-
const
|
|
255
|
-
|
|
640
|
+
const vmConsole = new Console();
|
|
641
|
+
const result = extractCodeBlock(text);
|
|
642
|
+
if (result.status === "empty") {
|
|
643
|
+
console.log("Nothing happens.");
|
|
256
644
|
return;
|
|
257
645
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
646
|
+
if (result.status === "error") {
|
|
647
|
+
vmConsole.log(result.message);
|
|
648
|
+
} else {
|
|
649
|
+
try {
|
|
650
|
+
const func = new AsyncFunction("tools", "console", result.code);
|
|
651
|
+
await func(new Tools(), vmConsole);
|
|
652
|
+
} catch (error) {
|
|
653
|
+
vmConsole.log(error);
|
|
654
|
+
}
|
|
264
655
|
}
|
|
265
|
-
if (
|
|
656
|
+
if (vmConsole.lines.length === 0) {
|
|
266
657
|
console.log("No output to copy.");
|
|
267
658
|
return;
|
|
268
659
|
}
|
|
269
660
|
try {
|
|
270
|
-
await clipboard.write(
|
|
661
|
+
await clipboard.write(
|
|
662
|
+
OUTPUT_HEAD_MARKER + vmConsole.lines.join("\n") + OUTPUT_TAIL_MARKER
|
|
663
|
+
);
|
|
271
664
|
} catch (error) {
|
|
272
|
-
console.log("could not write to the clipboard");
|
|
665
|
+
console.log(chalk2.red("could not write to the clipboard"));
|
|
273
666
|
return;
|
|
274
667
|
}
|
|
275
668
|
console.log("Output copied to the clipboard. Paste it back to the agent.");
|
|
@@ -278,34 +671,76 @@ async function run() {
|
|
|
278
671
|
// src/commands/init.ts
|
|
279
672
|
import clipboard2 from "clipboardy";
|
|
280
673
|
|
|
281
|
-
// src/prompt.ts
|
|
282
|
-
var INIT_PROMPT =
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
674
|
+
// src/constants/prompt.ts
|
|
675
|
+
var INIT_PROMPT = `<<<<<<< MEATPROXY SYSTEM
|
|
676
|
+
You are an AI agent powered by MeatProxy, which is a cli tool that reads your replies copied by user and runs the JavaScript in it locally.
|
|
677
|
+
|
|
678
|
+
To inspect or edit files, reply with one snippet shaped like this. There should be no more than one pair of MEATPROXY markers. Keep the marker lines exactly as shown, each on its own line, inside the code block. The cli reads everything between them and ignores the rest of your reply.
|
|
679
|
+
|
|
680
|
+
\`\`\`js
|
|
681
|
+
<<<<<<< MEATPROXY
|
|
682
|
+
// your JavaScript here, which is the code for a function body.
|
|
683
|
+
>>>>>>> MEATPROXY
|
|
684
|
+
\`\`\`
|
|
685
|
+
|
|
686
|
+
Guidelines:
|
|
687
|
+
- Use relative paths; the root is current working directory.
|
|
688
|
+
- Do one step per snippet: the user runs it, then pastes the output back to you.
|
|
689
|
+
- Use console.log for results and progress; you will see everything it prints.
|
|
690
|
+
- Keep explanations outside the code block, best to place it at the very end of the whole replies.
|
|
691
|
+
- Newlines: \`read\`/\`edit\` work in LF; the cli converts CRLF automatically. Just use LF in \`old_string\`/\`new_string\`.
|
|
692
|
+
|
|
693
|
+
The snippet runs in Node with a \`tools\` object:
|
|
694
|
+
|
|
695
|
+
\`\`\`
|
|
696
|
+
tools.read({
|
|
697
|
+
/** Path of the file to read, resolved against current working directory. */
|
|
698
|
+
file_path: string;
|
|
699
|
+
/** 1-based first line to return. Defaults to 1. */
|
|
700
|
+
offset?: number;
|
|
701
|
+
/** Maximum number of lines to return. Defaults to 2000. */
|
|
702
|
+
limit?: number;
|
|
703
|
+
}): {
|
|
704
|
+
path: string;
|
|
705
|
+
offset: number;
|
|
706
|
+
lines: {
|
|
707
|
+
number: number;
|
|
708
|
+
text: string;
|
|
709
|
+
}[];
|
|
710
|
+
totalLines: number;
|
|
711
|
+
}
|
|
712
|
+
tools.write({
|
|
713
|
+
/** Path of the file to write, resolved against the tool root. */
|
|
714
|
+
file_path: string;
|
|
715
|
+
/** Full UTF-8 contents of the file. */
|
|
716
|
+
content: string;
|
|
717
|
+
}): void
|
|
718
|
+
tools.edit({
|
|
719
|
+
/** Path of the file to edit, resolved against the tool root. */
|
|
720
|
+
file_path: string;
|
|
721
|
+
/** Literal text to look for. It has to be unique unless \`replace_all\` is set. */
|
|
722
|
+
old_string: string;
|
|
723
|
+
/** Replacement text. An empty string deletes the match. */
|
|
724
|
+
new_string: string;
|
|
725
|
+
/** Replace every occurrence instead of requiring a unique match. */
|
|
726
|
+
replace_all?: boolean;
|
|
727
|
+
}): void
|
|
728
|
+
tools.pwsh({
|
|
729
|
+
/** PowerShell command or script to run. */
|
|
730
|
+
command: string;
|
|
731
|
+
/** Working directory, resolved against the tool root. Defaults to the tool root. */
|
|
732
|
+
cwd?: string;
|
|
733
|
+
/** Timeout in milliseconds. Defaults to 120000. */
|
|
734
|
+
timeoutMs?: number;
|
|
735
|
+
}): {
|
|
736
|
+
stdout: string;
|
|
737
|
+
stderr: string;
|
|
738
|
+
exitCode: number | null;
|
|
739
|
+
}
|
|
740
|
+
\`\`\`
|
|
741
|
+
>>>>>>> MEATPROXY SYSTEM
|
|
742
|
+
|
|
743
|
+
`;
|
|
309
744
|
|
|
310
745
|
// src/commands/init.ts
|
|
311
746
|
function registerInit(program2) {
|
|
@@ -324,7 +759,16 @@ function registerInit(program2) {
|
|
|
324
759
|
|
|
325
760
|
// src/index.ts
|
|
326
761
|
var program = new Command();
|
|
327
|
-
program.name("
|
|
762
|
+
program.name("mtp").description("MeatProxy CLI").version("1.1.0").addHelpText("after", `
|
|
763
|
+
Usage:
|
|
764
|
+
1. mtp init | copy the agent prompt to the clipboard
|
|
765
|
+
2. | paste it into a web chatbot and describe what you want
|
|
766
|
+
3. | copy the chatbot's reply
|
|
767
|
+
4. mtp | run the snippet locally; its output is copied back to the clipboard
|
|
768
|
+
5. | paste the output back to the chatbot and repeat
|
|
769
|
+
|
|
770
|
+
Snippets run with the same access as your terminal; only run replies you trust.
|
|
771
|
+
`).action(run);
|
|
328
772
|
registerInit(program);
|
|
329
773
|
await program.parseAsync();
|
|
330
774
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core.ts","../src/console.ts","../src/tools.ts","../src/commands/init.ts","../src/prompt.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { run } from \"./core.js\";\nimport { registerInit } from \"./commands/init.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"mp\")\n .description(\"MeatProxy CLI\")\n .version(\"1.0.0\")\n .action(run);\n\nregisterInit(program);\n\nawait program.parseAsync();\n","import clipboard from \"clipboardy\";\r\nimport { OutputConsole } from \"./console.js\";\r\nimport { Tools } from \"./tools.js\";\r\n\r\nexport const HEAD_MARKER = \"<<<<<<< MEATPROXY\";\r\nexport const TAIL_MARKER = \">>>>>>> MEATPROXY\";\r\n\r\nconst INVALID_MARKER_MESSAGE = `Error: Invalid format.\r\nExpected format:\r\n${HEAD_MARKER}\r\n...code...\r\n${TAIL_MARKER}`\r\n\r\n/**\r\n * Extract the code between the MEATPROXY markers.\r\n *\r\n * Exactly one head marker and one tail marker are expected, with the tail after\r\n * the head.\r\n */\r\nexport function extractCodeBlock(text: string): string {\r\n const lines = text.split(/\\r?\\n/);\r\n const heads: number[] = [];\r\n const tails: number[] = [];\r\n\r\n lines.forEach((line, index) => {\r\n const trimmed = line.trim();\r\n if (trimmed === HEAD_MARKER) {\r\n heads.push(index);\r\n } else if (trimmed === TAIL_MARKER) {\r\n tails.push(index);\r\n }\r\n });\r\n\r\n if (heads.length === 0 && tails.length === 0) {\r\n console.log(INVALID_MARKER_MESSAGE);\r\n return \"\";\r\n }\r\n\r\n if (heads.length > 1 || tails.length > 1) {\r\n console.log(\"Error: Found more than one pair of MEATPROXY markers.\");\r\n return \"\";\r\n }\r\n\r\n const head = heads[0];\r\n const tail = tails[0];\r\n\r\n if (head === undefined) {\r\n console.log(INVALID_MARKER_MESSAGE);\r\n return \"\";\r\n }\r\n\r\n if (tail === undefined) {\r\n console.log(INVALID_MARKER_MESSAGE);\r\n return \"\";\r\n }\r\n\r\n if (tail < head) {\r\n console.log(INVALID_MARKER_MESSAGE);\r\n return \"\";\r\n }\r\n\r\n const code = lines\r\n .slice(head + 1, tail)\r\n .join(\"\\n\")\r\n .trim();\r\n\r\n if (code.length === 0) {\r\n console.log(\"No code. Nothing happens.\");\r\n return \"\";\r\n }\r\n\r\n return code;\r\n}\r\n\r\n\r\n// Function bodies are normally sync; an async body lets the clipboard code use\r\n// top-level await (for example `await tools.pwsh(...)`).\r\nconst AsyncFunction = Object.getPrototypeOf(async function () {}).constructor as new (\r\n ...args: string[]\r\n) => (...params: unknown[]) => Promise<unknown>;\r\n\r\n/**\r\n * Read the clipboard, pull the MEATPROXY code block out of it, run that code and\r\n * copy whatever it printed back to the clipboard for the next round.\r\n */\r\nexport async function run(): Promise<void> {\r\n let text: string;\r\n\r\n try {\r\n text = await clipboard.read();\r\n } catch (error) {\r\n console.log(\"could not read the clipboard\");\r\n return;\r\n }\r\n\r\n const code = extractCodeBlock(text);\r\n if (code.length === 0) {\r\n return;\r\n }\r\n\r\n const captured = new OutputConsole();\r\n\r\n try {\r\n const func = new AsyncFunction(\"tools\", \"console\", code);\r\n await func(new Tools(), captured);\r\n } catch (error) {\r\n captured.log(error);\r\n }\r\n\r\n if (captured.lines.length === 0) {\r\n console.log(\"No output to copy.\");\r\n return;\r\n }\r\n\r\n try {\r\n await clipboard.write(captured.lines.join(\"\\n\"));\r\n } catch (error) {\r\n console.log(\"could not write to the clipboard\");\r\n return;\r\n }\r\n\r\n console.log(\"Output copied to the clipboard. Paste it back to the agent.\");\r\n}\r\n","import { format } from \"node:util\";\n\n/**\n * Console handed to the clipboard code as the `console` parameter.\n *\n * Every call is forwarded to the real console and also recorded as formatted\n * text, so the output can be copied to the clipboard afterwards. Methods are\n * bound, which keeps `const { log }console` working.\n */\nexport class OutputConsole {\n /** Everything written so far, one entry per console call. */\n readonly lines: string[] = [];\n\n log(...args: unknown[]): void {\n console.log(...args);\n this.lines.push(format(...args));\n };\n\n info(...args: unknown[]): void {\n console.info(...args);\n this.lines.push(format(...args));\n };\n\n warn(...args: unknown[]): void {\n console.warn(...args);\n this.lines.push(format(...args));\n };\n\n error(...args: unknown[]): void {\n console.error(...args);\n this.lines.push(format(...args));\n };\n\n debug(...args: unknown[]): void {\n console.debug(...args);\n this.lines.push(format(...args));\n };\n}\n","import { spawnSync } from \"node:child_process\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"path\";\n\nexport interface ReadRequest {\n /** Path of the file to read, resolved against the tool root. */\n file_path: string;\n}\n\nexport interface WriteRequest {\n /** Path of the file to write, resolved against the tool root. */\n file_path: string;\n /** Full UTF-8 contents of the file. */\n content: string;\n}\n\nexport interface EditRequest {\n /** Path of the file to edit, resolved against the tool root. */\n file_path: string;\n /** Literal text to look for. It has to be unique unless `replace_all` is set. */\n old_string: string;\n /** Replacement text. An empty string deletes the match. */\n new_string: string;\n /** Replace every occurrence instead of requiring a unique match. */\n replace_all?: boolean;\n}\n\nexport interface PwshRequest {\n /** PowerShell command or script to run. */\n command: string;\n /** Working directory, resolved against the tool root. Defaults to the tool root. */\n cwd?: string;\n /** Timeout in milliseconds. Defaults to 120000. */\n timeoutMs?: number;\n}\n\nexport interface PwshResult {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n}\n\nconst DEFAULT_PWSH_TIMEOUT = 120_000;\nconst MAX_PWSH_OUTPUT_BYTES = 16 * 1024 * 1024;\n\n// PowerShell encodes redirected output with the console code page by default, which\n// mangles non-ASCII text, and it reports progress records on stderr. Switch the\n// output to UTF-8 without a BOM and silence progress before running anything.\nconst PWSH_PRELUDE = [\n \"$ProgressPreference = 'SilentlyContinue'\",\n \"try { [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) } catch {}\",\n].join(\"\\n\");\n\n// Windows PowerShell serializes error records as CLIXML when stderr is redirected.\nconst CLIXML_ERROR_PATTERN = /<S S=\"Error\">([\\s\\S]*?)<\\/S>/g;\n\nfunction unescapeClixml(text: string): string {\n return text\n .replace(/_x([0-9a-f]{4})_/gi, (_match, code: string) => String.fromCharCode(Number.parseInt(code, 16)))\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/&/g, '&');\n}\n\nfunction decodePowerShellStderr(stderr: string): string {\n if (!stderr.startsWith(\"#< CLIXML\")) {\n return stderr;\n }\n\n const messages = Array.from(stderr.matchAll(CLIXML_ERROR_PATTERN), (match) =>\n unescapeClixml(match[1] ?? \"\"),\n );\n\n return messages.length > 0 ? messages.join(\"\") : stderr;\n}\n\nfunction shellCandidates(): string[] {\n // PowerShell 7 first (it supports && and friends), Windows PowerShell as fallback.\n return process.platform === \"win32\" ? [\"pwsh\", \"powershell.exe\"] : [\"pwsh\"];\n}\n\nfunction describeError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction requirePath(value: unknown, method: string): string {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new TypeError(`${method} failed: file_path must be a non-empty string.`);\n }\n\n return value;\n}\n\nfunction requireText(value: unknown, method: string, field: string): string {\n if (typeof value !== \"string\") {\n throw new TypeError(`${method} failed: ${field} must be a string.`);\n }\n\n return value;\n}\n\nfunction countOccurrences(text: string, search: string): number {\n let count = 0;\n let index = text.indexOf(search);\n\n while (index !== -1) {\n count += 1;\n index = text.indexOf(search, index + search.length);\n }\n\n return count;\n}\n\nfunction replaceFirst(text: string, search: string, replacement: string): string {\n const index = text.indexOf(search);\n if (index === -1) {\n return text;\n }\n\n return text.slice(0, index) + replacement + text.slice(index + search.length);\n}\n\n/**\n * Editing tools handed to the clipboard code, so agent can use this object to\n * do edit.\n *\n * The code is compiled with a single parameter named `tools`, so it can call\n * `tools.read({ file_path })`, `tools.write({ file_path, content })`,\n * `tools.edit({ file_path, old_string, new_string })` and `tools.pwsh({ command })`.\n * All paths are resolved against `root` (the current working directory by default).\n */\nexport class Tools {\n private readonly root: string;\n\n constructor(root: string = process.cwd()) {\n this.root = path.resolve(root);\n }\n\n /** Read a UTF-8 text file and return its full contents. */\n read(request: ReadRequest): string {\n const filePath = requirePath(request?.file_path, \"read\");\n const target = this.resolve(filePath);\n\n try {\n return readFileSync(target, \"utf8\");\n } catch (error) {\n throw new Error(`Read failed for ${filePath}: ${describeError(error)}`);\n }\n }\n\n /** Create or fully overwrite a UTF-8 text file, creating parent directories. */\n write(request: WriteRequest): void {\n const filePath = requirePath(request?.file_path, \"write\");\n const content = requireText(request?.content, \"write\", \"content\");\n const target = this.resolve(filePath);\n\n try {\n mkdirSync(path.dirname(target), { recursive: true });\n writeFileSync(target, content, \"utf8\");\n } catch (error) {\n throw new Error(`Write failed for ${filePath}: ${describeError(error)}`);\n }\n }\n\n /**\n * Replace literal text in a file.\n *\n * `old_string` has to be unique unless `replace_all` is true, and an empty\n * `old_string` is rejected because it would match everywhere.\n */\n edit(request: EditRequest): void {\n const filePath = requirePath(request?.file_path, \"edit\");\n const oldText = requireText(request?.old_string, \"edit\", \"old_string\");\n const newText = requireText(request?.new_string, \"edit\", \"new_string\");\n\n if (oldText.length === 0) {\n throw new Error(\"Edit failed: old_string must not be empty.\");\n }\n\n const original = this.read({ file_path: filePath });\n const occurrences = countOccurrences(original, oldText);\n\n if (occurrences === 0) {\n throw new Error(`Edit failed for ${filePath}: old_string was not found.`);\n }\n\n if (occurrences > 1 && !request.replace_all) {\n throw new Error(\n `Edit failed for ${filePath}: old_string appears ${occurrences} times. ` +\n \"Make it unique or pass replace_all = true.\",\n );\n }\n\n const updated = request.replace_all\n ? original.split(oldText).join(newText)\n : replaceFirst(original, oldText, newText);\n\n this.write({ file_path: filePath, content: updated });\n }\n\n /**\n * Run a PowerShell command synchronously and return its output.\n *\n * A non-zero `exitCode` is returned instead of thrown, so the code can inspect\n * `stdout` and `stderr` itself. The command runs with `pwsh` when it is\n * available and falls back to `powershell.exe` on Windows.\n */\n pwsh(request: PwshRequest): PwshResult {\n const command = requireText(request?.command, \"pwsh\", \"command\");\n const timeout = request.timeoutMs ?? DEFAULT_PWSH_TIMEOUT;\n const cwd = request.cwd ? this.resolve(request.cwd) : this.root;\n const encoded = Buffer.from(`${PWSH_PRELUDE}\\n${command}`, \"utf16le\").toString(\"base64\");\n\n for (const shell of shellCandidates()) {\n const result = spawnSync(\n shell,\n [\"-NoProfile\", \"-NonInteractive\", \"-EncodedCommand\", encoded],\n {\n cwd,\n encoding: \"utf8\",\n maxBuffer: MAX_PWSH_OUTPUT_BYTES,\n timeout,\n windowsHide: true,\n },\n );\n\n if (result.error) {\n const code = (result.error as NodeJS.ErrnoException).code;\n\n if (code === \"ENOENT\") {\n continue;\n }\n\n if (code === \"ETIMEDOUT\") {\n throw new Error(`Pwsh timed out after ${timeout} ms.`);\n }\n\n throw new Error(`Pwsh failed: ${result.error.message}`);\n }\n\n return {\n stdout: result.stdout,\n stderr: decodePowerShellStderr(result.stderr),\n exitCode: result.status,\n };\n }\n\n throw new Error(\"Pwsh failed: PowerShell is not available on this machine.\");\n }\n\n private resolve(filePath: string): string {\n return path.resolve(this.root, filePath);\n }\n}\n","import clipboard from \"clipboardy\";\nimport type { Command } from \"commander\";\nimport { INIT_PROMPT } from \"../prompt.js\";\n\nexport function registerInit(program: Command) {\n program\n .command(\"init\")\n .description(\"Copy the agent prompt to the clipboard\")\n .action(async () => {\n try {\n await clipboard.write(INIT_PROMPT);\n } catch (error) {\n console.log(\"could not write to the clipboard\");\n return;\n }\n\n console.log(\"Prompt copied to the clipboard.\");\n console.log(\"Paste it into a web chatbot, add what you want it to do, and send.\");\n console.log(\"Then copy the chatbot's reply back here and run `mtp`.\");\n });\n}\n","/**\n * Text copied to the clipboard by `mtp init`.\n *\n * It is pasted into a web chatbot so the agent knows how to format the code that\n * this CLI will read back from the clipboard and execute.\n */\nexport const INIT_PROMPT = [\n \"You help through MeatProxy: the user pastes your replies into a terminal, which runs the JavaScript you send locally.\",\n \"\",\n \"To inspect or edit files, reply with one snippet shaped like this:\",\n \"\",\n \"```js\",\n \"<<<<<<< MEATPROXY\",\n \"// your JavaScript here\",\n \">>>>>>> MEATPROXY\",\n \"```\",\n \"\",\n \"Keep the marker lines exactly as shown, each on its own line, inside the code block. The terminal reads everything between them and ignores the rest of your reply.\",\n \"\",\n \"Guidelines:\",\n \"- Use relative paths; the working directory is the project root.\",\n \"- Do one step per snippet: the user runs it, then pastes the output back to you.\",\n \"- Use console.log for results and progress; everything printed is copied to the clipboard automatically.\",\n \"- Keep explanations outside the code block.\",\n \"\",\n \"The snippet runs in Node with a `tools` object:\",\n \"tools.read({ file_path }) -> string\",\n \"tools.write({ file_path, content })\",\n \"tools.edit({ file_path, old_string, new_string, replace_all? })\",\n \"tools.pwsh({ command, cwd?, timeoutMs? }) -> { stdout, stderr, exitCode }\",\n \"\",\n \"\"\n].join(\"\\n\");\n"],"mappings":"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","names":["clipboard","program","clipboard"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/runner/core.ts","../src/runner/console.ts","../src/tools/index.ts","../src/tools/types.ts","../src/tools/tool-cards.ts","../src/tools/card-diff.ts","../src/commands/init.ts","../src/constants/prompt.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport { Command } from \"commander\";\r\nimport { run } from \"./runner/core.js\";\r\nimport { registerInit } from \"./commands/init.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"mtp\")\r\n .description(\"MeatProxy CLI\")\r\n .version(__VERSION__)\r\n .addHelpText(\"after\", `\r\nUsage:\r\n 1. mtp init | copy the agent prompt to the clipboard\r\n 2. | paste it into a web chatbot and describe what you want\r\n 3. | copy the chatbot's reply\r\n 4. mtp | run the snippet locally; its output is copied back to the clipboard\r\n 5. | paste the output back to the chatbot and repeat\r\n\r\nSnippets run with the same access as your terminal; only run replies you trust.\r\n`)\r\n .action(run);\r\n\r\nregisterInit(program);\r\n\r\nawait program.parseAsync();\r\n","import clipboard from \"clipboardy\";\r\nimport { Console } from \"./console.js\";\r\nimport { Tools } from \"../tools/index.js\";\r\nimport chalk from \"chalk\";\r\n\r\nexport const HEAD_MARKER = \"<<<<<<< MEATPROXY\";\r\nexport const TAIL_MARKER = \">>>>>>> MEATPROXY\";\r\n\r\nexport const OUTPUT_HEAD_MARKER = \"<<<<<<< MEATPROXY OUTPUT\\n\";\r\nexport const OUTPUT_TAIL_MARKER = \"\\n>>>>>>> MEATPROXY OUTPUT\\n\\n\";\r\n\r\nconst INVALID_MARKER_MESSAGE = `Error: Invalid format.\r\nExpected format:\r\n${HEAD_MARKER}\r\n...code...\r\n${TAIL_MARKER}`\r\n\r\nexport type ExtractResult =\r\n | { status: \"ok\"; code: string }\r\n | { status: \"empty\" }\r\n | { status: \"error\"; message: string };\r\n\r\n/**\r\n * Extract the code between the MEATPROXY markers.\r\n *\r\n * Exactly one head marker and one tail marker are expected, with the tail after\r\n * the head.\r\n */\r\nexport function extractCodeBlock(text: string): ExtractResult {\r\n const lines = text.split(/\\r?\\n/);\r\n const heads: number[] = [];\r\n const tails: number[] = [];\r\n\r\n lines.forEach((line, index) => {\r\n const trimmed = line.trim();\r\n if (trimmed === HEAD_MARKER) {\r\n heads.push(index);\r\n } else if (trimmed === TAIL_MARKER) {\r\n tails.push(index);\r\n }\r\n });\r\n\r\n if (heads.length === 0 && tails.length === 0) {\r\n return { status: \"error\", message: INVALID_MARKER_MESSAGE };\r\n }\r\n\r\n if (heads.length > 1 || tails.length > 1) {\r\n return { status: \"error\", message: \"Error: Found more than one pair of MEATPROXY markers.\" };\r\n }\r\n\r\n const head = heads[0];\r\n const tail = tails[0];\r\n\r\n if (head === undefined || tail === undefined || tail < head) {\r\n return { status: \"error\", message: INVALID_MARKER_MESSAGE };\r\n }\r\n\r\n const code = lines\r\n .slice(head + 1, tail)\r\n .join(\"\\n\")\r\n .trim();\r\n\r\n if (code.length === 0) {\r\n return { status: \"empty\" };\r\n }\r\n\r\n return { status: \"ok\", code };\r\n}\r\n\r\n\r\n// Function bodies are normally sync; an async body lets the clipboard code use\r\n// top-level await (for example `await tools.pwsh(...)`).\r\nconst AsyncFunction = Object.getPrototypeOf(async function () {}).constructor as new (\r\n ...args: string[]\r\n) => (...params: unknown[]) => Promise<unknown>;\r\n\r\n/**\r\n * Read the clipboard, pull the MEATPROXY code block out of it, run that code and\r\n * copy whatever it printed back to the clipboard for the next round.\r\n */\r\nexport async function run(): Promise<void> {\r\n let text: string;\r\n\r\n try {\r\n text = await clipboard.read();\r\n } catch (error) {\r\n console.log(chalk.red(\"could not read the clipboard\"));\r\n return;\r\n }\r\n\r\n const vmConsole = new Console();\r\n\r\n const result = extractCodeBlock(text);\r\n if (result.status === \"empty\") {\r\n console.log(\"Nothing happens.\");\r\n return;\r\n }\r\n\r\n if (result.status === \"error\") {\r\n vmConsole.log(result.message);\r\n } else {\r\n try {\r\n const func = new AsyncFunction(\"tools\", \"console\", result.code);\r\n await func(new Tools(), vmConsole);\r\n } catch (error) {\r\n vmConsole.log(error);\r\n }\r\n }\r\n\r\n if (vmConsole.lines.length === 0) {\r\n console.log(\"No output to copy.\");\r\n return;\r\n }\r\n\r\n try {\r\n await clipboard.write(\r\n OUTPUT_HEAD_MARKER +\r\n vmConsole.lines.join(\"\\n\") +\r\n OUTPUT_TAIL_MARKER\r\n );\r\n } catch (error) {\r\n console.log(chalk.red(\"could not write to the clipboard\"));\r\n return;\r\n }\r\n\r\n console.log(\"Output copied to the clipboard. Paste it back to the agent.\");\r\n}\r\n","import { format } from \"util\";\n\n/** Console handed to the clipboard code as the `console` parameter. */\nexport class Console {\n /** Everything written so far, one entry per console call. */\n readonly lines: string[] = [];\n\n log(...args: unknown[]): void {\n this.lines.push(format(...args));\n };\n}\n","import { spawnSync } from \"child_process\";\r\nimport { mkdirSync, readFileSync, writeFileSync } from \"fs\";\r\nimport path from \"path\";\r\nimport { isEditParams, isPwshParams, isReadParams, isWriteParams, type PwshResult, type ReadResult } from \"./types.js\";\r\nimport { ToolCards } from \"./tool-cards.js\";\r\nimport { toDiffLines } from \"./card-diff.js\";\r\n\r\nconst DEFAULT_PWSH_TIMEOUT = 120_000;\r\nconst DEFAULT_READ_LIMIT = 2000;\r\nconst MAX_PWSH_OUTPUT_BYTES = 16 * 1024 * 1024;\r\n\r\n// PowerShell encodes redirected output with the console code page by default, which\r\n// mangles non-ASCII text, and it reports progress records on stderr. Switch the\r\n// output to UTF-8 without a BOM and silence progress before running anything.\r\nconst PWSH_PRELUDE = [\r\n \"$ProgressPreference = 'SilentlyContinue'\",\r\n \"try { [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) } catch {}\",\r\n].join(\"\\n\");\r\n\r\n// Windows PowerShell serializes error records as CLIXML when stderr is redirected.\r\nconst CLIXML_ERROR_PATTERN = /<S S=\"Error\">([\\s\\S]*?)<\\/S>/g;\r\n\r\nfunction unescapeClixml(text: string): string {\r\n return text\r\n .replace(/_x([0-9a-f]{4})_/gi, (_match, code: string) => String.fromCharCode(Number.parseInt(code, 16)))\r\n .replace(/</g, \"<\")\r\n .replace(/>/g, \">\")\r\n .replace(/"/g, \"\\\"\")\r\n .replace(/'/g, \"'\")\r\n .replace(/&/g, \"&\");\r\n}\r\n\r\nfunction decodePowerShellStderr(stderr: string): string {\r\n if (!stderr.startsWith(\"#< CLIXML\")) {\r\n return stderr;\r\n }\r\n\r\n const messages = Array.from(stderr.matchAll(CLIXML_ERROR_PATTERN), (match) =>\r\n unescapeClixml(match[1] ?? \"\"),\r\n );\r\n\r\n return messages.length > 0 ? messages.join(\"\") : stderr;\r\n}\r\n\r\nfunction shellCandidates(): string[] {\r\n // PowerShell 7 first (it supports && and friends), Windows PowerShell as fallback.\r\n return process.platform === \"win32\" ? [\"pwsh\", \"powershell.exe\"] : [\"pwsh\"];\r\n}\r\n\r\nfunction describeError(error: unknown): string {\r\n return error instanceof Error ? error.message : String(error);\r\n}\r\n\r\n/**\r\n * Editing tools handed to the clipboard code, so agent can use this object to\r\n * do edit.\r\n */\r\nexport class Tools {\r\n private readonly root: string;\r\n\r\n constructor(root: string = process.cwd()) {\r\n this.root = path.resolve(root);\r\n }\r\n\r\n /** Read a UTF-8 text file and return line-numbered content. */\r\n read(params: unknown): ReadResult {\r\n if (!isReadParams(params)) {\r\n ToolCards.read({ filePath: \"\", ok: false });\r\n throw new Error(\"Read failed: invalid parameters.\");\r\n }\r\n const filePath = params.file_path;\r\n const target = this.resolve(filePath);\r\n let content: string;\r\n try {\r\n content = readFileSync(target, \"utf8\");\r\n } catch (error) {\r\n ToolCards.read({ filePath, ok: false });\r\n throw new Error(`Read failed for ${filePath}: ${describeError(error)}`);\r\n }\r\n const normalized = content.replace(/\\r\\n/g, \"\\n\");\r\n const allLines = normalized.split(\"\\n\");\r\n // A trailing newline produces one empty element; drop it so totalLines\r\n // and the returned lines match what an editor shows.\r\n if (allLines.length > 0 && allLines[allLines.length - 1] === \"\") {\r\n allLines.pop();\r\n }\r\n // 0-based\r\n const offset = (params.offset ?? 1) - 1;\r\n const limit = params.limit ?? DEFAULT_READ_LIMIT;\r\n ToolCards.read({ filePath, ok: true });\r\n return {\r\n path: filePath,\r\n offset: offset + 1, // 1-based\r\n lines: allLines.slice(offset, offset + limit).map((text, index) => ({\r\n number: offset + index + 1,\r\n text,\r\n })),\r\n totalLines: allLines.length,\r\n };\r\n }\r\n\r\n /** Create or fully overwrite a UTF-8 text file, creating parent directories. */\r\n write(params: unknown): void {\r\n if (!isWriteParams(params)) {\r\n ToolCards.write({ filePath: \"\", content: \"\", ok: false });\r\n throw new Error(\"Write failed: invalid parameters.\");\r\n }\r\n const filePath = params.file_path;\r\n const target = this.resolve(filePath);\r\n\r\n try {\r\n mkdirSync(path.dirname(target), { recursive: true });\r\n writeFileSync(target, params.content, \"utf8\");\r\n } catch (error) {\r\n ToolCards.write({ filePath, content: params.content, ok: false });\r\n throw new Error(`Write failed for ${filePath}: ${describeError(error)}`);\r\n }\r\n ToolCards.write({ filePath, content: params.content, ok: true });\r\n }\r\n\r\n /**\r\n * Replace literal text in a file.\r\n *\r\n * `old_string` has to be unique unless `replace_all` is true, and an empty\r\n * `old_string` is rejected because it would match everywhere.\r\n */\r\n edit(params: unknown): void {\r\n if (!isEditParams(params)) {\r\n ToolCards.edit({ filePath: \"\", replaceAll: false, ok: false });\r\n throw new Error(\"Edit failed: invalid parameters.\");\r\n }\r\n\r\n const filePath = params.file_path;\r\n const oldText = params.old_string;\r\n const newText = params.new_string;\r\n const replaceAll = params.replace_all === true;\r\n\r\n if (oldText.length === 0) {\r\n ToolCards.edit({ filePath, replaceAll, ok: false });\r\n throw new Error(\"Edit failed: old_string must not be empty.\");\r\n }\r\n\r\n const target = this.resolve(filePath);\r\n let original: string;\r\n try {\r\n original = readFileSync(target, \"utf8\");\r\n } catch (error) {\r\n ToolCards.edit({ filePath, replaceAll, ok: false });\r\n throw new Error(`Edit failed for ${filePath}: ${describeError(error)}`);\r\n }\r\n\r\n // Work in LF internally and restore the file's own EOL on write, so\r\n // matching does not depend on how the caller spelled newlines.\r\n const eol = original.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\r\n const lf = (text: string) => text.replace(/\\r\\n/g, \"\\n\");\r\n\r\n const lfOriginal = lf(original);\r\n const lfOldText = lf(oldText);\r\n\r\n const firstIndex = lfOriginal.indexOf(lfOldText);\r\n if (firstIndex === -1) {\r\n // Old text not found\r\n ToolCards.edit({ filePath, replaceAll, ok: false });\r\n throw new Error(`Edit failed for ${filePath}: old_string was not found.`);\r\n }\r\n\r\n const secondIndex = lfOriginal.indexOf(lfOldText, firstIndex + lfOldText.length);\r\n if (secondIndex !== -1 && !replaceAll) {\r\n // No replaceAll but find more than one occurrances.\r\n ToolCards.edit({ filePath, replaceAll, ok: false });\r\n throw new Error(\r\n `Edit failed for ${filePath}: old_string appears multiple times. `\r\n + \"Make it unique or pass replace_all = true.\"\r\n );\r\n }\r\n\r\n // Use function as the second parameter, or parameter like \"$$\" will be\r\n // interpreted to have special meaning:\r\n // \"123\".replaceAll(\"2\",\"$$\") will be \"1$3\"\r\n const updated = lfOriginal\r\n .replaceAll(lfOldText, () => lf(newText))\r\n .replace(/\\n/g, eol);\r\n\r\n try {\r\n writeFileSync(target, updated, \"utf8\");\r\n } catch (error) {\r\n ToolCards.edit({ filePath, replaceAll, ok: false });\r\n throw new Error(`Edit failed for ${filePath}: ${describeError(error)}`);\r\n }\r\n\r\n const diff = toDiffLines(original, updated);\r\n\r\n ToolCards.edit({ filePath, replaceAll, ok: true, diff });\r\n }\r\n\r\n /**\r\n * Run a PowerShell command synchronously and return its output.\r\n */\r\n pwsh(params: unknown): PwshResult {\r\n if (!isPwshParams(params)) {\r\n ToolCards.pwsh({ command: \"\", exitCode: null, ok: false });\r\n throw new Error(\"Pwsh failed: invalid parameters.\");\r\n }\r\n\r\n const command = params.command;\r\n const timeout = params.timeoutMs ?? DEFAULT_PWSH_TIMEOUT;\r\n const cwd = params.cwd ? this.resolve(params.cwd) : this.root;\r\n const encoded = Buffer.from(PWSH_PRELUDE + \"\\n\" + command, \"utf16le\").toString(\"base64\");\r\n\r\n for (const shell of shellCandidates()) {\r\n const result = spawnSync(\r\n shell,\r\n [\"-NoProfile\", \"-NonInteractive\", \"-EncodedCommand\", encoded],\r\n {\r\n cwd,\r\n encoding: \"utf8\",\r\n maxBuffer: MAX_PWSH_OUTPUT_BYTES,\r\n timeout,\r\n windowsHide: true,\r\n },\r\n );\r\n\r\n if (result.error) {\r\n const code = (result.error as NodeJS.ErrnoException).code;\r\n\r\n if (code === \"ENOENT\") continue;\r\n ToolCards.pwsh({ command, exitCode: null, ok: false });\r\n if (code === \"ETIMEDOUT\") {\r\n throw new Error(\"Pwsh timed out after \" + timeout + \" ms.\");\r\n }\r\n\r\n throw new Error(\"Pwsh failed: \" + result.error.message);\r\n }\r\n\r\n ToolCards.pwsh({ command, exitCode: result.status, ok: true });\r\n return {\r\n stdout: result.stdout,\r\n stderr: decodePowerShellStderr(result.stderr),\r\n exitCode: result.status,\r\n };\r\n }\r\n\r\n ToolCards.pwsh({ command, exitCode: null, ok: false });\r\n throw new Error(\"Pwsh failed: PowerShell is not available on this machine.\");\r\n }\r\n\r\n private resolve(filePath: string): string {\r\n return path.resolve(this.root, filePath);\r\n }\r\n}\r\n","/** Read a UTF-8 text file and return line-numbered content. */\r\nexport interface ReadParams {\r\n /** Path of the file to read, resolved against current working directory. */\r\n file_path: string;\r\n /** 1-based first line to return. Defaults to 1. */\r\n offset?: number;\r\n /** Maximum number of lines to return. Defaults to 2000. */\r\n limit?: number;\r\n}\r\n\r\nexport interface ReadResult {\r\n path: string;\r\n /** 1-based line number of the first returned line. */\r\n offset: number;\r\n lines: {\r\n number: number;\r\n text: string;\r\n }[];\r\n totalLines: number;\r\n}\r\n\r\nexport interface WriteParams {\r\n /** Path of the file to write, resolved against the tool root. */\r\n file_path: string;\r\n /** Full UTF-8 contents of the file. */\r\n content: string;\r\n}\r\n\r\nexport interface EditParams {\r\n /** Path of the file to edit, resolved against the tool root. */\r\n file_path: string;\r\n /** Literal text to look for. It has to be unique unless `replace_all` is set. */\r\n old_string: string;\r\n /** Replacement text. An empty string deletes the match. */\r\n new_string: string;\r\n /** Replace every occurrence instead of requiring a unique match. */\r\n replace_all?: boolean;\r\n}\r\n\r\nexport interface PwshParams {\r\n /** PowerShell command or script to run. */\r\n command: string;\r\n /** Working directory, resolved against the tool root. Defaults to the tool root. */\r\n cwd?: string;\r\n /** Timeout in milliseconds. Defaults to 120000. */\r\n timeoutMs?: number;\r\n}\r\n\r\nexport interface PwshResult {\r\n stdout: string;\r\n stderr: string;\r\n exitCode: number | null;\r\n}\r\n\r\ntype UnknownRecord = Record<string, unknown>;\r\n\r\nfunction isRecord(value: unknown): value is UnknownRecord {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\r\n}\r\n\r\nfunction isNonEmptyString(value: unknown): value is string {\r\n return typeof value === \"string\" && value.length > 0;\r\n}\r\n\r\nfunction isOptionalBoolean(value: unknown): value is boolean | undefined {\r\n return value === undefined || typeof value === \"boolean\";\r\n}\r\n\r\nfunction isOptionalString(value: unknown): value is string | undefined {\r\n return value === undefined || typeof value === \"string\";\r\n}\r\n\r\nfunction isOptionalPositiveInteger(value: unknown): value is number | undefined {\r\n return value === undefined\r\n || (typeof value === \"number\" && Number.isInteger(value) && value >= 1);\r\n}\r\n\r\nexport function isReadParams(value: unknown): value is ReadParams {\r\n return isRecord(value)\r\n && isNonEmptyString(value.file_path)\r\n && isOptionalPositiveInteger(value.offset)\r\n && isOptionalPositiveInteger(value.limit);\r\n}\r\n\r\nexport function isWriteParams(value: unknown): value is WriteParams {\r\n return isRecord(value)\r\n && isNonEmptyString(value.file_path)\r\n && typeof value.content === \"string\";\r\n}\r\n\r\nexport function isEditParams(value: unknown): value is EditParams {\r\n return isRecord(value)\r\n && isNonEmptyString(value.file_path)\r\n && typeof value.old_string === \"string\"\r\n && typeof value.new_string === \"string\"\r\n && isOptionalBoolean(value.replace_all);\r\n}\r\n\r\nexport function isPwshParams(value: unknown): value is PwshParams {\r\n return isRecord(value)\r\n && typeof value.command === \"string\"\r\n && isOptionalString(value.cwd)\r\n && isOptionalPositiveInteger(value.timeoutMs);\r\n}","import chalk from \"chalk\";\r\nimport terminalSize from \"terminal-size\";\r\nimport { groupDiffHunks, toPairs, type DiffLine, type DiffPair, type DiffRow } from \"./card-diff.js\";\r\nimport { type LinePart, type Style } from \"./card-types.js\";\r\n\r\nfunction width(): number {\r\n let cols = FALLBACK_WIDTH;\r\n try {\r\n const size = terminalSize();\r\n if (Number.isFinite(size.columns) && size.columns > 0) cols = size.columns;\r\n } catch {\r\n // ignore\r\n }\r\n if (cols <= 64) return cols;\r\n if (cols <= 74) return 64;\r\n return cols - 10;\r\n}\r\n\r\nfunction truncate(text: string, maxLen: number): string {\r\n if (text.length <= maxLen) return text;\r\n if (maxLen <= 0) return \"\";\r\n if (maxLen <= 3) return \".\".repeat(maxLen);\r\n return text.slice(0, maxLen - 3) + \"...\";\r\n}\r\n\r\nconst PAD = \" \";\r\nconst FALLBACK_WIDTH = 80;\r\nconst SPLIT_MIN_WIDTH = 80;\r\nconst INNER_WIDTH = width() - PAD.length * 2;\r\n\r\n// #fcebdf oklch(0.95 0.025 56)\r\n// #783f09 oklch(0.43 0.1 56)\r\n// #34251a oklch(0.28 0.03 56)\r\n// #f5f1ef oklch(0.96 0.005 56)\r\n// #005E19 oklch(0.42 0.13 146)\r\n// #94020d oklch(0.42 0.17 27)\r\n// #595101 oklch(0.43 0.09 103)\r\nconst S = {\r\n header: (t: string) => chalk.bgHex(\"#fcebdf\").hex(\"#783f09\")(t),\r\n headerContent: (t: string) => chalk.bgHex(\"#fcebdf\").hex(\"#34251a\")(t),\r\n body: (t: string) => chalk.bgHex(\"#f5f1ef\").hex(\"#34251a\")(t),\r\n ok: (t: string) => chalk.bgHex(\"#f5f1ef\").hex(\"#005E19\")(t),\r\n fail: (t: string) => chalk.bgHex(\"#f5f1ef\").hex(\"#94020d\")(t),\r\n warn: (t: string) => chalk.bgHex(\"#f5f1ef\").hex(\"#595101\")(t),\r\n del: (t: string) => chalk.bgHex(\"#f5f1ef\").hex(\"#94020d\")(t),\r\n add: (t: string) => chalk.bgHex(\"#f5f1ef\").hex(\"#005E19\")(t),\r\n} as const;\r\n\r\nexport interface ReadCardOptions {\r\n filePath: string;\r\n ok: boolean;\r\n}\r\n\r\nexport interface WriteCardOptions {\r\n filePath: string;\r\n content: string;\r\n ok: boolean;\r\n}\r\n\r\nexport interface EditCardOptions {\r\n filePath: string;\r\n replaceAll: boolean;\r\n ok: boolean;\r\n /**\r\n * Line-level diff of the whole file. Present on success; the card renders\r\n * nothing but the header and status when absent.\r\n */\r\n diff?: DiffLine[] | undefined;\r\n}\r\n\r\nexport interface PwshCardOptions {\r\n command: string;\r\n exitCode: number | null;\r\n ok: boolean;\r\n}\r\n\r\nexport class ToolCards {\r\n private constructor() { }\r\n\r\n /** Total width of tools cards. */\r\n private static width(): number {\r\n let cols = FALLBACK_WIDTH;\r\n try {\r\n const size = terminalSize();\r\n if (Number.isFinite(size.columns) && size.columns > 0) cols = size.columns;\r\n } catch {\r\n // ignore\r\n }\r\n if (cols <= 64) return cols;\r\n if (cols <= 74) return 64;\r\n return cols - 10;\r\n }\r\n\r\n /** Keep maxLines of text, every lines truncated by maxLen */\r\n private static blockLines(text: string, maxLines: number, maxLen: number): string[] {\r\n const parts = text.split(\"\\n\");\r\n const shown = parts.slice(0, maxLines);\r\n return shown.map((raw) => {\r\n return truncate(raw.replace(/\\r$/, \"\"), Math.max(0, maxLen));\r\n });\r\n }\r\n\r\n private static line(parts: LinePart[], padStyle: Style = S.body): void {\r\n let used = 0;\r\n let lastStyle = padStyle;\r\n const chunks: string[] = [];\r\n\r\n for (const part of parts) {\r\n if (used >= INNER_WIDTH) break;\r\n\r\n const room = INNER_WIDTH - used;\r\n const text = part.text.length > room\r\n ? part.text.slice(0, room - 3) + \"...\"\r\n : part.text;\r\n\r\n if (text.length === 0) continue;\r\n\r\n chunks.push(part.style(text));\r\n used += text.length;\r\n lastStyle = part.style;\r\n }\r\n\r\n let padding = \"\";\r\n if (used < INNER_WIDTH) {\r\n padding = lastStyle(\" \".repeat(INNER_WIDTH - used));\r\n }\r\n\r\n console.log(padStyle(PAD) + chunks.join(\"\") + padding + padStyle(PAD));\r\n }\r\n\r\n // Cards for different tools.\r\n\r\n static read(opts: ReadCardOptions): void {\r\n this.line([{\r\n text: \"read \",\r\n style: S.header,\r\n }, {\r\n text: opts.filePath,\r\n style: S.headerContent,\r\n }], S.header);\r\n if (opts.ok) {\r\n this.line([{ text: \"success\", style: S.ok }], S.body);\r\n } else {\r\n this.line([{ text: \"failed\", style: S.fail }], S.body);\r\n }\r\n console.log();\r\n }\r\n\r\n static write(opts: WriteCardOptions): void {\r\n const BLOCK_MAX_LINES = 2;\r\n this.line([{\r\n text: \"write \",\r\n style: S.header,\r\n }, {\r\n text: opts.filePath,\r\n style: S.headerContent,\r\n }], S.header);\r\n if (opts.content) {\r\n for (const text of ToolCards.blockLines(opts.content, BLOCK_MAX_LINES, INNER_WIDTH)) {\r\n this.line([{ text, style: S.body }], S.body);\r\n }\r\n }\r\n if (opts.ok) {\r\n this.line([{ text: \"success\", style: S.ok }], S.body);\r\n } else {\r\n this.line([{ text: \"failed\", style: S.fail }], S.body);\r\n }\r\n console.log();\r\n }\r\n\r\n static edit(opts: EditCardOptions): void {\r\n ToolCards.line([{\r\n text: \"edit \",\r\n style: S.header,\r\n }, {\r\n text: opts.filePath,\r\n style: S.headerContent,\r\n }], S.header);\r\n\r\n if (opts.ok && opts.diff && opts.diff.length > 0) {\r\n const MAX_HUNKS = 3;\r\n const MAX_ROWS = 10;\r\n const hunks = groupDiffHunks(opts.diff);\r\n const moreHunks = hunks.length - MAX_HUNKS;\r\n const shownHunks = moreHunks > 0 ? hunks.slice(0, MAX_HUNKS) : hunks;\r\n\r\n const renderSingle = (rows: DiffRow[]): void => {\r\n const capped = ToolCards.capRows(rows, MAX_ROWS);\r\n // Calculate the most characters line numbers can occupy.\r\n let gutter = 1;\r\n for (const row of capped) {\r\n if (row.kind === \"gap\") continue;\r\n gutter = Math.max(\r\n gutter,\r\n String(row.oldLine ?? row.newLine ?? 0).length\r\n );\r\n }\r\n const textMax = Math.max(4, INNER_WIDTH - 2 - gutter - 1);\r\n for (const row of capped) {\r\n if (row.kind === \"gap\") {\r\n ToolCards.line([{ text: row.text, style: S.body }], S.body);\r\n continue;\r\n }\r\n const [sign, ln, style] = (\r\n row.kind === \"add\" ? [\"+\", row.newLine, S.add] :\r\n row.kind === \"del\" ? [\"-\", row.oldLine, S.del] :\r\n [\" \", \"\", S.body]\r\n );\r\n const text = truncate(row.text.replace(/\\r$/, \"\"), textMax);\r\n ToolCards.line([{ text: sign + String(ln ?? \"\").padStart(gutter) + \" \" + text, style }], S.body);\r\n }\r\n };\r\n\r\n const renderSplit = (rows: DiffRow[]): void => {\r\n const capped = ToolCards.capPairs(toPairs(rows), MAX_ROWS);\r\n const SEP = \" \\u2502 \";\r\n const cellTotal = INNER_WIDTH - SEP.length;\r\n const leftW = Math.floor(cellTotal / 2);\r\n const rightW = cellTotal - leftW;\r\n\r\n let leftGutter = 1;\r\n let rightGutter = 1;\r\n for (const pair of capped) {\r\n if (pair.kind === \"gap\") continue;\r\n leftGutter = Math.max(leftGutter, String(pair.left?.oldLine ?? 0).length);\r\n rightGutter = Math.max(rightGutter, String(pair.right?.newLine ?? 0).length);\r\n }\r\n\r\n const leftMax = Math.max(4, leftW - 2 - leftGutter);\r\n const rightMax = Math.max(4, rightW - 2 - rightGutter);\r\n\r\n const cell = (\r\n line: DiffLine | undefined, side: \"old\" | \"new\",\r\n cellW: number, gutterW: number, maxLen: number\r\n ): LinePart => {\r\n if (!line) return { text: \"\\u2591\".repeat(cellW), style: S.body };\r\n const ln = side === \"old\" ? line.oldLine : line.newLine;\r\n const [sign, style] = (\r\n line.kind === \"add\" ? [\"+\", S.add] :\r\n line.kind === \"del\" ? [\"-\", S.del] :\r\n [\" \", S.body]\r\n );\r\n const text = truncate(line.text.replace(/\\r$/, \"\"), maxLen);\r\n return { text: (sign + String(ln ?? \"\").padStart(gutterW) + \" \" + text).padEnd(cellW), style };\r\n };\r\n\r\n for (const pair of capped) {\r\n if (pair.kind === \"gap\") {\r\n ToolCards.line([{ text: pair.text, style: S.body }], S.body);\r\n continue;\r\n }\r\n ToolCards.line([\r\n cell(pair.left, \"old\", leftW, leftGutter, leftMax),\r\n { text: SEP, style: S.body },\r\n cell(pair.right, \"new\", rightW, rightGutter, rightMax),\r\n ], S.body);\r\n }\r\n };\r\n\r\n shownHunks.forEach((hunk, index) => {\r\n if (index > 0) {\r\n const gapText = hunk.gap?.text ?? \"...\";\r\n ToolCards.line([{ text: gapText, style: S.body }], S.body);\r\n }\r\n if (INNER_WIDTH < SPLIT_MIN_WIDTH) {\r\n renderSingle(hunk.rows);\r\n } else {\r\n renderSplit(hunk.rows);\r\n }\r\n });\r\n\r\n if (moreHunks > 0) {\r\n ToolCards.line([{ text: \"... \" + moreHunks + \" more hunk\" + (moreHunks === 1 ? \"\" : \"s\"), style: S.body }], S.body);\r\n }\r\n }\r\n if (opts.ok) {\r\n ToolCards.line([{ text: \"success\", style: S.ok }], S.body);\r\n } else {\r\n ToolCards.line([{ text: \"failed\", style: S.fail }], S.body);\r\n }\r\n console.log();\r\n }\r\n\r\n static pwsh(opts: PwshCardOptions): void {\r\n ToolCards.line([{\r\n text: \"pwsh \",\r\n style: S.header,\r\n }, {\r\n text: opts.command.replace(/\\r?\\n/g, \" \"),\r\n style: S.headerContent,\r\n }], S.header);\r\n\r\n if (!opts.ok) {\r\n ToolCards.line([{ text: \"failed\", style: S.fail }], S.body);\r\n } else {\r\n if (opts.exitCode === 0) {\r\n ToolCards.line([{ text: \"success\", style: S.ok }], S.body);\r\n } else {\r\n ToolCards.line([{ text: \"exit \" + opts.exitCode, style: S.warn }], S.body);\r\n }\r\n }\r\n console.log();\r\n }\r\n\r\n private static capRows(rows: DiffRow[], maxRows: number): DiffRow[] {\r\n if (rows.length <= maxRows) return rows;\r\n const shown = rows.slice(0, maxRows - 1);\r\n const hidden = rows.length - shown.length;\r\n return [...shown, { kind: \"gap\", text: \"... \" + hidden + \" more line\" + (hidden === 1 ? \"\" : \"s\") }];\r\n }\r\n\r\n private static capPairs(pairs: DiffPair[], maxRows: number): DiffPair[] {\r\n if (pairs.length <= maxRows) return pairs;\r\n const shown = pairs.slice(0, maxRows - 1);\r\n const hidden = pairs.length - shown.length;\r\n return [...shown, { kind: \"gap\", text: \"... \" + hidden + \" more line\" + (hidden === 1 ? \"\" : \"s\") }];\r\n }\r\n}\r\n","import { diffLines } from \"diff\";\r\n\r\nexport type DiffLineKind = \"add\" | \"del\" | \"ctx\";\r\n\r\n/** One line of a line-level diff, numbered against the old / new file. */\r\nexport interface DiffLine {\r\n kind: DiffLineKind;\r\n /** 1-based line number in the old file; undefined for added lines. */\r\n oldLine?: number | undefined;\r\n /** 1-based line number in the new file; undefined for removed lines. */\r\n newLine?: number | undefined;\r\n text: string;\r\n}\r\n\r\n/** A diff row, or a placeholder for a run of lines that was collapsed away. */\r\nexport type DiffRow = DiffLine | { kind: \"gap\"; text: string };\r\n\r\n/** A split-view row: either a collapsed gap or a left/right pair. */\r\nexport type DiffPair =\r\n | { kind: \"gap\"; text: string }\r\n | { kind: \"pair\"; left?: DiffLine | undefined; right?: DiffLine | undefined };\r\n\r\n/** A contiguous block of changed lines, optionally preceded by a collapsed gap. */\r\nexport interface DiffHunk {\r\n gap: { kind: \"gap\"; text: string } | undefined;\r\n // Here we use DiffRow instead of DiffLine, because render layer might\r\n // insert row like { kind: \"gap\"; text: \"... N more lines\" } for changes\r\n // block that spread too many lines, or similar other situations.\r\n rows: DiffRow[];\r\n}\r\n\r\n/**\r\n * Split one jsdiff change value into its individual lines, dropping the\r\n * trailing empty string produced by a final newline.\r\n */\r\nfunction splitChangeValue(value: string): string[] {\r\n const parts = value.split(\"\\n\");\r\n if (parts.length > 0 && parts[parts.length - 1] === \"\") parts.pop();\r\n return parts;\r\n}\r\n\r\n/**\r\n * Calculate one numbered row per line diff so the tool card can render a real\r\n * line-by-line diff.\r\n */\r\nexport function toDiffLines(original: string, updated: string): DiffLine[] {\r\n const changes = diffLines(original, updated, { stripTrailingCr: true });\r\n const rows: DiffLine[] = [];\r\n let oldLine = 1;\r\n let newLine = 1;\r\n\r\n for (const change of changes) {\r\n for (const text of splitChangeValue(change.value)) {\r\n if (change.added) {\r\n rows.push({ kind: \"add\", newLine: newLine++, text });\r\n } else if (change.removed) {\r\n rows.push({ kind: \"del\", oldLine: oldLine++, text });\r\n } else {\r\n rows.push({ kind: \"ctx\", oldLine: oldLine++, newLine: newLine++, text });\r\n }\r\n }\r\n }\r\n\r\n return rows;\r\n}\r\n\r\n/**\r\n * Keep only lines within [-context, +context] range of a change line; collapse\r\n * the rest into gaps.\r\n * \r\n * After that, we will get something like:\r\n * ```\r\n * [\r\n * { kind: 'gap', text: '... 2 unchanged lines' },\r\n * { kind: 'ctx', oldLine: 3, newLine: 3, text: 'row 3' },\r\n * { kind: 'ctx', oldLine: 4, newLine: 4, text: 'row 4' },\r\n * { kind: 'del', oldLine: 5 , text: 'row 5' },\r\n * { kind: 'ctx', oldLine: 6, newLine: 5, text: 'row 6' },\r\n * { kind: 'ctx', oldLine: 7, newLine: 6, text: 'row 7' },\r\n * { kind: 'gap', text: '... 3 unchanged lines' }\r\n * ]\r\n * ```\r\n */\r\nfunction hunkRows(lines: DiffLine[], context: number): DiffRow[] {\r\n const keep = new Array<boolean>(lines.length).fill(false);\r\n for (let i = 0; i < lines.length; i++) {\r\n const current = lines[i];\r\n if (!current || current.kind === \"ctx\") continue;\r\n const from = Math.max(0, i - context);\r\n const to = Math.min(lines.length - 1, i + context);\r\n for (let j = from; j <= to; j++) keep[j] = true;\r\n }\r\n\r\n const rows: DiffRow[] = [];\r\n for (let i = 0; i < lines.length;) {\r\n if (keep[i]) {\r\n const kept = lines[i];\r\n if (kept) rows.push(kept);\r\n i++;\r\n continue;\r\n }\r\n // Encounter a don't keep line.\r\n let j = i;\r\n while (j < lines.length && !keep[j]) j++;\r\n const skipped = j - i;\r\n rows.push({\r\n kind: \"gap\",\r\n text: \"... \" + skipped + \" unchanged line\" + (skipped === 1 ? \"\" : \"s\"),\r\n });\r\n i = j;\r\n }\r\n return rows;\r\n}\r\n\r\n/**\r\n * Filter a line diff into hunks, dropping leading/trailing unchanged context.\r\n * Each hunk carries the gap that separated it from the previous one.\r\n */\r\nexport function groupDiffHunks(lines: DiffLine[], context = 2): DiffHunk[] {\r\n const all = hunkRows(lines, context);\r\n let start = 0;\r\n let end = all.length;\r\n // Throw away all gaps at the very beginning and at the very end.\r\n while (start < end && all[start]?.kind === \"gap\") start++;\r\n while (end > start && all[end - 1]?.kind === \"gap\") end--;\r\n const trimmed = all.slice(start, end);\r\n if (trimmed.length === 0) return [];\r\n\r\n const hunks: DiffHunk[] = [];\r\n let pendingGap: { kind: \"gap\"; text: string } | undefined;\r\n let cur: DiffRow[] = [];\r\n for (const row of trimmed) {\r\n if (row.kind === \"gap\") {\r\n if (cur.length > 0) {\r\n hunks.push({ gap: pendingGap, rows: cur });\r\n cur = [];\r\n }\r\n pendingGap = row;\r\n } else {\r\n cur.push(row);\r\n }\r\n }\r\n if (cur.length > 0) hunks.push({ gap: pendingGap, rows: cur });\r\n return hunks;\r\n}\r\n\r\n/** Group rows into left/right pairs; unchanged lines pair with themselves. */\r\nexport function toPairs(rows: DiffRow[]): DiffPair[] {\r\n const pairs: DiffPair[] = [];\r\n for (let i = 0; i < rows.length;) {\r\n const row = rows[i];\r\n if (!row) { i++; continue; }\r\n if (row.kind === \"gap\") { pairs.push({ kind: \"gap\", text: row.text }); i++; continue; }\r\n if (row.kind === \"ctx\") { pairs.push({ kind: \"pair\", left: row, right: row }); i++; continue; }\r\n\r\n const lefts: DiffLine[] = [];\r\n const rights: DiffLine[] = [];\r\n while (i < rows.length) {\r\n const next = rows[i];\r\n if (!next || next.kind === \"ctx\" || next.kind === \"gap\") break;\r\n if (next.kind === \"del\") lefts.push(next);\r\n else rights.push(next);\r\n i++;\r\n }\r\n const count = Math.max(lefts.length, rights.length);\r\n for (let k = 0; k < count; k++) {\r\n // One side will be filled undefined if out of index, and those\r\n // undefined rows will be rendered as shadows later.\r\n pairs.push({ kind: \"pair\", left: lefts[k], right: rights[k] });\r\n }\r\n }\r\n return pairs;\r\n}","import clipboard from \"clipboardy\";\nimport type { Command } from \"commander\";\nimport { INIT_PROMPT } from \"../constants/prompt.js\";\n\nexport function registerInit(program: Command) {\n program\n .command(\"init\")\n .description(\"Copy the agent prompt to the clipboard\")\n .action(async () => {\n try {\n await clipboard.write(INIT_PROMPT);\n } catch (error) {\n console.log(\"could not write to the clipboard\");\n return;\n }\n\n console.log(\"Prompt copied to the clipboard.\");\n console.log(\"Paste it into a web chatbot, add what you want it to do, and send.\");\n console.log(\"Then copy the chatbot's reply back here and run `mtp`.\");\n });\n}\n","// Wrap prompt with a marker so that when user pasted it to chatbot, they can\n// add other instruction texts outside of the MEATPROXY SYSTEM block, which\n// enables chatbot to better distinguish user instructions and system\n// instructions.\nexport const INIT_PROMPT = `<<<<<<< MEATPROXY SYSTEM\nYou are an AI agent powered by MeatProxy, which is a cli tool that reads your replies copied by user and runs the JavaScript in it locally.\n\nTo inspect or edit files, reply with one snippet shaped like this. There should be no more than one pair of MEATPROXY markers. Keep the marker lines exactly as shown, each on its own line, inside the code block. The cli reads everything between them and ignores the rest of your reply.\n\n\\`\\`\\`js\n<<<<<<< MEATPROXY\n// your JavaScript here, which is the code for a function body.\n>>>>>>> MEATPROXY\n\\`\\`\\`\n\nGuidelines:\n- Use relative paths; the root is current working directory.\n- Do one step per snippet: the user runs it, then pastes the output back to you.\n- Use console.log for results and progress; you will see everything it prints.\n- Keep explanations outside the code block, best to place it at the very end of the whole replies.\n- Newlines: \\`read\\`/\\`edit\\` work in LF; the cli converts CRLF automatically. Just use LF in \\`old_string\\`/\\`new_string\\`.\n\nThe snippet runs in Node with a \\`tools\\` object:\n\n\\`\\`\\`\ntools.read({\n /** Path of the file to read, resolved against current working directory. */\n file_path: string;\n /** 1-based first line to return. Defaults to 1. */\n offset?: number;\n /** Maximum number of lines to return. Defaults to 2000. */\n limit?: number;\n}): {\n path: string;\n offset: number;\n lines: {\n number: number;\n text: string;\n }[];\n totalLines: number;\n}\ntools.write({\n /** Path of the file to write, resolved against the tool root. */\n file_path: string;\n /** Full UTF-8 contents of the file. */\n content: string;\n}): void\ntools.edit({\n /** Path of the file to edit, resolved against the tool root. */\n file_path: string;\n /** Literal text to look for. It has to be unique unless \\`replace_all\\` is set. */\n old_string: string;\n /** Replacement text. An empty string deletes the match. */\n new_string: string;\n /** Replace every occurrence instead of requiring a unique match. */\n replace_all?: boolean;\n}): void\ntools.pwsh({\n /** PowerShell command or script to run. */\n command: string;\n /** Working directory, resolved against the tool root. Defaults to the tool root. */\n cwd?: string;\n /** Timeout in milliseconds. Defaults to 120000. */\n timeoutMs?: number;\n}): {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n}\n\\`\\`\\`\n>>>>>>> MEATPROXY SYSTEM\n\n`;\n"],"mappings":"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","names":["chalk","clipboard","program","clipboard"]}
|
package/package.json
CHANGED
|
@@ -1,44 +1,52 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@adyingdeath/meatproxy",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "MeatProxy is a small bridge between your clipboard and chatbots.",
|
|
5
|
-
"scripts": {
|
|
6
|
-
"dev": "tsx src/index.ts",
|
|
7
|
-
"build": "tsup",
|
|
8
|
-
"typecheck": "tsc --noEmit",
|
|
9
|
-
"start": "node dist/index.js",
|
|
10
|
-
"prepublishOnly": "npm run typecheck && npm run build"
|
|
11
|
-
},
|
|
12
|
-
"keywords": [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@adyingdeath/meatproxy",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "MeatProxy is a small bridge between your clipboard and chatbots.",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "tsx src/index.ts",
|
|
7
|
+
"build": "tsup",
|
|
8
|
+
"typecheck": "tsc --noEmit",
|
|
9
|
+
"start": "node dist/index.js",
|
|
10
|
+
"prepublishOnly": "npm run typecheck && npm run build"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"cli",
|
|
14
|
+
"ai",
|
|
15
|
+
"llm",
|
|
16
|
+
"agent",
|
|
17
|
+
"agent harness"
|
|
18
|
+
],
|
|
19
|
+
"author": "adyingdeath",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/adyingdeath/meatproxy.git"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/adyingdeath/meatproxy",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/adyingdeath/meatproxy/issues"
|
|
28
|
+
},
|
|
29
|
+
"type": "module",
|
|
30
|
+
"bin": {
|
|
31
|
+
"mtp": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22.12.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"chalk": "^6.0.1",
|
|
41
|
+
"clipboardy": "^5.3.2",
|
|
42
|
+
"commander": "^15.0.0",
|
|
43
|
+
"diff": "^9.0.0",
|
|
44
|
+
"terminal-size": "^4.0.1"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^26.6.5",
|
|
48
|
+
"tsup": "^9.0.0",
|
|
49
|
+
"tsx": "^4.23.15",
|
|
50
|
+
"typescript": "^7.0.2"
|
|
51
|
+
}
|
|
52
|
+
}
|