@flyin-ai/alloy 0.1.1 → 0.2.0-beta.1
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 +28 -90
- package/commands/alloy/apply.md +274 -119
- package/commands/alloy/archive.md +116 -60
- package/commands/alloy/discard.md +57 -15
- package/commands/alloy/finish.md +92 -70
- package/commands/alloy/fix.md +272 -53
- package/commands/alloy/plan.md +125 -63
- package/commands/alloy/references/interaction-style.md +82 -0
- package/commands/alloy/references/main-branch-detection.md +32 -0
- package/commands/alloy/references/phase-routing.md +21 -0
- package/commands/alloy/references/skill-precheck.md +46 -0
- package/commands/alloy/start.md +187 -62
- package/commands/alloy/status.md +1 -1
- package/dist/cli/commands/doctor.d.ts +1 -0
- package/dist/cli/commands/doctor.js +28 -6
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/init.js +40 -37
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/internal/config.d.ts +1 -0
- package/dist/cli/commands/internal/config.js +45 -0
- package/dist/cli/commands/internal/config.js.map +1 -0
- package/dist/cli/commands/internal/guard.js +2 -41
- package/dist/cli/commands/internal/guard.js.map +1 -1
- package/dist/cli/commands/internal/record.js +10 -47
- package/dist/cli/commands/internal/record.js.map +1 -1
- package/dist/cli/commands/internal/skill-usage.d.ts +1 -0
- package/dist/cli/commands/internal/skill-usage.js +78 -0
- package/dist/cli/commands/internal/skill-usage.js.map +1 -0
- package/dist/cli/commands/internal/state.js +105 -6
- package/dist/cli/commands/internal/state.js.map +1 -1
- package/dist/cli/commands/status.d.ts +1 -0
- package/dist/cli/commands/status.js +50 -11
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/update.js +20 -17
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/index.js +73 -31
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/hash.d.ts +3 -0
- package/dist/cli/utils/hash.js +42 -0
- package/dist/cli/utils/hash.js.map +1 -0
- package/dist/cli/utils/state.d.ts +4 -2
- package/dist/cli/utils/state.js +34 -2
- package/dist/cli/utils/state.js.map +1 -1
- package/dist/core/artifacts.d.ts +3 -0
- package/dist/core/artifacts.js +45 -0
- package/dist/core/artifacts.js.map +1 -0
- package/dist/core/detect-installations.d.ts +19 -0
- package/dist/core/detect-installations.js +65 -0
- package/dist/core/detect-installations.js.map +1 -0
- package/dist/core/openspec.d.ts +2 -1
- package/dist/core/openspec.js +30 -12
- package/dist/core/openspec.js.map +1 -1
- package/dist/core/skills.js +16 -0
- package/dist/core/skills.js.map +1 -1
- package/dist/core/superpowers.d.ts +8 -1
- package/dist/core/superpowers.js +41 -20
- package/dist/core/superpowers.js.map +1 -1
- package/dist/core/types.d.ts +20 -0
- package/dist/utils/format.d.ts +31 -0
- package/dist/utils/format.js +101 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/output.d.ts +16 -0
- package/dist/utils/output.js +38 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/prompt.d.ts +0 -1
- package/dist/utils/prompt.js +11 -83
- package/dist/utils/prompt.js.map +1 -1
- package/openspec/schemas/alloy/instructions/retrospective.md +17 -35
- package/openspec/schemas/alloy/templates/design.md +2 -0
- package/openspec/schemas/alloy/templates/draft.md +2 -0
- package/openspec/schemas/alloy/templates/plans.md +2 -0
- package/openspec/schemas/alloy/templates/proposal.md +2 -0
- package/openspec/schemas/alloy/templates/retrospective.md +39 -19
- package/openspec/schemas/alloy/templates/specs.md +2 -0
- package/openspec/schemas/alloy/templates/tasks.md +2 -0
- package/package.json +8 -2
package/commands/alloy/plan.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "Alloy: Plan"
|
|
3
|
-
description: Alloy
|
|
3
|
+
description: Alloy 规划阶段 - draft.md 完成后进入
|
|
4
4
|
category: Workflow
|
|
5
5
|
tags: [alloy, workflow]
|
|
6
6
|
---
|
|
@@ -9,38 +9,52 @@ tags: [alloy, workflow]
|
|
|
9
9
|
|
|
10
10
|
你是 Alloy 的规划阶段编排器。你的职责是按 OpenSpec schema DAG 依赖顺序,制品生成设计文档,每步生成后提供审查窗口。
|
|
11
11
|
|
|
12
|
+
**核心原则:按 schema DAG 依赖顺序逐一产出制品,每步有审查闸门,不跳过上游直接产下游。**
|
|
13
|
+
|
|
14
|
+
**交互风格:** 所有审查窗口和用户选择使用 `AskUserQuestion` 工具(箭头选、Enter 确认),不用纯文本 "(a)(b)"。详见 `commands/alloy/references/interaction-style.md`。
|
|
15
|
+
|
|
12
16
|
**调用外部命令或技能前,先输出标题和状态描述,再执行操作。不要只出标题然后沉默。**
|
|
13
17
|
|
|
18
|
+
**什么算"plan 执行不到位"(反例):**
|
|
19
|
+
- 用户说"一次性生成"就直接出全部制品——跳过了 5 个审查窗口,失去了需求验证的时机
|
|
20
|
+
- 用户说"太慢了"就加速跳过审查——审查时间远小于返工时间
|
|
21
|
+
- 用户说"我看过 draft 了,内容都对"就松懈——draft 审查不能替代 proposal/design/specs/tasks 的逐级审查
|
|
22
|
+
|
|
23
|
+
### Red Flags——STOP,必须分步审查
|
|
24
|
+
|
|
25
|
+
| 借口 | 现实 |
|
|
26
|
+
|------|------|
|
|
27
|
+
| "一次性生成全部制品,提高效率" | plan 不提供一键生成。5 个制品 = 5 个审查窗口。每步审查的价值远大于省下的几秒。跳过审查 = 跳过需求验证。 |
|
|
28
|
+
| "太慢了,直接出全部吧" | 审查时间远小于后期返工时间。一个未审查的 specs 缺陷到 apply 阶段才发现,代价是重做全部代码。 |
|
|
29
|
+
| "我看过 draft 了,后面的不用看了" | draft 是方案设计,proposal 是提案范围,design 是技术方案,specs 是行为契约——四个层面不可互相替代。 |
|
|
30
|
+
| "这个项目很小,不需要那么正式" | 小项目和大项目的闸门完全一样。不存在"规模分级的保护等级"。 |
|
|
31
|
+
|
|
32
|
+
**捕获阶段启动时间**(命令调用后第一时间,前置检查之前,幂等——重入时返回已有值):
|
|
33
|
+
```bash
|
|
34
|
+
PHASE_START=$(alloy _state timestamp ensure openspec/changes/<name> plan)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
14
39
|
## 前置检查
|
|
15
40
|
|
|
16
41
|
1. 确认 change 目录 `openspec/changes/<name>/` 存在且 `.alloy.yaml` phase 为 `started`(由 `/alloy:start` 完成),否则报错
|
|
17
42
|
2. 若 change 目录存在但 `draft.md` 缺失 → 异常状态,提示重新运行 `/alloy:start`
|
|
18
43
|
3. 若指定 `[name]` 参数但 change 不存在 → "未找到 change '<name>',请先运行 `/alloy:start <name>` 创建 change"
|
|
19
|
-
4. **Skill / 命令预检:**
|
|
44
|
+
4. **Skill / 命令预检:** 确认以下依赖可用:
|
|
45
|
+
cmd: opsx/continue
|
|
46
|
+
skill: writing-plans
|
|
20
47
|
|
|
21
|
-
|
|
48
|
+
读取 `commands/alloy/references/skill-precheck.md` 了解检测方法。任一不可用 → 引导 `alloy init` → STOP。
|
|
22
49
|
|
|
23
|
-
|
|
50
|
+
---
|
|
24
51
|
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
COMPLETED_AT=$(date "+%Y-%m-%d %H:%M:%S")
|
|
28
|
-
TIMINGS=$(alloy _state read openspec/changes/<name> phase_timings 2>/dev/null || echo "{}")
|
|
29
|
-
echo "$TIMINGS" | python3 -c "
|
|
30
|
-
import sys,json
|
|
31
|
-
content = sys.stdin.read()
|
|
32
|
-
d = json.loads(content) if content.strip() else {}
|
|
33
|
-
p = d.setdefault('plan',{})
|
|
34
|
-
if 'started_at' not in p:
|
|
35
|
-
p['started_at']='$COMPLETED_AT'
|
|
36
|
-
print(json.dumps(d))
|
|
37
|
-
" | while read -r val; do alloy _state write openspec/changes/<name> phase_timings "$val"; done
|
|
38
|
-
```
|
|
52
|
+
### [Step 1/3] 确认 Change
|
|
39
53
|
|
|
40
54
|
```
|
|
41
55
|
┌──────────────────────────────────────┐
|
|
42
56
|
│ Alloy [2/5] · Phase: Plan │
|
|
43
|
-
│ 启动时间:
|
|
57
|
+
│ 启动时间: $PHASE_START
|
|
44
58
|
└──────────────────────────────────────┘
|
|
45
59
|
|
|
46
60
|
[Step 1/3] 确认 Change
|
|
@@ -51,7 +65,7 @@ print(json.dumps(d))
|
|
|
51
65
|
```bash
|
|
52
66
|
git log -1 --format="%s" -- openspec/changes/<name>/draft.md
|
|
53
67
|
```
|
|
54
|
-
若 commit message 不含 `
|
|
68
|
+
若 commit message 不含 `docs(<name>): draft 已确认`→ ⚠️ draft.md 可能未经过完整 `/alloy:start` 流程(手工创建),提示用户确认是否继续。不阻断——但给用户知情权。
|
|
55
69
|
2. 确认 `.alloy.yaml` phase 为 `started`:
|
|
56
70
|
```bash
|
|
57
71
|
alloy _state check openspec/changes/<name> started
|
|
@@ -60,34 +74,22 @@ print(json.dumps(d))
|
|
|
60
74
|
```bash
|
|
61
75
|
git rev-parse --git-dir
|
|
62
76
|
```
|
|
63
|
-
若失败 → 项目还不是 git
|
|
64
|
-
```
|
|
65
|
-
git init && git add -A && git commit -m "chore: 初始提交"
|
|
66
|
-
```
|
|
77
|
+
若失败 → HARD STOP:"项目还不是 git 仓库。请先运行 `/alloy:start` 完成初始化(包含 git init)。"
|
|
67
78
|
|
|
68
|
-
前置检查通过:draft.md ✓ phase=started ✓ git ✓
|
|
79
|
+
前置检查通过:draft.md ✓ phase=started ✓ git ✓ 技能 ✓
|
|
69
80
|
|
|
70
81
|
**若 phase 不匹配(phase != started):**
|
|
71
82
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
| 当前 phase | 行为 |
|
|
75
|
-
|-----------|------|
|
|
76
|
-
| planned | "plan 已完成,自动进入 /alloy:apply" → 加载 alloy-apply 指令 |
|
|
77
|
-
| applied | "已进入执行阶段,自动进入 /alloy:apply" → 加载 alloy-apply 指令 |
|
|
78
|
-
| archived | "已归档,自动进入 /alloy:finish" → 加载 alloy-finish 指令 |
|
|
79
|
-
| finished | "工作流已完成" → STOP |
|
|
80
|
-
|
|
81
|
-
**实现方式:** 输出对应命令文件的完整指令,将 change name 和当前进度信息作为上下文传入。
|
|
83
|
+
读取 `commands/alloy/references/phase-routing.md` 按路由表自动跳转。实现方式:输出对应命令文件的完整指令,将 change name 和当前进度信息作为上下文传入。
|
|
82
84
|
|
|
83
85
|
**若 change 目录不存在或 draft.md 缺失:**
|
|
84
86
|
→ 引导用户先运行 `/alloy:start <name>` 创建 change。这是唯一保留 HARD STOP 的场景——前序阶段完全没做。
|
|
85
87
|
|
|
86
88
|
---
|
|
87
89
|
|
|
88
|
-
|
|
90
|
+
### [Step 2/3] 制品生成 · /opsx:continue + writing-plans
|
|
89
91
|
|
|
90
|
-
|
|
92
|
+
[Step 2/3] 制品生成
|
|
91
93
|
──────────────────────────────────────
|
|
92
94
|
|
|
93
95
|
**每个制品(proposal / design / specs / tasks)必须通过 `/opsx:continue` 生成。禁止手动编写制品文件。** `/opsx:continue` 自动读取 schema DAG,按 `proposal → design → specs → tasks` 顺序依次产出,每次调用生成一个制品。**tasks 是 `/opsx:continue` 生成的最后一个制品。**plans.md 由 `superpowers:writing-plans` 技能生成(见下文)。
|
|
@@ -143,19 +145,69 @@ done
|
|
|
143
145
|
|
|
144
146
|
制品全部完成(plans.md 存在且 hash 有效)时,phase 推进到 planned,提示下一步。
|
|
145
147
|
|
|
148
|
+
**不要输出全局制品进度**(如"0/8 artifacts 完成")。[N/M] 是阶段内局部编号(M=5),不反映全局进度。全局进度由 `alloy status` 命令管理。
|
|
149
|
+
|
|
146
150
|
### 正常推进:逐个制品的审查流程
|
|
147
151
|
|
|
148
|
-
每个制品生成后,展示内容并进入审查窗口。**仅两个选项——不跳过。**
|
|
152
|
+
每个制品生成后,展示内容并进入审查窗口。**仅两个选项——不跳过。**
|
|
153
|
+
|
|
154
|
+
审查窗口分两步:**先**用 markdown 文本展示制品完整内容(审查窗口块引用),**后**用 `AskUserQuestion` 工具让用户确认(radio,2 个 option)。不要用纯文本 "(a)(b)" 等用户打字。
|
|
155
|
+
|
|
156
|
+
**制品 [1/5] proposal:**
|
|
157
|
+
|
|
158
|
+
> 制品 [1/5] proposal ✓ 完成
|
|
159
|
+
>
|
|
160
|
+
> [展示 proposal.md 完整内容]
|
|
161
|
+
>
|
|
162
|
+
> → 下一个:design(依赖 proposal + draft.md)
|
|
163
|
+
>
|
|
164
|
+
> → (a) 确认,锁定 proposal 并继续 design
|
|
165
|
+
> → (b) 需要调整 — 说明修改点
|
|
166
|
+
|
|
167
|
+
**制品 [2/5] design:**
|
|
168
|
+
|
|
169
|
+
> 制品 [2/5] design ✓ 完成
|
|
170
|
+
>
|
|
171
|
+
> [展示 design.md 完整内容]
|
|
172
|
+
>
|
|
173
|
+
> → 下一个:specs(依赖 proposal)
|
|
174
|
+
>
|
|
175
|
+
> → (a) 确认,锁定 design 并继续 specs
|
|
176
|
+
> → (b) 需要调整 — 说明修改点
|
|
177
|
+
|
|
178
|
+
**制品 [3/5] specs:**
|
|
149
179
|
|
|
150
180
|
> 制品 [3/5] specs ✓ 完成
|
|
151
181
|
>
|
|
152
|
-
> [
|
|
182
|
+
> [展示 specs/ 完整内容]
|
|
153
183
|
>
|
|
154
184
|
> → 下一个:tasks(依赖 specs + design)
|
|
155
185
|
>
|
|
156
186
|
> → (a) 确认,锁定 specs 并继续 tasks
|
|
157
187
|
> → (b) 需要调整 — 说明修改点
|
|
158
188
|
|
|
189
|
+
**制品 [4/5] tasks:**
|
|
190
|
+
|
|
191
|
+
> 制品 [4/5] tasks ✓ 完成
|
|
192
|
+
>
|
|
193
|
+
> [展示 tasks.md 完整内容]
|
|
194
|
+
>
|
|
195
|
+
> → 下一个:plans(依赖 tasks)
|
|
196
|
+
>
|
|
197
|
+
> → (a) 确认,锁定 tasks 并继续生成 plans
|
|
198
|
+
> → (b) 需要调整 — 说明修改点
|
|
199
|
+
|
|
200
|
+
**制品 [5/5] plans:**
|
|
201
|
+
|
|
202
|
+
> 制品 [5/5] plans ✓ 完成
|
|
203
|
+
>
|
|
204
|
+
> [展示 plans.md 完整内容]
|
|
205
|
+
>
|
|
206
|
+
> → plan 阶段完成
|
|
207
|
+
>
|
|
208
|
+
> → (a) 确认,锁定 plans 并完成 plan 阶段
|
|
209
|
+
> → (b) 需要调整 — 说明修改点
|
|
210
|
+
|
|
159
211
|
**审查窗口只展示制品内容,不打印 OpenSpec schema 的 instructions 模板。** instructions 是给 Agent 的内部指引,不是给用户审查的输出。
|
|
160
212
|
|
|
161
213
|
- **选 (a)**:当前制品锁定,进入下一个制品或阶段
|
|
@@ -169,7 +221,7 @@ done
|
|
|
169
221
|
- 需求层面变更(功能增删、行为变更、范围调整)→ 内部标记为需求变更
|
|
170
222
|
|
|
171
223
|
无论 AI 如何判断,始终向用户呈现相同的 (a)/(b) 两个选项。
|
|
172
|
-
用户选 (a) → 执行回溯清理步骤,加载 `superpowers:brainstorming`。
|
|
224
|
+
用户选 (a) → 执行回溯清理步骤,加载 `superpowers:brainstorming`。brainstorming 负责重新讨论需求,用户确认后回到这里继续生成制品(不是 invoke writing-plans——那是独立使用 brainstorming 时的行为)。
|
|
173
225
|
用户选 (b) → 回到当前审查窗口,重新展示 (a) 确认 / (b) 需要调整 选项。
|
|
174
226
|
|
|
175
227
|
**什么算"审查不充分"(反例):**
|
|
@@ -181,6 +233,11 @@ done
|
|
|
181
233
|
|
|
182
234
|
每个制品审批通过(用户选 a)后,立即 hash 锁定并 commit:
|
|
183
235
|
|
|
236
|
+
**hash 锁定前,记录技能使用:**
|
|
237
|
+
```bash
|
|
238
|
+
alloy _skill log openspec/changes/<name> plan opsx:continue
|
|
239
|
+
```
|
|
240
|
+
|
|
184
241
|
```bash
|
|
185
242
|
HASH=$(alloy _record compute openspec/changes/<name> <artifact>)
|
|
186
243
|
APPROVED_AT=$(date "+%Y-%m-%d %H:%M:%S")
|
|
@@ -190,6 +247,20 @@ git add openspec/changes/<name>/
|
|
|
190
247
|
git commit -m "docs(<name>): <artifact> 已确认"
|
|
191
248
|
```
|
|
192
249
|
|
|
250
|
+
**plans 是 plan 阶段最后一个制品。** plans 审批通过后,phase_timings + hash-lock 合并为**一个 commit**——禁止拆成两次提交:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# 写入完成时间 + hash 锁定 + 提交,一气呵成
|
|
254
|
+
COMPLETED_AT=$(date "+%Y-%m-%d %H:%M:%S")
|
|
255
|
+
alloy _state merge openspec/changes/<name> phase_timings "{\"plan\":{\"completed_at\":\"${COMPLETED_AT:-$(date '+%Y-%m-%d %H:%M:%S')}\"}}"
|
|
256
|
+
HASH=$(alloy _record compute openspec/changes/<name> plans)
|
|
257
|
+
APPROVED_AT=$(date "+%Y-%m-%d %H:%M:%S")
|
|
258
|
+
APPROVER=$(alloy _record approver openspec/changes/<name>)
|
|
259
|
+
alloy _record write openspec/changes/<name> plans "$HASH" "$APPROVED_AT" "$APPROVER"
|
|
260
|
+
git add openspec/changes/<name>/
|
|
261
|
+
git commit -m "docs(<name>): plans 已确认"
|
|
262
|
+
```
|
|
263
|
+
|
|
193
264
|
commit message 格式:`docs(<change-name>): <artifact> 已确认`(Conventional Commits `docs` type)。`<artifact>` 为 proposal / design / specs / tasks / plans。
|
|
194
265
|
|
|
195
266
|
**生成下一制品前,校验上游依赖制品的 hash 未被篡改:**
|
|
@@ -215,6 +286,11 @@ tasks 审批通过并 commit 后,**加载 `superpowers:writing-plans` 技能**
|
|
|
215
286
|
>
|
|
216
287
|
> alloy 不在 plan 阶段额外询问策略选择——writing-plans 的决策直接保留在 frontmatter 中,apply 阶段再读取并给用户确认。
|
|
217
288
|
|
|
289
|
+
**技能加载后立即记录:**
|
|
290
|
+
```bash
|
|
291
|
+
alloy _skill log openspec/changes/<name> plan superpowers:writing-plans
|
|
292
|
+
```
|
|
293
|
+
|
|
218
294
|
writing-plans 完成并保存 plans.md(含 strategy frontmatter)后,直接进入 plans 审查窗口。frontmatter 格式:
|
|
219
295
|
|
|
220
296
|
```yaml
|
|
@@ -226,7 +302,7 @@ reason: <writing-plans 执行交接环节的策略分析理由>
|
|
|
226
302
|
...
|
|
227
303
|
```
|
|
228
304
|
|
|
229
|
-
plans.md
|
|
305
|
+
plans.md 生成后展示审查窗口,审批通过后先写入 phase_timings.completed_at,再 hash 锁定并 commit(phase_timings 作为元数据附着在 plans 制品提交上,不单独 commit)。
|
|
230
306
|
|
|
231
307
|
### 回溯修改:修改已确认的上游制品
|
|
232
308
|
|
|
@@ -253,21 +329,23 @@ rm -rf openspec/changes/<name>/specs/
|
|
|
253
329
|
# 2. 清理 records(只保留 draft)
|
|
254
330
|
DRAFT_RECORD=$(alloy _state read openspec/changes/<name> records | python3 -c "
|
|
255
331
|
import sys,json
|
|
256
|
-
|
|
332
|
+
content = sys.stdin.read().strip()
|
|
333
|
+
records = json.loads(content) if content and content != 'null' else []
|
|
257
334
|
draft = [r for r in records if r.get('artifact') == 'draft']
|
|
258
335
|
print(json.dumps(draft))
|
|
259
336
|
")
|
|
260
337
|
alloy _state write openspec/changes/<name> records "$DRAFT_RECORD"
|
|
261
338
|
|
|
262
339
|
# 3. 清理 phase_timings(清除 plan/apply/archive/finish 记录,重置 start.completed_at)
|
|
340
|
+
# ⚠️ 此处是 phase_timings 唯一允许 _state write 的场景:回溯需要删除 key + 覆盖值,merge 语义不支持。
|
|
341
|
+
# 所有其他 phase_timings 更新必须使用 _state merge,禁止 _state write。
|
|
263
342
|
TIMINGS=$(alloy _state read openspec/changes/<name> phase_timings 2>/dev/null || echo "{}")
|
|
264
343
|
echo "$TIMINGS" | python3 -c "
|
|
265
344
|
import sys,json
|
|
266
|
-
|
|
267
|
-
|
|
345
|
+
content = sys.stdin.read().strip()
|
|
346
|
+
d = json.loads(content) if content and content != 'null' else {}
|
|
268
347
|
for k in ['plan','apply','archive','finish']:
|
|
269
348
|
d.pop(k, None)
|
|
270
|
-
# 重置 start 完成时间——start 重新变为进行中
|
|
271
349
|
if 'start' in d:
|
|
272
350
|
d['start']['completed_at'] = None
|
|
273
351
|
print(json.dumps(d))
|
|
@@ -284,30 +362,13 @@ git commit -m "chore(<name>): 回溯——清理 plan 制品,回到 brainstorm
|
|
|
284
362
|
|
|
285
363
|
---
|
|
286
364
|
|
|
287
|
-
|
|
365
|
+
### [Step 3/3] 完成
|
|
288
366
|
|
|
289
367
|
先读取所有 record 的时间戳用于汇总展示:
|
|
290
368
|
```bash
|
|
291
369
|
alloy _state read openspec/changes/<name> records
|
|
292
370
|
```
|
|
293
371
|
|
|
294
|
-
**记录阶段完成时间:**
|
|
295
|
-
```bash
|
|
296
|
-
COMPLETED_AT=$(date "+%Y-%m-%d %H:%M:%S")
|
|
297
|
-
TIMINGS=$(alloy _state read openspec/changes/<name> phase_timings 2>/dev/null || echo "{}")
|
|
298
|
-
echo "$TIMINGS" | python3 -c "
|
|
299
|
-
import sys,json
|
|
300
|
-
content = sys.stdin.read()
|
|
301
|
-
d = json.loads(content) if content.strip() else {}
|
|
302
|
-
p = d.setdefault('plan',{})
|
|
303
|
-
if 'completed_at' not in p:
|
|
304
|
-
p['completed_at']='$COMPLETED_AT'
|
|
305
|
-
print(json.dumps(d))
|
|
306
|
-
" | while read -r val; do alloy _state write openspec/changes/<name> phase_timings "$val"; done
|
|
307
|
-
git add openspec/changes/<name>/
|
|
308
|
-
git commit -m "chore(<name>): 记录 plan 阶段完成时间"
|
|
309
|
-
```
|
|
310
|
-
|
|
311
372
|
```
|
|
312
373
|
┌──────────────────────────────────────┐
|
|
313
374
|
│ Alloy [2/5] · Phase: Plan — DONE │
|
|
@@ -358,3 +419,4 @@ guard 校验 hash 一致性后自动推进 phase。如果 guard 返回非零,
|
|
|
358
419
|
- **制品生成完成后必须通过 alloy _guard 校验** —— 脚本检查 started→planned 转换的合法性及 hash 一致性
|
|
359
420
|
- **plans 完成后不要自动进入 apply** —— 给用户空间审视完整规划
|
|
360
421
|
- **plan 阶段调整统一回 brainstorming** —— 需求/设计层面的任何变更从 draft 根重新审视,不做就地修补。typo/措辞修正除外。apply 阶段的 verify/retrospective 只修代码不改规格
|
|
422
|
+
- **不输出全局制品进度** — [N/M] 是阶段内局部编号(M=5),不要输出"N/8 artifacts 完成"。全局进度由 `alloy status` 管理
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Alloy 交互风格指南
|
|
2
|
+
|
|
3
|
+
Alloy 各阶段需要用户输入时,**优先使用平台原生的交互式选择工具**。纯文本 "(a)(b)(c)" 只是换了格式的开放式提问——用户还是要打字。原生交互组件让用户用箭头选、空格勾、Enter 提交,一次按键完成决策。
|
|
4
|
+
|
|
5
|
+
## 平台工具对照
|
|
6
|
+
|
|
7
|
+
| 平台 | 交互式选择工具 | 能力 |
|
|
8
|
+
|------|-------------|------|
|
|
9
|
+
| **Claude Code** | `AskUserQuestion` | radio(单选)、checkbox(多选)、preview(代码对比) |
|
|
10
|
+
| **Codex** | 无等价工具 | 降级为结构化文本选项 |
|
|
11
|
+
| **Copilot CLI** | 无等价工具 | 降级为结构化文本选项 |
|
|
12
|
+
| **Gemini CLI** | 查平台工具映射 | 查 GEMINI.md 中的工具映射 |
|
|
13
|
+
|
|
14
|
+
**降级策略:** 当平台无原生交互工具时,使用清晰的文本选项格式——但必须结构化(每选项一行,带编号和简短说明),不要让用户猜要输入什么。
|
|
15
|
+
|
|
16
|
+
## 选择类型与工具映射
|
|
17
|
+
|
|
18
|
+
| 场景 | `AskUserQuestion` 配置 | 示例 |
|
|
19
|
+
|------|----------------------|------|
|
|
20
|
+
| **审查确认** (a/b 二元) | `multiSelect: false`,2 个 option | (a) 确认,锁定并继续 / (b) 需要调整 |
|
|
21
|
+
| **多选一** (3-4 选项) | `multiSelect: false`,3-4 个 option | 分支选择、策略选择、技术方案选型 |
|
|
22
|
+
| **范围确认** (独立选项) | `multiSelect: true`,空格勾选 | 功能范围、carry-forward items、边界确认 |
|
|
23
|
+
| **方案对比** (含代码差异) | `multiSelect: false` + `preview` 字段 | 架构方案 A vs B,preview 展示代码结构差异 |
|
|
24
|
+
|
|
25
|
+
## 审查窗口标准模式
|
|
26
|
+
|
|
27
|
+
制品审查是 Alloy 最常见的交互场景。遵循以下模式:
|
|
28
|
+
|
|
29
|
+
1. **先展示内容**——用 markdown 文本展示制品完整内容(审查窗口本身,不能被交互工具替代)
|
|
30
|
+
2. **再让用户确认**:
|
|
31
|
+
|
|
32
|
+
**Claude Code(推荐):**
|
|
33
|
+
```
|
|
34
|
+
AskUserQuestion: {
|
|
35
|
+
questions: [{
|
|
36
|
+
question: "确认并锁定 <制品名>?",
|
|
37
|
+
header: "<制品名>",
|
|
38
|
+
options: [
|
|
39
|
+
{ label: "(a) 确认,锁定并继续", description: "hash 锁定 + commit,进入下一制品" },
|
|
40
|
+
{ label: "(b) 需要调整", description: "说明修改点" }
|
|
41
|
+
],
|
|
42
|
+
multiSelect: false
|
|
43
|
+
}]
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**其他平台(降级):**
|
|
48
|
+
```
|
|
49
|
+
> → (a) 确认,锁定 <制品名> 并继续
|
|
50
|
+
> → (b) 需要调整 — 说明修改点
|
|
51
|
+
> 请输入 a 或 b:
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**硬规则:凡是技能文件中出现 `AskUserQuestion` JSON 块的,同一位置必须附带降级文本格式。** Agent 执行时先检测当前平台是否支持 `AskUserQuestion` 工具——支持则用原生交互组件,不支持则自动降级为文本选项。两个格式给出相同选项、相同数量,确保不同平台上用户看到的选项一致。
|
|
55
|
+
|
|
56
|
+
**反例:** 审查窗口只用文本 "(a) 确认 (b) 调整" 而不给明确的输入提示——用户不知道是要打字、复制粘贴还是直接说"确认"。
|
|
57
|
+
|
|
58
|
+
## 不能使用 AskUserQuestion 的场景
|
|
59
|
+
|
|
60
|
+
以下场景**保持精确文本确认**,因为它们是安全机制而非便利功能:
|
|
61
|
+
|
|
62
|
+
- **破坏性操作确认**(discard、merge 确认):用户必须输入精确字符串 `discard <name>` 或 `merge <branch> into <branch>`
|
|
63
|
+
- **已由外部技能处理的交互**:如 `finishing-a-development-branch` 技能的合并策略选择
|
|
64
|
+
|
|
65
|
+
## 提问密度
|
|
66
|
+
|
|
67
|
+
- `AskUserQuestion` 一次最多 4 个问题,相关问题**合并到一次调用**
|
|
68
|
+
- 不要一个问题调一次——那是文本选项的思维
|
|
69
|
+
- 每次选项的 `description` 写清楚选这个意味着什么,让用户无需额外思考
|
|
70
|
+
|
|
71
|
+
## 各阶段适用场景速查
|
|
72
|
+
|
|
73
|
+
| 阶段 | 主要 AskUserQuestion 场景 |
|
|
74
|
+
|------|--------------------------|
|
|
75
|
+
| start | 探查方向选择(radio)、需求范围确认(checkbox)、draft 审查(radio) |
|
|
76
|
+
| plan | 5 个制品审查窗口(radio)、回溯确认(radio) |
|
|
77
|
+
| apply | 分支异常处理(radio)、执行策略选择(radio)、verify/retrospective 审查(radio) |
|
|
78
|
+
| finish | 主分支确认(radio) |
|
|
79
|
+
| fix | 诊断确认(radio)、主分支确认(radio)、hotfix 合并确认(精确文本) |
|
|
80
|
+
| archive | 无用户交互(全自动) |
|
|
81
|
+
| status | 无用户交互(只读) |
|
|
82
|
+
| discard | **不使用**——保持精确文本确认 |
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 主分支检测
|
|
2
|
+
|
|
3
|
+
start 和 fix 命令共享的主分支自动检测逻辑。
|
|
4
|
+
|
|
5
|
+
## 检测优先级(3 级)
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 1. remote HEAD(标准默认分支)
|
|
9
|
+
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||')
|
|
10
|
+
# 2. 本地 init.defaultBranch 配置
|
|
11
|
+
[ -z "$DEFAULT_BRANCH" ] && DEFAULT_BRANCH=$(git config --get init.defaultBranch 2>/dev/null)
|
|
12
|
+
# 3. 名称匹配(main 或 master)
|
|
13
|
+
[ -z "$DEFAULT_BRANCH" ] && DEFAULT_BRANCH=$(git branch --list 'main' --list 'master' | head -1 | sed 's/[* ]//g')
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 配置优先
|
|
17
|
+
|
|
18
|
+
若 `openspec/config.yaml` 已有 `alloy.main_branch` 记录,直接用记录值,跳过检测和确认。
|
|
19
|
+
|
|
20
|
+
## 确认步骤
|
|
21
|
+
|
|
22
|
+
检测到主分支后,必须让用户确认(Y/n):
|
|
23
|
+
```
|
|
24
|
+
主分支: $DEFAULT_BRANCH。使用此分支作为基础分支?[Y/n]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
选 Y 或直接回车 → 使用检测结果。选 n → 让用户输入自定义名称。
|
|
28
|
+
|
|
29
|
+
确认后写入项目级配置:
|
|
30
|
+
```bash
|
|
31
|
+
alloy _config write <project-root> main_branch <用户确认的主分支名>
|
|
32
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Phase 路由表
|
|
2
|
+
|
|
3
|
+
所有 alloy 阶段命令共享的 phase 自动路由规则。当 `alloy _guard` 检测到 phase 不匹配时,按此表自动跳转到正确阶段。
|
|
4
|
+
|
|
5
|
+
## 路由表
|
|
6
|
+
|
|
7
|
+
| 当前 phase | 行为 |
|
|
8
|
+
|-----------|------|
|
|
9
|
+
| started | 尚未 plan → 加载 alloy-plan 指令 |
|
|
10
|
+
| planned | 尚未 apply → 加载 alloy-apply 指令 |
|
|
11
|
+
| applied | 尚未 archive → 加载 alloy-archive 指令 |
|
|
12
|
+
| archived | 尚未 finish → 加载 alloy-finish 指令 |
|
|
13
|
+
| finished | 工作流已完成 → STOP |
|
|
14
|
+
|
|
15
|
+
## 实现方式
|
|
16
|
+
|
|
17
|
+
输出对应命令文件的完整指令(`commands/alloy/plan.md` / `apply.md` / `archive.md` / `finish.md`),将 change name 和当前进度信息作为上下文传入。Agent 无缝进入对应阶段。
|
|
18
|
+
|
|
19
|
+
## HARD STOP 保留场景
|
|
20
|
+
|
|
21
|
+
change 目录不存在(前序阶段完全没做)→ 引导用户先运行 `/alloy:start`。这是唯一保留 HARD STOP 的场景。
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Skill 预检脚本
|
|
2
|
+
|
|
3
|
+
所有 alloy 阶段命令共享的技能/命令可用性检测脚本。各命令传入自己的技能列表即可。
|
|
4
|
+
|
|
5
|
+
## 使用方式
|
|
6
|
+
|
|
7
|
+
在命令中按以下格式声明所需依赖,然后执行下方预检脚本:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Skill 预检——确认以下可用:
|
|
11
|
+
cmd: opsx/explore opsx/new
|
|
12
|
+
skill: brainstorming
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 预检脚本
|
|
16
|
+
|
|
17
|
+
将上面声明的 cmd 和 skill 列表填入脚本中的对应位置:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
MISSING=0
|
|
21
|
+
|
|
22
|
+
# 检测 command(project → user)
|
|
23
|
+
for cmd in <cmd列表>; do
|
|
24
|
+
if test -f ".claude/commands/$cmd.md"; then echo " ✓ ${cmd//\//:}(项目级 command)"
|
|
25
|
+
elif test -f "$HOME/.claude/commands/$cmd.md"; then echo " ✓ ${cmd//\//:}(用户级 command)"
|
|
26
|
+
else echo " ✗ ${cmd//\//:} — 未找到"; MISSING=$((MISSING+1)); fi
|
|
27
|
+
done
|
|
28
|
+
|
|
29
|
+
# 检测 skill(project skill → user skill → user plugin)
|
|
30
|
+
for skill in <skill列表>; do
|
|
31
|
+
if test -d ".claude/skills/$skill"; then echo " ✓ superpowers:$skill(项目级 skill)"
|
|
32
|
+
elif test -d "$HOME/.claude/skills/$skill"; then echo " ✓ superpowers:$skill(用户级 skill)"
|
|
33
|
+
elif for d in "$HOME/.claude/plugins/cache/superpowers-marketplace/superpowers/"*"/skills/$skill"; do test -d "$d" && break; done 2>/dev/null; then echo " ✓ superpowers:$skill(用户级 plugin)"
|
|
34
|
+
else echo " ✗ superpowers:$skill — 未找到"; MISSING=$((MISSING+1)); fi
|
|
35
|
+
done
|
|
36
|
+
|
|
37
|
+
if [ "$MISSING" -gt 0 ]; then echo ""; echo " 需要先完成环境初始化。请运行: alloy init"; exit 1; fi
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 检测优先级
|
|
41
|
+
|
|
42
|
+
项目级 command → 项目级 skill → 用户级 command → 用户级 skill → 用户级 plugin
|
|
43
|
+
|
|
44
|
+
任一不可用 → 引导 `alloy init` → STOP。
|
|
45
|
+
|
|
46
|
+
**如果某命令只有 command 或只有 skill 依赖,省略对应的 for 循环即可。**
|