@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.
Files changed (76) hide show
  1. package/README.md +28 -90
  2. package/commands/alloy/apply.md +274 -119
  3. package/commands/alloy/archive.md +116 -60
  4. package/commands/alloy/discard.md +57 -15
  5. package/commands/alloy/finish.md +92 -70
  6. package/commands/alloy/fix.md +272 -53
  7. package/commands/alloy/plan.md +125 -63
  8. package/commands/alloy/references/interaction-style.md +82 -0
  9. package/commands/alloy/references/main-branch-detection.md +32 -0
  10. package/commands/alloy/references/phase-routing.md +21 -0
  11. package/commands/alloy/references/skill-precheck.md +46 -0
  12. package/commands/alloy/start.md +187 -62
  13. package/commands/alloy/status.md +1 -1
  14. package/dist/cli/commands/doctor.d.ts +1 -0
  15. package/dist/cli/commands/doctor.js +28 -6
  16. package/dist/cli/commands/doctor.js.map +1 -1
  17. package/dist/cli/commands/init.js +40 -37
  18. package/dist/cli/commands/init.js.map +1 -1
  19. package/dist/cli/commands/internal/config.d.ts +1 -0
  20. package/dist/cli/commands/internal/config.js +45 -0
  21. package/dist/cli/commands/internal/config.js.map +1 -0
  22. package/dist/cli/commands/internal/guard.js +2 -41
  23. package/dist/cli/commands/internal/guard.js.map +1 -1
  24. package/dist/cli/commands/internal/record.js +10 -47
  25. package/dist/cli/commands/internal/record.js.map +1 -1
  26. package/dist/cli/commands/internal/skill-usage.d.ts +1 -0
  27. package/dist/cli/commands/internal/skill-usage.js +78 -0
  28. package/dist/cli/commands/internal/skill-usage.js.map +1 -0
  29. package/dist/cli/commands/internal/state.js +105 -6
  30. package/dist/cli/commands/internal/state.js.map +1 -1
  31. package/dist/cli/commands/status.d.ts +1 -0
  32. package/dist/cli/commands/status.js +50 -11
  33. package/dist/cli/commands/status.js.map +1 -1
  34. package/dist/cli/commands/update.js +20 -17
  35. package/dist/cli/commands/update.js.map +1 -1
  36. package/dist/cli/index.js +73 -31
  37. package/dist/cli/index.js.map +1 -1
  38. package/dist/cli/utils/hash.d.ts +3 -0
  39. package/dist/cli/utils/hash.js +42 -0
  40. package/dist/cli/utils/hash.js.map +1 -0
  41. package/dist/cli/utils/state.d.ts +4 -2
  42. package/dist/cli/utils/state.js +34 -2
  43. package/dist/cli/utils/state.js.map +1 -1
  44. package/dist/core/artifacts.d.ts +3 -0
  45. package/dist/core/artifacts.js +45 -0
  46. package/dist/core/artifacts.js.map +1 -0
  47. package/dist/core/detect-installations.d.ts +19 -0
  48. package/dist/core/detect-installations.js +65 -0
  49. package/dist/core/detect-installations.js.map +1 -0
  50. package/dist/core/openspec.d.ts +2 -1
  51. package/dist/core/openspec.js +30 -12
  52. package/dist/core/openspec.js.map +1 -1
  53. package/dist/core/skills.js +16 -0
  54. package/dist/core/skills.js.map +1 -1
  55. package/dist/core/superpowers.d.ts +8 -1
  56. package/dist/core/superpowers.js +41 -20
  57. package/dist/core/superpowers.js.map +1 -1
  58. package/dist/core/types.d.ts +20 -0
  59. package/dist/utils/format.d.ts +31 -0
  60. package/dist/utils/format.js +101 -0
  61. package/dist/utils/format.js.map +1 -0
  62. package/dist/utils/output.d.ts +16 -0
  63. package/dist/utils/output.js +38 -0
  64. package/dist/utils/output.js.map +1 -0
  65. package/dist/utils/prompt.d.ts +0 -1
  66. package/dist/utils/prompt.js +11 -83
  67. package/dist/utils/prompt.js.map +1 -1
  68. package/openspec/schemas/alloy/instructions/retrospective.md +17 -35
  69. package/openspec/schemas/alloy/templates/design.md +2 -0
  70. package/openspec/schemas/alloy/templates/draft.md +2 -0
  71. package/openspec/schemas/alloy/templates/plans.md +2 -0
  72. package/openspec/schemas/alloy/templates/proposal.md +2 -0
  73. package/openspec/schemas/alloy/templates/retrospective.md +39 -19
  74. package/openspec/schemas/alloy/templates/specs.md +2 -0
  75. package/openspec/schemas/alloy/templates/tasks.md +2 -0
  76. package/package.json +8 -2
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: "Alloy: Start"
3
- description: Alloy 智能入口 - 自动检测状态,接续或新建 change
3
+ description: 新功能构思或接续已有工作时调用
4
4
  category: Workflow
