agents-gitflow-guard 0.0.1 → 0.0.2

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.zh.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  > **有没有受够了 agent 跳过你的合入流程?**
4
4
 
5
- 基于 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)的插件:
6
- 依据本地 git 事实强制 **feature 预览 → 基线** 合入顺序 —— agent 无法跳过流程,例外只能由你授予。
5
+ 一个可自由配置分支角色守卫的 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)插件。
6
+ 你自己定义分支——**集成分支**(feature PR/MR 合入)、**预览分支**(环境终点)、**生产分支**、**归档分支**——每个角色各自配规则。agent 无法跳过流程,敏感合并始终留在你手上。
7
7
 
8
8
  [English](README.md) · [许可证](LICENSE)
9
9
 
@@ -22,7 +22,7 @@
22
22
  - [工作原理——三句话](#工作原理三句话)
23
23
  - [配置参考](#配置参考)
24
24
  - [门禁矩阵——拦什么、放什么](#门禁矩阵拦什么放什么)
25
- - [用户例外权(特许)——打破规则的唯一方式](#用户例外权特许打破规则的唯一方式)
25
+ - [人保持控制权的地方](#人保持控制权的地方)
26
26
  - [安装详解](#安装详解)
27
27
  - [常见疑问(FAQ)](#常见疑问faq)
28
28
  - [术语表](#术语表)
@@ -46,57 +46,51 @@ dsh plugin --profile web add agents-gitflow-guard
46
46
  ```jsonc
47
47
  {
48
48
  "enabled": true,
49
- "mode": "pr",
49
+ "featurePattern": "feature/[\\w-]+",
50
50
  "branches": {
51
- "base": "develop",
52
- "preview": "staging",
53
- "trunk": "main"
51
+ "integration": ["develop"], // 集成分支: feature 经 PR 合入, 受保护
52
+ "archive": ["main"] // 归档分支: 发布后由你亲手合入
54
53
  }
55
54
  }
56
55
  ```
57
56
 
58
- 这一个文件就是全部配置:它声明"本项目启用守卫"、"我的基线是 `develop`"、"我的预览是 `staging`"。插件按项目 opt-in——文件不存在或 `enabled: false` 时什么都不做。
57
+ 这一个文件就是全部配置:其中的 **`integration` 是唯一必填**角色;`preview` / `production` / `archive` 都是可选,只有你配了才启用对应关卡。插件按项目 opt-in——文件不存在或 `enabled: false` 时什么都不做。
59
58
 
60
59
  **第 3 步——验证**。让 agent 执行 `git push origin develop`,预期工具调用被拒绝:
61
60
 
62
61
  ```text
63
62
  Error: [gitflow-guard] 已拦截: 受保护分支「develop」禁止直推
64
- 下一步: 基线分支(develop)由 PR 合入: 先合入预览并确认(P2), 再创建指向基线的 PR
63
+ 下一步: 集成分支(develop)由 PR/MR 合入 feature: 先推 feature 分支, 再 gh pr create --base develop / glab mr create --target-branch develop
65
64
  ```
66
65
 
67
- **完成。** 守卫对该仓库生效。继续往下看[完整实战示例](#完整实战示例一个-feature-的端到端旅程),或准备好映射自己的分支名时跳到[配置参考](#配置参考)
66
+ 拦截文案目前默认是中文(本地化在[路线图](#路线图));英文意思是:*blocked: protected branch `develop` — direct push forbidden. Next: integration branch is updated by PR/MR — push the feature branch first, then open a PR/MR into `develop`.*
67
+
68
+ **完成。** 守卫对该仓库生效。继续往下看[配置参考](#配置参考)映射自己的分支,或看[门禁矩阵](#门禁矩阵拦什么放什么)的完整判定表。
68
69
 
69
70
  ### 完整实战示例——一个 feature 的端到端旅程
70
71
 
71
- 场景:团队开发登录页(`feature/login-page`),基线 `develop`,预览 `staging`。每一步 agent 做什么、插件判定什么、你看到什么:
72
+ 场景:团队开发登录页(`feature/login-page`);`develop` 是集成分支,`main` 是归档分支。每一步 agent 做什么、插件判定什么、你看到什么:
72
73
 
73
- | # | agent 执行的命令 | 插件判定 | 你看到的结果 |
74
+ | # | agent 执行 | 插件判定 | 你看到 |
74
75
  |---|---|---|---|
75
- | 1 | `git checkout -b feature/login-page` | ✅ 放行(feature 工作自由) | 分支创建 |
76
+ | 1 | `git checkout -b feature/login-page`(从 develop 切) | ✅ 放行(feature 自由) | 分支已建 |
76
77
  | 2 | `git add . && git commit -m "feat: login"` | ✅ 放行 | 已提交 |
77
- | 3 | `git push -u origin feature/login-page` | ✅ 放行(推自己的 feature 没问题) | 已推送 |
78
- | 4 | `git checkout develop && git merge feature/login-page` | 🚫 **拦截** 尚未合入预览 | 提示: 先合入 staging(PR①), 测试后 P2 |
79
- | 5 | *(尝试绕序)* 一条串联命令 `git checkout develop && git merge feature/login-page` | 🚫 **拦截** 按段模拟分支切换, 无法绕过 | 同样的拒绝 |
80
- | 6 | `gh pr create --base staging` | 放行(PR①: feature 预览是流程第一步) | PR 创建 |
81
- | 7 | *(你合并 PR①)* | — | feature 进入 `staging`, 部署测试环境 |
82
- | 8 | 你在 DSH 聊天输入: `feature/login-page 测试 OK,可以合入` | 插件记录 **P2 特许**(审计 `grant`) | 已确认 |
83
- | 9 | `git checkout develop && git merge feature/login-page` | ✅ 放行 — 顺序(∈ 预览)+ P2 都满足 | 合并成功 |
84
- | 10 | *(合并完成后)* | 插件**消费** P2 特许(审计 `consume`) | 一次性用尽 |
85
- | 11 | `gitflow-guard status` / `gitflow-guard audit` | ✅ 放行(只读) | 完整状态与时间线: grant → consume |
86
-
87
- 注意整个流程中 agent **无法**做到的事:跳过第 6/7 步、在第 8 步自我确认、把同一次确认复用到下一个 feature。每个例外都是一次显式的用户动作,在审计中可见。
78
+ | 3 | `git push -u origin feature/login-page` | ✅ 放行( feature 没问题) | 已推送 |
79
+ | 4 | `git checkout develop && git merge feature/login-page` | 🚫 **拦截**——集成分支只收 PR/MR | 必须对 develop PR/MR |
80
+ | 5 | `gh pr create --base develop` | 放行(feature 集成) | PR 已建,由你审查并合并 |
81
+ | 6 | `git push origin main` 或合入 main | 🚫 **拦截**——归档仅用户亲手 | 发布后由你亲自 develop main 归档 |
82
+
83
+ 注意agent**做不到**的事:把 feature 直接合进 `develop`,或碰 `main` 一下都不行。每个敏感合并都是你在 PR/MR 页面或自己终端里的有意识动作。
88
84
 
89
85
  ---
90
86
 
91
87
  ## 为什么需要它——解决的问题
92
88
 
93
- AI 编码 agent 在你的仓库里工作。它通过系统提示词、项目智能体指令文件(`AGENTS.md`、`CLAUDE.md`、`GEMINI.md`、`.cursorrules` 等各家命名)和项目文档被"告知"要遵循合入流程:feature 分支开发 → 合入预览分支(自动部署的测试环境)→ 用户确认合入基线。
89
+ AI 编码 agent 在你的仓库里工作。它通过系统提示词、项目智能体指令文件(`AGENTS.md`、`CLAUDE.md`、`GEMINI.md`、`.cursorrules` 等各家命名)和项目文档被"告知"遵循合入流程:feature 分支开发 → 合入集成分支(以及你有的话各 preview/production 阶段)生产/归档交给你。
94
90
 
95
- **这是软约束。** agent 会跳过它、颠倒顺序、或"忘记"它——不是因为恶意,而是因为软性指令对模型来说是可选的。
91
+ **这是软规则。** Agent 会跳过、重排、干脆"忘记"它——不是因为恶意,而是因为软指令对模型来说本来就是可选的。
96
92
 
97
- 本插件把软约束变成**硬机制**。agent 每次 git 操作都在执行前被校验——对照本地仓库的真实状态。违规操作在命令运行前被拦截,并给出原因和下一步。
98
-
99
- 没有人需要记住规则——规则被强制执行。
93
+ 这个插件把软规则变成**硬机制**。agent 每次尝试的 git 操作都会对照*本地仓库的真实状态*检查;违规在命令执行前就被拦截,并给出原因和下一步。没人需要记得规则——规则被强制执行。
100
94
 
101
95
  ---
102
96
 
@@ -104,98 +98,85 @@ AI 编码 agent 在你的仓库里工作。它通过系统提示词、项目智
104
98
 
105
99
  ### 这些信号说明它适合你
106
100
 
107
- - 团队有 AI agent 参与仓库开发,并且有(或想建立)正式的分支流程(feature 预览 基线)。
108
- - agent 已经抄过一次近路:绕过预览直接合入基线,或未经测试确认就合入。发生过一次就会再发生——本插件就是结构性修复。
109
- - 你保护基线与主干,但不想靠人工 review 去抓每一次抄近路。
110
- - 多个 feature 并行开发、汇入同一个预览环境,你需要在合入基线前按 feature 逐个验证。
101
+ - 你有(或想要)一个明确的分支流程——从单条 `develop` 式集成分支,一直到多级 preview/production 流水线。
102
+ - agent 已经抄过近路:直推受保护分支,或合到不该合的地方。发生过一次就会再发生——这个插件是结构性修正。
103
+ - 你想保护集成/归档分支,又不想全靠人肉 review 抓每个抄近路。
104
+ - 多个 feature 并行开发、汇入同一个预览环境,你想让每个进入更严阶段的动作都被把关。
111
105
 
112
106
  ### 具体场景举例
113
107
 
114
- 1. **独立开发者 + agent 做客户项目。** 你把任务丢给 agent,它"好心"直接合入基线,预览环境就过期了。每个项目一个配置文件,agent 在机制上无法在"预览 + 你的确认"之前合入基线——哪怕你没盯着它。
115
- 2. **小团队(3–10 人)+ CI 自动部署预览。** staging 合入即自动部署;某天 agent 把一个从未部署、从未测试的 feature 合进了 `develop`。从此,每次基线合入都要求:feature ∈ staging **且**你的聊天确认——一次刻意的、留审计的动作,而不是一次"忘了"。
116
- 3. **大团队、多个 agent。** agent feature 分支上自由工作(commit / push / 同步 / rebase 全放行);门禁保证未经确认的东西进不了基线。feature 开发速度完全不变,被拿掉的只有抄近路。
117
- 4. **异步协作。** 你不是随时在线。守卫在你不在的时段维持流程秩序;例外依然只能由你授予,且每个例外都有审计留痕。
108
+ 1. **独立开发者 + agent 做客户项目。** 你把任务丢给 agent,它"好心"直接推集成分支。一份小配置,agent 在机制上不接受 PR/MR 就无法碰受保护分支——哪怕你没盯着它。
109
+ 2. **3–10 人小团队 + CI 部署的预览。** Staging 合入即自动部署;某天 agent 未审查就把 feature 合进 `develop`。此后进入任何受保护阶段都必须 PR/MR——一次有意识、有留痕的动作。
110
+ 3. **多环境流水线的大团队。** 很多预览终点 + 受管制的生产 + 归档线——每个角色各配各的规则,守卫不需要额外逻辑就能放大到任意规模。
111
+ 4. **异步协作。** 你不总在线。守卫在你的会话间隙保持流程正直;生产/归档合并仍然只属于你。
118
112
 
119
113
  **不适合你**(另见[它不能做什么](#它不能做什么诚实的边界)):
120
114
 
121
- - **单分支流(trunk-based)**——所有人直接合一条分支:插件会处处拦截。
122
- - **没有明确流程的个人仓库**——没有可强制的对象,没有价值。
123
- - **不愿建立 feature → 预览 → 基线 流程的团队**——插件强制一种流程,不会发明一种。
115
+ - **主干直推流**——所有人都直接合到一条分支:插件会一直拦,别开。
116
+ - **没有定义流程的私人仓库**——没东西可守,没价值。
117
+ - **一个分支角色都不愿意给的项目**——插件至少要有一个 `integration` 分支来保护。
124
118
 
125
119
  ---
126
120
 
127
121
  ## 它能做什么
128
122
 
129
- - **执行前拦截**:直推/强推/删除受保护分支;feature 未进预览就合入基线;合入主干;agent 试图给自己授权例外。
130
- - **用 git 事实强制顺序**:"这个 feature 合入预览了吗?"由本地 `merge-base --is-ancestor` 判定——不依赖任何托管服务,也不相信 agent 的自述。
131
- - **唯一例外权——你**:用户可以特许提前建 PR、确认 feature 测试通过、许可主干 PR。agent 永远不能自我授权。
132
- - **任何命名都可以**:分支名由你的配置映射,零硬编码(见[配置参考](#配置参考))。
133
- - **全程审计**:每次拦截/特许/消费写入 `.git/gitflow-guard/`(审计 + 状态)——在 .git 内,不进仓库。
134
- - **核心平台无关**:纯本地 git;有 `gh` 时可选查阅(PR 目标解析、CI 状态日志参考),没有也完全可用。
123
+ - **执行前拦截**:直推 / 强推 / 删除受保护角色分支(integration / preview / production / archive);agent 试图合入生产或归档。
124
+ - **角色驱动、完全可配**:`integration` 是唯一必填;`preview` / `production` / `archive` 是可选数组(精确名或正则),每个角色独立 `update`(`pr` / `flexible`)与 `mergeBy`。
125
+ - **在关键处保留人的操作权**:生产与归档合并始终在你手上——插件阻止 agent 点击合并,于是你的动作*就是*确认。
126
+ - **任何命名都行**:分支名全由配置映射,绝无硬编码(见[配置参考](#配置参考))。
127
+ - **全程审计**:每次拦截都写入 `.git/gitflow-guard/audit.jsonl`——在 `.git` 内,绝不进版本库。
128
+ - **平台无关核心**:纯本地 git;可选调用 `gh`(GitHub)或 `glab`(GitLab)做 PR/MR 目标解析,没有它们照样工作。
135
129
 
136
130
  ---
137
131
 
138
132
  ## 它不能做什么——诚实的边界
139
133
 
140
- - **不是安全工具**:命令解析是尽力而为,存心混淆命令的 agent 可能绕过文本分析。但**顺序校验本身无法伪造**——git 祖先关系是事实,不是声称。
141
- - **不做 CI 平台硬门禁**:`gh pr checks` 仅作日志参考,从不作为硬门禁。平台侧强制属于分支保护规则,可以叠加在插件之上。
142
- - **不替代流程本身**:你的项目必须真的使用 feature 预览 → 基线 流程。如果团队把所有东西直接合到一条分支,这个插件会处处拦截——不要在这种项目启用。
143
- - **v1 无多机状态同步**:特许状态存本地,另一台机器看不到(列入 v2)
144
- - **v1 无主动弹窗通知**:动作结果通过审计与对话呈现,不主动推送给你。
134
+ - **它不是安全边界。** 命令解析是尽力而为;铁了心要混淆命令的 agent 能绕过文本分析。
135
+ - **它不接管 CI。** CI 状态只作参考日志,从不作硬门槛。真正的分支保护应放到 GitHub/GitLab 设置里,可以叠加。
136
+ - **它不能替代流程本身。** 你的项目至少得有一个 `integration` 分支;如果所有人都往一条分支直推,这个插件会一直拦——那里别开。
137
+ - **生产/归档不自动化**——它们刻意留给你人工点击;插件只是对 agent 说"不行"
145
138
 
146
139
  ---
147
140
 
148
141
  ## 与服务器端分支保护的对比
149
142
 
150
- 服务器端分支保护(GitHub 分支规则、GitLab 受保护分支)与本插件解决的是**两个不同的问题**。它们是互补关系,不是二选一。
143
+ 服务器端分支保护(GitHub branch rules、GitLab protected branches)和这个插件解决**不同的问题**,互补而非替代。
151
144
 
152
- | 维度 | 服务器端分支保护 | 本插件 |
145
+ | 维度 | 服务器端保护 | 本插件 |
153
146
  |---|---|---|
154
- | 管什么 | **谁**能推/合入受保护分支(权限) | **合入的顺序与前提**(流程) |
155
- | 能否表达"用户确认测试通过" | 不能——最多要求 review 批准, agent 参与 review 时形同虚设 | 能——独立的、可审计的特许(P2), agent 无法自我授予 |
156
- | 能否强制"先预览后基线" | 不能——保护按分支, 不按流程 | 能——门禁在基线合入前检查 feature 预览 |
157
- | 作用范围 | 仓库所有用户, 含人 | 装了插件并启用的 DSH agent(人不受限) |
158
- | 强制时机 | 服务器端, push/merge 时 | 本地, 命令执行前 |
159
- | 平台 | 绑定托管服务 | 纯本地 git, 平台无关 |
160
- | 谁能绕过 | 有管理员权限的人 | DSH 之外的人, 或铁了心混淆的恶意 agent |
147
+ | 管什么 | *谁*能推/合并到受保护分支(权限) | *agent 怎么*进入流程(工作流)——这个合并落在哪个角色 |
148
+ | 防止 agent 合入生产/归档 | 不能——无法区分"是 agent 干的" | 能——生产/归档合并默认对 agent 禁用 |
149
+ | 按角色灵活 | 每个分支一条规则 | 一个配置文件里每角色 `update`(pr/flexible)+ `mergeBy`(user/anyone) |
150
+ | 范围 | 仓库所有用户,包括人 | 配置了插件的 DSH agent(人类不受限) |
151
+ | 执行点 | 服务端,推送/合并时 | 本地,命令执行前 |
152
+ | 平台 | 绑定托管服务 | 纯本地 git,平台无关(`gh`/`glab` 可选) |
153
+ | 谁能绕过 | 有管理员权限的人 | DSH 之外干活的人,或铁了心的恶意 agent |
161
154
 
162
- 为什么重要:分支保护回答"**这次推送能不能发生**";本插件回答"**这个 agent 现在该不该合并(按流程)**"。最强的配置是**两者都用**——插件保证 agent 对流程诚实,分支保护保证任何人(agent 或人)都不能直推受保护分支。
155
+ 为什么重要: 分支保护回答"这次推送到底能不能发生";本插件回答"这个 agent 按配置能不能进这个角色"。最强的方案**两者都用**——插件让 agent 守流程,分支保护保证任何人(agent 或人)都不能直推受保护分支。
163
156
 
164
157
  ---
165
158
 
166
159
  ## 工作原理——三句话
167
160
 
168
- 1. agent 调用 shell 工具(`pwsh` / `bash`)执行 git 命令。
169
- 2. 插件分类命令、读取本地 git 事实(当前分支、feature 是否为预览分支的祖先)、查询特许状态,套用门禁矩阵。
170
- 3. 违规 → 工具调用在**运行前被拒绝**,附原因与下一步;合规放行,留审计。
161
+ 1. agent 调用 shell 工具(`pwsh`/`bash`)执行一条 git 命令。
162
+ 2. 插件分类该命令,从 `gitflow-guard.config.json` 解析分支角色,套用门禁矩阵。
163
+ 3. 违规 → 工具调用在**运行前被拒绝**,附原因和下一步;放行命令照常执行,每次拦截都写入 `.git/gitflow-guard/audit.jsonl`。
171
164
 
172
- 确认通道:插件监听 DSH 聊天消息,只接受**真人**(`source.kind === 'user'`)来源——agent 无法伪造。
165
+ 没有聊天确认、也没有特许库:敏感合并(生产/归档)就是**仅用户**——agent 可以帮你准备 PR/MR,但点合并的始终是你。
173
166
 
174
167
  ### 设计原理——它为什么有效
175
168
 
176
- #### 1. 本地 git 事实是唯一可信来源
177
-
178
- 插件从不问 agent"你在哪个分支?"或"用户确认了吗?"——它自己跑只读 git 查询(`branch --show-current`、`merge-base --is-ancestor feature preview`)。
179
-
180
- git 祖先关系是仓库的事实:如果 feature 的 HEAD 是预览分支的祖先,合入就发生了;否则没有。agent 可以声称任何事,仓库不会撒谎。
181
-
182
- ---
183
-
184
- #### 2. 拦截发生在执行前,不是事后
169
+ #### 1. 配置是唯一事实来源
185
170
 
186
- 插件挂在工具管线的 `tools/pre-execute`——命令被分发**之前**的决策点。在这里 `deny`,命令**根本不会运行**,agent 只看到拒绝结果。事后检测(扫描日志)无法作为强制手段——破坏已经发生。
171
+ 分支名和规则没有任何硬编码。`integration` 是唯一必填角色;`preview` / `production` / `archive` 是可选数组(精确名或正则),每个都有自己的 `update` 与 `mergeBy`。同一个二进制从单条 `develop` 一直可扩到企业多环境流水线。
187
172
 
188
- ---
189
-
190
- #### 3. 确认通道在机制上不可伪造
173
+ #### 2. 拦截发生在执行前,不是执行后
191
174
 
192
- DSH 的聊天消息带生产者标记(`source`)。只有真人输入的消息带 `source.kind === 'user'`;模型输出、工具结果、插件注入都带不同的 source。插件只接受 user 来源的确认——"用户确认了"这件事,agent、模型、其他插件都无法伪造。
193
-
194
- ---
175
+ 插件挂在工具管线的 `tools/pre-execute`——命令分派*之前*的决策点。在那里 `deny`,命令**根本不会运行**,agent 只看到拒绝。事后检测(扫日志)无法作为强制手段——伤害早就造成了。
195
176
 
196
- #### 4. 特许一次性、动作成功后消费
177
+ #### 3. 敏感合并在机制上只能由人
197
178
 
198
- "一次性"意味着每个例外都是显式、可审计、不重复的——不存在"永久豁免的 feature"。"成功后消费"意味着失败的动作(如 PR 创建失败)不浪费特许:它保留到下次尝试。两个性质都在审计里可见(`grant` `consume`)。
179
+ 没有任何插件代码替生产/归档判断"这次合并行不行"。门禁只是拒绝让 *agent* 执行这些合并,于是唯一路径就是 PR/MR 页面里**你**点下合并——那个点击就是确认。不存在 agent 能伪造的令牌、特许或聊天消息绕过你。
199
180
 
200
181
  ---
201
182
 
@@ -203,234 +184,202 @@ DSH 的聊天消息带生产者标记(`source`)。只有真人输入的消息带
203
184
 
204
185
  ### 分支角色——插件校验的模型
205
186
 
206
- 插件建模**四个角色**。固定的是角色关系,不是分支名。
187
+ 只有 **`integration`** 是必填。其余全部可选——按你的流程配就好,每条目可以是精确分支名**或**正则。
207
188
 
208
189
  ```text
209
- trunk ─── (可选, 发布) 合入它: 一律拦截 —— 仅用户亲手
210
-
211
- baseline 合入它需满足: feature ∈ 预览 + 用户确认(P2)
212
-
213
- preview ── 合入它: 始终放行(PR①)—— feature 并行
214
-
215
- feature 分支 — 你的工作分支, 按 featurePattern 识别
190
+ feature 分支 ──(自由)──> integration(集成分支, PR/MR 合入)
191
+
192
+ ├──> preview(可选, 环境终点, 只走 PR/MR)
193
+
194
+ └──> production(可选, PR/MR + 只有你能点合并)
195
+ archive(可选, 发布后你亲手归档)
216
196
  ```
217
197
 
218
- | 角色 | 配置键 | 受保护? | 强制行为 |
198
+ | 角色 | 配置键 | 必填? | 强制行为 |
219
199
  |---|---|---|---|
220
- | **基线** | `branches.base` | 始终 | 禁止直推/强推/删除;合入需顺序 + P2 |
221
- | **预览** | `branches.preview` | pr 模式下 | pr 模式禁止直推/本地合入;合入它始终放行 |
222
- | **主干** | `branches.trunk`(可选) | 始终 | 除用户亲手外, 谁都不能合入 |
223
- | **feature** | `confirm.featurePattern` 匹配 | | 自由: commit / push / 同步 / rebase |
200
+ | **feature** | `featurePattern` | | 自由: commit / push / 同步 / rebase |
201
+ | **integration** | `branches.integration` | 必填 | 禁直推(默认 `pr`);feature 只经 PR/MR 合入 |
202
+ | **preview** | `branches.preview`(数组) | 可选 | 禁直推;只走 PR/MR(环境终点) |
203
+ | **production** | `branches.production`(数组) | 可选 | 只走 PR/MR;合并仅限你(`mergeBy: "user"`) |
204
+ | **archive** | `branches.archive`(数组) | 可选 | 仅用户——agent 连建 PR 都不行 |
224
205
 
225
- ### 自定义分支名——任何命名都可以
206
+ ### 自定义分支名与规则——任何命名都可以
226
207
 
227
- `branches` 把仓库的**真实分支名**映射到角色上,零硬编码。示例:基线 `master`、预览 `beta`、主干 `production`,feature 分支用 `fix/`、`task/` 前缀:
208
+ **小团队(个人 / 2-3 人)—— 最简,只有 integration:**
228
209
 
229
210
  ```jsonc
230
211
  {
231
212
  "enabled": true,
232
- "mode": "pr",
233
- "branches": {
234
- "base": "master",
235
- "preview": "beta",
236
- "trunk": "production"
237
- },
238
- "confirm": {
239
- "keywords": ["确认", "OK", "可以", "特许"],
240
- "featurePattern": "(fix|task)/[\\w-]+"
241
- }
213
+ "featurePattern": "feature/[\\w-]+",
214
+ "branches": { "integration": ["develop"] }
242
215
  }
243
216
  ```
244
217
 
245
- 用这份配置,插件对 `master` 的处理与默认示例中的 `develop` 完全一致:agent 直推 `master` 被拦;`fix/auth-42` 未合入 `beta` 且未经你确认时,合入 `master` 被拦;`gitflow-guard status` 以你的分支名展示报告。
218
+ **大团队(多预览环境 + 生产 + 归档):**
246
219
 
247
- **`featurePattern`**:JS 正则,匹配分支名。匹配 → feature 分支(自由 push/互合/同步);不匹配且非角色分支 → "其余"(放行)。按你团队的实际命名配置。
220
+ ```jsonc
221
+ {
222
+ "enabled": true,
223
+ "featurePattern": "(topic|feature)/[\\w-]+",
224
+ "branches": {
225
+ "integration": ["develop", "topic/[\\w-]+"],
226
+ "preview": {
227
+ "branches": ["ita1", "itb1", "itb2", "sg", "vb", "r1-conf", "r1-ope", "r2-conf", "r2-ope"],
228
+ "update": "pr"
229
+ },
230
+ "production": {
231
+ "branches": ["prd-conf", "prd-ope"],
232
+ "update": "pr",
233
+ "mergeBy": "user"
234
+ },
235
+ "archive": ["main"]
236
+ }
237
+ }
238
+ ```
248
239
 
249
240
  ### 完整字段参考
250
241
 
251
242
  ```jsonc
252
243
  {
253
- "enabled": true, // opt-in: 文件存在且 enabled=true 才生效
254
- "mode": "pr", // "pr" = 全程 PR | "flexible" = 预览分支可直推/本地合入
244
+ "enabled": true, // opt-in: 文件存在且 enabled=true
245
+ "featurePattern": "feature/[\\w-]+", // 识别工作/feature 分支的 JS 正则
255
246
  "branches": {
256
- "base": "develop", // 必填: 基线分支
257
- "preview": "staging", // 必填: 预览分支
258
- "trunk": "main" // 可选: 主干分支(发布)
247
+ "integration": { "branches": ["develop"], "update": "pr" }, // 必填
248
+ "preview": { "branches": ["ita1"], "update": "pr" }, // 可选
249
+ "production": { "branches": ["prd"], "update": "pr", "mergeBy": "user" }, // 可选
250
+ "archive": ["main"] // 可选
259
251
  },
260
- "confirm": {
261
- "keywords": ["确认", "OK", "可以", "特许"], // 聊天确认触发词
262
- "featurePattern": "feature/[\\w-]+" // 匹配你 feature 分支的 JS 正则
263
- },
264
- "ci": { "enabled": true } // 可选适配器: gh pr checks 记入日志(查不到自动跳过)
252
+ "ci": { "enabled": true } // 可选: gh pr checks 作参考日志
265
253
  }
266
254
  ```
267
255
 
268
- **校验**:`branches.base` `branches.preview` 必填;两个角色映射到同一分支会被拒绝;`mode` 必须为 `pr` 或 `flexible`;非法 `featurePattern` 正则被拒绝。**任何配置错误都会让该项目整体不启用**(并报告错误),而不是半猜半应用。
269
-
270
- **`mode` 说明**:
271
- - `pr`(默认):预览分支受保护——feature 只能经 PR 进入预览(禁止直推、禁止本地合入)
272
- - `flexible`:预览分支可直接推送/本地合入;基线合入在两种模式下都须顺序 + P2
256
+ - 每个角色既可用**数组**(简写),也可用**对象** `{ branches, update?, mergeBy? }`。
257
+ - `update`:`pr`(默认)= 只能 PR/MR 合入;`flexible` = 允许直推/本地合入(小团队)。
258
+ - `mergeBy`(生产):`user`(默认)= 只能你点合并;`anyone` = 放行 PR 合并。
259
+ - 每条分支条目是精确名或正则(自动识别)。
260
+ - **校验**:`integration` 必填;角色条目重叠会被拒;非法正则会报错。**任何错误都会让该项目的插件禁用并上报**(而不是用半吊子配置)
273
261
 
274
262
  ---
275
263
 
276
264
  ## 门禁矩阵——拦什么、放什么
277
265
 
278
- | agent 操作 | 判定 |
266
+ | agent 动作 | 判定 |
279
267
  |---|---|
280
- | 合入预览分支(PR①) | 放行(流程第一步, feature 并行) |
281
- | 创建指向基线的 PR | feature 预览 · 否则 P1 特许 ? 放行 : 🚫 拦截 |
282
- | 创建指向 trunk PR | P3 特许 ? ✅ 放行 : 🚫 拦截 |
283
- | 合入基线(PR merge / 本地 merge) | feature 预览 + P2 ? 放行 : 🚫 拦截 |
284
- | 合入 trunk | 🚫 一律拦截(仅用户亲手) |
285
- | 直推/强推/删除受保护分支 | 🚫 拦截 |
286
- | 串联命令(`checkout develop && merge feature/x`) | 🚫 拦截——按段模拟分支切换, 无法绕序 |
287
- | commit / 推 feature / 同步基线 / rebase / 只读 / `gitflow-guard status` | ✅ 放行 |
268
+ | commit / feature / 同步 / rebase / 只读命令 | ✅ 放行 |
269
+ | 直推 / 强推 / 删除 integration / preview / production / archive | 🚫 拦(integration/preview 配 `flexible` 时直推放行) |
270
+ | PR/MR: feature integration / preview | ✅ 放行 |
271
+ | PR/MR: feature production |可创建;**合并被拦**(你在 UI 合并) |
272
+ | 指向 archive 的 PR/MR | 🚫 |
273
+ | integration / preview 上 `git merge feature/x`(本地) | 🚫 拦(须 PR/MR);`update: flexible` 则放行 |
274
+ | 串联命令(`checkout develop && merge feature/x`) | 🚫 拦——逐段模拟分支切换,无法绕序 |
288
275
 
289
- `gh pr merge` 通过 `gh pr view` 解析目标(可选适配器);无 `gh` 时按基线规则保守处理。
276
+ PR/MR 目标通过 `gh pr view`(GitHub)或 `glab mr view`(GitLab)解析;没有平台 CLI 时插件走保守路径。
290
277
 
291
278
  ---
292
279
 
293
- ## 用户例外权(特许)——打破规则的唯一方式
280
+ ## 人保持控制权的地方
294
281
 
295
- | 特许 | 含义 | 产生方式 | 消费时机 |
296
- |---|---|---|---|
297
- | P1 `early-pr` | 顺序未满足时提前创建基线 PR | 聊天 / CLI | PR 创建成功后 |
298
- | P2 `confirm` | "feature X 测试 OK"——允许合入基线 | 聊天 / CLI | 合入成功后 |
299
- | P3 `trunk-pr` | 允许创建指向 trunk 的 PR | 聊天 / CLI | PR 创建成功后 |
300
-
301
- **一次性**:动作成功后自动消费(留审计)。可用 `--ttl` 设有效期,过期未用也会留痕。
302
-
303
- **agent 永远不能自我授权**——插件会拦截 agent 执行 `permit` / `confirm`。
304
-
305
- **① 聊天确认**——在 DSH 里直接输入(仅真人消息有效):
306
-
307
- ```text
308
- feature/dev-x-01 测试 OK,可以合入 → P2 confirm
309
- feature/dev-x-01 提前建 PR → P1 early-pr
310
- feature/dev-x-01 可以发布上主干 → P3 trunk-pr
311
- ```
312
-
313
- (默认触发词为中文,可按你的语言配置 `confirm.keywords`。)
314
-
315
- **② 终端 CLI**(用户专属):
316
-
317
- ```bash
318
- gitflow-guard permit <feature> [--kind early-pr|confirm|trunk-pr] [--ttl <分钟>]
319
- gitflow-guard confirm <feature> [--ttl <分钟>]
320
- gitflow-guard status [--repo <路径>] # 只读: 预览所含 feature / 各 feature 特许
321
- gitflow-guard audit [--lines <数量>] # 只读: 审计记录
322
- ```
282
+ - **生产合并与归档**默认仅用户:agent 可以帮你准备 PR/MR,但**合并按钮由你点**——那个点击*就是*确认。没有独立特许库能把这决定外包出去。
283
+ - 每次拦截都写入 `.git/gitflow-guard/audit.jsonl` 供查阅(`gitflow-guard audit`)。
323
284
 
324
285
  ---
325
-
326
286
  ## 安装详解
327
287
 
328
- **前置**:可用的 [DSH](https://github.com/deepseek-ai/deepseek-harness) 安装。
288
+ **前置**:一个可用的 [DSH](https://github.com/deepseek-ai/deepseek-harness) 安装。
329
289
 
330
- **从 npm 安装**——标准路径,已在[快速开始](#快速开始30-秒用上)覆盖:
290
+ **从 npm registry**——标准路径,已在[快速开始](#快速开始30-秒用上)覆盖:
331
291
 
332
292
  ```bash
333
293
  dsh plugin --profile web add agents-gitflow-guard
334
294
  ```
335
295
 
336
- 然后重启 DSH。升级用同一条命令,之后同样重启。
296
+ 然后重启 DSH。升级用同一命令,再重启一次。
337
297
 
338
- **从源码安装**——贡献者用,或想跑最新 checkout:
298
+ **从源码**——给贡献者,或想跑最新 checkout:
339
299
 
340
300
  ```bash
341
- pnpm install && pnpm build
301
+ npm install && npm run build
342
302
  dsh plugin --profile web add file:/path/to/agents-gitflow-guard
343
303
  ```
344
304
 
345
305
  包自带 `dsh.bundle.patch` 声明,`dsh plugin add` 自动把它挂为 profile 层,无需手工编辑 profile。
346
306
 
307
+ **Claude Code hook**——同一守卫也能在 Claude Code 里跑,不依赖 DSH。本仓库已自带 `.claude/settings.json`;其他仓库加这些 hooks:
308
+
309
+ ```json
310
+ {
311
+ "hooks": {
312
+ "PreToolUse": [
313
+ { "matcher": "Bash", "hooks": [{ "type": "command", "command": "/abs/path/gitflow-guard check --platform claude" }] }
314
+ ]
315
+ }
316
+ }
317
+ ```
318
+
319
+ - hook 读 stdin payload,按 `exit 0`(放行)/ `exit 2`(拦截,stderr 展示给模型的原因 + "下一步"提示)作答。
320
+ - 只需要 `PreToolUse`:守卫在命令执行*之前*拦截;没有特许可事后消费,因此无需 `PostToolUse` 钩子。
321
+ - 用**绝对路径**指向二进制——hook 子进程不一定继承你的 shell PATH。`${CLAUDE_PROJECT_DIR}/bin/gitflow-guard.mjs`(`npm run build` 后)也可以。
322
+ - 完全 opt-in:仓库没有 `gitflow-guard.config.json`(或 `enabled` 非 true)时 hook 什么都不做。
323
+
347
324
  ---
348
325
 
349
326
  ## 常见疑问(FAQ)
350
327
 
351
328
  ### 我的分支不叫默认名字,能用吗?
352
329
 
353
- 能——分支名没有任何一处是写死的。三个角色(基线、预览、主干)是概念;`branches` 字段把仓库的**真实分支名**映射到这些概念上,`featurePattern` 告诉插件怎么识别你的 feature 分支。
354
-
355
- 一个把基线叫 `master`、预览叫 `beta`、feature 分支用 `fix/` 前缀的团队,只需把这套命名写进配置,之后的一切——拦截文案、状态报告、审计记录——都说的是你的分支名。你不需要采纳任何固定约定,只需要声明一份映射。
330
+ 能用——分支名没有任何写死。`integration` 是唯一必填;它的条目(以及 `preview`/`production`/`archive` 的)可以是任意精确分支名或正则。`featurePattern` 告诉插件怎么认你的工作分支。
356
331
 
357
- 完整的例子见[自定义分支名](#自定义分支名任何命名都可以)。
332
+ 把集成分支叫 `master`、加一个 `beta` 预览、feature 前缀用 `fix/`——写进配置即可;拦截、报告、审计都跟着你的命名走。没有任何你必须遵守的约定,只有你声明的映射。见[自定义分支名与规则](#自定义分支名与规则任何命名都可以)。
358
333
 
359
334
  ---
360
335
 
361
- ### 我的项目不用 feature → 预览 → 基线 流程呢?
362
-
363
- 那这个插件不适合你,强行启用会是很挫败的错误:每次常规合并都会被拦截,因为守卫强制的是一个你的工作流里不存在的顺序。它是一套"已有流程的强制机制",不是流程的替代品。
336
+ ### 我非得配 preview/production/archive 吗?
364
337
 
365
- 有一个细节值得知道:如果团队已经很接近了——确实有 feature 分支和共享预览,只是喜欢直接推预览而不是走 PR——`flexible` 模式可以在基线上保留顺序 + 确认的要求,同时放宽预览的规则。
338
+ 不用。只配你流程里真实有的角色。只建 `develop` 的单人仓库配 `integration: ["develop"]` 就完事;有十个环境的企业再补 `preview` 数组和 `production` 角色。其余保持关闭。
366
339
 
367
340
  ---
368
341
 
369
342
  ### 它是安全工具吗?
370
343
 
371
- 不是,而且这一点很重要——不要把它当安全工具用。它是流程守卫:让一个约定的流程在机制上可执行。文本命令识别天生是尽力而为——铁了心混淆命令的 agent 有可能绕过解析器。
344
+ 不是,请注意别把它当安全工具。它是工作流守卫:把既定流程变成可机制执行的东西。基于文本的命令识别天然是尽力而为——铁心混淆命令的 agent 可以绕过解析器。
372
345
 
373
- 但**顺序校验本身无法伪造**:一个 feature 是不是预览分支的祖先,是仓库的属性,不是 agent 能编造的声称。如果你需要防御恶意 agent 的真正保护,那属于托管服务上的分支保护规则;本插件是把诚实的工作流维持诚实的层次。
346
+ 但**角色边界本身**无法绕过:合入受保护角色分支(integration / preview / production / archive)必须走配置好的路径(PR/MR,或生产/归档的人工合并)。要真正防恶意 agent,那属于你托管服务的分支保护设置。
374
347
 
375
348
  ---
376
349
 
377
- ### 为什么 agent 不能自己跑 `gitflow-guard permit ...`?
350
+ ### 为什么 agent 不能自己合并进生产/归档?
378
351
 
379
- 因为两条例外通道都对它封闭。`permit` / `confirm` 命令被分类为用户专属:它们以工具调用形式出现时,插件直接拒绝。
380
-
381
- 聊天通道以同样的方式封闭——插件只接受消息来源为 `source.kind === 'user'` 的确认,这个标记只有真人输入才携带;模型输出、工具结果、插件注入都带不同的来源。
382
-
383
- 两条通道收敛到同一个保证:**例外只能来自人,绝不来自 agent**。这就是"用户是唯一例外权"不是一句口号的技术基础。
352
+ 因为门禁把那些动作判定为**仅用户**。agent 可以创建 PR/MR,但对生产的*合并*、对归档的*建 PR 与合并*插件一律拒绝。唯一路径是**你**点合并——不存在 agent 能用来给自己授权的特许、令牌或聊天消息。
384
353
 
385
354
  ---
386
355
 
387
- ### 必须装 `gh` CLI 吗?
356
+ ### 必须装 `gh` 或 `glab` CLI 吗?
388
357
 
389
- 不必。`gh` 集成是可选适配器:它让插件能解析 `gh pr merge` 实际指向的目标,并把 `pr checks` 状态作为日志参考记录。
390
-
391
- 没有 `gh`,插件走保守路径——无法解析的 `pr merge` 按基线规则处理——其余一切照常。核心强制从不触碰托管服务,这也是为什么插件在 GitHub、GitLab、自建服务器、甚至离线仓库上表现完全一致。
358
+ 不用。它们只是可选适配器,用来解析 `pr merge` / `mr merge` 到底指向哪个分支,好让门禁区分"合入 integration/preview"(放行)与"合入 production/archive"(拦截)。没有时插件走保守路径——无法确认目标就拒绝——其余一切照常。核心校验不碰任何托管服务,所以它在 GitHub、GitLab、自托管或离线环境里行为一致。
392
359
 
393
360
  ---
394
361
 
395
362
  ### 会误拦我的正常工作吗?
396
363
 
397
- 刻意地不会。feature 分支该做的事——commit、push、同步基线、rebase、只读命令、`gitflow-guard status`——全部无摩擦放行。
398
-
399
- 拦截只保留给两类动作:对受保护分支的写入,以及跳过顺序或确认的基线合入。
364
+ 刻意不会。feature 分支该干的事——提交、推送、从集成同步、rebase、只读命令、`gitflow-guard status`——全部无阻碍放行。
400
365
 
401
- 如果看到疑似误拦,先跑 `gitflow-guard status`——报告会展示判定所依据的精确事实(feature 是否在预览中、有哪些特许),让误判可见、可纠正,而不是莫名奇妙。
366
+ 拦截只留给:(1) 直接写受保护角色分支,(2) agent 试图合入生产或归档。若你看到一笔错误拦截,先跑 `gitflow-guard status`——它显示每个本地分支被归为哪个角色,误判一眼可见、可纠正。
402
367
 
403
368
  ---
404
369
 
405
370
  ### 配置写错了会怎样?
406
371
 
407
- 插件倾向"宁可禁用":配置有任何校验错误,该项目整体禁用并报告错误——半猜半套的配置绝不会被悄悄应用。
372
+ 插件偏好 fail-closed:任何校验错误都会让该项目的守卫禁用并上报错误,半吊子配置绝不会意外生效。
408
373
 
409
- 最常见的错误是:两个角色映射到同一分支(明确拒绝)、`featurePattern` 无法编译(按非法正则拒绝)、`mode` 拼写错误。因为失败是响亮的、文件只是单个 JSON 对象,修复通常是三十秒的改正,然后守卫正常工作。
410
-
411
- ---
412
-
413
- ### 多台机器能用吗?
414
-
415
- 单机内,完全可用——特许状态和审计在 `.git/gitflow-guard/` 里,跨 DSH 重启持久。
416
-
417
- 跨机器,还不行:如果你和 agent 在不同电脑工作,一台机器上授予的确认另一台看不到,那边的合并会一直拦截直到你再次确认。这是 v1 的限制,已有清晰的 v2 方案(同步状态),见[路线图](#路线图)。
418
-
419
- ---
420
-
421
- ### 合法的 PR①(feature → 预览)会被误拦吗?
422
-
423
- 不会。合入预览分支是流程的第一步,始终放行——多个 feature 并行进入预览正是这个模型期待的形态。
424
-
425
- 顺序门禁只作用于基线合入,所以正常路径(feature → 预览 → 确认 → 基线)永远不会触发它。
374
+ 常见错误:`integration` 缺失(必填)、同一个分支被配到两个角色里(显式拒绝)、`featurePattern` 写不成合法正则(报错)。失败提示很明确,文件又是一个 JSON 对象,通常三十秒改好。
426
375
 
427
376
  ---
428
377
 
429
378
  ### 插件到底查了本地仓库的什么?
430
379
 
431
- 三个只读查询,仅此而已:当前分支(`git branch --show-current`)、feature 是否为预览分支的祖先(`git merge-base --is-ancestor`)、以及——仅针对 `gh pr merge`——PR 的目标分支(`gh pr view`)
380
+ 当前分支(`git branch --show-current`),以及——只在 `pr merge` / `mr merge` 时——通过 `gh pr view` / `glab mr view` 查 PR/MR 目标。不需要任何祖先关系判断,因为模型是**角色驱动**(目标是哪个分支),而不是顺序驱动。
432
381
 
433
- 不写任何东西,不碰远程,不依赖任何托管服务特性。这正是插件能对顺序做出硬承诺的全部原因:它信任的事实来自仓库本身。
382
+ 核心校验不写任何东西、不碰远端、不需要托管服务功能。生产/归档合并直接对 agent 拒绝;人工合并发生在你的 UI 里。
434
383
 
435
384
  ---
436
385
 
@@ -438,36 +387,30 @@ dsh plugin --profile web add file:/path/to/agents-gitflow-guard
438
387
 
439
388
  MIT,免费,无条件。随便用、随便改、随便发,唯一义务是保留版权声明。
440
389
 
441
- 如果它帮你和团队拦下了一次抄近路的事故,页面顶部的咖啡按钮值得点一下,但绝不是必需。见[许可证](#许可证)。
390
+ 如果它帮你挡掉了一次抄近路,页顶的咖啡按钮欢迎但绝不要求。见[许可证](#许可证)。
442
391
 
443
392
  ---
444
-
445
393
  ## 术语表
446
394
 
447
395
  | 术语 | 含义 |
448
396
  |---|---|
449
- | **基线 baseline** | 你的稳定集成分支(`branches.base`);受保护;合入需顺序 + P2 |
450
- | **预览 preview** | 测试环境分支(`branches.preview`);feature 自由合入(PR①) |
451
- | **主干 trunk** | 发布分支(`branches.trunk`, 可选);仅用户亲手 |
452
- | **feature 分支** | 你的工作分支, 由 `featurePattern` 匹配 |
453
- | **PR① / PR②** | feature 预览 / feature → 基线 |
454
- | **特许 permit** | 一次性用户例外(P1 early-pr / P2 confirm / P3 trunk-pr) |
455
- | **门禁矩阵** | 命令分类 → 放行/拦截 的判定表 |
456
- | **P2** | 解锁基线合入的用户确认 |
457
- | **pre-execute** | 工具管线中执行拦截的钩子点——命令运行之前 |
458
- | **`source.kind === 'user'`** | DSH 标记真人输入的消息标签——不可伪造的确认通道 |
459
- | **`merge-base --is-ancestor`** | 回答"这个 feature 合入预览了吗"的 git 查询——真实可信 |
397
+ | **integration** | 集成分支,唯一必填角色(`branches.integration`);feature PR/MR 合入;受保护 |
398
+ | **preview** | 可选环境终点分支(`branches.preview`,数组);只走 PR/MR 更新 |
399
+ | **production** | 可选生产分支(`branches.production`,数组);PR/MR + 合并仅限用户 |
400
+ | **archive** | 可选发布后归档分支(`branches.archive`);仅用户亲手 |
401
+ | **feature 分支** | 你的工作分支,由 `featurePattern` 识别;自由区 |
402
+ | **门禁矩阵** | 把每条被分类的命令映射为放行/拦截的判定表 |
403
+ | **pre-execute** | 工具管线中拦截发生的钩子——在命令运行之前 |
404
+ | **合并仅限用户** | 生产/归档合并留在你手上——你在 PR/MR 上的点击就是确认 |
460
405
 
461
406
  ---
462
407
 
463
408
  ## 路线图
464
409
 
465
- - **i18n — 拦截文案多语言**:当前拦截文案默认中文;改为跟随用户语言(和插件配置)。
466
- - **v2 — 多机状态同步**:跨机器同步特许/审计。
467
- - **v2 — 平台适配器**:GitLab / Gitea 支持(接口已预留)
468
- - **v2 通知**:特许消费时主动推送用户(当前为审计 + 对话)。
469
- - **v2 — CI 硬门禁调研**:评估 `gh pr checks` 是否可成为真门禁而不损害平台无关核心。
470
- - **生态**:常见工作流配置模板;社区贡献多语言确认关键词。
410
+ - **i18n——拦截文案本地化**:目前默认中文;让它跟随用户语言(和插件配置)。
411
+ - **v2——审计同步**:跨机器同步 `.git/gitflow-guard/audit.jsonl`(现仅本地)。
412
+ - **v2——更多预制模板**:常用流程(solo `develop`、多环境企业)的现成配置模板,由社区贡献。
413
+ - **v2——CI 硬门槛研究**:`pr checks` 能否在不伤平台无关核心的前提下变成真实门槛。
471
414
 
472
415
  欢迎贡献——见[开发](#开发)。
473
416
 
@@ -475,7 +418,7 @@ MIT,免费,无条件。随便用、随便改、随便发,唯一义务是保留
475
418
 
476
419
  ## 赞助支持
477
420
 
478
- 插件免费开源(MIT)。如果它帮你和团队拦下了一次抄近路的事故,一杯咖啡就是最好的鼓励:
421
+ 插件免费开源(MIT)。如果它帮你和团队挡掉了一次抄近路,一杯咖啡感谢:
479
422
 
480
423
  [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/keanz21)
481
424
 
@@ -484,10 +427,10 @@ MIT,免费,无条件。随便用、随便改、随便发,唯一义务是保留
484
427
  ## 开发
485
428
 
486
429
  ```bash
487
- pnpm install
488
- pnpm test # 单测: classify / gate / config / permits / session / 真实 git 集成
489
- pnpm typecheck # tsc --noEmit, 0 Error
490
- pnpm build # tsdown → lib/(CLI 与插件共用)
430
+ npm install
431
+ npm test # 单测: classify / gate / config / cli / repo / platform
432
+ npm run typecheck # tsc --noEmit, 0 Error
433
+ npm run build # tsdown → lib/(CLI 与插件共用)
491
434
  ```
492
435
 
493
436
  **铁律**:任何逻辑改动必须 0 Error 构建 + 单测全绿后才算完成。
@@ -498,4 +441,4 @@ pnpm build # tsdown → lib/(CLI 与插件共用)
498
441
 
499
442
  [MIT](LICENSE) © FeatureAgents
500
443
 
501
- 设计定稿(决策记录):[docs/design.md](docs/design.md)。
444
+ 设计规格(中文,决策记录):[docs/design.md](docs/design.md)。