@bunny-agent/runner-cli 0.9.57 → 0.9.58

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 CHANGED
@@ -18,12 +18,13 @@ runner-cli → runner-* (direct, NO dependencies on manager or sandbox)
18
18
  ├─ runner-claude ✅
19
19
  ├─ runner-codex ✅
20
20
  ├─ runner-gemini ✅
21
- └─ runner-copilot 🚧
21
+ └─ runner-copilot ✅
22
22
 
23
23
  Dependencies:
24
24
  ✅ @bunny-agent/runner-claude (runtime)
25
25
  ✅ @bunny-agent/runner-codex (runtime)
26
26
  ✅ @bunny-agent/runner-gemini (runtime)
27
+ ✅ @bunny-agent/runner-copilot (runtime)
27
28
  ❌ NO @bunny-agent/manager
28
29
  ❌ NO @bunny-agent/sandbox-*
29
30
  ```
@@ -69,7 +70,7 @@ bunny-agent run --runner codex -- "Build a REST API with Express"
69
70
  # Using Gemini
70
71
  bunny-agent run --runner gemini -- "Build a REST API with Express"
71
72
 
72
- # Using GitHub Copilot (when implemented)
73
+ # Using GitHub Copilot
73
74
  bunny-agent run --runner copilot -- "Refactor this code"
74
75
 
75
76
  # With custom system prompt
@@ -86,7 +87,9 @@ bunny-agent run --runner claude --system-prompt "You are a coding assistant" --
86
87
  | `--system-prompt <prompt>` | `-s` | Custom system prompt | - |
87
88
  | `--max-turns <n>` | `-t` | Maximum conversation turns | - |
88
89
  | `--allowed-tools <tools>` | `-a` | Comma-separated list of allowed tools | - |
89
- | `--resume <session-id>` | `-r` | Resume a previous session | - |
90
+ | `--yolo` | - | Automatically approve tool permission requests | `false` |
91
+ | `--effort <level>` | - | Reasoning effort or thinking level, depending on runner | - |
92
+ | `--resume <session-id>` | - | Resume a previous session | - |
90
93
  | `--help` | `-h` | Show help message | - |
91
94
 
92
95
  `--allowed-tools` limits built-in runner tools. Custom tools are provided
@@ -114,6 +117,7 @@ data: [DONE]
114
117
  | `ANTHROPIC_API_KEY` | Anthropic API key (Claude runner) | No |
115
118
  | `OPENAI_API_KEY` or `CODEX_API_KEY` | OpenAI API key (Codex runner) | No |
116
119
  | `GEMINI_API_KEY` | Gemini API key (Gemini runner) | No |
120
+ | `GITHUB_TOKEN` or `GH_TOKEN` | GitHub token (Copilot runner); falls back to the logged-in Copilot user | No |
117
121
  | `BUNNY_AGENT_WORKSPACE` | Default workspace path | No |
118
122
  | `BUNNY_AGENT_LOG_LEVEL` | Logging level (debug, info, warn, error) | No |
119
123
 
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Bundle entry for the standalone `apply_patch` shell command. esbuild
3
+ * builds this into the self-contained `dist/apply-patch-bin.js` that the
4
+ * sandbox image wires up as /usr/local/bin/apply_patch and that the pi
5
+ * runner's PATH shim execs (see @bunny-agent/runner-pi's
6
+ * apply-patch-shim.ts). The imported module runs the CLI on import.
7
+ */
8
+ import "@bunny-agent/apply-patch/bin";
9
+ //# sourceMappingURL=apply-patch-bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-patch-bin.d.ts","sourceRoot":"","sources":["../src/apply-patch-bin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,8BAA8B,CAAC"}
@@ -0,0 +1,237 @@
1
+ // ../../packages/apply-patch/dist/bin.js
2
+ import { readFileSync as readFileSync2 } from "node:fs";
3
+
4
+ // ../../packages/apply-patch/dist/core.js
5
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
6
+ import { dirname, resolve } from "node:path";
7
+ var BEGIN_PATCH = "*** Begin Patch";
8
+ var END_PATCH = "*** End Patch";
9
+ var UPDATE_FILE = "*** Update File: ";
10
+ var ADD_FILE = "*** Add File: ";
11
+ var DELETE_FILE = "*** Delete File: ";
12
+ var MOVE_TO = "*** Move to: ";
13
+ var HUNK_MARKER = "@@";
14
+ var EOF_MARKER = "*** End of File";
15
+ var PatchParseError = class extends Error {
16
+ };
17
+ function isSectionMarker(line) {
18
+ return line.startsWith(UPDATE_FILE) || line.startsWith(ADD_FILE) || line.startsWith(DELETE_FILE) || line.trim() === END_PATCH;
19
+ }
20
+ function parseUpdateHunks(lines, start) {
21
+ const hunks = [];
22
+ let i = start;
23
+ let current = null;
24
+ const flush = () => {
25
+ if (current && (current.before.length > 0 || current.after.length > 0)) {
26
+ hunks.push(current);
27
+ }
28
+ current = null;
29
+ };
30
+ while (i < lines.length && !isSectionMarker(lines[i])) {
31
+ const line = lines[i];
32
+ if (line.startsWith(HUNK_MARKER)) {
33
+ flush();
34
+ current = { before: [], after: [] };
35
+ i++;
36
+ continue;
37
+ }
38
+ if (line.trim() === EOF_MARKER) {
39
+ i++;
40
+ continue;
41
+ }
42
+ if (current === null)
43
+ current = { before: [], after: [] };
44
+ if (line.startsWith("+")) {
45
+ current.after.push(line.slice(1));
46
+ } else if (line.startsWith("-")) {
47
+ current.before.push(line.slice(1));
48
+ } else if (line.startsWith(" ")) {
49
+ const content = line.slice(1);
50
+ current.before.push(content);
51
+ current.after.push(content);
52
+ } else if (line.trim() === "") {
53
+ current.before.push("");
54
+ current.after.push("");
55
+ } else {
56
+ throw new PatchParseError(`Malformed hunk line: ${line}`);
57
+ }
58
+ i++;
59
+ }
60
+ flush();
61
+ return { hunks, next: i };
62
+ }
63
+ function parsePatch(patchText) {
64
+ const lines = patchText.split("\n");
65
+ let i = 0;
66
+ while (i < lines.length && lines[i].trim() === "")
67
+ i++;
68
+ if (i < lines.length && lines[i].trim() === BEGIN_PATCH)
69
+ i++;
70
+ const ops = [];
71
+ while (i < lines.length) {
72
+ const line = lines[i];
73
+ if (line.trim() === END_PATCH || line.trim() === "") {
74
+ i++;
75
+ continue;
76
+ }
77
+ if (line.startsWith(UPDATE_FILE)) {
78
+ const path = line.slice(UPDATE_FILE.length).trim();
79
+ i++;
80
+ let moveTo;
81
+ if (lines[i]?.startsWith(MOVE_TO)) {
82
+ moveTo = lines[i].slice(MOVE_TO.length).trim();
83
+ i++;
84
+ }
85
+ const { hunks, next } = parseUpdateHunks(lines, i);
86
+ ops.push({ kind: "update", path, moveTo, hunks, addLines: [] });
87
+ i = next;
88
+ continue;
89
+ }
90
+ if (line.startsWith(ADD_FILE)) {
91
+ const path = line.slice(ADD_FILE.length).trim();
92
+ i++;
93
+ const addLines = [];
94
+ while (i < lines.length && !isSectionMarker(lines[i])) {
95
+ if (lines[i].startsWith("+"))
96
+ addLines.push(lines[i].slice(1));
97
+ else if (lines[i].trim() !== "") {
98
+ throw new PatchParseError(`Add File "${path}": expected "+"-prefixed content, got: ${lines[i]}`);
99
+ }
100
+ i++;
101
+ }
102
+ ops.push({ kind: "add", path, hunks: [], addLines });
103
+ continue;
104
+ }
105
+ if (line.startsWith(DELETE_FILE)) {
106
+ const path = line.slice(DELETE_FILE.length).trim();
107
+ i++;
108
+ ops.push({ kind: "delete", path, hunks: [], addLines: [] });
109
+ continue;
110
+ }
111
+ throw new PatchParseError(`Unexpected line in patch: ${line}`);
112
+ }
113
+ if (ops.length === 0) {
114
+ throw new PatchParseError("Patch contains no file operations (Update File / Add File / Delete File).");
115
+ }
116
+ return ops;
117
+ }
118
+ function findHunkStart(haystack, needle, from) {
119
+ if (needle.length === 0)
120
+ return from;
121
+ const canonicalizers = [
122
+ (s) => s,
123
+ (s) => s.replace(/\s+$/, ""),
124
+ (s) => s.trim()
125
+ ];
126
+ for (const canon of canonicalizers) {
127
+ const canonNeedle = needle.map(canon);
128
+ for (let start = from; start <= haystack.length - needle.length; start++) {
129
+ let matched = true;
130
+ for (let j = 0; j < needle.length; j++) {
131
+ if (canon(haystack[start + j]) !== canonNeedle[j]) {
132
+ matched = false;
133
+ break;
134
+ }
135
+ }
136
+ if (matched)
137
+ return start;
138
+ }
139
+ }
140
+ return -1;
141
+ }
142
+ function applyHunks(originalContent, hunks) {
143
+ const trailingNewline = originalContent.endsWith("\n");
144
+ const lines = originalContent.length === 0 ? [] : originalContent.split("\n");
145
+ if (trailingNewline)
146
+ lines.pop();
147
+ let cursor = 0;
148
+ const resultLines = [];
149
+ for (const hunk of hunks) {
150
+ const start = findHunkStart(lines, hunk.before, cursor);
151
+ if (start === -1) {
152
+ throw new PatchParseError(`Could not locate context for hunk:
153
+ ${hunk.before.join("\n")}`);
154
+ }
155
+ resultLines.push(...lines.slice(cursor, start));
156
+ resultLines.push(...hunk.after);
157
+ cursor = start + hunk.before.length;
158
+ }
159
+ resultLines.push(...lines.slice(cursor));
160
+ return resultLines.join("\n") + (trailingNewline ? "\n" : "");
161
+ }
162
+ function applyPatch(cwd, patchText) {
163
+ const ops = parsePatch(patchText);
164
+ const changedFiles = [];
165
+ const addedFiles = [];
166
+ const deletedFiles = [];
167
+ for (const op of ops) {
168
+ const target = resolve(cwd, op.path);
169
+ if (op.kind === "add") {
170
+ if (existsSync(target)) {
171
+ throw new PatchParseError(`Add File "${op.path}": file already exists.`);
172
+ }
173
+ mkdirSync(dirname(target), { recursive: true });
174
+ writeFileSync(target, `${op.addLines.join("\n")}
175
+ `);
176
+ addedFiles.push(op.path);
177
+ continue;
178
+ }
179
+ if (op.kind === "delete") {
180
+ if (!existsSync(target)) {
181
+ throw new PatchParseError(`Delete File "${op.path}": file not found.`);
182
+ }
183
+ unlinkSync(target);
184
+ deletedFiles.push(op.path);
185
+ continue;
186
+ }
187
+ if (!existsSync(target)) {
188
+ throw new PatchParseError(`Update File "${op.path}": file not found.`);
189
+ }
190
+ const original = readFileSync(target, "utf8");
191
+ const updated = applyHunks(original, op.hunks);
192
+ if (op.moveTo) {
193
+ const destination = resolve(cwd, op.moveTo);
194
+ mkdirSync(dirname(destination), { recursive: true });
195
+ writeFileSync(destination, updated);
196
+ unlinkSync(target);
197
+ } else {
198
+ writeFileSync(target, updated);
199
+ }
200
+ changedFiles.push(op.moveTo ?? op.path);
201
+ }
202
+ return { changedFiles, addedFiles, deletedFiles };
203
+ }
204
+ function formatPatchResult(result) {
205
+ return [
206
+ ...result.addedFiles.map((f) => `A ${f}`),
207
+ ...result.changedFiles.map((f) => `M ${f}`),
208
+ ...result.deletedFiles.map((f) => `D ${f}`)
209
+ ].join("\n");
210
+ }
211
+
212
+ // ../../packages/apply-patch/dist/bin.js
213
+ function readPatchText() {
214
+ const arg = process.argv[2];
215
+ if (arg !== void 0 && arg.trim() !== "")
216
+ return arg;
217
+ if (process.stdin.isTTY) {
218
+ process.stderr.write("usage: apply_patch '<patch>' (or pipe the patch via stdin/heredoc)\n");
219
+ process.exit(2);
220
+ }
221
+ return readFileSync2(0, "utf8");
222
+ }
223
+ try {
224
+ const patchText = readPatchText();
225
+ if (patchText.trim() === "") {
226
+ throw new PatchParseError("Empty patch input.");
227
+ }
228
+ const result = applyPatch(process.cwd(), patchText);
229
+ const summary = formatPatchResult(result);
230
+ process.stdout.write(`${summary || "Patch applied."}
231
+ `);
232
+ } catch (e) {
233
+ const msg = e instanceof Error ? e.message : String(e);
234
+ process.stderr.write(`apply_patch: ${msg}
235
+ `);
236
+ process.exit(1);
237
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-patch-bin.js","sourceRoot":"","sources":["../src/apply-patch-bin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,8BAA8B,CAAC"}