5
5
  tags: [alloy, workflow]
6
6
  ---
@@ -11,7 +11,7 @@ tags: [alloy, workflow]
11
11
 
12
12
  **核心原则:把实际工作委托给专门的技能,不要自己做。Alloy 是编排器,不是执行者。**
13
13
 
14
- > **`<TIMESTAMP>` 的含义:** 每次渲染阶段头部框时,执行 `date "+%Y-%m-%d %H:%M:%S"` 获取本地时间,替换 `<TIMESTAMP>`。不要输出字面字符串 `<TIMESTAMP>`。`<SESSION_START>` 是"全新开始"路径在 header 渲染前捕获的会话启动时间,后续 step 8 写入 phase_timings 时复用该值。`<created_at>` 从 `.alloy.yaml` 的 `created_at` 字段读取。
14
+ > **`<TIMESTAMP>` 的含义:** 每次渲染阶段头部框时,执行 `date "+%Y-%m-%d %H:%M:%S"` 获取本地时间,替换 `<TIMESTAMP>`。不要输出字面字符串 `<TIMESTAMP>`。`<START_TIME>` 是"全新开始"路径中捕获的当前时间——agent 捕获 date 命令的输出后,在 header 渲染和 phase_timings 写入时复用该值。`<created_at>` 从 `.alloy.yaml` 的 `created_at` 字段读取。
15
15
 
16
16
  ---
17
17
 
@@ -27,28 +27,35 @@ tags: [alloy, workflow]
27
27
 
28
28
  ## 全新开始(无活跃 change + 用户提供了 topic)
29
29
 
30
- **记录会话启动时间**(后续写入 phase_timings.start.started_at):
30
+ **捕获阶段启动时间**(命令调用后第一时间输出当前时间,agent 捕获输出值后在 header 和 phase_timings 中复用):
31
31
  ```bash
32
- echo "SESSION_START=$(date "+%Y-%m-%d %H:%M:%S")"
32
+ date "+%Y-%m-%d %H:%M:%S"
33
33
  ```
34
+ > 提示:不要混用 bash 变量——bash 状态在两次工具调用间不持久。直接捕获 date 的输出文本,填入 `<START_TIME>`。
34
35
 
35
36
  ```
36
37
  ┌──────────────────────────────────────┐
37
38
  │ Alloy [1/5] · Phase: Start │
38
- │ 启动时间: 使用上面 SESSION_START 的值
39
+ │ 启动时间: <START_TIME>
39
40
  └──────────────────────────────────────┘
40
41
  ```
41
42
 
42
43
  ### [Step 1/2] 上下文探查
43
44
 
45
+ **Skill 预检:** 确认以下依赖可用:
46
+ cmd: opsx/explore opsx/new
47
+ skill: brainstorming
48
+
49
+ 读取 `commands/alloy/references/skill-precheck.md` 了解检测方法。任一不可用 → 引导 `alloy init` → STOP。
50
+
44
51
  > 正在探查项目上下文和需求空间...
45
52
 
46
53
  **立即执行:** 使用 Skill 工具加载 `opsx:explore` 技能。禁止跳过此步骤。
47
54
 
48
- 如果 `opsx:explore` 不可用(OpenSpec 未安装或命令不存在),引导用户运行 `alloy init` 完成环境初始化。
49
-
50
55
  技能加载后,按其指引自由探索项目上下文和需求空间。
51
56
 
57
+ **交互风格:** 探查结果反馈给用户时,使用 `AskUserQuestion` 工具呈现结构化选项。详见 `commands/alloy/references/interaction-style.md`。箭头上下选择、Enter 确认——不要用纯文本 "(a)(b)(c)" 让用户打字。
58
+
52
59
  **额外上下文——来自历史 retrospective 的教训:** 在探查阶段,扫描 `openspec/changes/archive/` 下最近 3 个已归档 change 的 `retrospective.md`,提取以下信息作为本次 brainstorming 的参考:
53
60
 
54
61
  - **§5 意外发现**:上一次有哪些假设被推翻?这次可能也有类似盲区
@@ -63,8 +70,6 @@ echo "SESSION_START=$(date "+%Y-%m-%d %H:%M:%S")"
63
70
 
64
71
  > 正在启动 brainstorming...
65
72
 
