避免多 Day 实施中的细节漂移(目录乱跑、协议换栈、风格走样)。
与 spec.md(做什么)/ plan.md(按天做什么)的关系:本文件写"不要做错什么 + 已定下的硬决策"。
- 工作区:只写到
YOUR WORKSPACE DIRECTORY,不碰 Desktop / Downloads / /tmp 根 - git 写操作:不替用户执行 commit / push / reset /
checkout --,只生成提交信息措辞 - 顶层目录:不擅自新增 / 改名 / 移动 cmd/ internal/ apps/ docs/,发现被外部工具改动先停下报告
- 依赖:不引入第三方 Go module,除非 plan.md 显式允许或与用户对齐
- Provider 协议:Day 1 已选 OpenAI 兼容,Day 10 抽象前不要切到 Anthropic / Gemini 专用
- 退出码:成功 0;运行时错误 1;配置/输入错误 2
apps/ # Go module 根(不是仓库根)
├── cmd/minicode/main.go # CLI 入口
├── internal/agent/ # 模型循环、消息历史与工具分发
├── internal/provider/ # 模型协议 + OpenAI 兼容客户端(纯源码,无 _test.go)
│ ├── types.go
│ └── openai.go
├── internal/terminal/markdown.go # 终端检测与 Markdown 渲染
└── test/provider/ # 测试单独目录,black-box
└── openai_test.go # package provider_test
- 后续 Day:源码
apps/internal/<name>/,测试apps/test/<name>/,同名目录 internal/不依赖cmd/;第三方渲染与终端依赖集中在internal/terminal/,provider和tools继续只依赖标准库- go 命令全部
go -C apps build/test/vet ./...
- 中文 doc comment + 包注释
- 固定配置常量统一放在文件顶部(import 之后),不在函数内部声明 const
- 工具名称等业务标识使用命名常量,在声明和分发中复用,避免魔法值
- 错误一律
fmt.Errorf("context: %w", err)包装 main不直接os.Exit,由run(args, stdin, stdout, stderr) int返回退出码,便于测试- HTTP handler 阻塞 ctx 时用
select { case <-ctx.Done(): case <-time.After(backup): }防srv.Close()hang - 测试覆盖正常 + 至少一个错误路径
轻量原则:写前先问 ① 必要抽象吗 ② dead field 吗 ③ 1 处常量要抽吗 ④ 中间层能拆吗 ⑤ 注释自明吗 ⑥ doc-only 导出真需要吗。 判断:新读者从 0 读这段,删掉是否更省力?是 → 删;否 → 留。
go -C apps build ./...→ 0go -C apps vet ./...→ 0 告警go -C apps test -count=1 ./...→ 全过- 端到端 smoke:mock OpenAI 端点,跑 happy + 401
docs/plan.md+README.md该 Day 改 ✅git status复核
- Provider 协议 = OpenAI 兼容(
/chat/completions、Bearer、application/json)。理由:DeepSeek / MiniMax / Moonshot / 智谱 / 硅基流动 / OpenAI 都兼容,Day 10 抽象时切换成本最低 - API 错误双格式兼容:OpenAI 嵌套
{"error": {...}}与平铺,非 JSON 退化为带状态码的通用错误 - 环境变量:
MINICODE_API_KEY/MINICODE_BASE_URL/MINICODE_MODEL;flag 优先 - 配置模板:仓库根
.env.example(不进 git,本地.env),列出 env var + 常用服务的 BaseURL/Model 示例值;不引入第三方配置库 - 目录布局 =
apps/:monorepo-friendly。本次实施观察到go mod tidy后目录被外部自动化从根 cmd/ internal/ 重组为 apps/,agent 接受,后续发现再改动先停下报告
- 工具协议 = Chat Completions
tools/tool_calls:函数声明携带 JSON Schema,调用参数在线格式保持 JSON 字符串 - 参数校验边界:Provider 校验调用 ID、类型、名称和顶层 JSON 对象;required、字段类型与未知字段由具体工具在执行前校验
strict默认不发送:协议结构保留可选字段,但为兼容不同 OpenAI 风格服务不强制开启,宿主侧参数校验不能省略- 消息 content 可空:
Message.Content使用*string且不设omitempty,保留 assistant 工具调用的null,空工具结果仍回传"" - 工具结果结构:
role: tool+tool_call_id+content,一个调用对应一条结果消息 - Day 2 范围:最初仅声明并展示工具调用;后续按用户要求接入真实 bash 执行和最小 Agent Loop。
- 保留现有协议:使用
bash{command},通过bash -c在启动 CLI 的工作目录执行,每次调用使用独立 shell。 - 直接执行:按用户当前要求展示并执行模型生成的命令,实时输出并把执行结果回传模型;本阶段不增加逐次确认交互,后续权限机制仍按 Day 6 推进。
- 执行边界:最多 10 轮可使用工具的模型请求;达到上限后额外请求一次纯文本总结,不再提供或执行工具。总结沿用整项任务的 timeout,失败时输出停止说明;达到上限仍返回非零退出码。取消时终止命令进程组,单次命令输出最多保留 64 KiB。
- 实现范围:简单循环和 bash 执行函数,不提前引入工具注册表、交互会话和持久化。
- 职责划分:
internal/agent管理模型循环、消息历史、轮数和工具分发;工具协议适配保留在该包的tools.go,实际命令执行复用internal/tools。cmd/minicode负责参数、输入、任务取消、展示和退出码。 - 展示边界:Agent 通过
Output接口交付模型文本、工具调用、实时输出、结果和工具错误;不依赖终端渲染,不打印 CLI 前缀。终止任务的错误由Run返回。
- 职责划分:
internal/terminal负责终端检测、宽度读取和 Glamour 渲染;cmd/minicode调用该包,不直接依赖渲染库。 - 输出规则:终端中的模型回复渲染 Markdown,按终端宽度换行;管道、文件和渲染失败时输出原文。工具输出和回传模型的消息保持原样。
- 主题:默认使用
dracula,通过GLAMOUR_STYLE覆盖。
- bash session 不保留 cwd,需
cd path && cmd或go -C path cmd,不要假设 PWD 已被切过 - Edit/Write 路径相对仓库根,不是 shell cwd
- httptest handler 阻塞
r.Context().Done()不会在客户端断开时立即返回,必须加 server-side 兜底超时 - OpenAI 错误嵌套在
error字段下,只用平铺解析会退化成原始 body - 用户偏好:微信端纯文本;commit 用
conventional + 中文 subject;能查文件/plan/spec 就查,不替用户瞎猜
任何与本规范冲突的改动,先改本文件,再改实现。