@house365/comate 0.9.7 → 0.9.11
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 +8 -4
- package/bin/init-run.mjs +5 -7
- package/bin/init.mjs +11 -32
- package/docs/COMATE_BEGINNER_GUIDE.md +429 -0
- package/docs/COMATE_MANUAL.md +2 -2
- package/package.json +55 -55
- package/skills/comate/reference/prerequisites.md +1 -1
- package/skills/comate/scripts/comate-lib.sh +70 -70
- package/skills/comate/scripts/comate.sh +8 -23
package/README.md
CHANGED
|
@@ -93,13 +93,17 @@ skills/
|
|
|
93
93
|
comate-fix/ 预设路径 skill(bug + 小调整)
|
|
94
94
|
comate-grill/ 决策点穷追式澄清工具
|
|
95
95
|
comate-kb-integration/ 第三方对接档案生成工具(知识库配套)
|
|
96
|
-
docs/
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
docs/
|
|
97
|
+
COMATE_BEGINNER_GUIDE.md 面向新手的安装与使用手册
|
|
98
|
+
COMATE_MANUAL.md 完整操作手册
|
|
99
|
+
```
|
|
99
100
|
|
|
100
101
|
## 文档
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
文档:
|
|
104
|
+
|
|
105
|
+
- [入门操作手册](docs/COMATE_BEGINNER_GUIDE.md):面向第一次使用 comate 的同学,覆盖安装、`init`、目录、PRD、Skill 流程和常见问题。
|
|
106
|
+
- [完整技术手册](docs/COMATE_MANUAL.md):安装前置、命令参考、阶段详解、状态文件字段、决策阻塞点、故障排除。
|
|
103
107
|
|
|
104
108
|
## License
|
|
105
109
|
|
package/bin/init-run.mjs
CHANGED
|
@@ -68,15 +68,13 @@ export async function init(pkgRoot) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
// comate's
|
|
72
|
-
//
|
|
73
|
-
// first /comate call die inside a sourced library.
|
|
71
|
+
// comate's scripts intentionally support macOS's system Bash 3.2. Fail only
|
|
72
|
+
// when Bash is older than the syntax the state machine supports.
|
|
74
73
|
const bash = bashVersion();
|
|
75
|
-
if (bash !== null && bash <
|
|
74
|
+
if (bash !== null && bash < 3) {
|
|
76
75
|
console.error(
|
|
77
|
-
`\ncomate 的脚本需要 bash >=
|
|
78
|
-
'
|
|
79
|
-
'装好后重跑 comate init。',
|
|
76
|
+
`\ncomate 的脚本需要 bash >= 3.2,当前 ${bash === 0 ? '未知版本' : `bash ${bash}.x`}。\n` +
|
|
77
|
+
'请安装 Bash 3.2 或更高版本后重跑 comate init。',
|
|
80
78
|
);
|
|
81
79
|
return 3;
|
|
82
80
|
}
|
package/bin/init.mjs
CHANGED
|
@@ -256,9 +256,9 @@ function pruneDeprecatedSkills(
|
|
|
256
256
|
return removed;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
// True only when the pinned `skills` dependency resolves locally.
|
|
260
|
-
//
|
|
261
|
-
//
|
|
259
|
+
// True only when the pinned `skills` dependency resolves locally. A postinstall
|
|
260
|
+
// must never reach the network, so this gates syncComateSkills: if resolution
|
|
261
|
+
// fails we skip rather than let skillsInvocation fall back to `npx skills@latest`.
|
|
262
262
|
function skillsResolvesLocally(pkgRoot) {
|
|
263
263
|
try {
|
|
264
264
|
createRequire(join(pkgRoot, 'package.json')).resolve('skills/package.json');
|
|
@@ -268,40 +268,19 @@ function skillsResolvesLocally(pkgRoot) {
|
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
//
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
function syncComateSkills(
|
|
276
|
-
pkgRoot,
|
|
277
|
-
{
|
|
278
|
-
home = process.env.HOME ?? process.env.USERPROFILE ?? '',
|
|
279
|
-
runner = run,
|
|
280
|
-
exists = existsSync,
|
|
281
|
-
} = {},
|
|
282
|
-
) {
|
|
271
|
+
// Copy comate's own skills into ~/.agents/skills via the bundled skills CLI —
|
|
272
|
+
// the exact command `comate init` runs, so the layout is identical. Local-only:
|
|
273
|
+
// skips (never goes to network) when the skills dep can't be resolved, leaving
|
|
274
|
+
// `comate init` as the fallback. Returns { ok, skipped }.
|
|
275
|
+
function syncComateSkills(pkgRoot, { home = process.env.HOME ?? process.env.USERPROFILE ?? '', runner = run } = {}) {
|
|
283
276
|
if (!skillsResolvesLocally(pkgRoot)) return { ok: false, skipped: 'no-local-cli' };
|
|
284
|
-
|
|
285
|
-
const superpowersMarker = join(home, '.agents', 'skills', 'using-superpowers', 'SKILL.md');
|
|
286
|
-
let superpowers = { ok: true };
|
|
287
|
-
if (!exists(superpowersMarker)) {
|
|
288
|
-
superpowers = runner(
|
|
289
|
-
skillsInvocation(pkgRoot, ['add', 'obra/superpowers', '-g', '-y']),
|
|
290
|
-
SKILLS_TIMEOUT_MS,
|
|
291
|
-
{ quiet: true },
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const comate = runner(
|
|
277
|
+
const r = runner(
|
|
296
278
|
skillsInvocation(pkgRoot, ['add', pkgRoot, '--skill', '*', '-g', '-y']),
|
|
297
279
|
SKILLS_TIMEOUT_MS,
|
|
298
280
|
{ quiet: true },
|
|
299
281
|
);
|
|
300
|
-
const
|
|
301
|
-
return {
|
|
302
|
-
ok: superpowers.ok && exists(superpowersMarker) && comate.ok && exists(comateMarker),
|
|
303
|
-
skipped: null,
|
|
304
|
-
};
|
|
282
|
+
const landed = existsSync(join(home, '.agents', 'skills', 'comate', 'SKILL.md'));
|
|
283
|
+
return { ok: r.ok && landed, skipped: null };
|
|
305
284
|
}
|
|
306
285
|
|
|
307
286
|
export {
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# comate 入门操作手册
|
|
2
|
+
|
|
3
|
+
这是一份给第一次使用 comate 的同学看的操作手册。按“安装 → 初始化 → 输入 /comate”的顺序,就可以开始使用。
|
|
4
|
+
|
|
5
|
+
## 1. comate 是什么
|
|
6
|
+
|
|
7
|
+
comate 是一个 AI 编程流程编排器。它把一次需求变更拆成几个可以检查的阶段,让 AI 先理解需求和设计,再写代码,最后验证并归档。
|
|
8
|
+
|
|
9
|
+
完整流程:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
原始 PRD(可选)
|
|
13
|
+
↓
|
|
14
|
+
/comate-gate PRD 门禁和拆分
|
|
15
|
+
↓
|
|
16
|
+
/comate-open 开题,创建 change
|
|
17
|
+
↓
|
|
18
|
+
/comate-design 深度设计
|
|
19
|
+
↓
|
|
20
|
+
/comate-build 计划和编码
|
|
21
|
+
↓
|
|
22
|
+
/comate-verify 验证和收尾
|
|
23
|
+
↓
|
|
24
|
+
/comate-archive 归档规格
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- **OpenSpec** 负责“要做什么”:proposal、spec、tasks 和归档。
|
|
28
|
+
- **Superpowers** 负责“怎么做好”:头脑风暴、技术设计、计划、执行和验证。
|
|
29
|
+
- **comate** 负责识别当前阶段,并把这些能力串起来。
|
|
30
|
+
|
|
31
|
+
## 2. 安装前准备
|
|
32
|
+
|
|
33
|
+
### 2.1 必需软件
|
|
34
|
+
|
|
35
|
+
| 软件 | 建议版本 | 用途 |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| Node.js | 20 或更高 | 运行 comate 和 npm |
|
|
38
|
+
| npm | 随 Node.js 安装 | 安装 comate、OpenSpec |
|
|
39
|
+
| Git | 任意现代版本 | 管理代码和分支 |
|
|
40
|
+
| Bash | 3.2 或更高 | 执行 comate 的状态机脚本 |
|
|
41
|
+
|
|
42
|
+
Windows 用户建议安装 [Git for Windows](https://git-scm.com/download/win),它会提供 Git Bash。PowerShell 可以运行 `comate`,但流程脚本仍需要 Bash。
|
|
43
|
+
|
|
44
|
+
检查环境:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
node --version
|
|
48
|
+
npm --version
|
|
49
|
+
git --version
|
|
50
|
+
bash --version
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2.2 哪些步骤需要网络
|
|
54
|
+
|
|
55
|
+
首次初始化时,`comate init` 可能需要访问 npm registry 安装 OpenSpec CLI,并访问 GitHub 安装 Superpowers。
|
|
56
|
+
|
|
57
|
+
comate 自己的 npm 包会随包携带 comate skill。npm 安装钩子只会尽力同步 comate 自身 skill,不会安装 Superpowers,也不会创建项目的 `openspec/` 和 `docs/`;这些由项目目录中的 `comate init` 完成。
|
|
58
|
+
|
|
59
|
+
## 3. 安装 comate
|
|
60
|
+
|
|
61
|
+
全局安装:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install -g @house365/comate
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
确认命令可用:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
comate --version
|
|
71
|
+
comate --help
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
安装后,skill 通常会同步到用户级目录:
|
|
75
|
+
|
|
76
|
+
- macOS/Linux:`~/.agents/skills/`
|
|
77
|
+
- Windows:`%USERPROFILE%\.agents\skills\`
|
|
78
|
+
|
|
79
|
+
用户级 skill 可以被多个项目共用。项目自己的规格文件放在项目目录下的 `openspec/`。
|
|
80
|
+
|
|
81
|
+
## 4. 初始化项目:`comate init`
|
|
82
|
+
|
|
83
|
+
进入项目根目录再运行:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
cd path/to/your-project
|
|
87
|
+
comate init
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Windows PowerShell 示例:
|
|
91
|
+
|
|
92
|
+
```powershell
|
|
93
|
+
Set-Location D:\work\my-project
|
|
94
|
+
comate init
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
请不要在用户主目录直接运行。comate 会检查当前目录是否有 `.git`、`package.json`、`pyproject.toml`、`Cargo.toml` 等项目标记;空目录会先询问是否确认初始化。
|
|
98
|
+
|
|
99
|
+
### 4.1 `init` 会做什么
|
|
100
|
+
|
|
101
|
+
1. 检查 Bash 版本。
|
|
102
|
+
2. 检查 OpenSpec,不存在或版本过旧时执行 `npm install -g @fission-ai/openspec@latest`。
|
|
103
|
+
3. 检查 Superpowers,不存在时执行 `skills add obra/superpowers -g -y`。
|
|
104
|
+
4. 把 comate 自身 skill 同步到用户级 skill 目录。
|
|
105
|
+
5. 创建项目级 `openspec/`(已经存在则保留)。
|
|
106
|
+
6. 创建 `docs/` 知识库骨架(只补缺少的文件,不覆盖已有文件)。
|
|
107
|
+
|
|
108
|
+
某一步失败时,修好网络或环境后重新运行 `comate init` 即可。它是幂等的,已完成的步骤会跳过。
|
|
109
|
+
|
|
110
|
+
## 5. 初始化后的目录结构
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
your-project/
|
|
114
|
+
├─ openspec/
|
|
115
|
+
│ ├─ changes/ # 每次需求变更的工作区
|
|
116
|
+
│ └─ specs/ # 已沉淀的主规格
|
|
117
|
+
├─ docs/
|
|
118
|
+
│ ├─ README.md
|
|
119
|
+
│ ├─ architecture/
|
|
120
|
+
│ │ └─ README.md
|
|
121
|
+
│ └─ knowledge-base/
|
|
122
|
+
│ ├─ README.md
|
|
123
|
+
│ ├─ requirements/
|
|
124
|
+
│ │ └─ _templates/PRD.md
|
|
125
|
+
│ └─ integrations/
|
|
126
|
+
│ ├─ README.md
|
|
127
|
+
│ └─ _TEMPLATE/
|
|
128
|
+
└─ 你的业务代码...
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 5.1 `openspec/`:项目规格
|
|
132
|
+
|
|
133
|
+
- `changes/<change-name>/`:正在进行的变更,包含 `proposal.md`、`design.md`、`tasks.md`、delta spec 和 `.comate.yaml`。
|
|
134
|
+
- `specs/<capability>/spec.md`:归档后长期维护的能力规格。
|
|
135
|
+
- `changes/archive/`:已经完成并归档的 change。
|
|
136
|
+
|
|
137
|
+
`.comate.yaml` 是 comate 的状态真相源,记录当前阶段、构建模式、验证结果等。
|
|
138
|
+
|
|
139
|
+
### 5.2 `docs/`:长期知识库
|
|
140
|
+
|
|
141
|
+
- `docs/`:项目背景、架构约束、原始 PRD、第三方对接资料。
|
|
142
|
+
- `openspec/`:某次变更的提案、设计、任务、能力规格和归档记录。
|
|
143
|
+
|
|
144
|
+
### 5.3 用户级 skill 目录
|
|
145
|
+
|
|
146
|
+
```text
|
|
147
|
+
~/.agents/skills/ # macOS/Linux
|
|
148
|
+
%USERPROFILE%\.agents\skills\ # Windows
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
已有的 Superpowers 或其他 skill 不会因为 `comate init` 被覆盖。
|
|
152
|
+
|
|
153
|
+
## 6. `docs/` 目录详解
|
|
154
|
+
|
|
155
|
+
### 6.1 `docs/README.md`
|
|
156
|
+
|
|
157
|
+
说明知识库整体规则、`architecture/` 与 `knowledge-base/` 的边界,以及它们和 `openspec/` 的关系。
|
|
158
|
+
|
|
159
|
+
### 6.2 `docs/architecture/`
|
|
160
|
+
|
|
161
|
+
放项目架构约束,例如前后端分层、API/数据库/错误码约定、命名规范和部署约束。`comate init` 只创建占位 README,真正的规则需要项目自己补充。这里是 `/comate-gate` 检查契约时的事实来源。
|
|
162
|
+
|
|
163
|
+
### 6.3 `docs/knowledge-base/`
|
|
164
|
+
|
|
165
|
+
放长期维护的需求和外部服务资料:
|
|
166
|
+
|
|
167
|
+
- `requirements/`:原始 PRD;
|
|
168
|
+
- `integrations/`:第三方服务对接档案;
|
|
169
|
+
- `README.md`:命名、溯源和 PRD 到 change 的流程说明。
|
|
170
|
+
|
|
171
|
+
### 6.4 `docs/knowledge-base/requirements/`
|
|
172
|
+
|
|
173
|
+
这里是 PRD 的固定位置。模板位于:
|
|
174
|
+
|
|
175
|
+
```text
|
|
176
|
+
docs/knowledge-base/requirements/_templates/PRD.md
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
复制模板到功能目录,填写后运行 `/comate-gate`。
|
|
180
|
+
|
|
181
|
+
### 6.5 `docs/knowledge-base/integrations/`
|
|
182
|
+
|
|
183
|
+
每个第三方平台建立一个子目录,例如:
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
docs/knowledge-base/integrations/alipay/README.md
|
|
187
|
+
docs/knowledge-base/integrations/alipay/api-snapshot.md
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
需要从官方文档生成对接档案时,使用 `/comate-kb-integration`。
|
|
191
|
+
|
|
192
|
+
## 7. PRD 存放位置和命名规范
|
|
193
|
+
|
|
194
|
+
推荐格式:
|
|
195
|
+
|
|
196
|
+
```text
|
|
197
|
+
docs/knowledge-base/requirements/<YYYYMMDD-功能名>/PRD-<三位序号>-<功能名>.md
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
示例:
|
|
201
|
+
|
|
202
|
+
```text
|
|
203
|
+
docs/knowledge-base/requirements/20260820-用户登录/PRD-001-用户登录.md
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
规则:
|
|
207
|
+
|
|
208
|
+
1. 功能目录使用“日期 + 功能名”,日期格式为 `YYYYMMDD`。
|
|
209
|
+
2. PRD 文件以 `PRD-001`、`PRD-002` 这样的三位序号开头。
|
|
210
|
+
3. 文件名中的功能名应和目录功能名保持一致。
|
|
211
|
+
4. 同一个功能目录中的序号不要重复。
|
|
212
|
+
5. 不建议使用 `PRD-1.md` 或把 PRD 放到 `openspec/changes/`。
|
|
213
|
+
|
|
214
|
+
`/comate-gate` 会在 PRD 同级自动生成:
|
|
215
|
+
|
|
216
|
+
```text
|
|
217
|
+
PRD-001.gate.yaml # 机器读取的门禁状态
|
|
218
|
+
PRD-001-REVIEW.md # 人类可读的评审报告
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
PRD 通过门禁后如果又被修改,哈希会变化,门禁会变成过期状态,需要重新运行 `/comate-gate`。
|
|
222
|
+
|
|
223
|
+
## 8. 从需求到代码的 Skill 流程
|
|
224
|
+
|
|
225
|
+
### 8.1 需求来自一份 PRD
|
|
226
|
+
|
|
227
|
+
先把 PRD 放到 `docs/knowledge-base/requirements/`,然后直接指定 PRD 路径:
|
|
228
|
+
|
|
229
|
+
```text
|
|
230
|
+
/comate docs/knowledge-base/requirements/20260820-用户登录/PRD-001-用户登录.md
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`/comate` 会识别这是原始 PRD,自动进入 PRD 门禁和拆分流程;不需要先手动调用 `/comate-gate`。门禁通过后,它会继续处理拆分出来的 change。
|
|
234
|
+
|
|
235
|
+
如果只想单独审阅或重新运行门禁,也可以直接输入:
|
|
236
|
+
|
|
237
|
+
```text
|
|
238
|
+
/comate-gate docs/knowledge-base/requirements/20260820-用户登录/PRD-001-用户登录.md
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
对已有 change 继续工作时,输入:
|
|
242
|
+
|
|
243
|
+
```text
|
|
244
|
+
/comate
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
comate 会自动从当前阶段继续:`open → design → build → verify → archive`。
|
|
248
|
+
|
|
249
|
+
### 8.2 需求来自普通对话
|
|
250
|
+
|
|
251
|
+
不需要先写 PRD,直接输入:
|
|
252
|
+
|
|
253
|
+
```text
|
|
254
|
+
/comate 我想增加用户登录功能,支持邮箱和密码登录
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
复杂需求仍建议先整理成 PRD,便于评审和追踪。
|
|
258
|
+
|
|
259
|
+
### 8.3 Bug 或很小的调整
|
|
260
|
+
|
|
261
|
+
```text
|
|
262
|
+
/comate-fix 修复登录失败时页面没有提示的问题
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
它走简化流程:`open → build → verify → archive`,跳过完整 brainstorming。涉及新能力、架构变化、数据库 schema 或多个模块时,应升级回完整 `/comate` 流程。
|
|
266
|
+
|
|
267
|
+
## 9. Skill 介绍
|
|
268
|
+
|
|
269
|
+
| Skill | 作用 |
|
|
270
|
+
| --- | --- |
|
|
271
|
+
| `/comate` | 总入口,自动检测当前 change 阶段并分派 |
|
|
272
|
+
| `/comate-gate` | 审阅 PRD、评分、记录阻断问题并拆分 change |
|
|
273
|
+
| `/comate-open` | 探索需求,创建 change 和 proposal/design/tasks 骨架 |
|
|
274
|
+
| `/comate-design` | 技术设计和方案头脑风暴,形成 Design Doc 和 delta spec |
|
|
275
|
+
| `/comate-build` | 制定实施计划、按任务编码并更新 tasks |
|
|
276
|
+
| `/comate-verify` | 运行测试和检查,生成验证报告并处理分支收尾 |
|
|
277
|
+
| `/comate-archive` | 同步 delta spec、标记设计文档并归档 change |
|
|
278
|
+
| `/comate-fix` | Bug 或小调整的快速路径 |
|
|
279
|
+
| `/comate-grill` | 对 PRD、计划或设计中的模糊决策逐个追问 |
|
|
280
|
+
| `/comate-kb-integration` | 根据第三方官方文档生成 integrations 对接档案 |
|
|
281
|
+
|
|
282
|
+
### 9.1 底层依赖分工
|
|
283
|
+
|
|
284
|
+
- **OpenSpec**:负责 `openspec/`、change 规格和归档。
|
|
285
|
+
- **Superpowers**:由 `comate init` 通过 `skills` CLI 从 `obra/superpowers` 安装,负责设计、规划、执行和收尾。
|
|
286
|
+
- **comate skill**:本项目提供的编排规则和状态机。
|
|
287
|
+
|
|
288
|
+
首次使用时,如果 Superpowers 安装失败,design/build/verify 相关流程不能完整运行。先修复网络或手工安装,再重跑 `comate init`。
|
|
289
|
+
|
|
290
|
+
## 10. 常用命令
|
|
291
|
+
|
|
292
|
+
### 10.1 安装和检查
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
comate --help
|
|
296
|
+
comate --version
|
|
297
|
+
comate init
|
|
298
|
+
comate doctor
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### 10.2 查看 token 用量
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
comate usage
|
|
305
|
+
comate usage daily
|
|
306
|
+
comate usage --global
|
|
307
|
+
comate usage --json
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## 11. 配套 UI 查看器
|
|
311
|
+
|
|
312
|
+
`comate-ui` 是 comate 的本地只读浏览器工作区,用来可视化查看项目中的变更状态、OpenSpec 规格、PRD、Superpowers 设计文档、计划和验证报告。它不会修改项目文件,也不需要数据库。
|
|
313
|
+
|
|
314
|
+
全局安装:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
npm install -g @house365/comate-ui@latest
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
安装后,在包含 `openspec/` 的项目根目录运行:
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
comate-ui
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
也可以指定项目路径、端口或禁止自动打开浏览器:
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
comate-ui D:\work\my-project
|
|
330
|
+
comate-ui D:\work\my-project --port 4300
|
|
331
|
+
comate-ui --no-open
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
查看内置示例:
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
comate-ui --demo
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
查看器只绑定本机 `127.0.0.1`。默认会自动打开浏览器;端口被占用时会自动选择下一个可用端口,按 `Ctrl+C` 停止。
|
|
341
|
+
|
|
342
|
+
## 12. 更新 comate
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
npm install -g @house365/comate@latest
|
|
346
|
+
cd path/to/your-project
|
|
347
|
+
comate init
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
npm 更新 CLI 和包内文件;`comate init` 负责把新版 comate skill 同步到用户级目录,并补齐当前项目缺少的 `openspec/`、`docs/` 文件。
|
|
351
|
+
|
|
352
|
+
更新是幂等的:已有 Superpowers 不覆盖,已有项目规格和 docs 文件不覆盖。若使用 `npm install --ignore-scripts`,请手动执行 `comate init`。
|
|
353
|
+
|
|
354
|
+
## 13. 网络问题和手工补装
|
|
355
|
+
|
|
356
|
+
### OpenSpec 安装失败
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
npm install -g @fission-ai/openspec@latest
|
|
360
|
+
openspec --version
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Superpowers 安装失败
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
skills add obra/superpowers -g -y
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
GitHub 无法访问时,可以配置 npm/Git 代理、使用公司网络或 Git Bash/WSL,安装成功后再运行:
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
comate init
|
|
373
|
+
comate doctor
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
当前 npm 包没有内置 Superpowers 离线快照;不要把“npm 安装成功”理解成 Superpowers 已安装完成。
|
|
377
|
+
|
|
378
|
+
## 14. 常见问题
|
|
379
|
+
|
|
380
|
+
### `comate init` 应该在哪运行?
|
|
381
|
+
|
|
382
|
+
在业务项目根目录运行,也就是能看到 `.git` 或 `package.json` 的目录。不要在 `C:\Users\你的用户名` 或 `~` 直接运行。
|
|
383
|
+
|
|
384
|
+
### npm 安装后为什么还要 `comate init`?
|
|
385
|
+
|
|
386
|
+
npm 只安装 CLI 和包本身。`init` 才会准备 OpenSpec、Superpowers 与 comate skill,并创建当前项目的 `openspec/`、`docs/`。
|
|
387
|
+
|
|
388
|
+
### 重跑 init 会不会覆盖我的文件?
|
|
389
|
+
|
|
390
|
+
不会。已有 skill、`openspec/`、docs 文件会保留,只补缺少的内容。
|
|
391
|
+
|
|
392
|
+
### 找不到 `openspec` 怎么办?
|
|
393
|
+
|
|
394
|
+
重开终端让全局 npm PATH 生效,然后运行 `openspec --version`。仍找不到就执行 `npm install -g @fission-ai/openspec@latest`,再运行 `comate init`。
|
|
395
|
+
|
|
396
|
+
### 找不到 Bash 怎么办?
|
|
397
|
+
|
|
398
|
+
Windows 安装 Git for Windows;macOS 自带的 Bash 3.2 可以直接使用,不需要另外安装 Bash 4+。
|
|
399
|
+
|
|
400
|
+
### 流程中断了怎么办?
|
|
401
|
+
|
|
402
|
+
回到同一个项目目录,再输入 `/comate`。comate 会读取 `openspec/changes/<name>/.comate.yaml` 和已有产物,从当前阶段继续。先运行 `comate doctor` 可以排除环境问题。
|
|
403
|
+
|
|
404
|
+
### 有多个 change 怎么办?
|
|
405
|
+
|
|
406
|
+
在对话中明确 change 名称。不要同时修改多个 change 的 `tasks.md`。
|
|
407
|
+
|
|
408
|
+
## 15. 新手最短上手路径
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
npm install -g @house365/comate
|
|
412
|
+
cd your-project
|
|
413
|
+
comate init
|
|
414
|
+
comate doctor
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
然后在 Claude Code、Codex、Cursor 等支持 Agent Skills 的工具中输入:
|
|
418
|
+
|
|
419
|
+
```text
|
|
420
|
+
/comate 我想实现一个用户登录功能
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
等待 AI 在需要你决定的地方提问,确认设计和构建选项后,让流程走到验证和归档。
|
|
424
|
+
|
|
425
|
+
## 16. 进一步阅读
|
|
426
|
+
|
|
427
|
+
- [完整技术手册](COMATE_MANUAL.md):状态字段、门禁规则、脚本接口和故障排查。
|
|
428
|
+
- [项目 README](../README.md):项目概览和快速安装。
|
|
429
|
+
- 初始化后生成的 `docs/knowledge-base/README.md`:PRD 溯源和命名规则。
|
package/docs/COMATE_MANUAL.md
CHANGED
|
@@ -89,7 +89,7 @@ cd your-project && comate init
|
|
|
89
89
|
|
|
90
90
|
| 依赖 | 要求 | 检查方式 |
|
|
91
91
|
|------------|-------------|-------|
|
|
92
|
-
| bash | >=
|
|
92
|
+
| bash | >= 3.2 | `bash --version` |
|
|
93
93
|
| git | 任意现代版本 | `git --version` |
|
|
94
94
|
| sha256 | `sha256sum` 或 `shasum`(至少一个) | `command -v sha256sum shasum` |
|
|
95
95
|
| LF 换行符 | 所有 `.sh` 文件必须使用 LF | `bash comate/scripts/comate.sh doctor` |
|
|
@@ -645,7 +645,7 @@ bash comate/scripts/comate.sh status # 所有活动变更状态
|
|
|
645
645
|
| 变更目录结构不完整 | 流程中断 | 按 comate-open 产出物要求补全 |
|
|
646
646
|
| `openspec list --json` 失败 | openspec 未安装或未初始化 | 运行 `comate init`,或 `openspec init . --tools none` |
|
|
647
647
|
| 子技能不可用 | Superpowers 未安装 | 运行 `comate init` 补装 |
|
|
648
|
-
| bash 版本过低 | bash <
|
|
648
|
+
| bash 版本过低 | bash < 3.2 | Windows:Git Bash/WSL;macOS 自带 Bash 3.2 可直接使用 |
|
|
649
649
|
| .sh 文件包含 CRLF | git 配置问题 | 仓库有 `.gitattributes` 强制 `eol=lf`;如已损坏,使用 `dos2unix` |
|
|
650
650
|
| 脏工作树无法推进 | 未遵循 dirty-worktree 协议 | 按协议归属,暂停并询问用户 |
|
|
651
651
|
|
package/package.json
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@house365/comate",
|
|
3
|
-
"version": "0.9.
|
|
4
|
-
"description": "comate —— SDD (规约驱动开发) 的 AI 开发编排器(Agent Skills)",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"publishConfig": {
|
|
8
|
-
"access": "public",
|
|
9
|
-
"registry": "https://registry.npmjs.org"
|
|
10
|
-
},
|
|
11
|
-
"bin": {
|
|
12
|
-
"comate": "bin/cli.mjs",
|
|
13
|
-
"comate-skills": "bin/cli.mjs"
|
|
14
|
-
},
|
|
15
|
-
"files": [
|
|
16
|
-
"skills",
|
|
17
|
-
"bin",
|
|
18
|
-
"!bin/*.test.mjs",
|
|
19
|
-
"docs",
|
|
20
|
-
"README.md",
|
|
21
|
-
"LICENSE"
|
|
22
|
-
],
|
|
23
|
-
"keywords": [
|
|
24
|
-
"agent-skills",
|
|
25
|
-
"skills",
|
|
26
|
-
"claude-code",
|
|
27
|
-
"openspec",
|
|
28
|
-
"superpowers",
|
|
29
|
-
"spec-driven-development",
|
|
30
|
-
"ai-agents"
|
|
31
|
-
],
|
|
32
|
-
"repository": {
|
|
33
|
-
"type": "git",
|
|
34
|
-
"url": "git+https://github.com/liangjiankk/comate.git"
|
|
35
|
-
},
|
|
36
|
-
"homepage": "https://github.com/liangjiankk/comate#readme",
|
|
37
|
-
"bugs": {
|
|
38
|
-
"url": "https://github.com/liangjiankk/comate/issues"
|
|
39
|
-
},
|
|
40
|
-
"engines": {
|
|
41
|
-
"node": ">=20"
|
|
42
|
-
},
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"ccusage": "^20.0.19",
|
|
45
|
-
"skills": "^1.5.21"
|
|
46
|
-
},
|
|
47
|
-
"scripts": {
|
|
48
|
-
"postinstall": "node bin/postinstall.mjs",
|
|
49
|
-
"test": "node bin/init.test.mjs && node bin/usage.test.mjs && bash skills/comate/tests/run.sh",
|
|
50
|
-
"doctor": "cd skills && bash comate/scripts/comate.sh doctor",
|
|
51
|
-
"prepublish-check": "node scripts/prepublish-check.mjs",
|
|
52
|
-
"pack-check": "node scripts/pack-check.mjs",
|
|
53
|
-
"prepublishOnly": "npm run prepublish-check && npm run pack-check && npm run doctor && npm test"
|
|
54
|
-
}
|
|
55
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@house365/comate",
|
|
3
|
+
"version": "0.9.11",
|
|
4
|
+
"description": "comate —— SDD (规约驱动开发) 的 AI 开发编排器(Agent Skills)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public",
|
|
9
|
+
"registry": "https://registry.npmjs.org"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"comate": "bin/cli.mjs",
|
|
13
|
+
"comate-skills": "bin/cli.mjs"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"skills",
|
|
17
|
+
"bin",
|
|
18
|
+
"!bin/*.test.mjs",
|
|
19
|
+
"docs",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"agent-skills",
|
|
25
|
+
"skills",
|
|
26
|
+
"claude-code",
|
|
27
|
+
"openspec",
|
|
28
|
+
"superpowers",
|
|
29
|
+
"spec-driven-development",
|
|
30
|
+
"ai-agents"
|
|
31
|
+
],
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/liangjiankk/comate.git"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/liangjiankk/comate#readme",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/liangjiankk/comate/issues"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=20"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"ccusage": "^20.0.19",
|
|
45
|
+
"skills": "^1.5.21"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"postinstall": "node bin/postinstall.mjs",
|
|
49
|
+
"test": "node bin/init.test.mjs && node bin/usage.test.mjs && bash skills/comate/tests/run.sh",
|
|
50
|
+
"doctor": "cd skills && bash comate/scripts/comate.sh doctor",
|
|
51
|
+
"prepublish-check": "node scripts/prepublish-check.mjs",
|
|
52
|
+
"pack-check": "node scripts/pack-check.mjs",
|
|
53
|
+
"prepublishOnly": "npm run prepublish-check && npm run pack-check && npm run doctor && npm test"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
| 依赖 | 要求 | 检查 | 缺失处理 |
|
|
12
12
|
|------|------|------|----------|
|
|
13
|
-
| bash | ≥
|
|
13
|
+
| bash | ≥ 3.2 | `bash --version` | Windows 用 Git Bash / WSL;macOS 自带 3.2 可直接使用 |
|
|
14
14
|
| git | 任意近代版本 | `git --version` | 安装 git |
|
|
15
15
|
| sha256 工具 | `sha256sum` 或 `shasum` 至少其一 | `command -v sha256sum shasum` | Linux 装 coreutils;macOS 自带 `shasum`(perl) |
|
|
16
16
|
| LF 换行 | 所有 `.sh` 必须是 LF | `doctor` 会检测 CRLF | 仓库已带 `.gitattributes` 强制 `eol=lf`;若被改坏,用 `dos2unix` 或重新 checkout |
|
|
@@ -80,42 +80,44 @@ FIELD_ORDER=(
|
|
|
80
80
|
# Current schema version written by `state init`.
|
|
81
81
|
readonly COMATE_SCHEMA_VERSION=1
|
|
82
82
|
|
|
83
|
-
# --- Enum
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
)
|
|
83
|
+
# --- Enum tables ---
|
|
84
|
+
# Keep these as case-based lookups instead of associative arrays. macOS ships
|
|
85
|
+
# Bash 3.2, which has normal arrays but not `declare -A`.
|
|
86
|
+
enum_keys() {
|
|
87
|
+
printf '%s\n' \
|
|
88
|
+
schema_version workflow phase build_mode tdd_mode isolation verify_mode \
|
|
89
|
+
verify_result branch_status direct_override archived source_kind
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
enum_values() {
|
|
93
|
+
case "$1" in
|
|
94
|
+
schema_version) printf '%s\n' '1' ;;
|
|
95
|
+
workflow) printf '%s\n' 'full fix' ;;
|
|
96
|
+
phase) printf '%s\n' 'open design build verify archive' ;;
|
|
97
|
+
build_mode) printf '%s\n' 'subagent-driven-development executing-plans direct' ;;
|
|
98
|
+
tdd_mode) printf '%s\n' 'tdd direct' ;;
|
|
99
|
+
isolation) printf '%s\n' 'branch worktree' ;;
|
|
100
|
+
verify_mode) printf '%s\n' 'light full' ;;
|
|
101
|
+
verify_result) printf '%s\n' 'pending pass fail' ;;
|
|
102
|
+
branch_status) printf '%s\n' 'pending handled discarded' ;;
|
|
103
|
+
direct_override) printf '%s\n' 'true false' ;;
|
|
104
|
+
archived) printf '%s\n' 'true false' ;;
|
|
105
|
+
source_kind) printf '%s\n' 'conversation file' ;;
|
|
106
|
+
*) return 1 ;;
|
|
107
|
+
esac
|
|
108
|
+
}
|
|
98
109
|
|
|
99
110
|
# Keys whose values are free-form (paths, dates, commands, hashes, refs).
|
|
100
|
-
# Listed so
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
[base_branch]=1
|
|
111
|
-
[source_manifest]=1
|
|
112
|
-
[build_command]=1
|
|
113
|
-
[verify_command]=1
|
|
114
|
-
[created_at]=1
|
|
115
|
-
[verified_at]=1
|
|
116
|
-
[phase_step]=1
|
|
117
|
-
[phase_step_data]=1
|
|
118
|
-
)
|
|
111
|
+
# Listed as a case lookup so unknown fields can still be rejected on Bash 3.2.
|
|
112
|
+
is_freeform_key() {
|
|
113
|
+
case "$1" in
|
|
114
|
+
design_doc|plan|verification_report|handoff_context|handoff_hash|\
|
|
115
|
+
impl_context|impl_hash|base_ref|base_branch|source_manifest|\
|
|
116
|
+
build_command|verify_command|created_at|verified_at|phase_step|phase_step_data)
|
|
117
|
+
return 0 ;;
|
|
118
|
+
*) return 1 ;;
|
|
119
|
+
esac
|
|
120
|
+
}
|
|
119
121
|
|
|
120
122
|
# --- Path location ---
|
|
121
123
|
|
|
@@ -157,7 +159,8 @@ validate_change_name() {
|
|
|
157
159
|
# Free-form keys and unlisted keys pass through. null/empty always allowed.
|
|
158
160
|
validate_enum() {
|
|
159
161
|
local key="$1" value="$2"
|
|
160
|
-
local legal
|
|
162
|
+
local legal
|
|
163
|
+
legal=$(enum_values "$key" 2>/dev/null) || legal=""
|
|
161
164
|
[ -z "$legal" ] && return 0
|
|
162
165
|
[ -z "$value" ] && return 0
|
|
163
166
|
[ "$value" = "null" ] && return 0
|
|
@@ -216,18 +219,6 @@ yaml_keys() {
|
|
|
216
219
|
grep -E '^[A-Za-z0-9_-]+:' "$file" 2>/dev/null | sed 's/:.*//' || true
|
|
217
220
|
}
|
|
218
221
|
|
|
219
|
-
# _yaml_read_all <file> <assoc-array-name> — load every known field into the
|
|
220
|
-
# named associative array, defaulting missing fields to "null".
|
|
221
|
-
_yaml_read_all() {
|
|
222
|
-
local file="$1" arrname="$2" k
|
|
223
|
-
for k in "${FIELD_ORDER[@]}"; do
|
|
224
|
-
local v
|
|
225
|
-
v=$(yaml_get "$file" "$k")
|
|
226
|
-
[ -z "$v" ] && v="null"
|
|
227
|
-
printf -v "${arrname}[$k]" '%s' "$v"
|
|
228
|
-
done
|
|
229
|
-
}
|
|
230
|
-
|
|
231
222
|
# validate_flat_yaml <file> — enforce that .comate.yaml is the
|
|
232
223
|
# declared subset: top-level `key: value` scalars plus `#` comments only.
|
|
233
224
|
# Reject indented lines, list items, block/flow collections. die(CORRUPT) on violation.
|
|
@@ -278,22 +269,25 @@ yaml_set() {
|
|
|
278
269
|
[ -f "$file" ] || die "$COMATE_CORRUPT" "ERROR: state file not found: $file"
|
|
279
270
|
|
|
280
271
|
# Reject keys we do not know about (prevents silent typo fields).
|
|
281
|
-
if
|
|
272
|
+
if ! enum_values "$key" >/dev/null 2>&1 && ! is_freeform_key "$key" && [ "$key" != "schema_version" ]; then
|
|
282
273
|
die "$COMATE_USAGE" "ERROR: unknown field '$key'"
|
|
283
274
|
fi
|
|
284
275
|
validate_enum "$key" "$value"
|
|
285
276
|
|
|
286
|
-
declare -A _cur
|
|
287
|
-
_yaml_read_all "$file" _cur
|
|
288
|
-
_cur[$key]="$value"
|
|
289
|
-
|
|
290
277
|
local tmp
|
|
291
278
|
tmp=$(mktemp "${TMPDIR:-/tmp}/comate.XXXXXX") || die "$COMATE_ENV" "ERROR: mktemp failed"
|
|
292
279
|
{
|
|
293
280
|
printf '# comate state — flat key:value only, no nesting\n'
|
|
294
281
|
local k
|
|
295
282
|
for k in "${FIELD_ORDER[@]}"; do
|
|
296
|
-
|
|
283
|
+
local current
|
|
284
|
+
if [ "$k" = "$key" ]; then
|
|
285
|
+
current="$value"
|
|
286
|
+
else
|
|
287
|
+
current=$(yaml_get "$file" "$k")
|
|
288
|
+
fi
|
|
289
|
+
[ -z "$current" ] && current="null"
|
|
290
|
+
printf '%s: %s\n' "$k" "$current"
|
|
297
291
|
done
|
|
298
292
|
} > "$tmp"
|
|
299
293
|
|
|
@@ -460,10 +454,13 @@ GATE_FIELD_ORDER=(
|
|
|
460
454
|
|
|
461
455
|
# gate_status is the only enum. `stale` is computed (hash mismatch) but also
|
|
462
456
|
# storable so a detected-stale gate persists across runs.
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
)
|
|
457
|
+
gate_enum_values() {
|
|
458
|
+
case "$1" in
|
|
459
|
+
schema_version) printf '%s\n' '1' ;;
|
|
460
|
+
gate_status) printf '%s\n' 'reviewing blocked passed passed-with-tbd stale' ;;
|
|
461
|
+
*) return 1 ;;
|
|
462
|
+
esac
|
|
463
|
+
}
|
|
467
464
|
|
|
468
465
|
# gate_stem_for <prd> — derive the short stem used to name a PRD's gate.yaml
|
|
469
466
|
# and review report. A conforming PRD file is `PRD-<NNN>-<title>.md`, whose
|
|
@@ -472,12 +469,15 @@ declare -A GATE_ENUMS=(
|
|
|
472
469
|
# names (no PRD-NNN prefix) fall back to the full basename so callers still get
|
|
473
470
|
# a stable, collision-free stem instead of an error.
|
|
474
471
|
gate_stem_for() {
|
|
475
|
-
local base
|
|
472
|
+
local base stem
|
|
476
473
|
base=$(basename "$1")
|
|
477
474
|
base="${base%.markdown}"
|
|
478
475
|
base="${base%.md}"
|
|
479
|
-
|
|
480
|
-
|
|
476
|
+
# Avoid a capture group in `[[ =~ ]]`: the old parser in macOS Bash 3.2
|
|
477
|
+
# treats an unquoted `(` as syntax before the conditional can run.
|
|
478
|
+
stem=$(printf '%s\n' "$base" | sed -n 's/^\([Pp][Rr][Dd]-[0-9][0-9]*\).*/\1/p')
|
|
479
|
+
if [ -n "$stem" ]; then
|
|
480
|
+
echo "$stem"
|
|
481
481
|
else
|
|
482
482
|
echo "$base"
|
|
483
483
|
fi
|
|
@@ -514,27 +514,27 @@ gate_yaml_set() {
|
|
|
514
514
|
done
|
|
515
515
|
[ "$known" -eq 1 ] || die "$COMATE_USAGE" "ERROR: unknown gate field '$key'"
|
|
516
516
|
|
|
517
|
-
local legal
|
|
517
|
+
local legal
|
|
518
|
+
legal=$(gate_enum_values "$key" 2>/dev/null) || legal=""
|
|
518
519
|
if [ -n "$legal" ] && [ -n "$value" ] && [ "$value" != "null" ]; then
|
|
519
520
|
local ok=0 v
|
|
520
521
|
for v in $legal; do [ "$value" = "$v" ] && { ok=1; break; }; done
|
|
521
522
|
[ "$ok" -eq 1 ] || die "$COMATE_USAGE" "ERROR: invalid value '$value' for '$key' (expected: $legal)"
|
|
522
523
|
fi
|
|
523
524
|
|
|
524
|
-
declare -A _g
|
|
525
|
-
for k in "${GATE_FIELD_ORDER[@]}"; do
|
|
526
|
-
local cv; cv=$(yaml_get "$file" "$k")
|
|
527
|
-
[ -z "$cv" ] && cv="null"
|
|
528
|
-
_g[$k]="$cv"
|
|
529
|
-
done
|
|
530
|
-
_g[$key]="$value"
|
|
531
|
-
|
|
532
525
|
local tmp
|
|
533
526
|
tmp=$(mktemp "${TMPDIR:-/tmp}/comate-gate.XXXXXX") || die "$COMATE_ENV" "ERROR: mktemp failed"
|
|
534
527
|
{
|
|
535
528
|
printf '# comate PRD gate — flat key:value only, no nesting\n'
|
|
536
529
|
for k in "${GATE_FIELD_ORDER[@]}"; do
|
|
537
|
-
|
|
530
|
+
local current
|
|
531
|
+
if [ "$k" = "$key" ]; then
|
|
532
|
+
current="$value"
|
|
533
|
+
else
|
|
534
|
+
current=$(yaml_get "$file" "$k")
|
|
535
|
+
fi
|
|
536
|
+
[ -z "$current" ] && current="null"
|
|
537
|
+
printf '%s: %s\n' "$k" "$current"
|
|
538
538
|
done
|
|
539
539
|
} > "$tmp"
|
|
540
540
|
mv "$tmp" "$file" || { rm -f "$tmp"; die "$COMATE_ENV" "ERROR: failed to replace $file"; }
|
|
@@ -31,23 +31,8 @@
|
|
|
31
31
|
|
|
32
32
|
set -euo pipefail
|
|
33
33
|
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
# ordinary array assignment — the subscript is then evaluated as arithmetic and
|
|
37
|
-
# `set -u` aborts with "schema_version: unbound variable". doctor has its own
|
|
38
|
-
# bash>=4 check, but it never got to run: the source above it already died.
|
|
39
|
-
if [ "${BASH_VERSINFO[0]:-0}" -lt 4 ]; then
|
|
40
|
-
cat >&2 <<EOF
|
|
41
|
-
comate needs bash >= 4 (associative arrays), found ${BASH_VERSION:-unknown}.
|
|
42
|
-
|
|
43
|
-
macOS ships bash 3.2 for licensing reasons. Install a current one:
|
|
44
|
-
brew install bash
|
|
45
|
-
|
|
46
|
-
Then re-run through it, e.g.:
|
|
47
|
-
$(command -v brew >/dev/null 2>&1 && echo "\$(brew --prefix)/bin/bash" || echo /opt/homebrew/bin/bash) $0 $*
|
|
48
|
-
EOF
|
|
49
|
-
exit 3
|
|
50
|
-
fi
|
|
34
|
+
# The scripts intentionally stay compatible with macOS's system Bash 3.2.
|
|
35
|
+
# Do not add Bash 4-only features (notably associative arrays) here.
|
|
51
36
|
|
|
52
37
|
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")")" && pwd)"
|
|
53
38
|
# shellcheck source=comate-lib.sh
|
|
@@ -356,11 +341,11 @@ state_validate() {
|
|
|
356
341
|
|
|
357
342
|
# Enum validity (null/empty allowed by validate_enum, but it dies on bad — so check inline).
|
|
358
343
|
local key legal v ok w
|
|
359
|
-
for key in
|
|
344
|
+
for key in $(enum_keys); do
|
|
360
345
|
v=$(yaml_get "$yaml" "$key")
|
|
361
346
|
[ -z "$v" ] && continue
|
|
362
347
|
[ "$v" = "null" ] && continue
|
|
363
|
-
legal
|
|
348
|
+
legal=$(enum_values "$key")
|
|
364
349
|
ok=0
|
|
365
350
|
for w in $legal; do
|
|
366
351
|
if [ "$v" = "$w" ]; then ok=1; break; fi
|
|
@@ -1825,11 +1810,11 @@ doctor() {
|
|
|
1825
1810
|
|
|
1826
1811
|
log_info "comate doctor"
|
|
1827
1812
|
|
|
1828
|
-
#
|
|
1829
|
-
if [ "${BASH_VERSINFO[0]:-0}" -ge
|
|
1830
|
-
_d_pass "bash ${BASH_VERSION} (>=
|
|
1813
|
+
# The state machine is written for Bash 3.2-compatible syntax.
|
|
1814
|
+
if [ "${BASH_VERSINFO[0]:-0}" -ge 3 ]; then
|
|
1815
|
+
_d_pass "bash ${BASH_VERSION} (>= 3)"
|
|
1831
1816
|
else
|
|
1832
|
-
_d_fail "bash ${BASH_VERSION:-unknown} — comate needs bash >=
|
|
1817
|
+
_d_fail "bash ${BASH_VERSION:-unknown} — comate needs bash >= 3"
|
|
1833
1818
|
fi
|
|
1834
1819
|
|
|
1835
1820
|
# git.
|