66
- **前置预检:** 确认 `superpowers:brainstorming` 技能可用。若不可用 → 引导用户运行 `alloy init` 重新安装 → STOP。不在 Step 2 才发现缺失。
67
-
68
73
  **立即执行:** 使用 Skill 工具加载 `superpowers:brainstorming` 技能。禁止跳过此步骤。
69
74
 
70
75
  将探查结果作为 ARGUMENTS 传入:
@@ -72,13 +77,26 @@ echo "SESSION_START=$(date "+%Y-%m-%d %H:%M:%S")"
72
77
  探查结果:<Step 1 的关键发现摘要>
73
78
  主题:<topic>
74
79
  项目类型:<新项目/存量项目>
75
- ```
76
80
 
77
- 技能加载后,按其指引进行交互式需求设计。
81
+ **Alloy 流程覆盖:** 本调用在 Alloy start 流程内,brainstorming 完成后产出是 draft.md(openspec/changes/<name>/draft.md),**不是** docs/superpowers/specs/ 文件。请跳过 brainstorming checklist 中的"Write design doc"步骤和"Invoke writing-plans"步骤。用户确认方案后直接输出方案内容即可,由 Alloy start 流程负责生成 draft.md。
82
+
83
+ **交互风格——使用交互式选择组件,不要用纯文本选项:**
84
+
85
+ brainstorming 每个问题都是沟通成本。使用平台的交互式提示工具(Claude Code 中为 `AskUserQuestion`)来降低来回次数。**不要用纯文本 "(a)(b)(c)"——那只是换了格式的开放式提问。**
86
+
87
+ - **单选用 radio:** `multiSelect: false`,箭头上下导航,Enter 确认。技术选型、架构决策用这个。每个选项的 `description` 写推荐理由,帮用户做决定。
88
+ - **多选用 checkbox:** `multiSelect: true`,空格勾选/取消,Enter 提交。功能范围、边界确认用这个。一次确认 3-5 个独立选项,比逐个问 5 个判断题快 5 倍。
89
+ - **代码方案对比用 preview:** 如果不同方案涉及代码结构差异,用选项的 `preview` 字段展示代码片段,用户并排对比后选择。
90
+ - **每次提问不超过 4 个问题**(`AskUserQuestion` 的上限),相关问题合并到一次调用。不要一个问题调一次——那是文本选项的思维。
91
+ - **关键决策单选、范围确认多选:** 架构选择用 radio(互斥),功能范围用 checkbox(独立),不混用。
92
+ - **给出默认推荐:** 推荐的选项在 `description` 中标注理由,让用户可以一键确认而不是逐项评估。
93
+ ```
78
94
 
79
95
  如果 `superpowers:brainstorming` 不可用,引导用户运行 `alloy init` 完成环境初始化。brainstorming 技能内置了审批闸门和 Q&A 深度——普通对话无法复现这些行为。
80
96
 
81
- **brainstorming 完成后,你必须等待用户确认方案,然后生成 `draft.md`:**
97
+ **brainstorming 负责"想清楚要做什么"——通过交互式问答明确问题、方案和关键决策。** 用户确认方案后,这一步的产出是 `draft.md`,不是 superpowers spec 文件。
98
+
99
+ 用户确认方案后,生成 `draft.md`:
82
100
 
83
101
  ```markdown
84
102
  # [功能名称]
@@ -90,33 +108,45 @@ echo "SESSION_START=$(date "+%Y-%m-%d %H:%M:%S")"
90
108
  <!-- 方案概述 -->
91
109
 
92
110
  ## 关键决策
93
- <!-- 关键技术决策及理由 -->
94
- <!-- 将 brainstorming 的详细设计论述写入此章节,不单独产出 superpowers spec 文件 -->
111
+ <!-- brainstorming 中确定的关键技术决策及理由,方案对比、架构考量都写在这里 -->
95
112
 
96
113
  ## 范围与边界
97
114
  <!-- 做什么、明确不做什么 -->
98
115
  ```
99
116
 
100
- **关键:** brainstorming 的所有设计论述(方案对比、技术决策、架构考量)全部写入 draft.md 的"关键决策"章节。不单独在 `docs/superpowers/specs/` 生成文件——draft.md 是 brainstorming 的唯一产出。
101
-
102
117
  **用户明确确认方案之前,不要生成 draft.md。** 如果用户要求调整方案,回到 brainstorming 继续讨论,不要急于产出文件。
103
118
 
