mini-coder 0.5.11 → 0.5.12

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/BENCHMARK.md ADDED
@@ -0,0 +1,408 @@
1
+ # Benchmark workflow
2
+
3
+ Use this as the default tuning loop for `mini-coder` on Terminal-Bench.
4
+
5
+ The goal is not to rerun the whole benchmark after every change. The goal is to get fast enough feedback that small prompt / loop / tool-use changes can be judged quickly, then promote only the promising ones to bigger runs.
6
+
7
+ Terminal-Bench is a signal, not the product. The target is a better coding agent, not a higher benchmark score from benchmark-shaped patches.
8
+
9
+ ## Principles
10
+
11
+ - Keep changes small.
12
+ - Change one thing at a time.
13
+ - Compare against a fresh baseline, not an old leaderboard run.
14
+ - Use fast suites for iteration, broad suites for promotion.
15
+ - Keep structured `mc --json` logs in trial artifacts so behavior can be analyzed.
16
+ - Optimize for general coding-agent behavior first.
17
+ - Use benchmark failures to extract general behavior gaps, not to encode benchmark lore into the agent.
18
+ - Prefer generic improvements over task-named patches, reminders, or stop-time nudges.
19
+
20
+ ## Suites
21
+
22
+ ### 1. Guardrail suite
23
+
24
+ Use this to catch regressions on known-good behavior.
25
+
26
+ Tasks:
27
+
28
+ - `cancel-async-tasks`
29
+ - `bn-fit-modify`
30
+ - `sparql-university`
31
+ - `extract-elf`
32
+
33
+ Settings:
34
+
35
+ - `1` attempt
36
+ - `2` concurrent
37
+ - `0` retries
38
+
39
+ Estimated wall time:
40
+
41
+ - about `8–10 minutes`
42
+
43
+ ### 2. Fast suite
44
+
45
+ Use this after almost every small change.
46
+
47
+ Tasks:
48
+
49
+ - `polyglot-rust-c`
50
+ - `overfull-hbox`
51
+ - `gcode-to-text`
52
+ - `mteb-retrieve`
53
+ - `dna-insert`
54
+ - `torch-tensor-parallelism`
55
+
56
+ Settings:
57
+
58
+ - `2` attempts
59
+ - `2` concurrent
60
+ - `0` retries
61
+
62
+ Estimated wall time:
63
+
64
+ - about `44 minutes`
65
+
66
+ ### 3. Focused suite
67
+
68
+ Use this only if a change looks good on the fast suite.
69
+
70
+ Tasks:
71
+
72
+ - `polyglot-rust-c`
73
+ - `overfull-hbox`
74
+ - `gcode-to-text`
75
+ - `mteb-retrieve`
76
+ - `torch-tensor-parallelism`
77
+ - `filter-js-from-html`
78
+ - `configure-git-webserver`
79
+ - `qemu-alpine-ssh`
80
+
81
+ Settings:
82
+
83
+ - `2` attempts
84
+ - `2` concurrent
85
+ - `0` retries
86
+
87
+ Estimated wall time:
88
+
89
+ - about `1.1 hours`
90
+
91
+ ### 4. Broad promotion suite
92
+
93
+ Use this after 1–3 promising changes, or overnight.
94
+
95
+ Options:
96
+
97
+ - rerun recent failures
98
+ - wider local failure suite
99
+ - full leaderboard-style run
100
+
101
+ Typical settings for rerunning recent failures:
102
+
103
+ - `4` attempts
104
+ - `2` concurrent
105
+ - `0` retries
106
+
107
+ ## Baseline procedure
108
+
109
+ Before changing code:
110
+
111
+ 1. run the guardrail suite
112
+ 2. run the fast suite
113
+ 3. run the fast suite again
114
+
115
+ That gives a fresh local baseline for:
116
+
117
+ - pass count
118
+ - runtime
119
+ - variance / noise
120
+
121
+ Do not compare a new change only against an old run from days ago if a fresh same-HEAD baseline is available.
122
+
123
+ ## Experiment quality bar
124
+
125
+ Before changing code, write the hypothesis in two layers:
126
+
127
+ 1. the benchmark symptom
128
+ 2. the general coding-agent behavior gap behind it
129
+
130
+ Only run an experiment if you can answer all of these:
131
+
132
+ - what general behavior is being improved?
133
+ - why should that help outside Terminal-Bench?
134
+ - what would make this change obviously overfit?
135
+
136
+ Good experiment themes:
137
+
138
+ - verification-equivalence before completion
139
+ - artifact-grounded verification of final outputs
140
+ - preferring a task-named local source of truth over approximations
141
+ - reducing shell thrash before first meaningful verification
142
+
143
+ Reject or redesign experiments that:
144
+
145
+ - depend on benchmark-specific task names, file names, package names, or tool names in product logic
146
+ - inject reminders or guards keyed to one benchmark noun unless that rule maps cleanly to a real product behavior
147
+ - only make sense because a particular verifier is known
148
+ - cannot be explained without citing a single task transcript
149
+
150
+ Hard rule:
151
+
152
+ - no task-specific nouns in agent logic unless they map to a real product feature
153
+
154
+ ## Iteration loop
155
+
156
+ For each change:
157
+
158
+ 1. inspect the last fast / focused failures
159
+ 2. translate them into **one** general behavior gap
160
+ 3. reject benchmark-shaped ideas; if you cannot phrase the change without task-specific nouns, keep diagnosing
161
+ 4. if the change depends on a dynamic trigger, confirm that the trigger actually appears in the target failures
162
+ 5. form **one** narrow hypothesis
163
+ 6. make **one** small change
164
+ 7. run:
165
+ - guardrails
166
+ - fast suite
167
+ 8. compare to baseline
168
+ 9. decide:
169
+ - keep
170
+ - revert
171
+ - refine
172
+
173
+ Only run the focused suite if:
174
+
175
+ - the fast suite improved, and
176
+ - the guardrails did not regress
177
+
178
+ Only run the broad promotion suite if:
179
+
180
+ - the focused suite also looks good, or
181
+ - enough promising changes have accumulated to justify it
182
+
183
+ ## Decision rules
184
+
185
+ Treat the fast suite as a noisy but useful signal.
186
+
187
+ For the 12-trial fast suite:
188
+
189
+ - `+2` or more passes: probably meaningful improvement
190
+ - `-2` or more passes: probably meaningful regression
191
+ - `±1`: likely noise unless it repeats
192
+
193
+ Keep a change if:
194
+
195
+ - the fast suite improves materially
196
+ - guardrails do not regress
197
+ - the result repeats on another fast-suite rerun
198
+ - the mechanism is still a general agent-quality improvement, not just a benchmark-specific patch
199
+
200
+ Revert a change if:
201
+
202
+ - it clearly loses fast-suite passes, or
203
+ - it regresses guardrails
204
+
205
+ Call it inconclusive if:
206
+
207
+ - the delta is tiny and does not repeat
208
+
209
+ Also prefer:
210
+
211
+ - a smaller, more general improvement over a larger but obviously benchmark-specific patch
212
+ - a clearly exercised mechanism over a theory that never fired in the target runs
213
+
214
+ ## Failure buckets to optimize against
215
+
216
+ Use these as the main buckets when inspecting logs:
217
+
218
+ ### Exact-contract / cleanup misses
219
+
220
+ Examples:
221
+
222
+ - right artifact, wrong path
223
+ - right output plus extra junk
224
+ - correct core work, but violates a strict file / in-place requirement
225
+
226
+ ### Verification-equivalence / premature completion
227
+
228
+ Examples:
229
+
230
+ - waited too long to run a meaningful check
231
+ - never checked the exact contract
232
+ - stopped after a weaker non-equivalent check
233
+ - verified the intended design instead of the written artifact
234
+
235
+ ### Source-of-truth selection gaps
236
+
237
+ Examples:
238
+
239
+ - task names an exact local tool/package/interface, but the agent uses an approximation instead
240
+ - lower-level library reasoning replaces package-local or task-local semantics
241
+
242
+ ### Over-exploration / shell thrash
243
+
244
+ Examples:
245
+
246
+ - too many shell / read steps before first write
247
+ - repeated discovery commands without narrowing the problem
248
+
249
+ ### Persistent correctness gaps
250
+
251
+ Examples:
252
+
253
+ - `torch-tensor-parallelism`
254
+ - `filter-js-from-html`
255
+ - `configure-git-webserver`
256
+
257
+ ### Long-horizon / timeout-heavy tasks
258
+
259
+ These are useful for promotion runs, not tight inner loops.
260
+ Examples:
261
+
262
+ - `gpt2-codegolf`
263
+ - `query-optimize`
264
+ - `winning-avg-corewars`
265
+ - `train-fasttext`
266
+
267
+ ## Behavior analysis requirements
268
+
269
+ Behavior analysis depends on structured agent logs.
270
+
271
+ Keep wrappers on:
272
+
273
+ - `mc --json -p ...`
274
+
275
+ Per trial, keep:
276
+
277
+ - result JSON
278
+ - verifier output
279
+ - exception type
280
+ - agent stderr
281
+ - structured `agent/mini-coder.ndjson`
282
+ - timestamps
283
+
284
+ With those artifacts, analyze things like:
285
+
286
+ - time to first tool call
287
+ - time to first edit
288
+ - number of `shell` / `read` / `grep` / `edit` calls
289
+ - whether the agent ran a verifier-like shell command
290
+ - whether it used the named local source of truth when one was available
291
+ - whether it verified the final artifact it actually wrote
292
+ - whether a dynamic intervention visibly exercised
293
+ - whether it had a local pass before ending
294
+ - whether it kept changing files after a pass-worthy state
295
+ - whether it left extra artifacts
296
+
297
+ ## Commands
298
+
299
+ ### Guardrail suite
300
+
301
+ ```bash
302
+ GUARDRAIL_TASKS=(
303
+ cancel-async-tasks
304
+ bn-fit-modify
305
+ sparql-university
306
+ extract-elf
307
+ )
308
+
309
+ cmd=(
310
+ harbor run -y
311
+ --job-name local-guardrails-$(date +%F__%H-%M-%S)
312
+ --jobs-dir "$PWD/terminal-bench/jobs"
313
+ --agent-import-path mini_coder_local_agent:MiniCoderLocalAgent
314
+ --model openai-codex/gpt-5.4
315
+ --dataset terminal-bench@2.0
316
+ --n-attempts 1
317
+ --n-concurrent 2
318
+ --max-retries 0
319
+ )
320
+
321
+ for t in "${GUARDRAIL_TASKS[@]}"; do
322
+ cmd+=(--include-task-name "$t")
323
+ done
324
+
325
+ PYTHONPATH="$PWD/terminal-bench" "${cmd[@]}"
326
+ ```
327
+
328
+ ### Fast suite
329
+
330
+ ```bash
331
+ FAST_TASKS=(
332
+ polyglot-rust-c
333
+ overfull-hbox
334
+ gcode-to-text
335
+ mteb-retrieve
336
+ dna-insert
337
+ torch-tensor-parallelism
338
+ )
339
+
340
+ cmd=(
341
+ harbor run -y
342
+ --job-name local-fast-$(date +%F__%H-%M-%S)
343
+ --jobs-dir "$PWD/terminal-bench/jobs"
344
+ --agent-import-path mini_coder_local_agent:MiniCoderLocalAgent
345
+ --model openai-codex/gpt-5.4
346
+ --dataset terminal-bench@2.0
347
+ --n-attempts 2
348
+ --n-concurrent 2
349
+ --max-retries 0
350
+ )
351
+
352
+ for t in "${FAST_TASKS[@]}"; do
353
+ cmd+=(--include-task-name "$t")
354
+ done
355
+
356
+ PYTHONPATH="$PWD/terminal-bench" "${cmd[@]}"
357
+ ```
358
+
359
+ ### Focused suite
360
+
361
+ ```bash
362
+ FOCUS_TASKS=(
363
+ polyglot-rust-c
364
+ overfull-hbox
365
+ gcode-to-text
366
+ mteb-retrieve
367
+ torch-tensor-parallelism
368
+ filter-js-from-html
369
+ configure-git-webserver
370
+ qemu-alpine-ssh
371
+ )
372
+
373
+ cmd=(
374
+ harbor run -y
375
+ --job-name local-focus-$(date +%F__%H-%M-%S)
376
+ --jobs-dir "$PWD/terminal-bench/jobs"
377
+ --agent-import-path mini_coder_local_agent:MiniCoderLocalAgent
378
+ --model openai-codex/gpt-5.4
379
+ --dataset terminal-bench@2.0
380
+ --n-attempts 2
381
+ --n-concurrent 2
382
+ --max-retries 0
383
+ )
384
+
385
+ for t in "${FOCUS_TASKS[@]}"; do
386
+ cmd+=(--include-task-name "$t")
387
+ done
388
+
389
+ PYTHONPATH="$PWD/terminal-bench" "${cmd[@]}"
390
+ ```
391
+
392
+ ## Minimal experiment log format
393
+
394
+ Keep this in `PROGRESS.md`, a final summary for each completed change:
395
+
396
+ - benchmark symptom
397
+ - general behavior gap
398
+ - why this should help outside Terminal-Bench
399
+ - overfitting risk / why this is still general
400
+ - trigger evidence, if relevant
401
+ - hypothesis
402
+ - files changed
403
+ - local verification
404
+ - guardrail result
405
+ - fast-suite result
406
+ - focused-suite result, if run
407
+ - mechanism exercised?
408
+ - keep / revert / refine (Make the decision very visible in the file).
package/PROGRESS.md ADDED
@@ -0,0 +1,5 @@
1
+ # CURRENT TASK PROGRESS
2
+
3
+ - Benchmark loop stopped.
4
+ - In-progress experiment changes reverted.
5
+ - Progress reset for a fresh benchmark restart.
package/README.md CHANGED
@@ -39,9 +39,11 @@ $ mc
39
39
 
