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