104
- **什么算"用户确认了"(反例):**
105
- - 用户说"还行"、"可以"——追问他是否满意关键决策和范围边界
106
- - 用户只确认了部分内容——确保所有关键决策都被明确认可
119
+ **什么算"不够"(反例):**
120
+ - brainstorming 完成后生成了 `docs/superpowers/specs/` 文件——draft.md 是 brainstorming 在 alloy 流程中的唯一产出
121
+ - brainstorming 完成后 invoke writing-plans——那是独立使用 brainstorming 时的行为,在 alloy:start 中下一步是生成 draft.md
122
+ - 用户说"还行"、"可以"就直接生成——追问他是否满意关键决策和范围边界
123
+
124
+ ### Red Flags——STOP,不要跳过闸门
125
+
126
+ 以下任何一个念头出现,都意味着 start 的闸门正在被绕过:
127
+
128
+ | 借口 | 现实 |
129
+ |------|------|
130
+ | "不用建分支了,就在 main 上干吧" | 主分支上直接开发会污染 main 历史。每个 change 必须有独立 feature 分支。拒绝——建分支只需 2 秒。 |
131
+ | "已经在某个分支上了,跳过分支步骤" | 在某个分支上 ≠ 在正确的分支上。仍需验证当前分支 ≠ 主分支,并让用户确认。 |
132
+ | "分支创建是可选步骤" | 分支创建不是可选的——它是步骤 3 的硬性闸门。没有通过 ⑥ 验证,步骤 4-9 全部禁止执行。 |
133
+ | "用户没提分支,继续吧" | 用户没提 ≠ 用户同意跳过。闸门不需要用户主动请求才生效——它默认生效,除非用户明确选择分支。 |
134
+ | "项目简单/一个人开发,不需要分支" | 分支隔离保护的是 discard 安全性,不是团队协作。简单项目一样需要独立分支,否则 discard 会丢失主分支上的无关变更。 |
135
+ | "不用 brainstorming 了,直接写代码" | brainstorming 不是可选项。跳过需求设计 = 规格和代码分叉的起点。必须加载 superpowers:brainstorming。 |
136
+ | "我一个人开发,不用那么正式" | 流程保护的是一致性和可追溯性,不是团队规模。一个人的项目和团队项目的闸门完全一样。 |
137
+ | "我看过了,内容都对"(跳过审查) | 用户"看过了"不等于审查到位。必须按流程确认 change name、主分支、feature 分支。 |
138
+ | "brainstorming 完成了,写 spec 文件吧" | Alloy start 的产出是 draft.md,不是 docs/superpowers/specs/ 文件。brainstorming 完成后直接输出方案,由 Alloy 流程负责生成 draft.md。 |
139
+ | "start 完成了,我帮你直接进 plan" | start 完成后绝不自动进入 plan。即使用户之前说过"赶紧做完",也需要用户在看过 draft.md 后明确运行 /alloy:plan。替用户做阶段转换决定 = 剥夺审查机会。 |
140
+ | "用户没回复,我先继续生成 proposal 吧" | 用户沉默 ≠ 授权继续。在审查窗口等待用户明确选 (a) 或 (b)。擅自继续 = 生成的制品未经审查,后期返工代价远大于等待时间。 |
141
+ | "draft.md 在 brainstorming 时已经讨论过了,直接 commit 吧" | brainstorming 讨论的是方案概念,draft.md 是最终文本。两者不等价——文本可能有措辞偏差、遗漏细节。必须展示 draft.md 完整内容,等用户确认后才能 commit。 |
107
142
 
108
143
  ---
109
144
 
110
145
  用户确认方案后,执行以下步骤:
111
146
 
112
147
  1. **建议 change name**——根据确认的方案建议 kebab-case 名称,用户确认
113
- 2. **调用 `/opsx:new <name>`** 创建 change 目录
114
- 3. **按模板生成 `draft.md`** 到 `openspec/changes/<name>/draft.md`(直接在 change 目录下生成,无需移动)
115
- 4. **写入 state**——使用 `_state init` 一步创建完整初始状态(包含 `records: []`、正确类型),避免逐字段写入遗漏 records 数组:
116
- ```bash
117
- alloy _state init openspec/changes/<name>
118
- ```
119
- 5. **确保 git 仓库就绪:**
148
+
149
+ 2. **确保 git 仓库就绪:**
120
150
 