40
40
  ## Tools
41
41
 
42
- Four built-in tools, plus a read-only image tool:
42
+ Six built-in tools, plus a conditional read-only image tool:
43
43
 
44
44
  - **`shell`** — runs commands in the user's shell. Returns stdout, stderr, and exit code. Large output is truncated to protect model context.
45
+ - **`read`** — reads UTF-8 text files from disk, optionally by line window.
46
+ - **`grep`** — searches file contents with ripgrep-style options and returns structured matches.
45
47
  - **`edit`** — exact-text replacement in a single file. Fails deterministically if the target is missing or ambiguous. Creates new files when old text is empty.
46
48
  - **`todoWrite`** — creates or updates the session todo list incrementally and returns the full current snapshot.
47
49
  - **`todoRead`** — returns the full current session todo list snapshot.
@@ -123,6 +125,17 @@ bun run format
123
125
  bun run typecheck
124
126
  ```
125
127
 
128
+ ## Also makes LLMs smarter
129
+
130
+ LLMs famously tell you to walk 50 meters to the car wash — forgetting the car needs to be there too. Not on our watch.
131
+
132
+ <table align="center">
133
+ <tr>
134
+ <td><img src="assets/mc-claude-smart.png" alt="Claude correctly answering the car wash question" width="400" /></td>
135
+ <td><img src="assets/mc-gpt-smart.png" alt="GPT correctly answering the car wash question" width="400" /></td>
136
+ </tr>
137
+ </table>
138
+
126
139
  ## License
127
140
 
128
141
  MIT
Binary file
Binary file
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ stop() {
5
+ echo "stopping loop"
6
+ exit 0
7
+ }
8
+
9
+ trap stop INT TERM
10
+
11
+ while :; do
12
+ echo ""
13
+ echo "> Running step"
14
+ echo ""
15
+ mc -p "See BENCHMARK.md and PROGRESS.md first. Do the next step in the process. Once the step is complete update PROGRESS.md"
16
+
17
+ sleep 60
18
+ done
19
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mini-coder",
3
- "version": "0.5.11",
3
+ "version": "0.5.12",
4
4
  "description": "A small, fast CLI coding agent",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
package/src/headless.ts CHANGED
@@ -22,32 +22,64 @@ type HeadlessStopReason = "stop" | "length" | "error" | "aborted";
22
22
  /** Options for a headless NDJSON run. */
23
23
  export interface HeadlessRunOptions {
24
24
  /** Optional line writer for completed NDJSON event output. */
25
- writeLine?: (line: string) => void;
25
+ writeLine?: (line: string) => void | Promise<void>;
26
26
  }
27
27
 
28
28
  /** Options for a headless final-text run. */
29
29
  export interface HeadlessTextRunOptions {
30
30
  /** Optional writer for the final assistant text output. */
31
- writeText?: (text: string) => void;
31
+ writeText?: (text: string) => void | Promise<void>;
32
32
  }
33
33
 
34
34
  interface HeadlessOutputController {
35
- /** Write text to stdout with broken-pipe handling. */
35
+ /** Queue text for stdout with broken-pipe handling. */
36
36
  write(text: string): void;
37
37
  /** Attach SIGINT/stdout error handlers for the active run. */
38
38
  attach(): void;
39
39
  /** Remove SIGINT/stdout error handlers after the run. */
40
40
  detach(): void;
41
- /** Resolve the final stop reason, converting broken pipes into quiet shutdowns. */
42
- finalize(stopReason: HeadlessStopReason): HeadlessStopReason;
41
+ /** Wait for queued writes and resolve the final stop reason. */
42
+ finalize(stopReason: HeadlessStopReason): Promise<HeadlessStopReason>;
43
43
  }
44
44
 
45
45
  // ---------------------------------------------------------------------------
46
46
  // Helpers
47
47
  // ---------------------------------------------------------------------------
48
48
 
49
- function defaultWrite(text: string): void {
50
- process.stdout.write(text);
49
+ function defaultWrite(text: string): Promise<void> {
50
+ return new Promise((resolve, reject) => {
51
+ let settled = false;
52
+
53
+ const cleanup = (): void => {
54
+ process.stdout.off("error", handleError);
55
+ };
56
+
57
+ const settle = (callback: () => void): void => {
58
+ if (settled) {
59
+ return;
60
+ }
61
+ settled = true;
62
+ cleanup();
63
+ callback();
64
+ };
65
+
66
+ const handleError = (error: unknown): void => {
67
+ settle(() => {
68
+ reject(error);
69
+ });
70
+ };
71
+
72
+ process.stdout.on("error", handleError);
73
+ try {
74
+ process.stdout.write(text, () => {
75
+ settle(resolve);
76
+ });
77
+ } catch (error) {
78
+ settle(() => {
79
+ reject(error);
80
+ });
81
+ }
82
+ });
51
83
  }
52
84
 
53
85
  function isBrokenPipeError(error: unknown): boolean {
@@ -126,10 +158,11 @@ function shouldWriteHeadlessJsonEvent(event: AgentEvent): boolean {
126
158
 
127
159
  function createHeadlessOutputController(
128
160
  state: AppState,
129
- writeImpl: (text: string) => void,
161
+ writeImpl: (text: string) => void | Promise<void>,
130
162
  ): HeadlessOutputController {
131
163
  let brokenPipe = false;
132
164
  let outputError: unknown = null;
165
+ let pendingWrite = Promise.resolve();
133
166
  const sigintHandler = createSigintHandler(state);
134
167
 
135
168
  const stopForBrokenPipe = (): void => {
@@ -140,30 +173,37 @@ function createHeadlessOutputController(
140
173
  state.abortController?.abort();
141
174
  };
142
175
 
143
- const stdoutErrorHandler = (error: unknown): void => {
176
+ const failOutput = (error: unknown): void => {
144
177
  if (isBrokenPipeError(error)) {
145
178
  stopForBrokenPipe();
146
179
  return;
147
180
  }
148
181
 
149
- outputError = error;
182
+ outputError = outputError ?? error;
150
183
  state.abortController?.abort();
151
184
  };
152
185
 
186
+ const stdoutErrorHandler = (error: unknown): void => {
187
+ failOutput(error);
188
+ };
189
+
153
190
  return {
154
191
  write(text) {
155
- if (brokenPipe) {
192
+ if (brokenPipe || outputError) {
156
193
  return;
157
194
  }
158
195
 
159
- try {
160
- writeImpl(text);
161
- } catch (error) {
162
- if (!isBrokenPipeError(error)) {
163
- throw error;
196
+ pendingWrite = pendingWrite.then(async () => {
197
+ if (brokenPipe || outputError) {
198
+ return;
164
199
  }
165
- stopForBrokenPipe();
166
- }
200
+
201
+ try {
202
+ await writeImpl(text);
203
+ } catch (error) {
204
+ failOutput(error);
205
+ }
206
+ });
167
207
  },
168
208
  attach() {
169
209
  process.stdout.on("error", stdoutErrorHandler);
@@ -173,7 +213,8 @@ function createHeadlessOutputController(
173
213
  process.stdout.off("error", stdoutErrorHandler);
174
214
  process.off("SIGINT", sigintHandler);
175
215
  },
176
- finalize(stopReason) {
216
+ async finalize(stopReason) {
217
+ await pendingWrite;
177
218
  if (outputError) {
178
219
  throw outputError;
179
220
  }
@@ -222,7 +263,7 @@ export async function runHeadlessPrompt(
222
263
  state,
223
264
  hooks,
224
265
  );
225
- return output.finalize(stopReason);
266
+ return await output.finalize(stopReason);
226
267
  } finally {
227
268
  output.detach();
228
269
  }
@@ -271,7 +312,7 @@ export async function runHeadlessPromptText(
271
312
  if (finalText.length > 0) {
272
313
  output.write(finalText);
273
314
  }
274
- return output.finalize(stopReason);
315
+ return await output.finalize(stopReason);
275
316
  } finally {
276
317
  output.detach();
277
318
  }