@adyingdeath/meatproxy 0.0.0-stage → 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/LICENSE +7 -0
- package/README.md +49 -2
- package/dist/index.js +774 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2026 adyingdeath
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
-
#
|
|
1
|
+
# MeatProxy
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> A meat proxy is a person who acts as an unthinking relay for AI-generated text, code, or other outputs by copying and pasting them to others without reading, validating, or adding personal insight.
|
|
4
|
+
|
|
5
|
+
MeatProxy helps you be a better meat proxy.
|
|
6
|
+
|
|
7
|
+
It is a small bridge between your clipboard and chatbots: paste the prompt into the chat, ask for a change, copy the reply, then run `mtp` in the terminal. The code runs against your project, and everything it prints is copied back to the clipboard, ready for the next round.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Node.js 22.12 or newer
|
|
12
|
+
- PowerShell (`pwsh` or Windows PowerShell) if you use the `pwsh` tool
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm install -g @adyingdeath/meatproxy
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This provides the `mtp` command.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
# 1. copy the agent prompt to the clipboard
|
|
26
|
+
mtp init
|
|
27
|
+
|
|
28
|
+
# 2. paste it into a web chatbot and say what you want
|
|
29
|
+
|
|
30
|
+
# 3. copy the chatbot's reply, then run
|
|
31
|
+
mtp
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Run `mtp --help` to see the workflow again at any time.
|
|
35
|
+
|
|
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.
|
|
37
|
+
|
|
38
|
+
The snippet runs with the same access as your terminal, so only paste replies you trust.
|
|
39
|
+
|
|
40
|
+
## Tools
|
|
41
|
+
|
|
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.
|
|
43
|
+
|
|
44
|
+
## Development
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
npm install
|
|
48
|
+
npm run build # bundles to dist/
|
|
49
|
+
npm run dev # runs src/index.ts directly
|
|
50
|
+
```
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/runner/core.ts
|
|
7
|
+
import clipboard from "clipboardy";
|
|
8
|
+
|
|
9
|
+
// src/runner/console.ts
|
|
10
|
+
import { format } from "util";
|
|
11
|
+
var Console = class {
|
|
12
|
+
/** Everything written so far, one entry per console call. */
|
|
13
|
+
lines = [];
|
|
14
|
+
log(...args) {
|
|
15
|
+
this.lines.push(format(...args));
|
|
16
|
+
}
|
|
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
|
+
}
|
|
79
|
+
}
|
|
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;
|
|
90
|
+
}
|
|
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;
|
|
107
|
+
}
|
|
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") }];
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
// src/tools/index.ts
|
|
406
|
+
var DEFAULT_PWSH_TIMEOUT = 12e4;
|
|
407
|
+
var DEFAULT_READ_LIMIT = 2e3;
|
|
408
|
+
var MAX_PWSH_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
409
|
+
var PWSH_PRELUDE = [
|
|
410
|
+
"$ProgressPreference = 'SilentlyContinue'",
|
|
411
|
+
"try { [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) } catch {}"
|
|
412
|
+
].join("\n");
|
|
413
|
+
var CLIXML_ERROR_PATTERN = /<S S="Error">([\s\S]*?)<\/S>/g;
|
|
414
|
+
function unescapeClixml(text) {
|
|
415
|
+
return text.replace(/_x([0-9a-f]{4})_/gi, (_match, code) => String.fromCharCode(Number.parseInt(code, 16))).replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
416
|
+
}
|
|
417
|
+
function decodePowerShellStderr(stderr) {
|
|
418
|
+
if (!stderr.startsWith("#< CLIXML")) {
|
|
419
|
+
return stderr;
|
|
420
|
+
}
|
|
421
|
+
const messages = Array.from(
|
|
422
|
+
stderr.matchAll(CLIXML_ERROR_PATTERN),
|
|
423
|
+
(match) => unescapeClixml(match[1] ?? "")
|
|
424
|
+
);
|
|
425
|
+
return messages.length > 0 ? messages.join("") : stderr;
|
|
426
|
+
}
|
|
427
|
+
function shellCandidates() {
|
|
428
|
+
return process.platform === "win32" ? ["pwsh", "powershell.exe"] : ["pwsh"];
|
|
429
|
+
}
|
|
430
|
+
function describeError(error) {
|
|
431
|
+
return error instanceof Error ? error.message : String(error);
|
|
432
|
+
}
|
|
433
|
+
var Tools = class {
|
|
434
|
+
root;
|
|
435
|
+
constructor(root = process.cwd()) {
|
|
436
|
+
this.root = path.resolve(root);
|
|
437
|
+
}
|
|
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;
|
|
445
|
+
const target = this.resolve(filePath);
|
|
446
|
+
let content;
|
|
447
|
+
try {
|
|
448
|
+
content = readFileSync(target, "utf8");
|
|
449
|
+
} catch (error) {
|
|
450
|
+
ToolCards.read({ filePath, ok: false });
|
|
451
|
+
throw new Error(`Read failed for ${filePath}: ${describeError(error)}`);
|
|
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
|
+
};
|
|
471
|
+
}
|
|
472
|
+
/** Create or fully overwrite a UTF-8 text file, creating parent directories. */
|
|
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;
|
|
479
|
+
const target = this.resolve(filePath);
|
|
480
|
+
try {
|
|
481
|
+
mkdirSync(path.dirname(target), { recursive: true });
|
|
482
|
+
writeFileSync(target, params.content, "utf8");
|
|
483
|
+
} catch (error) {
|
|
484
|
+
ToolCards.write({ filePath, content: params.content, ok: false });
|
|
485
|
+
throw new Error(`Write failed for ${filePath}: ${describeError(error)}`);
|
|
486
|
+
}
|
|
487
|
+
ToolCards.write({ filePath, content: params.content, ok: true });
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Replace literal text in a file.
|
|
491
|
+
*
|
|
492
|
+
* `old_string` has to be unique unless `replace_all` is true, and an empty
|
|
493
|
+
* `old_string` is rejected because it would match everywhere.
|
|
494
|
+
*/
|
|
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;
|
|
504
|
+
if (oldText.length === 0) {
|
|
505
|
+
ToolCards.edit({ filePath, replaceAll, ok: false });
|
|
506
|
+
throw new Error("Edit failed: old_string must not be empty.");
|
|
507
|
+
}
|
|
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 });
|
|
523
|
+
throw new Error(`Edit failed for ${filePath}: old_string was not found.`);
|
|
524
|
+
}
|
|
525
|
+
const secondIndex = lfOriginal.indexOf(lfOldText, firstIndex + lfOldText.length);
|
|
526
|
+
if (secondIndex !== -1 && !replaceAll) {
|
|
527
|
+
ToolCards.edit({ filePath, replaceAll, ok: false });
|
|
528
|
+
throw new Error(
|
|
529
|
+
`Edit failed for ${filePath}: old_string appears multiple times. Make it unique or pass replace_all = true.`
|
|
530
|
+
);
|
|
531
|
+
}
|
|
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 });
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Run a PowerShell command synchronously and return its output.
|
|
544
|
+
*/
|
|
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");
|
|
554
|
+
for (const shell of shellCandidates()) {
|
|
555
|
+
const result = spawnSync(
|
|
556
|
+
shell,
|
|
557
|
+
["-NoProfile", "-NonInteractive", "-EncodedCommand", encoded],
|
|
558
|
+
{
|
|
559
|
+
cwd,
|
|
560
|
+
encoding: "utf8",
|
|
561
|
+
maxBuffer: MAX_PWSH_OUTPUT_BYTES,
|
|
562
|
+
timeout,
|
|
563
|
+
windowsHide: true
|
|
564
|
+
}
|
|
565
|
+
);
|
|
566
|
+
if (result.error) {
|
|
567
|
+
const code = result.error.code;
|
|
568
|
+
if (code === "ENOENT") continue;
|
|
569
|
+
ToolCards.pwsh({ command, exitCode: null, ok: false });
|
|
570
|
+
if (code === "ETIMEDOUT") {
|
|
571
|
+
throw new Error("Pwsh timed out after " + timeout + " ms.");
|
|
572
|
+
}
|
|
573
|
+
throw new Error("Pwsh failed: " + result.error.message);
|
|
574
|
+
}
|
|
575
|
+
ToolCards.pwsh({ command, exitCode: result.status, ok: true });
|
|
576
|
+
return {
|
|
577
|
+
stdout: result.stdout,
|
|
578
|
+
stderr: decodePowerShellStderr(result.stderr),
|
|
579
|
+
exitCode: result.status
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
ToolCards.pwsh({ command, exitCode: null, ok: false });
|
|
583
|
+
throw new Error("Pwsh failed: PowerShell is not available on this machine.");
|
|
584
|
+
}
|
|
585
|
+
resolve(filePath) {
|
|
586
|
+
return path.resolve(this.root, filePath);
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
// src/runner/core.ts
|
|
591
|
+
import chalk2 from "chalk";
|
|
592
|
+
var HEAD_MARKER = "<<<<<<< MEATPROXY";
|
|
593
|
+
var TAIL_MARKER = ">>>>>>> MEATPROXY";
|
|
594
|
+
var OUTPUT_HEAD_MARKER = "<<<<<<< MEATPROXY OUTPUT\n";
|
|
595
|
+
var OUTPUT_TAIL_MARKER = "\n>>>>>>> MEATPROXY OUTPUT\n\n";
|
|
596
|
+
var INVALID_MARKER_MESSAGE = `Error: Invalid format.
|
|
597
|
+
Expected format:
|
|
598
|
+
${HEAD_MARKER}
|
|
599
|
+
...code...
|
|
600
|
+
${TAIL_MARKER}`;
|
|
601
|
+
function extractCodeBlock(text) {
|
|
602
|
+
const lines = text.split(/\r?\n/);
|
|
603
|
+
const heads = [];
|
|
604
|
+
const tails = [];
|
|
605
|
+
lines.forEach((line, index) => {
|
|
606
|
+
const trimmed = line.trim();
|
|
607
|
+
if (trimmed === HEAD_MARKER) {
|
|
608
|
+
heads.push(index);
|
|
609
|
+
} else if (trimmed === TAIL_MARKER) {
|
|
610
|
+
tails.push(index);
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
if (heads.length === 0 && tails.length === 0) {
|
|
614
|
+
return { status: "error", message: INVALID_MARKER_MESSAGE };
|
|
615
|
+
}
|
|
616
|
+
if (heads.length > 1 || tails.length > 1) {
|
|
617
|
+
return { status: "error", message: "Error: Found more than one pair of MEATPROXY markers." };
|
|
618
|
+
}
|
|
619
|
+
const head = heads[0];
|
|
620
|
+
const tail = tails[0];
|
|
621
|
+
if (head === void 0 || tail === void 0 || tail < head) {
|
|
622
|
+
return { status: "error", message: INVALID_MARKER_MESSAGE };
|
|
623
|
+
}
|
|
624
|
+
const code = lines.slice(head + 1, tail).join("\n").trim();
|
|
625
|
+
if (code.length === 0) {
|
|
626
|
+
return { status: "empty" };
|
|
627
|
+
}
|
|
628
|
+
return { status: "ok", code };
|
|
629
|
+
}
|
|
630
|
+
var AsyncFunction = Object.getPrototypeOf(async function() {
|
|
631
|
+
}).constructor;
|
|
632
|
+
async function run() {
|
|
633
|
+
let text;
|
|
634
|
+
try {
|
|
635
|
+
text = await clipboard.read();
|
|
636
|
+
} catch (error) {
|
|
637
|
+
console.log(chalk2.red("could not read the clipboard"));
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
const vmConsole = new Console();
|
|
641
|
+
const result = extractCodeBlock(text);
|
|
642
|
+
if (result.status === "empty") {
|
|
643
|
+
console.log("Nothing happens.");
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
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
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (vmConsole.lines.length === 0) {
|
|
657
|
+
console.log("No output to copy.");
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
try {
|
|
661
|
+
await clipboard.write(
|
|
662
|
+
OUTPUT_HEAD_MARKER + vmConsole.lines.join("\n") + OUTPUT_TAIL_MARKER
|
|
663
|
+
);
|
|
664
|
+
} catch (error) {
|
|
665
|
+
console.log(chalk2.red("could not write to the clipboard"));
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
console.log("Output copied to the clipboard. Paste it back to the agent.");
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// src/commands/init.ts
|
|
672
|
+
import clipboard2 from "clipboardy";
|
|
673
|
+
|
|
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
|
+
`;
|
|
744
|
+
|
|
745
|
+
// src/commands/init.ts
|
|
746
|
+
function registerInit(program2) {
|
|
747
|
+
program2.command("init").description("Copy the agent prompt to the clipboard").action(async () => {
|
|
748
|
+
try {
|
|
749
|
+
await clipboard2.write(INIT_PROMPT);
|
|
750
|
+
} catch (error) {
|
|
751
|
+
console.log("could not write to the clipboard");
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
console.log("Prompt copied to the clipboard.");
|
|
755
|
+
console.log("Paste it into a web chatbot, add what you want it to do, and send.");
|
|
756
|
+
console.log("Then copy the chatbot's reply back here and run `mtp`.");
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// src/index.ts
|
|
761
|
+
var program = new Command();
|
|
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);
|
|
772
|
+
registerInit(program);
|
|
773
|
+
await program.parseAsync();
|
|
774
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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,6 +1,52 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@adyingdeath/meatproxy",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
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
|
+
}
|