121
151
  ```bash
122
152
  if ! git rev-parse --git-dir 2>/dev/null; then
@@ -130,52 +160,127 @@ echo "SESSION_START=$(date "+%Y-%m-%d %H:%M:%S")"
130
160
 
131
161
  已有项目则跳过(git repo 已存在,HEAD 已有锚点)。
132
162
 
133
- 6. **分支选择**——检测 git 状态,确认工作分支:
163
+ 3. **分支选择**——在创建 change 目录之前完成分支切换,确保所有制品落在 feature 分支上:
134
164
 
135
- 先获取当前分支:
165
+ **① 自动识别主分支:** 读取 `commands/alloy/references/main-branch-detection.md`,按 3 级优先级检测主分支。
166
+
167
+ 若 `openspec/config.yaml` 已有 `alloy.main_branch` 记录,直接用记录值,跳过检测和确认。
168
+
169
+ **② 确认主分支:** 检测到后让用户确认(Y/n)。确认后写入项目级配置:
170
+ ```bash
171
+ alloy _config write . main_branch <用户确认的主分支名>
172
+ ```主分支是项目级概念,所有 change 共享,不写入 per-change 的 .alloy.yaml。
173
+
174
+ **③ 检测当前分支:**
136
175
  ```bash
137
176
  CURRENT_BRANCH=$(git branch --show-current)
138
- echo "当前分支:$CURRENT_BRANCH"
177
+ CHANGENAME="<name>"
139
178
  ```
140
179
 
141
- 展示选项,让用户选择:
180
+ **④ 按当前分支位置决策:**
181
+
182
+ - **在主分支上** → HARD STOP:"当前在主分支 `<main_branch>`,不允许在主分支开发。commit 会污染主分支历史。" → 只展示"新建分支"选项
183
+ - **在 feature 分支上且名称包含 change 名**(如 `feature/<name>` 或 `fix/<name>`)→ 提示"当前已在 `<$CURRENT_BRANCH>`,直接在该分支上继续工作?[Y/n]"
184
+ - 选 Y → 使用当前分支,继续步骤 4
185
+ - 选 n → 展示选项(见⑤)
186
+ - **在非主分支的已有分支上** → 展示选项(见⑤)
142
187
 
188
+ **⑤ 展示选项:**
189
+
190
+ 本地非主分支(排除刚确认的主分支)存在时:
143
191
  > 选择工作分支
144
192
  > ──────────────────────────────────────
145
193
  >
146
- > 当前在 `<$CURRENT_BRANCH>` 分支
194
+ > 当前在 `<$CURRENT_BRANCH>`,主分支:`<main_branch>`
147
195
  >
148
- > 1. 在当前分支继续 —— 直接在此分支开发
149
- > 2. 切换到已有分支 —— 选择一个已有分支
150
- > 3. 新建分支 —— 创建新分支并切换
196
+ > 1. 切换到已有分支 —— 选择非主分支的已有分支
197
+ > 2. 新建分支 —— 创建新 feature 分支并切换
198
+
199
+ 无可用本地非主分支时 → 直接进入新建分支流程(跳过选项 1)。
200
+
201
+ 每个 change 必须有独立的 feature 分支,确保 discard 时可安全清理。
202
+
203
+ - **选 1:** 列出本地非主分支(`git branch` 排除主分支),用户选择后执行 `git checkout <branch>`
204
+ - **选 2:** 新建分支命名:
205
+ - 默认建议:`feature/<change-name>`
206
+ - 用户可输入自定义名称
207
+ - 校验:不允许与主分支同名
208
+ - `git checkout -b <branch-name>`
209
+
210
+ **⑥ 分支验证——HARD STOP:** 分支创建/切换后,必须验证才能继续。这是防止在主分支上开发的关键闸门——没有这个检查,步骤 3 的所有逻辑都是空谈。
211
+
212
+ ```bash
213
+ CURRENT=$(git branch --show-current)
214
+ echo "当前分支: $CURRENT | 主分支: $MAIN_BRANCH"
215
+ ```
216
+
217
+ - `$CURRENT` = `$MAIN_BRANCH` → **HARD STOP**——"仍在主分支上,不允许继续。"返回⑤重新选择分支
218
+ - `$CURRENT` ≠ `$MAIN_BRANCH` → 验证通过,展示分支状态供用户确认
219
+
220
+ > 分支状态
221
+ > ──────────────────────────────────────
222
+ > 当前分支: `<$CURRENT>`(主分支: `<$MAIN_BRANCH>`)
151
223
  >
152
- > → 建议新建 `<change-name>` 分支,保持 main 干净
224
+ > → 确认,继续创建 change 目录
225
+ > → 需要换分支 — 返回分支选择
226
+
227
+ 用户确认后才能继续步骤 4。**未通过验证或用户未确认时,禁止执行步骤 4-9。**
153
228
 
154
- - **选 1:** 不操作,直接继续
155
- - **选 2:** 展示已有分支列表(`git branch -a`),用户选择后执行 `git checkout <branch>`
156
- - 如果该 change 后续使用 worktree,此分支名仅作参考记录
157
- - **选 3:** 询问用户输入新分支名,执行 `git checkout -b <new-branch>`
158
- - Agent 可建议分支名(如 `<change-name>`),由用户确认
229
+ **什么算"跳过闸门"(反例):**
230
+ - 分支选择后直接进入步骤 4,不验证当前分支——验证只需 1 git 命令
231
+ - 已在 feature 分支上就跳过整个步骤 3——仍需确认当前分支名并记录
232
+ - 用户没回复就继续——分支状态必须用户确认
159
233
 
160
- 分支选择完成后,记录到状态:
234
+ 4. **调用 `/opsx:new <name>`** 创建 change 目录
235
+
236
+ **前置条件:步骤 3 ⑥ 的分支验证已通过且用户已确认。** 如果当前仍在主分支上,STOP——回到步骤 3 选择分支。
237
+
238
+ 5. **批量记录技能使用——** change 目录已创建,将在 Step 1/2 中使用的技能一次性写入 `.alloy.yaml`:
161
239
  ```bash
