Metadata-Version: 2.3
Name: mini-agent-cli
Version: 0.4.0
Summary: 终端里的编码 Agent：配置驱动，支持 OpenAI Chat / Responses 与 Anthropic 协议、MCP、Agent Skills
Keywords: agent,llm,mcp,cli,coding-agent,skills
Author: zhaomo08
Classifier: Environment :: Console
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development
Requires-Dist: mcp>=2.1.1
Requires-Dist: prompt-toolkit>=3.0.53
Requires-Dist: rich>=15.0.0
Requires-Python: >=3.13
Project-URL: Repository, https://github.com/zhaomo08/mini-agent
Description-Content-Type: text/markdown

# mini-agent

终端里的编码 Agent，面向会写配置的开发者。模型、MCP、权限都写在一个 `mini-agent.json` 里，
skill 用和 Claude Code / Codex / opencode 相同的 `SKILL.md` 格式。依赖只有 `mcp`、`prompt_toolkit`（交互输入）和 `rich`（渲染）。

```
Agent = LLM + 工具 + 循环
```

## 安装

三种方式任选，装完都是 `mini-agent` 命令。需要先有 [uv](https://docs.astral.sh/uv/)（`curl -LsSf https://astral.sh/uv/install.sh | sh` 或 `brew install uv`），它会自动准备 Python 3.13：

```bash
uv tool install mini-agent-cli        # PyPI（包名带 -cli，命令是 mini-agent）
npm i -g @chesterzhao/mini-agent         # npm（启动器，内部用 uvx 跑同版本的 PyPI 包）
uvx --from mini-agent-cli mini-agent  # 不安装，直接跑
```

首次运行会把包里的 `mini-agent.example.json` 复制到 `~/.config/mini-agent/mini-agent.json`，改这份就行。

开发时用 `uv tool install -e .`，改代码即时生效。

## 使用

```bash
mini-agent                                  # 交互模式，在哪个目录启动就在哪干活
mini-agent -c                               # 接着这个项目最近一次的会话
mini-agent -p "@src/app.py 这个文件有什么问题" > out.md   # 问一句就退出；stdout 只有答案
mini-agent -m bailian-anthropic/qwen3.8-flash
mini-agent --yes                            # ask 类操作全部放行（deny 仍然禁止）
```

### 交互

- 输入 `/` 弹出命令菜单，输入 `@` 弹出项目文件（`git ls-files`，尊重 .gitignore）。Tab 补全，↑ ↓ 翻历史
- 图片和 Claude Code 一样放进同一句话里：**Ctrl+V** 贴剪贴板里的截图（或在访达里 Cmd+C 复制的图片文件），
  **把图片拖进终端**也行，都会变成 `[Image #1]` 占位符，接着打字提问，一起发出去。编号整个会话有效，
  后面可以说「再看下 [Image #1]」
- 回答按 Markdown 渲染：一段写完整就定格显示，代码块高亮；底部一行状态显示 `✻ 思考中…` / `✻ 回答中…`
- 工具调用显示成一行，下面是结果摘要，失败的标红：

  ```
  ● read  app.py
    ⎿      1  def greet(name):
  ```

- 审批按一个键：`y` 允许、`a` 本次会话都允许、`n` / Esc 拒绝、Ctrl+C 中断这一轮。改文件时显示彩色 diff
  - `a` 的范围：命令按开头的词（`git ...`）；带 `;` `&&` `|` `$()` 这类拼接的命令只放行完全相同的那一条
- 当前模型因为 key / 余额 / 模型名用不了时，按数字换一个模型自动重发，可以顺手设为默认
- **Shift+Tab** 切换模式：默认（逐项询问）→ 自动改文件（改文件不问，命令仍问）→ 计划（只读，只出方案不动手）
- **多行输入**：Alt+Enter 换行，或者行尾打 `\` 再回车；粘贴多行原样保留
- **任务清单**：三步以上的任务，模型用 `todo` 工具列出步骤并随进度打勾（☐ ◐ ☑）
- 底部状态栏：模型 · 目录 · 模式 · 上下文 token · 任务进度

| 命令 | 作用 |
|---|---|
| `/model [provider/model]` | 看或切换模型，对话保留，可以跨厂商、跨协议。补全候选是启动后向各家实时拉取的模型列表 |
| `/resume` | 选一个这个项目之前的会话接着聊 |
| `/clear` | 开始新会话（旧的还能 `/resume` 找回） |
| `/compact [重点]` | 把对话压缩成交接摘要，腾出上下文；历史超出预算时也会自动压缩 |
| `/reload` | 重新读配置、skills、MCP、项目说明、自定义命令，对话保留 |
| `/<命令名>` | 自定义命令（见下）；skill 也能直接 `/<skill 名>` 调用 |
| `/skills` `/tools` `/help` `/exit` | |
| Ctrl+C | 回答中：打断这一轮，已写出的部分留在历史里；输入时：清空当前行 |
| Ctrl+D | 退出 |

### 自定义命令

和 Claude Code 同一个格式：一个 md 文件就是一条命令，文件名即命令名。

```
<项目>/.agents/commands/review.md     →  /review（也认 .claude/commands/）
~/.config/mini-agent/commands/*.md    全局；~/.agents/commands/*.md 和其他 Agent 共用
```

```markdown
---
description: 用三句话点评一个文件
---
读 $ARGUMENTS，用三句话点评它：写得好的、可以改进的、风险。
```

`$ARGUMENTS` 换成命令后面的参数（`/review app.py`）；同名时项目的覆盖全局的，内置命令优先。

### 项目说明

和 Codex / Claude Code 一样，从 git 根目录到当前目录，每层的 `AGENTS.md`、`CLAUDE.md`、`.claude/CLAUDE.md`
都会读进 system prompt（越靠近当前目录越靠后）；全局的写在 `~/.config/mini-agent/AGENTS.md`。
**不读** `~/.claude/CLAUDE.md`：那是 Claude Code 的私人配置，常带本机密码之类，读进来会发给第三方模型。

### 本地状态

`~/.local/state/mini-agent/` 下：`sessions/<项目>/` 会话、`history` 输入历史、`models.json` 用过的模型、`mcp.log` MCP server 日志。

## 配置：mini-agent.json

完整带注释的示例见 [mini_agent/mini-agent.example.json](mini_agent/mini-agent.example.json)。

| 位置 | 作用 |
|---|---|
| `~/.config/mini-agent/mini-agent.json` | 全局（`$MINI_AGENT_CONFIG` 可改） |
| 项目里的 `mini-agent.json` | 从 git 根目录到当前目录逐层深度合并，越近优先级越高 |

```jsonc
{
  "model": "deepseek/deepseek-flash",
  "provider": {
    "deepseek": { "api": "openai-chat", "baseURL": "https://api.deepseek.com", "apiKey": "{env:DEEPSEEK_API_KEY}" }
  },
  "mcp": {
    "memory": { "type": "local",  "command": ["npx", "-y", "@modelcontextprotocol/server-memory"] },
    "amap":   { "type": "remote", "url": "https://mcp.amap.com/mcp?key={env:AMAP_MAPS_API_KEY}" }
  },
  "permission": { "bash": "ask", "edit": "ask", "external": "ask", "mcp": "ask" }
}
```

**协议**（`api` 字段），baseURL 的写法和各家 SDK 一致：

| api | 请求 | baseURL 示例 |
|---|---|---|
| `openai-chat` | `POST {baseURL}/chat/completions` | `https://api.deepseek.com` |
| `openai-responses` | `POST {baseURL}/responses` | `https://api.openai.com/v1` |
| `anthropic` | `POST {baseURL}/v1/messages` | `https://api.anthropic.com` |

`options` 原样并进请求体（`enable_thinking`、`max_tokens`、`temperature`…），
`models.<id>.options` 只对某个模型生效。密钥写 `{env:变量名}`；配置文件支持整行 `//` 注释。

**让模型改配置**：直接说「加一个 xxx MCP」。内置的 `mini-agent-config` skill 会引导模型
用 edit 改文件、校验 JSON，然后你输入 `/reload` 就生效。

## 工具

| 工具 | 说明 | 权限类别 |
|---|---|---|
| `bash` | 执行命令；awk / sed / jq 都走它 | `bash` |
| `read` | 带行号，默认 2000 行，`offset` 分段 | 工作目录外归 `external` |
| `write` / `edit` | 整体写入 / 精确字符串替换，确认时显示 diff | `edit` |
| `grep` | 有 ripgrep 就用 ripgrep，没有就用 grep -rn | 工作目录外归 `external` |
| `glob` | 按通配符找文件，最近修改的在前 | 工作目录外归 `external` |
| `skill` | 按需读取 skill 正文 | — |
| MCP 工具 | 名字以 create / delete / send / run … 开头的 | `mcp` |

权限：`ask` 先问，`allow` 直接放行，`deny` 禁止。非交互模式（管道或 `-p`）下没加 `--yes` 时，ask 一律按拒绝处理。

## Skills

一个目录加一个 `SKILL.md`（frontmatter 写 `name`、`description`），脚本和参考资料放在同一目录，
正文里让模型用 bash / read 去调用。启动时只把描述放进 system prompt，正文由模型按需读取。

查找顺序，同名的先找到先用：

1. 项目：从当前目录往上到 git 根目录，每层的 `.agents/skills`、`.claude/skills`、`.opencode/skills`
2. 全局：`~/.config/mini-agent/skills`、`~/.agents/skills`、`~/.claude/skills`、`~/.config/opencode/skills`
3. 内置：`mini_agent/skills/`

## 中途换模型

参考 pi-ai 的 cross-provider handoff 设计。历史用中立格式保存，每条 assistant 消息都记着它是由哪个协议、哪个模型产生的：

- **同一个模型产生的**：原样回放协议原文，thinking 签名、加密 reasoning 都不会丢
- **其他模型产生的**：只用中立字段重建。thinking 转成 `<thinking>` 文本，签名丢弃，
  工具调用 id 规范成 `[A-Za-z0-9_-]{1,64}`（Anthropic 的要求）
- **中断后留下的"有调用没结果"的工具调用**：补一条占位结果，保证每家接口都接受这段历史

## 上下文预算

- 工具结果超过 3 万字符就截断
- 只有最新一条消息保留图片
- 历史超过 40 万字符，就从最旧的一轮开始整轮丢弃

## 代码

```
mini_agent/
  __main__.py  命令行 / REPL        agent.py   装配、权限、循环、上下文预算
  config.py    配置加载与合并        llm.py     三种协议，标准库 urllib
  tools.py     内置工具              mcp.py     MCP 连接
  skills.py    skill 发现与加载      images.py  剪贴板 / 文件 → 多模态
test_agent.py  不调模型的自检：uv run python test_agent.py
```

排查用：`uv run python -m mini_agent.mcp`、`uv run python -m mini_agent.skills`。

早期的教学版（step1–3、extras/）在 git 历史里：`git show c619ecb`。

## 发布

版本号要在三个地方保持一致：`pyproject.toml`、`npm/package.json` 和 git tag。推送 tag 后，由
`.github/workflows/release.yml` 依次完成：检查版本号 → 自检 → 验证 wheel 能装 → 发布 PyPI → 发布 npm。
两边都用 Trusted Publishing（OIDC），仓库里不存任何 token。

```bash
# 改好两处 version 之后
git tag v0.2.1 && git push origin v0.2.1
```
