mini-coder 0.0.23 → 0.1.1

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
@@ -6,194 +6,76 @@
6
6
 
7
7
  > _Small. Fast. Gets out of your way._
8
8
 
9
- [📖 Read the Full Manual](https://sacenox.github.io/mini-coder/)
9
+ [📖 Read the Full Manual](docs/mini-coder.1.md)
10
10
 
11
- Hey there! I'm **mini-coder** — a CLI coding agent built for developers who want a sharp tool, not a bloated IDE plugin. Think of me as the pocket knife of AI coding assistants: lightweight, reliable, and always ready.
12
-
13
- ---
14
-
15
- ## 🤙 Who Am I?
16
-
17
- I'm `mc` — your new terminal companion. I live in your shell, speak to large language models, and help you explore, understand, and modify code at the speed of thought.
18
-
19
- I was built with a simple philosophy: **dev flow first**. No slow startup. No clunky GUI. No vendor lock-in. Just you, your terminal, and an AI that keeps up.
11
+ A terminal coding agent for developers who want a sharp tool, not a bloated IDE plugin. Shell-first, multi-provider, minimal tool surface. Just you, your terminal, and an AI that keeps up.
20
12
 
21
13
  ![Minicoder Preview](./assets/preview.gif)
22
14
 
23
15
  ---
24
16
 
25
- ## 🛠️ What Can I Do?
26
-
27
- My toolkit is lean on purpose — every tool earns its spot, no passengers:
28
-
29
- | Tool | What it does |
30
- | --------------- | -------------------------------------------------------------------------- |
31
- | 🐚 `shell` | Run shell commands, inspect the repo, and use `mc-edit` for targeted edits |
32
- | 🤖 `subagent` | Spawn a focused mini-me for parallel subtasks |
33
- | 🧰 `listSkills` | List discovered skills without loading full skill bodies |
34
- | 📘 `readSkill` | Load one `SKILL.md` on demand |
35
- | 🌐 `webSearch` | Search the web when `EXA_API_KEY` is set |
36
- | 📄 `webContent` | Fetch full page content from URLs when `EXA_API_KEY` is set |
37
-
38
- mini-coder is intentionally **shell-first**: inspect with shell, edit with `mc-edit`, verify with shell.
39
-
40
- Need more firepower? I also connect to **MCP servers** over HTTP or stdio — attached MCP tools show up dynamically whenever the job calls for them.
41
-
42
- ---
43
-
44
- ## ⚡ Key Features
45
-
46
- - **Multi-provider** — set `OPENCODE_API_KEY` for Zen, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY` or `GEMINI_API_KEY`, or just run Ollama locally (`OLLAMA_BASE_URL` optional). I auto-discover whatever's available.
47
- - **Built-in web search** — set `EXA_API_KEY` and I expose `webSearch` + `webContent` tools.
48
- - **Session memory** — conversations are saved in a local SQLite database. Resume where you left off with `-c` or pick a specific session with `-r <id>`.
49
- - **Shell integration** — prefix with `!` to run shell commands inline. Use `@` to reference files in your prompt (with Tab completion).
50
- - **Slash commands** — `/model` or `/models` to list/switch models, `/model effort <low|medium|high|xhigh|off>` for reasoning effort, `/reasoning [on|off]` to toggle reasoning display, `/context` to inspect or tune pruning/tool-result caps, `/cache` to configure prompt caching, `/review` for a code review (global custom command, auto-created at `~/.agents/commands/review.md`), `/agent [name]` to set or clear an active primary agent, `/undo` to remove the last conversation turn (it does not revert filesystem changes), `/new` for a clean session, `/mcp list|add|remove` to manage MCP servers, and `/exit` (`/quit`, `/q`) to leave. See all with `/help`.
51
-
52
- - **Custom commands** — drop a `.md` file in `.agents/commands/` and it becomes a `/command`. Claude-compatible `.claude/commands/` works too. Supports argument placeholders (`$ARGUMENTS`, `$1`…`$9`) and shell interpolation (`` !`cmd` ``). Global commands live in `~/.agents/commands/` and `~/.claude/commands/`. Custom commands take precedence over built-ins. → [docs/custom-commands.md](docs/custom-commands.md)
53
- - **Custom agents** — drop a `.md` file in `.agents/agents/` or `.claude/agents/` (or `~/.agents/agents/` / `~/.claude/agents/` globally) and activate it with `/agent [name]`. Agent definitions are also exposed to subagent delegation unless `mode: primary`. → [docs/custom-agents.md](docs/custom-agents.md)
54
- - **Skills** — place a `SKILL.md` in `.agents/skills/<name>/` and inject it into any prompt with `@skill-name`. Claude-compatible `.claude/skills/<name>/SKILL.md` works too. Skills are _never_ auto-loaded — always explicit. → [docs/skills.md](docs/skills.md)
55
- - **Beautiful, minimal output** — compact tool output, formatted trees for file searches, a live status bar with model, git branch, and token counts.
56
- - **16 ANSI colors only** — my output inherits _your_ terminal theme. Dark mode, light mode, Solarized, Gruvbox — I fit right in.
57
-
58
- ---
59
-
60
- ## 🧠 Interesting Things About Me
61
-
62
- - **I eat my own dog food.** I was built _by_ a mini-coder agent. It's agents all the way down. 🐢
63
- - **I'm tiny but mighty.** The whole runtime is [Bun.js](https://bun.com) — fast startup, native TypeScript, and a built-in SQLite driver.
64
- - **I respect existing conventions.** Context lives in `AGENTS.md` or `CLAUDE.md`, commands in `.agents/commands/`, agents in `.agents/agents/`, skills in `.agents/skills/` — I follow the ecosystem instead of inventing new standards.
65
- - **I spin while I think.** ⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ (It's the little things.)
66
- - **I can clone myself.** The `subagent` tool lets me spin up parallel instances of myself to tackle independent subtasks simultaneously. Divide and conquer! (Up to 10 levels deep.)
67
-
68
- ---
69
-
70
- ## 📁 Config folders
71
-
72
- I follow the [`.agents` convention](https://github.com/agentsmd/agents) — the shared standard across AI coding tools — and I also understand `.claude` layouts for **commands**, **skills**, and **agents**.
73
-
74
- | Path | What it does |
75
- | -------------------------------- | ----------------------------------------------------- |
76
- | `.agents/commands/*.md` | Custom slash commands (`/name`) |
77
- | `.claude/commands/*.md` | Claude-compatible custom commands |
78
- | `.agents/agents/*.md` | Custom agents |
79
- | `.claude/agents/*.md` | Alternate `.claude` path for custom agents |
80
- | `.agents/skills/<name>/SKILL.md` | Reusable skill instructions (`@name`) |
81
- | `.claude/skills/<name>/SKILL.md` | Claude-compatible skills |
82
- | `.agents/AGENTS.md` | Preferred local project context |
83
- | `CLAUDE.md` | Local fallback context if `.agents/AGENTS.md` is absent |
84
- | `AGENTS.md` | Local fallback context if `.agents/AGENTS.md` and `CLAUDE.md` are absent |
85
- | `~/.agents/AGENTS.md` | Preferred global context, prepended before local context |
86
- | `~/.agents/CLAUDE.md` | Global fallback context if `~/.agents/AGENTS.md` is absent |
87
-
88
- Global commands, agents, and skills also work from `~/.agents/...` and `~/.claude/...`.
89
-
90
- For commands, skills, and agents: local overrides global, and `.agents` overrides `.claude` at the same scope. Context files are combined differently: global context is injected first, then local context. → [docs/configs.md](docs/configs.md)
91
-
92
- ---
93
-
94
- ## 🚀 Getting Started
17
+ ## Quick Start
95
18
 
96
- One thing before you dive in: **I run on Bun**. You can install me via npm just fine, but [Bun](https://bun.com) still needs to be on your machine — no way around it.
19
+ I run on [Bun](https://bun.com) install me via bun or npm, but Bun needs to be on your machine.
97
20
 
98
21
  ```bash
99
- # Install globally
100
- bun add -g mini-coder
101
- # or: npm install -g mini-coder
102
-
103
- # Set a provider key (pick one — or run Ollama locally)
104
- export OPENCODE_API_KEY=your-zen-key # recommended
105
- export ANTHROPIC_API_KEY=your-key # direct Anthropic
106
- export OPENAI_API_KEY=your-key # direct OpenAI
107
- export GOOGLE_API_KEY=your-key # direct Gemini
108
- # or: export GEMINI_API_KEY=your-key
109
-
110
- # Optional extras
111
- export OLLAMA_BASE_URL=http://localhost:11434
112
- export EXA_API_KEY=your-exa-key # enables webSearch/webContent
113
-
114
- # Launch
115
- mc
116
- ```
117
-
118
- Or drop me a prompt straight away for one-shot mode (runs once, then exits):
22
+ # Install
23
+ bun add -g mini-coder # or: npm install -g mini-coder
119
24
 
120
- ```bash
121
- mc "Refactor the auth module to use async/await"
122
- ```
25
+ # Set one API key (pick any)
26
+ export OPENCODE_API_KEY=your-key # recommended
27
+ export ANTHROPIC_API_KEY=your-key # direct Anthropic
28
+ export OPENAI_API_KEY=your-key # direct OpenAI
29
+ export GOOGLE_API_KEY=your-key # direct Gemini (or GEMINI_API_KEY)
123
30
 
124
- Useful flags:
31
+ # Optional
32
+ export OLLAMA_BASE_URL=http://localhost:11434 # local models
33
+ export EXA_API_KEY=your-key # web search tools
125
34
 
126
- ```bash
127
- mc -c # continue last session
128
- mc -r <id> # resume a specific session
129
- mc -l # list recent sessions
130
- mc -m zen/claude-sonnet-4-6 # pick a model
131
- mc --cwd ~/src/other-project # set working directory
132
- mc -h # show help
35
+ # Go
36
+ mc
133
37
  ```
134
38
 
135
- ---
136
-
137
- ## 🗃️ App data
138
-
139
- Everything I remember lives in `~/.config/mini-coder/` — here's what I'm holding onto:
39
+ One-shot mode: `mc "refactor auth to use async/await"` — runs once, then exits.
140
40
 
141
- - `sessions.db` your full session history, MCP server config, and model metadata, all in one tidy SQLite file
142
- - `api.log` — a request/response log for every provider call this run, if you want to peek under the hood
143
- - `errors.log` — anything that went sideways, caught and written down so you can actually debug it
41
+ Useful flags: `-c` continue last session, `-r <id>` resume, `-l` list sessions, `-m <model>` pick a model, `-h` help.
144
42
 
145
43
  ---
146
44
 
147
- ## 📚 Go Deeper
45
+ ## 🔑 OAuth Login
148
46
 
149
- The README hits the highlights the docs have the full story:
150
-
151
- - [docs/custom-commands.md](docs/custom-commands.md)
152
- - [docs/custom-agents.md](docs/custom-agents.md)
153
- - [docs/skills.md](docs/skills.md)
154
- - [docs/configs.md](docs/configs.md)
155
-
156
- ## 🗂️ Project Structure
157
-
158
- ```
159
- src/
160
- index.ts # Entry point + CLI arg parsing
161
- agent/ # Main REPL loop + tool registry
162
- cli/ # Input, output, slash commands, markdown rendering
163
- llm-api/ # Provider factory + streaming turn logic
164
- tools/ # shell, subagent, skill tools
165
- # + webSearch, webContent
166
- internal/ # shared internals, including the mc-edit helper implementation
167
- mcp/ # MCP server connections
168
- session/ # SQLite-backed session & history management
169
- ```
47
+ Use `/login` inside the REPL to authenticate via browser-based OAuth (currently Anthropic). No need to manage API keys manually.
170
48
 
171
49
  ---
172
50
 
173
- ## 🔮 Tech Stack
51
+ ## 🛠️ Features
174
52
 
175
- - **Runtime:** [Bun.js](https://bun.com) fast, modern, all-in-one
176
- - **LLM routing:** [AI SDK](https://ai-sdk.dev) multi-provider with streaming
177
- - **Colors:** [yoctocolors](https://github.com/sindresorhus/yoctocolors)tiny and terminal-theme-aware
178
- - **Schema validation:** [Zod](https://zod.dev)
179
- - **Linting/formatting:** [Biome](https://biomejs.dev)
180
- - **Storage:** `bun:sqlite`zero-dependency local sessions
53
+ - **Multi-provider**auto-discovers Anthropic, OpenAI, Gemini, Ollama, or any OpenAI-compatible endpoint
54
+ - **Session memory** SQLite-backed. Resume with `-c` or `-r <id>`
55
+ - **Shell integration**`!` prefix for inline commands, `@` to reference files with tab completion
56
+ - **Subagents** spawn parallel mini-coder instances for independent subtasks
57
+ - **Web search** — `webSearch` + `webContent` tools when `EXA_API_KEY` is set
58
+ - **MCP support**connect external tool servers over HTTP or stdio
59
+ - **Custom commands** — drop `.md` files in `.agents/commands/` → instant `/slash` commands
60
+ - **Custom agents** — `.agents/agents/*.md` for specialized personas, usable as primary or via subagent
61
+ - **Skills** — `.agents/skills/<name>/SKILL.md`, inject with `@name`
62
+ - **`mc-edit`** — safe, exact-text file editing (no full-file rewrites)
63
+ - **16 ANSI colors** — inherits your terminal theme. Always looks right.
181
64
 
182
65
  ---
183
66
 
184
- ## 💬 Philosophy
67
+ ## 📚 Getting Deeper
185
68
 
186
- > Accurate. Fast. Focused on the conversation.
69
+ The README is the highlight reel. For the full story — slash commands, config folders, context files, app data, and everything else:
187
70
 
188
- I believe the best tools disappear into your workflow. I don't want to be the star of the show — I want _you_ to ship great code, faster.
71
+ **[📖 Read the Full Manual](docs/mini-coder.1.md)**
189
72
 
190
73
  ---
191
74
 
192
- ## 💬 What People Are Saying
75
+ ## 🔮 Tech Stack
193
76
 
194
- > "sean this is fucking sick"
195
- > — [vpr99](https://github.com/vpr99)
77
+ [Bun.js](https://bun.com) · [AI SDK](https://ai-sdk.dev) · [yoctocolors](https://github.com/sindresorhus/yoctocolors)
196
78
 
197
- ---
79
+ ## 📄 License
198
80
 
199
- _Built with ❤️ and a healthy obsession with terminal aesthetics._
81
+ MIT [github.com/sacenox/mini-coder](https://github.com/sacenox/mini-coder)
package/dist/mc.js CHANGED
@@ -9,7 +9,7 @@ import * as c23 from "yoctocolors";
9
9
  import * as c12 from "yoctocolors";
10
10
 
11
11
  // src/cli/load-markdown-configs.ts
12
- import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
12
+ import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
13
13
  import { homedir as homedir4 } from "os";
14
14
  import { basename, join as join5 } from "path";
15
15
 
@@ -99,16 +99,58 @@ ${content}
99
99
  }
100
100
 
101
101
  // src/cli/error-log.ts
102
- import { mkdirSync, writeFileSync } from "fs";
102
+ import { writeFileSync } from "fs";
103
+
104
+ // src/cli/log-paths.ts
105
+ import { mkdirSync, readdirSync, unlinkSync } from "fs";
103
106
  import { homedir } from "os";
104
107
  import { join as join2 } from "path";
108
+ var LOGS_DIR = join2(homedir(), ".config", "mini-coder", "logs");
109
+ function getLogsDir() {
110
+ mkdirSync(LOGS_DIR, { recursive: true });
111
+ return LOGS_DIR;
112
+ }
113
+ function pidLogPath(prefix) {
114
+ return join2(getLogsDir(), `${prefix}-${process.pid}.log`);
115
+ }
116
+ function isProcessAlive(pid) {
117
+ try {
118
+ process.kill(pid, 0);
119
+ return true;
120
+ } catch {
121
+ return false;
122
+ }
123
+ }
124
+ var LOG_FILE_RE = /^(api|errors)-(\d+)\.log$/;
125
+ function cleanStaleLogs() {
126
+ let entries;
127
+ try {
128
+ entries = readdirSync(LOGS_DIR);
129
+ } catch {
130
+ return;
131
+ }
132
+ for (const name of entries) {
133
+ const m = LOG_FILE_RE.exec(name);
134
+ if (!m)
135
+ continue;
136
+ const pidStr = m[2];
137
+ const pid = Number.parseInt(pidStr, 10);
138
+ if (pid === process.pid)
139
+ continue;
140
+ if (!isProcessAlive(pid)) {
141
+ try {
142
+ unlinkSync(join2(LOGS_DIR, name));
143
+ } catch {}
144
+ }
145
+ }
146
+ }
147
+
148
+ // src/cli/error-log.ts
105
149
  var writer = null;
106
150
  function initErrorLog() {
107
151
  if (writer)
108
152
  return;
109
- const dirPath = join2(homedir(), ".config", "mini-coder");
110
- const logPath = join2(dirPath, "errors.log");
111
- mkdirSync(dirPath, { recursive: true });
153
+ const logPath = pidLogPath("errors");
112
154
  writeFileSync(logPath, "");
113
155
  writer = Bun.file(logPath).writer();
114
156
  process.on("uncaughtException", (err) => {
@@ -295,7 +337,7 @@ import {
295
337
  closeSync,
296
338
  existsSync as existsSync2,
297
339
  openSync,
298
- readdirSync,
340
+ readdirSync as readdirSync2,
299
341
  readFileSync as readFileSync2,
300
342
  readSync,
301
343
  statSync
@@ -384,7 +426,7 @@ function listSkillCandidates(skillsDir, source, rootPath) {
384
426
  return [];
385
427
  let entries;
386
428
  try {
387
- entries = readdirSync(skillsDir).sort((a, b) => a.localeCompare(b));
429
+ entries = readdirSync2(skillsDir).sort((a, b) => a.localeCompare(b));
388
430
  } catch {
389
431
  return [];
390
432
  }
@@ -1367,6 +1409,8 @@ function buildShellSummaryParts(opts) {
1367
1409
  function shouldPreviewShellStdout(opts) {
1368
1410
  if (opts.stdoutLines === 0)
1369
1411
  return false;
1412
+ if (opts.stdoutSingleLine !== null && opts.stderrLines === 0)
1413
+ return false;
1370
1414
  if (!opts.success || opts.stderrLines > 0)
1371
1415
  return true;
1372
1416
  return opts.stdoutSingleLine === null;
@@ -1514,6 +1558,7 @@ function renderWebContentResult(result, opts) {
1514
1558
  function renderMcpResult(result, opts) {
1515
1559
  const content = Array.isArray(result) ? result : [result];
1516
1560
  const maxBlocks = opts?.verboseOutput ? content.length : 5;
1561
+ let rendered = false;
1517
1562
  for (const block of content.slice(0, maxBlocks)) {
1518
1563
  if (block?.type === "text" && block.text) {
1519
1564
  const maxLines = opts?.verboseOutput ? Number.POSITIVE_INFINITY : 6;
@@ -1521,9 +1566,10 @@ function renderMcpResult(result, opts) {
1521
1566
  `).slice(0, maxLines);
1522
1567
  for (const line of lines)
1523
1568
  writeln(` ${c4.dim("\u2502")} ${line}`);
1569
+ rendered = true;
1524
1570
  }
1525
1571
  }
1526
- return true;
1572
+ return rendered;
1527
1573
  }
1528
1574
  var TOOL_RESULT_RENDERERS = {
1529
1575
  shell: renderShellResult,
@@ -1553,6 +1599,10 @@ function toolGlyph(name) {
1553
1599
  return G.read;
1554
1600
  if (name === "listSkills")
1555
1601
  return G.search;
1602
+ if (name === "webSearch")
1603
+ return G.search;
1604
+ if (name === "webContent")
1605
+ return G.read;
1556
1606
  if (name.startsWith("mcp_"))
1557
1607
  return G.mcp;
1558
1608
  return G.info;
@@ -1580,6 +1630,17 @@ function buildToolCallLine(name, args) {
1580
1630
  const skillName = typeof a.name === "string" ? a.name : "";
1581
1631
  return `${G.read} ${c5.dim("read skill")}${skillName ? ` ${skillName}` : ""}`;
1582
1632
  }
1633
+ if (name === "webSearch") {
1634
+ const query = typeof a.query === "string" ? a.query : "";
1635
+ const short = query.length > 60 ? `${query.slice(0, 57)}\u2026` : query;
1636
+ return `${G.search} ${c5.dim("search")}${short ? ` ${short}` : ""}`;
1637
+ }
1638
+ if (name === "webContent") {
1639
+ const urls = Array.isArray(a.urls) ? a.urls : [];
1640
+ const label = urls.length === 1 ? String(urls[0]) : `${urls.length} url${urls.length !== 1 ? "s" : ""}`;
1641
+ const short = label.length > 60 ? `${label.slice(0, 57)}\u2026` : label;
1642
+ return `${G.read} ${c5.dim("fetch")} ${short}`;
1643
+ }
1583
1644
  if (name.startsWith("mcp_")) {
1584
1645
  return `${G.mcp} ${c5.dim(name)}`;
1585
1646
  }
@@ -1621,6 +1682,8 @@ async function renderTurn(events, spinner, opts) {
1621
1682
  let contextTokens = 0;
1622
1683
  let newMessages = [];
1623
1684
  const startedToolCalls = new Set;
1685
+ const toolCallInfo = new Map;
1686
+ let parallelCallCount = 0;
1624
1687
  let renderedVisibleOutput = false;
1625
1688
  let reasoningComputed = false;
1626
1689
  let reasoningText = "";
@@ -1657,11 +1720,19 @@ async function renderTurn(events, spinner, opts) {
1657
1720
  if (startedToolCalls.has(event.toolCallId)) {
1658
1721
  break;
1659
1722
  }
1723
+ const isConsecutiveToolCall = startedToolCalls.size > 0 && toolCallInfo.size > 0;
1660
1724
  startedToolCalls.add(event.toolCallId);
1725
+ toolCallInfo.set(event.toolCallId, {
1726
+ toolName: event.toolName,
1727
+ label: buildToolCallLine(event.toolName, event.args)
1728
+ });
1729
+ if (toolCallInfo.size > 1) {
1730
+ parallelCallCount = toolCallInfo.size;
1731
+ }
1661
1732
  liveReasoning.finish();
1662
1733
  content.flushOpenContent();
1663
1734
  spinner.stop();
1664
- if (renderedVisibleOutput)
1735
+ if (renderedVisibleOutput && !isConsecutiveToolCall)
1665
1736
  writeln();
1666
1737
  renderToolCall(event.toolName, event.args);
1667
1738
  renderedVisibleOutput = true;
@@ -1670,8 +1741,15 @@ async function renderTurn(events, spinner, opts) {
1670
1741
  }
1671
1742
  case "tool-result": {
1672
1743
  startedToolCalls.delete(event.toolCallId);
1744
+ const callInfo = toolCallInfo.get(event.toolCallId);
1745
+ toolCallInfo.delete(event.toolCallId);
1673
1746
  liveReasoning.finish();
1674
1747
  spinner.stop();
1748
+ if (parallelCallCount > 1 && callInfo) {
1749
+ writeln(` ${c6.dim("\u21B3")} ${callInfo.label}`);
1750
+ }
1751
+ if (toolCallInfo.size === 0)
1752
+ parallelCallCount = 0;
1675
1753
  renderToolResult(event.toolName, event.result, event.isError, {
1676
1754
  verboseOutput
1677
1755
  });
@@ -1725,7 +1803,7 @@ async function renderTurn(events, spinner, opts) {
1725
1803
 
1726
1804
  // src/cli/output.ts
1727
1805
  var HOME = homedir3();
1728
- var PACKAGE_VERSION = "0.0.23";
1806
+ var PACKAGE_VERSION = "0.1.1";
1729
1807
  function tildePath(p) {
1730
1808
  return p.startsWith(HOME) ? `~${p.slice(HOME.length)}` : p;
1731
1809
  }
@@ -1948,7 +2026,7 @@ function loadMarkdownConfigs(opts) {
1948
2026
  return configs;
1949
2027
  let entries;
1950
2028
  try {
1951
- entries = readdirSync2(dir);
2029
+ entries = readdirSync3(dir);
1952
2030
  } catch {
1953
2031
  return configs;
1954
2032
  }
@@ -2088,7 +2166,7 @@ async function connectMcpServer(config) {
2088
2166
 
2089
2167
  // src/session/db/connection.ts
2090
2168
  import { Database } from "bun:sqlite";
2091
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, unlinkSync } from "fs";
2169
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
2092
2170
  import { homedir as homedir5 } from "os";
2093
2171
  import { join as join6 } from "path";
2094
2172
  function getConfigDir() {
@@ -2219,7 +2297,7 @@ function getDb() {
2219
2297
  } catch {}
2220
2298
  for (const path of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
2221
2299
  if (existsSync5(path))
2222
- unlinkSync(path);
2300
+ unlinkSync2(path);
2223
2301
  }
2224
2302
  db = new Database(dbPath, { create: true });
2225
2303
  configureDb(db);
@@ -3502,17 +3580,13 @@ async function getAccessToken(providerId) {
3502
3580
  }
3503
3581
 
3504
3582
  // src/llm-api/api-log.ts
3505
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "fs";
3506
- import { homedir as homedir6 } from "os";
3507
- import { join as join9 } from "path";
3583
+ import { writeFileSync as writeFileSync2 } from "fs";
3508
3584
  var writer2 = null;
3509
3585
  var MAX_ENTRY_BYTES = 8 * 1024;
3510
3586
  function initApiLog() {
3511
3587
  if (writer2)
3512
3588
  return;
3513
- const dirPath = join9(homedir6(), ".config", "mini-coder");
3514
- const logPath = join9(dirPath, "api.log");
3515
- mkdirSync3(dirPath, { recursive: true });
3589
+ const logPath = pidLogPath("api");
3516
3590
  writeFileSync2(logPath, "");
3517
3591
  writer2 = Bun.file(logPath).writer();
3518
3592
  }
@@ -3933,24 +4007,31 @@ async function fetchOpenAIModels() {
3933
4007
  free: false
3934
4008
  }));
3935
4009
  }
3936
- async function getAnthropicApiKey() {
4010
+ async function getAnthropicAuth() {
3937
4011
  const envKey = process.env.ANTHROPIC_API_KEY;
3938
4012
  if (envKey)
3939
- return envKey;
3940
- if (isLoggedIn("anthropic"))
3941
- return getAccessToken("anthropic");
4013
+ return { key: envKey, oauth: false };
4014
+ if (isLoggedIn("anthropic")) {
4015
+ const token = await getAccessToken("anthropic");
4016
+ if (token)
4017
+ return { key: token, oauth: true };
4018
+ }
3942
4019
  return null;
3943
4020
  }
3944
4021
  async function fetchAnthropicModels() {
3945
- const key = await getAnthropicApiKey();
3946
- if (!key)
4022
+ const auth = await getAnthropicAuth();
4023
+ if (!auth)
3947
4024
  return null;
3948
- const payload = await fetchJson(`${ANTHROPIC_BASE}/v1/models`, {
3949
- headers: {
3950
- "x-api-key": key,
3951
- "anthropic-version": "2023-06-01"
3952
- }
3953
- }, 6000);
4025
+ const headers = {
4026
+ "anthropic-version": "2023-06-01"
4027
+ };
4028
+ if (auth.oauth) {
4029
+ headers.Authorization = `Bearer ${auth.key}`;
4030
+ headers["anthropic-beta"] = "oauth-2025-04-20";
4031
+ } else {
4032
+ headers["x-api-key"] = auth.key;
4033
+ }
4034
+ const payload = await fetchJson(`${ANTHROPIC_BASE}/v1/models`, { headers }, 6000);
3954
4035
  return processModelsList(payload, "data", "id", (item, modelId) => {
3955
4036
  const displayName = typeof item.display_name === "string" && item.display_name.trim().length > 0 ? item.display_name : modelId;
3956
4037
  return {
@@ -5487,8 +5568,8 @@ function getMostRecentSession() {
5487
5568
 
5488
5569
  // src/agent/system-prompt.ts
5489
5570
  import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
5490
- import { homedir as homedir7 } from "os";
5491
- import { join as join10 } from "path";
5571
+ import { homedir as homedir6 } from "os";
5572
+ import { join as join9 } from "path";
5492
5573
  function tryReadFile(p) {
5493
5574
  if (!existsSync6(p))
5494
5575
  return null;
@@ -5499,11 +5580,11 @@ function tryReadFile(p) {
5499
5580
  }
5500
5581
  }
5501
5582
  function loadGlobalContextFile(homeDir) {
5502
- const globalDir = join10(homeDir, ".agents");
5503
- return tryReadFile(join10(globalDir, "AGENTS.md")) ?? tryReadFile(join10(globalDir, "CLAUDE.md"));
5583
+ const globalDir = join9(homeDir, ".agents");
5584
+ return tryReadFile(join9(globalDir, "AGENTS.md")) ?? tryReadFile(join9(globalDir, "CLAUDE.md"));
5504
5585
  }
5505
5586
  function loadLocalContextFile(cwd) {
5506
- return tryReadFile(join10(cwd, ".agents", "AGENTS.md")) ?? tryReadFile(join10(cwd, "CLAUDE.md")) ?? tryReadFile(join10(cwd, "AGENTS.md"));
5587
+ return tryReadFile(join9(cwd, ".agents", "AGENTS.md")) ?? tryReadFile(join9(cwd, "CLAUDE.md")) ?? tryReadFile(join9(cwd, "AGENTS.md"));
5507
5588
  }
5508
5589
  var AUTONOMY = `
5509
5590
 
@@ -5545,7 +5626,7 @@ var FINAL_MESSAGE = `
5545
5626
  - If verification could not be run, say so clearly.`;
5546
5627
  var SUBAGENT_DELEGATION = `You are running as a subagent. Complete the task you have been given directly using your tools. Do not spawn further subagents unless the subtask is unambiguously separable and self-contained.`;
5547
5628
  function buildSystemPrompt(sessionTimeAnchor, cwd, extraSystemPrompt, isSubagent, homeDir) {
5548
- const globalContext = loadGlobalContextFile(homeDir ?? homedir7());
5629
+ const globalContext = loadGlobalContextFile(homeDir ?? homedir6());
5549
5630
  const localContext = loadLocalContextFile(cwd);
5550
5631
  const cwdDisplay = tildePath(cwd);
5551
5632
  let prompt = `You are mini-coder, a small and fast CLI coding agent.
@@ -6094,7 +6175,7 @@ import { z as z2 } from "zod";
6094
6175
 
6095
6176
  // src/internal/file-edit/command.ts
6096
6177
  import { existsSync as existsSync7 } from "fs";
6097
- import { dirname as dirname2, extname, join as join11 } from "path";
6178
+ import { dirname as dirname2, extname, join as join10 } from "path";
6098
6179
  import { fileURLToPath } from "url";
6099
6180
  function quoteShellArg(value) {
6100
6181
  return `'${value.replaceAll("'", `'\\''`)}'`;
@@ -6106,7 +6187,7 @@ function resolveSiblingFileEditScript(scriptPath) {
6106
6187
  const mainDir = dirname2(scriptPath);
6107
6188
  const mainBase = scriptPath.slice(mainDir.length + 1);
6108
6189
  if (mainBase === `index${ext}` || mainBase === `mc${ext}`) {
6109
- return join11(mainDir, `mc-edit${ext}`);
6190
+ return join10(mainDir, `mc-edit${ext}`);
6110
6191
  }
6111
6192
  return null;
6112
6193
  }
@@ -6115,7 +6196,7 @@ function resolveModuleLocalFileEditScript(moduleUrl) {
6115
6196
  const ext = extname(modulePath);
6116
6197
  if (!ext)
6117
6198
  return null;
6118
- const helperPath = join11(dirname2(modulePath), "..", "..", `mc-edit${ext}`);
6199
+ const helperPath = join10(dirname2(modulePath), "..", "..", `mc-edit${ext}`);
6119
6200
  return existsSync7(helperPath) ? helperPath : null;
6120
6201
  }
6121
6202
  function resolveFileEditCommand(execPath, mainModule, argv1, moduleUrl = import.meta.url) {
@@ -6482,7 +6563,7 @@ function parseArgs(argv) {
6482
6563
  const args = {
6483
6564
  model: null,
6484
6565
  sessionId: null,
6485
- listSessions: false,
6566
+ list: false,
6486
6567
  resumeLast: false,
6487
6568
  prompt: null,
6488
6569
  cwd: process.cwd(),
@@ -6509,7 +6590,7 @@ function parseArgs(argv) {
6509
6590
  break;
6510
6591
  case "--list":
6511
6592
  case "-l":
6512
- args.listSessions = true;
6593
+ args.list = true;
6513
6594
  break;
6514
6595
  case "--cwd":
6515
6596
  args.cwd = argv[++i] ?? process.cwd();
@@ -6581,9 +6662,9 @@ ${c13.bold("Examples:")}`);
6581
6662
  }
6582
6663
 
6583
6664
  // src/cli/bootstrap.ts
6584
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
6585
- import { homedir as homedir8 } from "os";
6586
- import { join as join12 } from "path";
6665
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
6666
+ import { homedir as homedir7 } from "os";
6667
+ import { join as join11 } from "path";
6587
6668
  import * as c14 from "yoctocolors";
6588
6669
  var REVIEW_COMMAND_CONTENT = `---
6589
6670
  description: Review recent changes for correctness, code quality, and performance
@@ -6603,17 +6684,17 @@ Perform a sensible code review:
6603
6684
  Output a small summary with only the issues found. If nothing is wrong, say so.
6604
6685
  `;
6605
6686
  function bootstrapGlobalDefaults() {
6606
- const commandsDir = join12(homedir8(), ".agents", "commands");
6607
- const reviewPath = join12(commandsDir, "review.md");
6687
+ const commandsDir = join11(homedir7(), ".agents", "commands");
6688
+ const reviewPath = join11(commandsDir, "review.md");
6608
6689
  if (!existsSync8(reviewPath)) {
6609
- mkdirSync4(commandsDir, { recursive: true });
6690
+ mkdirSync3(commandsDir, { recursive: true });
6610
6691
  writeFileSync3(reviewPath, REVIEW_COMMAND_CONTENT, "utf-8");
6611
6692
  writeln(`${c14.green("\u2713")} created ${c14.dim("~/.agents/commands/review.md")} ${c14.dim("(edit it to customise your reviews)")}`);
6612
6693
  }
6613
6694
  }
6614
6695
 
6615
6696
  // src/cli/file-refs.ts
6616
- import { join as join13 } from "path";
6697
+ import { join as join12 } from "path";
6617
6698
  async function resolveFileRefs(text, cwd) {
6618
6699
  const atPattern = /@([\w./\-_]+)/g;
6619
6700
  let result = text;
@@ -6641,7 +6722,7 @@ ${content}
6641
6722
  result = result.slice(0, match.index) + replacement + result.slice((match.index ?? 0) + match[0].length);
6642
6723
  continue;
6643
6724
  }
6644
- const filePath = ref.startsWith("/") ? ref : join13(cwd, ref);
6725
+ const filePath = ref.startsWith("/") ? ref : join12(cwd, ref);
6645
6726
  if (isImageFilename(ref)) {
6646
6727
  const attachment = await loadImageFile(filePath);
6647
6728
  if (attachment) {
@@ -7570,6 +7651,9 @@ function writeJsonLine(write2, payload) {
7570
7651
  registerTerminalCleanup();
7571
7652
  initErrorLog();
7572
7653
  initApiLog();
7654
+ if (!process.env.MC_SUBAGENT_DEPTH || process.env.MC_SUBAGENT_DEPTH === "0") {
7655
+ cleanStaleLogs();
7656
+ }
7573
7657
  initModelInfoCache();
7574
7658
  pruneOldData();
7575
7659
  refreshModelInfoInBackground().catch(() => {});
@@ -7603,7 +7687,7 @@ async function main() {
7603
7687
  printHelp();
7604
7688
  process.exit(0);
7605
7689
  }
7606
- if (args.listSessions) {
7690
+ if (args.list) {
7607
7691
  printSessionList();
7608
7692
  process.exit(0);
7609
7693
  }