162
- alloy _state write openspec/changes/<name> worktree null
240
+ alloy _skill log openspec/changes/<name> start opsx:explore && \
241
+ alloy _skill log openspec/changes/<name> start superpowers:brainstorming && \
242
+ alloy _skill log openspec/changes/<name> start opsx:new
163
243
  ```
164
244
 
165
- > ⚠️ apply 阶段仍会通过 `using-git-worktrees` 再次确认隔离方式。此处的分支选择是给后续阶段一个推荐的开发分支。
245
+ 6. **写入 state**——使用 `_state init` 一步创建完整初始状态(包含 `records: []`、正确类型),避免逐字段写入遗漏 records 数组:
246
+ ```bash
247
+ alloy _state init openspec/changes/<name>
248
+ ```
166
249
 
167
- 7. **提交:**
250
+ **记录阶段启动时间:**
251
+ ```bash
252
+ alloy _state merge openspec/changes/<name> phase_timings "{\"start\":{\"started_at\":\"$(date '+%Y-%m-%d %H:%M:%S')\"}}"
253
+ ```
168
254
 
169
- **draft hash + commit:**
255
+ 7. **记录分支信息**——将 feature_branch 和 worktree null 写入 state:
170
256
  ```bash
171
- DRAFT_HASH=$(alloy _record compute openspec/changes/<name> draft)
172
- APPROVED_AT=$(date "+%Y-%m-%d %H:%M:%S")
173
- APPROVER=$(git config user.name)
174
- alloy _record write openspec/changes/<name> draft "$DRAFT_HASH" "$APPROVED_AT" "$APPROVER"
175
- git add openspec/changes/<name>/
176
- git commit -m "feat(<name>): draft 已确认"
257
+ alloy _state write openspec/changes/<name> feature_branch <branch-name>
258
+ alloy _state write openspec/changes/<name> worktree null
177
259
  ```
178
260
 
261
+ 8. **按模板生成 `draft.md`** 到 `openspec/changes/<name>/draft.md`(直接在 change 目录下生成,无需移动)
262
+
263
+ **draft.md 审查窗口——这是 start 阶段唯一的制品审查闸门。用户明确确认后才能 commit。**
264
+
265
+ > 制品 draft ✓ 完成
266
+ >
267
+ > [展示 draft.md 完整内容]
268
+ >
269
+ > → (a) 确认,锁定 draft 并完成 start 阶段
270
+ > → (b) 需要调整 — 回到 brainstorming 重新讨论
271
+
272
+ - **选 (a)**:继续步骤 9,hash 锁定 + commit
273
+ - **选 (b)**:不生成文件、不 commit。回到 Step 2/2 的 brainstorming,基于用户反馈重新讨论方案。brainstorming 完成后重新生成 draft.md,再次进入此审查窗口
274
+
275
+ **什么算"审查不充分"(反例):**
276
+ - 只问"看起来可以吗?"不展示 draft.md 实际内容
277
+ - 用户说"还行"、"可以"就跳过——必须明确选 (a) 或 (b)
278
+ - 把 brainstorming 阶段的方案确认等同于 draft.md 审查——brainstorming 确认的是概念,draft.md 审查的是最终文本
279
+
280
+ > 前面步骤写入的 `.alloy.yaml` 变更(init、started_at、feature_branch、worktree)不单独提交——它们在 draft commit 中一并提交。`git add openspec/changes/<name>/` 会覆盖目录内的所有变更。
281
+
282
+ 9. **提交(start 阶段唯一 commit)——仅在用户选 (a) 后执行:**
283
+
179
284
  **alloy init 基础设施提交:**
180
285
  ```bash
181
286
  git add .claude/ .gitignore openspec/config.yaml openspec/schemas/ 2>/dev/null
