WinUtil 编码代理作业规范深度解析:AGENTS.md 如何约束构建、测试与文档工作流
AGENTS.md 是 WinUtil 仓库为编码代理(Coding Agent)编写的"可插入式"作业指令:它以 13 个章节规定了哪些文件是生成产物不可手改、哪些命令是构建与验证的唯一入口、npm 工具为何必须在 Docker 中运行,以及改动前后必须完成的验证闭环。读完本篇,你将掌握这套"规则即文档"的仓库治理方式:它如何与 SPEC.md 项目契约分工、如何用 Compile.ps1 生成的单脚本产物替代模块式开发,以及 Pester 5.8.0、PowerShell Script Analyzer、Astro 文档站三条验证链路的具体执行方法。
文档定位:AGENTS.md 与 SPEC.md 的契约分工
AGENTS.md 开篇明确了自己的角色与边界:
Drop-in operating instructions for coding agents. Read this file before every task. Working code only. Finish the job. Plausibility is not correctness.
它声明 SPEC.md 是"项目契约"(描述 WinUtil 是什么、如何构建、如何运行),而 AGENTS.md 本身只覆盖"如何在这个仓库上工作"。这一分工在仓库中有直接的物证:
- CLAUDE.md 全文只有一行
@AGENTS.md,即 Claude 系工具进入仓库后自动加载本文件; - GEMINI.md 同样只指示"先读仓库根目录的 AGENTS.md";
- SPEC.md 第 5 行明确写着"AGENTS.md 指向这里获取事实,并单独覆盖代理在仓库中的行为"。
这种"单入口 + 契约分置"的结构意味着:所有行为规则只维护在 AGENTS.md 一处,各 AI 工具的配置入口文件只是指针,避免多处规则漂移。
八条不可协商规则(Non-Negotiables)
AGENTS.md 第 0 章列出 8 条在冲突时压倒其他一切规则的硬性条款,每条都能在仓库中找到对应证据:
- 不要直接编辑
winutil.ps1。它是生成产物——Compile.ps1 按固定顺序拼接scripts/start.ps1、functions/下全部函数文件、config/*.json转换出的$sync.configs内嵌配置、xaml/inputXML.xaml与tools/autounattend.xml、scripts/main.ps1,最终写出根目录的winutil.ps1。改源码、跑编译才是正确路径。 - 不要提交
winutil.ps1。仓库根 .gitignore 中显式列出了winutil.ps1与binary/,发布产物由 GitHub Actions 统一生成。 - 绝不触碰
docs/src/content/docs/code-reference/tweaks/与docs/src/content/docs/code-reference/features/。这两个目录由 tools/devdocs-generator.ps1 从 config/tweaks.json、config/feature.json 及对应 PowerShell 函数文件自动生成;要改内容就改 JSON 或函数文件。而code-reference/下的手写页面(如architecture.mdx)不在生成范围内,可以直接编辑。 - 绝不虚构。不凭空捏造文件路径、函数名、命令输出、测试结果、commit 哈希或 API 行为——先读文件或跑命令。
- 前提错误时先指出再行动。
- 真正有歧义时停下:如果两种理解会产生实质性不同的 diff,先问再改。
- 只碰任务要求的部分:不顺手重构、不做大面积格式化、不做无关清理。
- 说"完成"之前先验证:看起来合理的 diff 不等于证明。
前 3 条本质上是在保护"单一脚本构建模型":由于最终产物是拼接结果,代码不能依赖运行时模块导入或源码相对路径的 dot-sourcing,任何手改生成物都会在下次编译时被静默覆盖。
关键命令:编译、测试、静态检查与文档站
AGENTS.md 第 1 章给出了项目全部关键命令,并附带了非显而易处的操作细节:
编译与运行
.\Compile.ps1 # 仅编译,生成根目录 winutil.ps1
.\Compile.ps1 -Run # 编译并直接启动 GUI
对应 Compile.ps1 的 -Run 开关:第 44-46 行在 Set-Content 写出生成脚本后,若带 -Run 则直接 .\Winutil.ps1 执行,用于手动 GUI 验证。
安装受支持的 Pester 版本(一次性)
Install-Module -Name Pester -RequiredVersion 5.8.0 -Repository PSGallery -Scope CurrentUser -Force -SkipPublisherCheck
文档特别解释了 -SkipPublisherCheck 的必要性:Windows 自带(inbox)Pester 3.4.0 是 catalog-signed,而 PowerShell Gallery 上的 Pester 5.8.0 是 Authenticode-signed,Install-Module 默认拒绝这种"降级签名类型"的升级,因此必须显式跳过发布者检查。同时强调该参数不会跳过下载完整性校验(仍是 HTTPS + NuGet 包哈希校验);-Repository PSGallery 则是显式锁定受信来源,而不是依赖机器上恰好注册了哪些仓库。
运行测试
Import-Module Pester -RequiredVersion 5.8.0 -Force
Invoke-Pester -Path 'pester/*.Tests.ps1' -Output Detailed -CI
-CI 参数会生成 testResults.xml 并在失败时返回非零退出码(对应 SPEC.md 中 GitHub Actions unittests.yaml 的行为)。第 13 章的项目经验再次强调:必须先导入 Pester 5.8.0 再运行测试,否则 Invoke-Pester 会解析到 Windows 自带的 3.4.0。测试套件本体位于 pester/ 目录,覆盖 appx、config、tweaks、runspace 生命周期等 30 余个测试文件。
静态分析
Invoke-ScriptAnalyzer -Path . -Settings .\lint\PSScriptAnalyser.ps1 -Recurse
项目设置文件 lint/PSScriptAnalyser.ps1 仅排除一条规则 PSAvoidUsingWriteHost。AGENTS.md 在此命令前埋了一个容易踩的坑:该设置文件"只排除规则,不排除文件",所以如果本地存在已编译的 winutil.ps1,-Recurse 会把它一并 lint,产生一堆映射不到任何源码文件的行号噪音——运行前先删除本地生成的 winutil.ps1。第 13 章还补充了治理原则:优先修复可操作的源码警告,不要全局压制被接受的惯例性警告(复数命名、UI 辅助函数上的 ShouldProcess、$global:sync、编译期跨文件误报等)。
文档站开发服务器与生产构建(必须走 Docker)
# 在 docs/ 目录下执行
docker compose up winutil-astro # 开发服务器
docker compose run --rm winutil-astro npm run build # 生产构建
迭代时优先使用"最窄但有用的验证",收尾前再跑完整的相关检查。
依赖安装与构建环境:npm 一律进容器
AGENTS.md 第 2 章是全文最具安全针对性的一节。鉴于 npm/pnpm/yarn 供应链投毒(恶意 postinstall/preinstall 脚本、窃密包)的持续风险,规则写得非常绝对:永不在宿主机上直接运行 npm/pnpm/yarn/npx。
仓库中唯一的 npm 项目是 docs/(Astro + Starlight 文档站)。它的容器化配置在仓库中可以完整验证:
- docs/Dockerfile:基于
node:22-bookworm-slim,corepack enable后先拷贝package*.json并npm install(利用层缓存),再以非 root 的node用户运行,暴露 4321 端口,默认命令npm run dev -- --host 0.0.0.0。 - docs/docker-compose.yml:服务名
winutil-astro,端口绑定127.0.0.1:4321:4321(仅本地可达),卷挂载.: /app(宿主机编辑即时反映到容器,常规代码改动无需重建镜像),并声明了命名卷astro_node_modules映射到/app/node_modules、tmpfs挂载/app/.astro,环境变量CHOKIDAR_USEPOLLING=true(轮询文件监听,规避 overlayfs inotify 问题)与ASTRO_TELEMETRY_DISABLED=1。
这一节还固化了几个容易出错的运维细节:
- 新增文档依赖:直接改 docs/package.json,然后
docker compose build winutil-astro重建镜像并docker compose down -v丢弃node_modules卷——因为 Docker 只在命名卷首次创建时从镜像播种,普通 rebuild 会静默保留旧node_modules。 - Docker 不可用时的行为:给出当前操作系统的安装命令并等待确认,而不是"退而求其次"在宿主机上跑 npm;若只是守护进程没启动,告知用户而不是自行启动。
- 生命周期脚本审查:新依赖中的
postinstall/preinstall脚本在安装前向用户摘要其作用。 - 秘密管理陷阱:
docs/.dockerignore只影响docker build拷贝进镜像的内容,不影响docker compose的 bind mount——整个docs/目录(包括任何.env)对每个 dev/build/preview 命令都在容器内可见,所以"不要把真实密钥放在 docs/ 下任何位置"。 - 边界:这条 Docker 要求只针对
docs/;仓库其余部分是 PowerShell(Compile.ps1、Pester、Script Analyzer),直接在宿主机上运行。
真实源与编辑前检查清单
第 3 章规定:凡是影响编译后脚本行为的改动,只能落在 SPEC.md "Repository Layout" 描述的源文件上(functions/、config/、scripts/、xaml/ 等),绝不落在 winutil.ps1;若行为变化需要编译产物变化,更新源文件后运行 .\Compile.ps1 仅用于验证生成。
这一作用域只覆盖"编译脚本行为"。仓库元数据——AGENTS.md、SPEC.md、CLAUDE.md/GEMINI.md/.github/copilot-instructions.md、.github/workflows/、根 .gitignore——在任务需要时直接编辑。
第 4 章的"编辑前"清单要求:
- 动手前用一两句话陈述计划,非平凡工作要附带拟运行的验证方式;
- 读要改的文件和调用它的文件;
- 匹配既有模式,即使另一种"绿地设计"更干净;
- 影响行为、兼容性或用户数据的假设要显式说出;
- 两种方案存在实质权衡时,先命名权衡再选择。
这些规则与第 5 章编码指南中的命名契约呼应:按钮/动作接线遵循 SPEC.md 的 UI And Event Contract——XAML 元素命名为 WPFThingButton 就映射到 Invoke-WPFThingButton 函数,这与 functions/public/ 中 Invoke-WPFInstall、Invoke-WPFOOSU 等文件的实际命名一致。
编码指南与运行时安全规则
第 5 章编码指南的核心条目(均指向 SPEC.md 的对应契约章节):
- 优先"解决所述问题的最少代码",除非任务当下需要,不添加抽象、可配置性、钩子或"未来扩展性";
- PowerShell 函数尽量保持在单个函数文件内,文件名与主函数名一致(functions/private/ 与 functions/public/ 的结构即如此);
- 使用批准的 PowerShell 动词-名词命名,遵循既有的
WPF/WinUtil命名约定; - 共享状态与 UI 引用统一使用
$sync——编译产物里 scripts/main.ps1 正是把$sync.configs.applications展开为$sync.configs.applicationsHashtable等哈希表供跨 runspace 共享; - 在后台 runspace 中工作时,通过 UI dispatcher 更新 WPF 控件(对应第 13 章中
Invoke-WPFUIThread必须可无窗调用的经验); - 符合既有 schema 的配置驱动功能放在 JSON 中(
config/*.json),而不是在 PowerShell 里硬编码列表;重命名配置键必须同步更新所有 preset、UI 引用、文档与代码路径; - 保留 tweak 的 undo/原始状态数据,让用户可以逆转变更;
- 清理自己改动产生的孤儿代码;避免大面积纯格式化改动,尤其是 JSON、XAML、文档与生成产物。
第 6 章运行时安全规则将 WinUtil 定位为执行系统级 Windows 变更的工具:注册表、服务、AppX 移除、包管理器、Windows Update、ISO 与无人值守安装变更一律视为高危。具体守则包括:优先复用 WinGet/Chocolatey/注册表/服务/进度/UI 更新等既有辅助函数;tweak 在 schema 支持时保持可逆(记录原始值或原始状态);永不就地修改用户的原始 ISO,遵循既有的拷贝/挂载/导出模式;不在仓库文件中存储凭据、秘密或机器特定路径;长时/破坏性操作保留日志与用户反馈模式。
外科式改动与验证闭环
第 7 章"Surgical Changes"把最小 diff 原则展开为可检查的行为约束:不顺手改进相邻代码、注释、格式、import 或文档;不因"人已经在文件里了"而重构能跑的代码;不删除既有死代码(相关时在总结中提及即可);每一行改动都应可追溯到用户请求;改动开始向无关区域蔓延时,暂停并重新评估计划。
第 8 章验证矩阵按改动类型给出可检查的成功标准:
| 改动类型 | 必做验证 |
|---|---|
| 编译/构建相关 | 运行 .\Compile.ps1 |
| GUI 行为 | 可行时运行 .\Compile.ps1 -Run 并手动验证受影响路径 |
| 配置变更 | 编译检查 + 相关 Pester 测试 |
| 函数变更 | 相关 Pester 测试,可行时新增/更新聚焦测试 |
| 纯文档 | 校对改动文件;除非涉及文档生成,跳过运行时测试 |
两条硬性纪律:读了命令输出才能说测试通过;验证失败时修复原因,而不是削弱测试。如果某项检查跑不了,必须精确说明为什么、剩余风险是什么。
生成文件与 Git 卫生
第 9 章规定:本地 winutil.ps1 的改动按"可丢弃的编译输出"对待;绝不暂存或提交 winutil.ps1、binary/ 或任何被根 .gitignore 或 docs/.gitignore 忽略的内容——且要求"读那些文件而不是凭假设"(根 .gitignore 实际列出的还包括 testResults.xml、winutil.exe.config 与系统文件)。docs/public/ 是被跟踪的静态资源源(favicon 等),不是生成产物。收尾前运行 git status --short,把自己的改动与用户既有改动分开;未被明确要求时不回滚用户改动。提交信息要求:短标题(72 字符内)+ 必要时解释"为什么"的正文;把改动拆成小而逻辑内聚的多个 commit,使每个 commit 的 diff 可作为一组相关变更被审阅。
文档预期与沟通风格
第 10 章文档预期把"行为变化"与"文档落点"绑定:
- 用户可见行为变化 → 更新 docs/src/content/docs/guides/;
- 架构、构建流程、配置 schema 或贡献工作流变化 → 更新手写的 docs/src/content/docs/code-reference/architecture.mdx 等开发者文档,但绝不手改自动生成的
code-reference/tweaks/与code-reference/features/子目录; - docs/astro.config.mjs 中的侧边栏条目必须与
docs/src/content/docs/下的实际页面 slug 保持同步; - README 的改动保持简短、高层级,详细用户与开发者文档一律放
docs/; - SPEC.md 跟随项目/架构变化更新,AGENTS.md 跟随流程变化更新——两者各自单一职责。
第 11 章沟通风格要求:直接、简洁,答案/动作先行;不奉承、不客套、不假装确定;报告"改了什么、如何验证、什么没做";被要求 review 时以发现和文件/行号引用开头。第 12 章给出"何时问、何时直接做"的双向清单:两种合理解释且选择实质影响行为或触碰文件时、影响发布生成/迁移/高危 Windows 行为而用户未指定时、需要无权限的凭据或生产资源时、用户陈述目标与字面请求冲突时——先问;而任务平凡可逆、歧义可通过读代码或跑本地命令消解、或用户本会话已回答过时——直接做。
项目经验沉淀(Project Learnings)
第 13 章是这套规范最"活"的部分:每当用户纠正代理的做法,会话结束前就在这一节新增或收紧一条具体规则,并持续删减不再重要的条目。当前的经验列表浓缩了该项目大量实战踩坑:
- 日志:WinUtil 运行时日志保持在带时间戳的
%LocalAppData%\winutil\logs\winutil_*.log会话文件中,不另建根目录winutil.log;当活动日志文件正被Start-Transcript持有时,不要对该文件调用Add-Content(会记录终止错误诊断),改为写到宿主输出,让 transcript 把该行收进同一日志文件。 - UI 生命周期:
Invoke-WPFUIThread、Set-WinUtilTweaksProgressIndicator等 UI 辅助函数必须可在无窗口时安全调用——-Preset与-Config路径在表单创建之前、PresentationCore 加载之前就执行工作流。 - 包管理日志:入队后台 runspace 工作之前记录包名与包管理器 ID,不依赖 runspace 宿主输出获取包标识;winget/Chocolatey 的进程启动保持简单,除非明确要求,不新增独立的 stdout/stderr 进程日志辅助。
- Win11 Creator(ISO 工作流):每次新的 ISO 修改都从全新的
WinUtil_Win11ISO_*临时目录开始,"已有工作检测"仅用于恢复/导出已修改介质;驱动注入的离线 WIM 维护保持"一次挂载、一次/Add-Driver、一次提交",不做版本导出或无关 WIM 清理,导出 ISO 前拒绝损坏的元数据。 - DNS:DHCP 重置时保留 cmdlet 重置,并显式把 IPv4 与 IPv6 的 DNS 来源都设为 DHCP。
小结:一套可审计的代理作业规范
AGENTS.md 的设计要点可以归纳为三层闭环:
- 事实层:SPEC.md 作为唯一项目契约,AGENTS.md 只引用不重复,
CLAUDE.md/GEMINI.md/.github/copilot-instructions.md 全部收敛到同一入口; - 执行层:每条规则都绑定一个可运行的命令或一个可读的文件(Compile.ps1 的拼接顺序、lint/PSScriptAnalyser.ps1 的规则排除、docs/Dockerfile 与 docs/docker-compose.yml 的卷布局、根 .gitignore 的忽略清单),规则因此可被机器和人同时审计;
- 演化层:第 13 章的 Project Learnings 把每次纠正固化为一条可删除的短期规则,使规范随项目经验滚动更新而不膨胀。
对贡献者而言,这份文件同时也是一份"仓库地图":读懂它,就理解了 WinUtil 为什么是"模块化源码 + 单脚本产物"的形态、验证链如何分三段(编译、Pester、静态分析)、以及文档站为何被隔离在 Docker 内独立构建。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00