Metadata-Version: 2.3
Name: mini-agent-cli
Version: 0.2.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-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`。

```
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 @zhaomo08/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 -p "这个仓库是干什么的" > out.md   # 问一句就退出；stdout 只有答案，思考和工具进度走 stderr
mini-agent -m bailian-anthropic/qwen3.8-flash
mini-agent --yes                            # ask 类操作全部放行（deny 仍然禁止）
```

| 命令 | 作用 |
|---|---|
| `/model [provider/model]` | 查看或切换模型。对话保留，可以跨厂商、跨协议 |
| `/reload` | 重新读配置、skills、MCP，对话保留 |
| `/img [路径]` | 挂一张图，不带路径从剪贴板取；也可以直接把图片文件拖进来 |
| `/clear` `/tools` `/help` | |
| Ctrl+C | 回答中：打断这一轮（包括审批提示）；输入时：清空当前行 |
| Ctrl+D / `exit` | 退出 |

输出是流式的，思考过程以灰色显示。

## 配置：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
```