@@ -184,14 +289,16 @@ echo "SESSION_START=$(date "+%Y-%m-%d %H:%M:%S")"
184
289
  ```
185
290
  已提交过则自动跳过。`.superpowers/` 已在 `.gitignore` 中忽略,不入仓库。
186
291
 
187
- 8. **记录阶段时间:**
292
+ **记录阶段时间 + draft hash-lock + commit:**
188
293
  ```bash
189
294
  COMPLETED_AT=$(date "+%Y-%m-%d %H:%M:%S")
190
- # STARTED_AT 使用步骤开始时捕获的 SESSION_START
191
- STARTED_AT="<SESSION_START>"
192
- alloy _state write openspec/changes/<name> phase_timings "{\"start\":{\"started_at\":\"$STARTED_AT\",\"completed_at\":\"$COMPLETED_AT\"}}"
295
+ alloy _state merge openspec/changes/<name> phase_timings "{\"start\":{\"completed_at\":\"${COMPLETED_AT:-$(date '+%Y-%m-%d %H:%M:%S')}\"}}"
296
+ DRAFT_HASH=$(alloy _record compute openspec/changes/<name> draft)
297
+ APPROVED_AT=$(date "+%Y-%m-%d %H:%M:%S")
298
+ APPROVER=$(git config user.name)
299
+ alloy _record write openspec/changes/<name> draft "$DRAFT_HASH" "$APPROVED_AT" "$APPROVER"
193
300
  git add openspec/changes/<name>/
194
- git commit -m "chore(<name>): 记录 start 阶段完成时间"
301
+ git commit -m "docs(<name>): draft 已确认"
195
302
  ```
196
303
 
197
304
  ---
@@ -203,7 +310,7 @@ echo "SESSION_START=$(date "+%Y-%m-%d %H:%M:%S")"
203
310
  │ Alloy [1/5] · Phase: Start — DONE │
204
311
  │ 启动时间: 从 phase_timings.start.started_at 读取 │
205
312
  │ 完成时间: 从 phase_timings.start.completed_at 读取 │
206
- │ 耗时: XmXs │
313
+ │ 耗时: (phase_timings.start.completed_at - started_at 计算)
207
314
  └──────────────────────────────────────┘
208
315
 
209
316
  → Change: <name>
@@ -219,7 +326,16 @@ echo "SESSION_START=$(date "+%Y-%m-%d %H:%M:%S")"
219
326
  ```
220
327
 
221
328
  - draft.md 已在 change 目录,项目根目录不再有 draft.md
222
- - 完成后不要自动进入 plan
329
+
330
+ **HARD STOP —— start 阶段到此结束。以下行为绝对禁止:**
331
+
332
+ - 不要自动运行 `/alloy:plan` 或加载 `alloy-plan` 技能
333
+ - 不要生成 proposal.md、design.md、specs/、tasks.md、plans.md 或任何 plan 阶段制品
334
+ - 不要调用 `opsx:continue` 或 `superpowers:writing-plans`
335
+ - 不要因为"用户没回复"而继续——沉默 ≠ 授权
336
+ - 不要因为"这样更高效"而替用户做决定——用户必须自己发起下一阶段
337
+
338
+ **你的唯一操作:展示上述完成信息,等待用户输入下一个命令。**
223
339
 
224
340
  ---
225
341
 
@@ -280,16 +396,25 @@ echo "SESSION_START=$(date "+%Y-%m-%d %H:%M:%S")"
280
396
 
281
397
  展示检测结果后,根据 phase 和制品状态决定路由:
282
398
 
283
- | phase | 制品状态 | 自动加载命令 |
284
- |-------|---------|-------------|
399
+ | phase | 制品状态 | 路由 |
400
+ |-------|---------|------|
285
401
  | started | proposal.md 存在 | alloy-plan(正常接续——plan 制品已有,继续生成) |
286
- | started | proposal.md 不存在 | 重新进入 brainstorming(回溯后——以现有 draft.md 为基础重新讨论需求) |
402
+ | started | proposal.md 不存在 + draft.md 存在且 hash 有效 | **提示用户选择:**(a) 进入 plan 阶段 (b) 回到 brainstorming 修改需求。draft 已确认,默认预期是用户想进 plan——不要默认假设用户想重来。 |
403
+ | started | proposal.md 不存在 + draft.md 不存在或 hash 不匹配 | 重新进入 brainstorming(draft 缺失或已被篡改,需重新讨论需求) |
287
404
  | planned | — | alloy-apply |
288
405
  | applied | — | alloy-archive |
289
406
  | archived | — | alloy-finish |
290
407
  | finished | — | 工作流已完成——如需继续修改,使用自然对话提交新变更 |
291
408
 
292
- **实现方式:** 根据 phase 值,输出对应命令文件的完整指令(`commands/alloy/plan.md` / `apply.md` / `archive.md` / `finish.md`),将 change name 和检测到的进度信息作为上下文传入。Agent 无缝进入对应阶段。
409
+ **实现方式:**
410
+
411
+ - **需自动加载命令时**(proposal.md 存在 → plan、planned → apply 等):输出对应命令文件的完整指令(`commands/alloy/plan.md` / `apply.md` / `archive.md` / `finish.md`),将 change name 和检测到的进度信息作为上下文传入。Agent 无缝进入对应阶段。
412
+ - **需用户选择时**(draft 已确认、proposal 不存在):先校验 draft hash:
413
+ ```bash
414
+ alloy _record check openspec/changes/<name> draft
415
+ ```
416
+ hash 有效 → 展示选择:"draft 已确认。 (a) 进入 plan 阶段 (b) 回到 brainstorming 修改需求"。等用户选择后执行对应路由。
417
+ hash 不匹配 → 走"draft 不存在或 hash 不匹配"路径。
293
418
 
294
419
  一致性检查(双向):
295
420
  - worktree 字段有值但磁盘路径不存在 → ⚠️ "worktree 残留:.alloy.yaml 声称有 worktree 但磁盘不存在"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: "Alloy: Status"
3
- description: 查看 Alloy change 的当前阶段、制品状态和下一步操作
3
+ description: 需要查看 change 进度或状态时调用
4
4
  category: Workflow
5
5
  tags: [alloy, workflow]
6
6
  ---
@@ -5,3 +5,4 @@ export interface DoctorResult {
5
5
  }
6
6
  export declare function doctorCommand(projectPath: string): Promise<DoctorResult>;
7
7
  export declare function formatDoctorResult(result: DoctorResult, useJson: boolean): string;
8
+ export declare function printDoctorResult(result: DoctorResult): void;
@@ -3,6 +3,8 @@ import { fileURLToPath } from "node:url";
3
3
  import { existsSync } from "node:fs";
4
4
  import { runHealthCheck } from "../../core/health.js";
5
5
  import { findActiveChanges } from "../utils/state.js";
6
+ import { color } from "../../utils/format.js";
7
+ import { section, check, warn } from "../../utils/output.js";
6
8
  function detectScope(projectPath) {
7
9
  const home = process.env.HOME || process.env.USERPROFILE || "~";
8
10
  const probe = (dir) => existsSync(join(dir, ".claude", "commands", "alloy"));
@@ -74,20 +76,40 @@ export function formatDoctorResult(result, useJson) {
74
76
  return JSON.stringify(result, null, 2);
75
77
  }
76
78
  const lines = [];
77
- lines.push("健康检查:");
79
+ lines.push(color.bold("健康检查:"));
78
80
  for (const r of result.healthResults) {
79
- const mark = r.status === "pass" ? "✓" : r.status === "warn" ? "⚠️" : "✗";
80
- lines.push(` ${mark} ${r.name}: ${r.current}(要求 ${r.required})`);
81
+ const mark = r.status === "pass"
82
+ ? color.green("✓")
83
+ : r.status === "warn"
84
+ ? color.yellow("⚠️")
85
+ : color.red("✗");
86
+ lines.push(` ${mark} ${r.name}: ${color.cyan(r.current)}(要求 ${color.dim(r.required)})`);
81
87
  }
82
88
  if (result.consistencyWarnings.length > 0) {
83
- lines.push("\n文件一致性:");
89
+ lines.push("\n" + color.bold("文件一致性:"));
84
90
  for (const w of result.consistencyWarnings) {
85
- lines.push(` ⚠️ ${w}`);
91
+ lines.push(` ${color.yellow("⚠️")} ${w}`);
86
92
  }
87
93
  }
88
94
  else {
89
- lines.push("\n文件一致性:✓ 无问题");
95
+ lines.push("\n" + color.bold("文件一致性:") + color.green(" ✓ 无问题"));
90
96
  }
91
97
  return lines.join("\n");
92
98
  }
99
+ export function printDoctorResult(result) {
100
+ section("健康检查");
101
+ for (const r of result.healthResults) {
102
+ check(r.name, `${r.current}(要求 ${r.required})`, r.status);
103
+ }
104
+ if (result.consistencyWarnings.length > 0) {
105
+ section("文件一致性");
106
+ for (const w of result.consistencyWarnings) {
107
+ warn(w);
108
+ }
109
+ }
110
+ else {
111
+ section("文件一致性");
112
+ check("一致性", "无问题", "pass");
113
+ }
114
+ }
93
115
  //# sourceMappingURL=doctor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"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"}