@gordon.gan/specflow 1.0.0 → 1.0.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 (66) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +54 -32
  3. package/dist/cli/commands/change-phase.d.ts +1 -1
  4. package/dist/cli/commands/change-phase.js +1 -1
  5. package/dist/cli/commands/change-status.js +15 -6
  6. package/dist/core/archive.js +2 -2
  7. package/dist/core/artifact-graph/explore-status.d.ts +5 -0
  8. package/dist/core/artifact-graph/explore-status.js +21 -0
  9. package/dist/core/artifact-graph/index.d.ts +3 -1
  10. package/dist/core/artifact-graph/index.js +2 -1
  11. package/dist/core/artifact-graph/outputs.d.ts +3 -3
  12. package/dist/core/artifact-graph/outputs.js +24 -6
  13. package/dist/core/artifact-graph/state.d.ts +4 -0
  14. package/dist/core/artifact-graph/state.js +22 -0
  15. package/dist/core/templates/index.js +1 -0
  16. package/dist/core/templates/types.d.ts +1 -1
  17. package/dist/integrations/codex/adapter.js +1 -1
  18. package/dist/integrations/cursor/adapter.js +1 -1
  19. package/dist/integrations/shared/capability-evidence.js +12 -6
  20. package/dist/integrations/shared/command-catalog.js +4 -3
  21. package/dist/integrations/shared/parity-manifest.js +12 -6
  22. package/dist/utils/change-metadata.d.ts +1 -1
  23. package/dist/utils/change-metadata.js +1 -1
  24. package/package.json +5 -5
  25. package/prompts/{build → apply}/ecc-java-reviewer.md +1 -1
  26. package/prompts/{build → apply}/language-router.md +6 -6
  27. package/prompts/{build → apply}/phase-a-plan.md +14 -14
  28. package/prompts/{build → apply}/phase-b-execute.md +17 -17
  29. package/prompts/{build → apply}/phase-b-review.md +2 -2
  30. package/prompts/{build → apply}/phase-b-worktree.md +3 -3
  31. package/prompts/{build → apply}/plan-document-reviewer.md +6 -6
  32. package/prompts/{build → apply}/tdd.md +3 -3
  33. package/prompts/{done → archive}/branch-finish.md +4 -4
  34. package/prompts/explore/explore-session.md +152 -0
  35. package/prompts/fix/debug.md +2 -2
  36. package/prompts/{plan → propose}/design-draft.md +1 -1
  37. package/prompts/{plan → propose}/proposal.md +30 -0
  38. package/prompts/{plan → propose}/specs.md +1 -1
  39. package/prompts/{plan → propose}/tasks-draft.md +9 -9
  40. package/prompts/reference/specflow/example-design.md +19 -19
  41. package/prompts/reference/superpowers/anthropic-best-practices.md +6 -6
  42. package/prompts/reference/superpowers/codex-tools.md +1 -1
  43. package/prompts/refine/brainstorm.md +15 -15
  44. package/prompts/refine/design-output.md +1 -1
  45. package/prompts/refine/spec-document-reviewer.md +1 -1
  46. package/prompts/review/code-review.md +4 -4
  47. package/prompts/shared/code-reviewer-prompt.md +1 -1
  48. package/prompts/shared/executing-plans.md +12 -12
  49. package/prompts/shared/implementer-prompt.md +3 -3
  50. package/prompts/test/verification.md +1 -1
  51. package/prompts/verify/verification.md +3 -3
  52. package/schemas/specflow/schema.yaml +8 -1
  53. package/skills/{specflow-build → specflow-apply}/SKILL.md +12 -12
  54. package/skills/{specflow-done → specflow-archive}/SKILL.md +5 -5
  55. package/skills/specflow-explore/SKILL.md +102 -0
  56. package/skills/specflow-fix/SKILL.md +2 -2
  57. package/skills/{specflow-plan → specflow-propose}/SKILL.md +38 -11
  58. package/skills/specflow-refine/SKILL.md +4 -4
  59. package/skills/specflow-scan/SKILL.md +2 -2
  60. package/skills/specflow-verify/SKILL.md +1 -1
  61. package/templates/explore.md +89 -0
  62. /package/prompts/{build → apply}/ecc-go-reviewer.md +0 -0
  63. /package/prompts/{build → apply}/ecc-kotlin-reviewer.md +0 -0
  64. /package/prompts/{build → apply}/ecc-python-reviewer.md +0 -0
  65. /package/prompts/{build → apply}/ecc-rust-reviewer.md +0 -0
  66. /package/prompts/{build → apply}/ecc-typescript-reviewer.md +0 -0
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 bstzyf
3
+ Copyright (c) 2026 Gordon-Gan-Jiang
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # SpecFlow
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/specflow.svg)](https://www.npmjs.com/package/specflow)
4
- [![npm downloads](https://img.shields.io/npm/dm/specflow.svg)](https://www.npmjs.com/package/specflow)
3
+ [![npm version](https://img.shields.io/npm/v/@gordon.gan/specflow.svg)](https://www.npmjs.com/package/@gordon.gan/specflow)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@gordon.gan/specflow.svg)](https://www.npmjs.com/package/@gordon.gan/specflow)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
6
6
 
7
7
  **一句话:plan 想清楚,refine 打磨透,build 做到位,done 归档好。**
@@ -30,10 +30,10 @@ SpecFlow 把两个开源框架合并成一个工具:
30
30
 
31
31
  ## 安装
32
32
 
33
- ### 方式 1:从 npm 全局安装(推荐,发布后可用)
33
+ ### 方式 1:从 npm 全局安装(推荐)
34
34
 
35
35
  ```bash
36
- npm install -g specflow
36
+ npm install -g @gordon.gan/specflow
37
37
  ```
38
38
 
39
39
  ### 方式 2:从 GitHub 直接安装(发布前/最新代码)
@@ -59,25 +59,25 @@ npm link
59
59
  在有源码的机器上打包:
60
60
  ```bash
61
61
  npm run build
62
- npm pack # 生成 specflow-0.2.2.tgz
62
+ npm pack # 生成 gordon.gan-specflow-1.0.1.tgz
63
63
  ```
64
64
  在目标机器上安装:
65
65
  ```bash
66
- npm install -g ./specflow-0.2.2.tgz
66
+ npm install -g ./gordon.gan-specflow-1.0.1.tgz
67
67
  ```
68
68
 
69
69
  ### 验证安装
70
70
 
71
71
  ```bash
72
- specflow --version # 应输出 0.2.2
72
+ specflow --version # 应输出 1.0.1
73
73
  specflow --help
74
74
  ```
75
75
 
76
76
  ### 卸载
77
77
 
78
78
  ```bash
79
- npm uninstall -g specflow
80
- # 如果用的是 npm link
79
+ npm uninstall -g @gordon.gan/specflow
80
+ # 如果用的是 npm link(本地源码安装):
81
81
  npm unlink -g specflow
82
82
  ```
83
83
 
@@ -125,9 +125,11 @@ specflow init
125
125
  **每阶段深度思考 → 多轮迭代精化**:每个阶段一次性产出实质内容(非占位骨架),再通过内部多轮迭代把 artifact 精化到稳态,最后进入执行。
126
126
 
127
127
  ```
128
- /specflow:scan [规划中 · v0.3] 扫描已有代码生成 specs 基线;当前请直接用 /specflow:plan 描述已有行为
128
+ /specflow:scan [规划中 · v0.3] 扫描已有代码生成 specs 基线;当前请直接用 /specflow:propose 描述已有行为
129
129
 
130
- /specflow:plan 一次产出 4 artifact:proposal + delta specs + design + tasks(first-iteration 深度思考,非纯骨架)
130
+ /specflow:explore [可选] 需求模糊时:读代码、比方案、定边界 产出 explore.md
131
+
132
+ /specflow:propose 一次产出 4 个 artifact:proposal + delta specs + design + tasks(first-iteration 深度思考,非纯骨架)
131
133
 
132
134
  /specflow:refine 内部多轮精化循环(≥2 rounds,AI 收敛判定,不设上限)
133
135
  每轮攻击性审查 + 4 个挑战行为:
@@ -135,7 +137,7 @@ specflow init
135
137
  · 探边界 · 质疑 scope
136
138
  可更新任意 artifact(proposal / specs / design / tasks)
137
139
 
138
- /specflow:build Phase A:基于 refine 稳定后的 artifact,用 writing-plans 严格精化(rewrite)tasks.md
140
+ /specflow:apply Phase A:基于 refine 稳定后的 artifact,用 writing-plans 严格精化(rewrite)tasks.md
139
141
  Phase B:逐任务 TDD 执行(subagent 模式)
140
142
  design 有漏即停,回 refine 补齐
141
143
 
@@ -145,7 +147,7 @@ specflow init
145
147
 
146
148
  /specflow:verify 双重校验(delta specs 验收 + 主 specs 回归)
147
149
 
148
- /specflow:done 归档变更 → specs 合并 → git 分支清理
150
+ /specflow:archive 归档变更 → specs 合并 → git 分支清理
149
151
  默认要求 phase=built,可用 --force 跳过
150
152
  ```
151
153
 
@@ -170,7 +172,7 @@ specflow init
170
172
 
171
173
  ```bash
172
174
  # 1. 安装(任选一种)
173
- npm install -g specflow
175
+ npm install -g @gordon.gan/specflow
174
176
 
175
177
  # 2. 进入你的项目
176
178
  cd my-project
@@ -178,12 +180,19 @@ cd my-project
178
180
  # 3. 初始化
179
181
  specflow init
180
182
 
181
- # 4. 打开 Claude Code,在会话里输入:
182
- /specflow:plan "给用户管理模块加个批量导入功能"
183
+ # 4. 打开 Claude Code,在会话里输入(二选一):
184
+
185
+ # 需求已清晰 — 直接 plan:
186
+ /specflow:propose "给用户管理模块加个批量导入功能"
187
+
188
+ # 需求模糊 — 先 explore 再 propose:
189
+ /specflow:explore "批量导入时不确定该用 CSV 还是 Excel,也不清楚现有用户表结构"
190
+ # 确认 explore.md 后:
191
+ /specflow:propose
183
192
  ```
184
193
 
185
194
  Claude Code 接下来会:
186
- 1. 读 `.claude/skills/specflow-plan/SKILL.md` 编排器
195
+ 1. 读 `.claude/skills/specflow-propose/SKILL.md` 编排器
187
196
  2. 问你几个澄清问题(一次一个)
188
197
  3. 生成 `specflow/changes/bulk-import/proposal.md`
189
198
  4. 展示 proposal 等你**确认**
@@ -192,16 +201,27 @@ Claude Code 接下来会:
192
201
 
193
202
  ### 更完整的示例(4 个场景)
194
203
 
195
- #### 场景 1:新项目从零开始
204
+ #### 场景 1:新项目从零开始(需求已清晰)
196
205
 
197
206
  ```
198
- /specflow:plan "添加用户注册和登录功能"
207
+ /specflow:propose "添加用户注册和登录功能"
199
208
  /specflow:refine # 讨论技术方案,输出 design.md
200
- /specflow:build # TDD 实现,每任务用户确认
209
+ /specflow:apply # TDD 实现,每任务用户确认
201
210
  /specflow:review # 代码审查
202
211
  /specflow:test # 跑测试
203
212
  /specflow:verify # 对照 specs 验收
204
- /specflow:done # 归档 + merge
213
+ /specflow:archive # 归档 + merge
214
+ ```
215
+
216
+ #### 场景 1b:需求模糊,先探索再规划
217
+
218
+ ```
219
+ /specflow:explore "登录有时返回 500,不确定是 token 校验还是 session 存储的问题"
220
+ # AI 读代码、比方案、产出 explore.md;你确认方向后:
221
+ /specflow:propose # 读取 explore.md,合成 proposal + specs + design + tasks
222
+ /specflow:refine
223
+ /specflow:apply
224
+ /specflow:archive
205
225
  ```
206
226
 
207
227
  #### 场景 2:接手已有项目(v0.2.x 工作流)
@@ -212,12 +232,13 @@ specflow init
212
232
 
213
233
  # 在 Claude Code 里:
214
234
  # 注意:/specflow:scan 当前规划在 v0.3,未实现。
215
- # v0.2.x 的推荐做法是直接进 plan,在 proposal Q&A 里描述已有行为 +
216
- # 新增变更,让 plan 为将要改动的 capability 生成 delta spec 基线。
217
- /specflow:plan "描述已有行为 + 你这次要改的新功能"
218
- /specflow:refine # plan 产出做 ≥2 轮攻击性审查
219
- /specflow:build # Phase A 重写 tasks.md → Phase B TDD
220
- /specflow:done # 归档,delta spec 合入主 specs/
235
+ # v0.2.x 的推荐做法是:需求模糊时先 /specflow:explore;或需求清晰时直接进 plan
236
+ # 在 proposal Q&A 里描述已有行为 + 新增变更,让 propose 为将要改动的 capability 生成 delta spec 基线。
237
+ /specflow:explore "描述模糊问题或不确定的改法" # 可选
238
+ /specflow:propose "描述已有行为 + 你这次要改的新功能"
239
+ /specflow:refine # propose 产出做 ≥2 轮攻击性审查
240
+ /specflow:apply # Phase A 重写 tasks.md → Phase B TDD
241
+ /specflow:archive # 归档,delta spec 合入主 specs/
221
242
  ```
222
243
 
223
244
  #### 场景 3:紧急修 Bug
@@ -257,13 +278,14 @@ specflow init
257
278
  | 命令 | 说明 |
258
279
  |---|---|
259
280
  | `/specflow:scan` | **[规划中 · v0.3]** 扫描已有代码库生成 specs 基线;v0.2.x 未实现,触发时 skill 会提示替代方案 |
260
- | `/specflow:plan` | 需求规划:生成 proposal + delta specs |
281
+ | `/specflow:explore` | **需求模糊时** 先探索:读代码、比方案、定边界,产出 `explore.md`,再 handoff propose |
282
+ | `/specflow:propose` | 需求规划:生成 proposal + delta specs |
261
283
  | `/specflow:refine` | 技术方案探讨(brainstorming + design.md) |
262
- | `/specflow:build` | 两阶段构建:生成计划 → subagent TDD 执行 |
284
+ | `/specflow:apply` | 两阶段构建:生成计划 → subagent TDD 执行 |
263
285
  | `/specflow:review` | 代码审查(含 specs 回归检查) |
264
286
  | `/specflow:test` | 全量测试 + 验证(单元/集成/E2E/回归) |
265
287
  | `/specflow:verify` | 双重校验:delta specs 验收 + 主 specs 回归 |
266
- | `/specflow:done` | 归档变更 + specs 合并 + git 分支清理 |
288
+ | `/specflow:archive` | 归档变更 + specs 合并 + git 分支清理 |
267
289
  | `/specflow:fix` | 修 Bug 快速通道(调试 → TDD → 归档) |
268
290
  | `/specflow:snap` | 事后补档(从 git diff 反推变更记录) |
269
291
 
@@ -403,7 +425,7 @@ your-project/
403
425
 
404
426
  ### `specflow: command not found`
405
427
 
406
- - 确认 `npm install -g` 成功
428
+ - 确认 `npm install -g @gordon.gan/specflow` 成功
407
429
  - 检查 `npm config get prefix` 下的 `bin` 目录是否在 PATH 里
408
430
 
409
431
  ### `No specflow project found at or above <dir>`
@@ -422,7 +444,7 @@ SpecFlow 需要 Node ≥ 20.19.0。用 `nvm` 切版本:
422
444
  ```bash
423
445
  nvm install 20
424
446
  nvm use 20
425
- npm install -g specflow
447
+ npm install -g @gordon.gan/specflow
426
448
  ```
427
449
 
428
450
  ### code-review-graph 安装失败
@@ -28,6 +28,6 @@ export declare function setPhase(name: string, phase: string, projectRoot: strin
28
28
  *
29
29
  * Usage:
30
30
  * specflow change phase <name> # print current phase
31
- * specflow change phase <name> --set plan # update phase
31
+ * specflow change phase <name> --set propose # update phase
32
32
  */
33
33
  export declare function registerChangePhaseCommand(changeCmd: Command): void;
@@ -46,7 +46,7 @@ export async function setPhase(name, phase, projectRoot) {
46
46
  *
47
47
  * Usage:
48
48
  * specflow change phase <name> # print current phase
49
- * specflow change phase <name> --set plan # update phase
49
+ * specflow change phase <name> --set propose # update phase
50
50
  */
51
51
  export function registerChangePhaseCommand(changeCmd) {
52
52
  changeCmd
@@ -7,7 +7,7 @@ import { join } from 'node:path';
7
7
  import * as fs from 'node:fs';
8
8
  import yaml from 'js-yaml';
9
9
  import { resolveSchema } from '../../core/artifact-graph/resolver.js';
10
- import { getCompletedArtifacts } from '../../core/artifact-graph/state.js';
10
+ import { getCompletedArtifacts, getInProgressArtifacts } from '../../core/artifact-graph/state.js';
11
11
  import { formatStatus } from '../../core/artifact-graph/outputs.js';
12
12
  import { requireProjectRoot } from '../../utils/project-root.js';
13
13
  import { readChangeMetadata } from '../../utils/change-metadata.js';
@@ -49,7 +49,8 @@ export async function getChangeStatus(changeName, projectRoot) {
49
49
  // Resolve schema from project-local schemas/ directory, falling back to package schemas
50
50
  const schema = resolveSchema(schemaName, projectRoot);
51
51
  const completed = getCompletedArtifacts(changeDir, schema);
52
- const statusOutput = formatStatus(schema.artifacts, completed);
52
+ const inProgress = getInProgressArtifacts(changeDir, schema);
53
+ const statusOutput = formatStatus(schema.artifacts, completed, inProgress);
53
54
  const completedSet = new Set(completed);
54
55
  const allDone = schema.artifacts.every((a) => completedSet.has(a.id));
55
56
  return {
@@ -66,10 +67,18 @@ export async function getChangeStatus(changeName, projectRoot) {
66
67
  */
67
68
  export function formatChangeStatus(status) {
68
69
  const lines = status.artifacts.map((entry) => {
69
- const checkbox = entry.status === 'done' ? '[x]' : '[ ]';
70
- const suffix = entry.missingDeps && entry.missingDeps.length > 0
71
- ? ` (blocked by: ${entry.missingDeps.join(', ')})`
72
- : '';
70
+ const checkbox = entry.status === 'done'
71
+ ? '[x]'
72
+ : entry.status === 'in_progress'
73
+ ? '[~]'
74
+ : '[ ]';
75
+ let suffix = '';
76
+ if (entry.status === 'in_progress') {
77
+ suffix = ' (draft — confirm before propose)';
78
+ }
79
+ else if (entry.missingDeps && entry.missingDeps.length > 0) {
80
+ suffix = ` (blocked by: ${entry.missingDeps.join(', ')})`;
81
+ }
73
82
  return `${checkbox} ${entry.id}${suffix}`;
74
83
  });
75
84
  return lines.join('\n');
@@ -80,14 +80,14 @@ export async function archiveChange(changeName, projectRoot, options = {}) {
80
80
  return {
81
81
  success: false,
82
82
  errors: [
83
- `Cannot archive: change '${changeName}' is in phase '${phaseLabel}', expected 'built'. Complete '/specflow:build' first, or pass '--force' to archive anyway.`,
83
+ `Cannot archive: change '${changeName}' is in phase '${phaseLabel}', expected 'built'. Complete '/specflow:apply' first, or pass '--force' to archive anyway.`,
84
84
  ],
85
85
  };
86
86
  }
87
87
  if (options.force && currentPhase !== 'built') {
88
88
  const phaseLabel = currentPhase ?? 'unknown';
89
89
  console.warn(`Warning: archiving "${changeName}" in phase ${phaseLabel} with --force. ` +
90
- `Consider running /specflow:build first.`);
90
+ `Consider running /specflow:apply first.`);
91
91
  }
92
92
  // 1. Find delta spec files
93
93
  const deltaFiles = await listMarkdownFiles(deltaSpecsDir);
@@ -0,0 +1,5 @@
1
+ export type ExploreArtifactStatus = 'missing' | 'draft' | 'confirmed';
2
+ /**
3
+ * Reads explore.md Status field. Only `confirmed` counts as exploration complete.
4
+ */
5
+ export declare function readExploreArtifactStatus(changeDir: string): ExploreArtifactStatus;
@@ -0,0 +1,21 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ /**
4
+ * Reads explore.md Status field. Only `confirmed` counts as exploration complete.
5
+ */
6
+ export function readExploreArtifactStatus(changeDir) {
7
+ const explorePath = path.join(changeDir, 'explore.md');
8
+ let content;
9
+ try {
10
+ content = fs.readFileSync(explorePath, 'utf-8');
11
+ }
12
+ catch {
13
+ return 'missing';
14
+ }
15
+ const match = content.match(/\*\*Status\*\*:\s*(confirmed|draft)\b/i);
16
+ if (!match) {
17
+ // File exists but Status is missing or malformed — treat as in-progress draft.
18
+ return 'draft';
19
+ }
20
+ return match[1].toLowerCase();
21
+ }
@@ -3,7 +3,9 @@ export type { Artifact, ApplyPhase, SchemaYaml, CompletedSet, BlockedArtifacts,
3
3
  export { loadSchema, parseSchema, SchemaValidationError, } from './schema.js';
4
4
  export { resolveSchema, getSchemaDir, getPackageSchemasDir, getProjectSchemasDir, listSchemas, SchemaLoadError, } from './resolver.js';
5
5
  export { ArtifactGraph } from './graph.js';
6
- export { getCompletedArtifacts } from './state.js';
6
+ export { getCompletedArtifacts, getInProgressArtifacts } from './state.js';
7
+ export { readExploreArtifactStatus } from './explore-status.js';
8
+ export type { ExploreArtifactStatus } from './explore-status.js';
7
9
  export { loadInstructions } from './instruction-loader.js';
8
10
  export type { ArtifactInstructions, DependencyInfo, ProjectConfig, } from './instruction-loader.js';
9
11
  export { formatStatus, formatStatusHuman } from './outputs.js';
@@ -2,6 +2,7 @@ export { ArtifactSchema, ApplyPhaseSchema, SchemaYamlSchema, } from './types.js'
2
2
  export { loadSchema, parseSchema, SchemaValidationError, } from './schema.js';
3
3
  export { resolveSchema, getSchemaDir, getPackageSchemasDir, getProjectSchemasDir, listSchemas, SchemaLoadError, } from './resolver.js';
4
4
  export { ArtifactGraph } from './graph.js';
5
- export { getCompletedArtifacts } from './state.js';
5
+ export { getCompletedArtifacts, getInProgressArtifacts } from './state.js';
6
+ export { readExploreArtifactStatus } from './explore-status.js';
6
7
  export { loadInstructions } from './instruction-loader.js';
7
8
  export { formatStatus, formatStatusHuman } from './outputs.js';
@@ -4,7 +4,7 @@ import type { Artifact } from './types.js';
4
4
  */
5
5
  export interface ArtifactStatusEntry {
6
6
  readonly id: string;
7
- readonly status: 'done' | 'ready' | 'blocked';
7
+ readonly status: 'done' | 'ready' | 'blocked' | 'in_progress';
8
8
  readonly missingDeps?: readonly string[];
9
9
  }
10
10
  /**
@@ -20,7 +20,7 @@ export interface StatusOutput {
20
20
  * @param completed - Array of completed artifact IDs
21
21
  * @returns Structured status with each artifact's state
22
22
  */
23
- export declare function formatStatus(artifacts: readonly Artifact[], completed: readonly string[]): StatusOutput;
23
+ export declare function formatStatus(artifacts: readonly Artifact[], completed: readonly string[], inProgress?: readonly string[]): StatusOutput;
24
24
  /**
25
25
  * Formats artifact statuses as human-readable checkbox text.
26
26
  *
@@ -32,4 +32,4 @@ export declare function formatStatus(artifacts: readonly Artifact[], completed:
32
32
  * @param completed - Array of completed artifact IDs
33
33
  * @returns Checkbox-style text string
34
34
  */
35
- export declare function formatStatusHuman(artifacts: readonly Artifact[], completed: readonly string[]): string;
35
+ export declare function formatStatusHuman(artifacts: readonly Artifact[], completed: readonly string[], inProgress?: readonly string[]): string;
@@ -5,12 +5,16 @@
5
5
  * @param completed - Array of completed artifact IDs
6
6
  * @returns Structured status with each artifact's state
7
7
  */
8
- export function formatStatus(artifacts, completed) {
8
+ export function formatStatus(artifacts, completed, inProgress = []) {
9
9
  const completedSet = new Set(completed);
10
+ const inProgressSet = new Set(inProgress);
10
11
  const statuses = artifacts.map(artifact => {
11
12
  if (completedSet.has(artifact.id)) {
12
13
  return { id: artifact.id, status: 'done' };
13
14
  }
15
+ if (inProgressSet.has(artifact.id)) {
16
+ return { id: artifact.id, status: 'in_progress' };
17
+ }
14
18
  const missingDeps = artifact.requires.filter(req => !completedSet.has(req));
15
19
  if (missingDeps.length === 0) {
16
20
  return { id: artifact.id, status: 'ready' };
@@ -34,14 +38,28 @@ export function formatStatus(artifacts, completed) {
34
38
  * @param completed - Array of completed artifact IDs
35
39
  * @returns Checkbox-style text string
36
40
  */
37
- export function formatStatusHuman(artifacts, completed) {
41
+ export function formatStatusHuman(artifacts, completed, inProgress = []) {
38
42
  const completedSet = new Set(completed);
43
+ const inProgressSet = new Set(inProgress);
39
44
  const lines = artifacts.map(artifact => {
40
- const checkbox = completedSet.has(artifact.id) ? '[x]' : '[ ]';
45
+ let checkbox;
46
+ if (completedSet.has(artifact.id)) {
47
+ checkbox = '[x]';
48
+ }
49
+ else if (inProgressSet.has(artifact.id)) {
50
+ checkbox = '[~]';
51
+ }
52
+ else {
53
+ checkbox = '[ ]';
54
+ }
41
55
  const missingDeps = artifact.requires.filter(req => !completedSet.has(req));
42
- const suffix = missingDeps.length > 0
43
- ? ` (blocked by: ${missingDeps.join(', ')})`
44
- : '';
56
+ let suffix = '';
57
+ if (inProgressSet.has(artifact.id)) {
58
+ suffix = ' (draft — confirm before propose)';
59
+ }
60
+ else if (missingDeps.length > 0) {
61
+ suffix = ` (blocked by: ${missingDeps.join(', ')})`;
62
+ }
45
63
  return `${checkbox} ${artifact.id}${suffix}`;
46
64
  });
47
65
  return lines.join('\n');
@@ -7,3 +7,7 @@ import type { SchemaYaml } from './types.js';
7
7
  * @returns Array of completed artifact IDs
8
8
  */
9
9
  export declare function getCompletedArtifacts(changeDir: string, schema: SchemaYaml): string[];
10
+ /**
11
+ * Artifacts that exist but are not yet complete (e.g. explore.md with Status: draft).
12
+ */
13
+ export declare function getInProgressArtifacts(changeDir: string, schema: SchemaYaml): string[];
@@ -1,5 +1,6 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
+ import { readExploreArtifactStatus } from './explore-status.js';
3
4
  /**
4
5
  * Checks if an artifact's generated file(s) exist in the change directory.
5
6
  * Supports simple file paths and basic glob patterns with "**".
@@ -85,9 +86,30 @@ export function getCompletedArtifacts(changeDir, schema) {
85
86
  }
86
87
  const completed = [];
87
88
  for (const artifact of schema.artifacts) {
89
+ if (artifact.id === 'explore') {
90
+ if (readExploreArtifactStatus(changeDir) === 'confirmed') {
91
+ completed.push('explore');
92
+ }
93
+ continue;
94
+ }
88
95
  if (artifactFileExists(changeDir, artifact.generates)) {
89
96
  completed.push(artifact.id);
90
97
  }
91
98
  }
92
99
  return completed;
93
100
  }
101
+ /**
102
+ * Artifacts that exist but are not yet complete (e.g. explore.md with Status: draft).
103
+ */
104
+ export function getInProgressArtifacts(changeDir, schema) {
105
+ if (!fs.existsSync(changeDir)) {
106
+ return [];
107
+ }
108
+ const inProgress = [];
109
+ for (const artifact of schema.artifacts) {
110
+ if (artifact.id === 'explore' && readExploreArtifactStatus(changeDir) === 'draft') {
111
+ inProgress.push('explore');
112
+ }
113
+ }
114
+ return inProgress;
115
+ }
@@ -10,6 +10,7 @@ const TEMPLATE_IDS = [
10
10
  'spec',
11
11
  'design',
12
12
  'tasks',
13
+ 'explore',
13
14
  ];
14
15
  /**
15
16
  * Error thrown when a template cannot be found or loaded.
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * Valid template identifiers corresponding to markdown files in the templates/ directory.
3
3
  */
4
- export type TemplateId = 'proposal' | 'spec' | 'design' | 'tasks';
4
+ export type TemplateId = 'proposal' | 'spec' | 'design' | 'tasks' | 'explore';
@@ -36,7 +36,7 @@ function renderCodexAgentsBlock() {
36
36
  '- Respect specflow phase gates: plan -> refined -> built -> archived',
37
37
  '- Never bypass CLI validation and archive checks',
38
38
  '- Use artifacts under specflow/changes/<change>/ before coding',
39
- '- Workflow skills: $specflow-plan, $specflow-refine, $specflow-build, $specflow-review, $specflow-test, $specflow-verify, $specflow-done, $specflow-fix, $specflow-snap',
39
+ '- Workflow skills: $specflow-explore, $specflow-propose, $specflow-refine, $specflow-apply, $specflow-review, $specflow-test, $specflow-verify, $specflow-archive, $specflow-fix, $specflow-snap',
40
40
  '- Runtime prompts live under .agents/specflow/prompts/',
41
41
  '',
42
42
  ].join('\n');
@@ -26,7 +26,7 @@ function renderCursorRule() {
26
26
  return [
27
27
  '# SpecFlow Cursor Context',
28
28
  '',
29
- '- Respect specflow phase gates: plan -> refined -> built -> archived',
29
+ '- Respect specflow phase gates: propose -> refined -> built -> archived',
30
30
  '- Never bypass CLI validation and archive checks',
31
31
  '- Use artifacts under specflow/changes/<change>/ before coding',
32
32
  '',
@@ -1,20 +1,26 @@
1
1
  import { COMMAND_IDS } from './command-catalog.js';
2
2
  const MARKER_RULES = [
3
3
  { id: 'sub.scan.planned_v03_notice', skill: 'scan', includes: ['[PLANNED v0.3]', 'NOT YET FUNCTIONAL'] },
4
- { id: 'sub.plan.first_iteration', skill: 'plan', includes: ['first-iteration deep-analysis pass'] },
4
+ { id: 'sub.explore.conversation_first', skill: 'explore', includes: ['think-before-propose', 'Do NOT write proposal'] },
5
+ { id: 'sub.explore.handoff_propose', skill: 'explore', includes: ['explore.md', 'handoff'] },
6
+ { id: 'sub.explore.mid_change_reexplore', skill: 'explore', includes: ['Re-explore Mid-Change', 'stuck during'] },
7
+ { id: 'sub.propose.explore_handoff', skill: 'propose', includes: ['explore.md', 'Explore Handoff'] },
8
+ { id: 'sub.propose.first_iteration', skill: 'propose', includes: ['first-iteration deep-analysis pass'] },
5
9
  { id: 'sub.refine.multi_round', skill: 'refine', includes: ['auto-multi-round loop', '## LOOP (Stages 2-4)'] },
6
10
  { id: 'sub.refine.challenge_scope', skill: 'refine', includes: ['Challenge Behaviors #3 and #4', 'question scope'] },
7
- { id: 'sub.build.phase_a', skill: 'build', includes: ['## Phase A: Task Rewrite'] },
8
- { id: 'sub.build.phase_b', skill: 'build', includes: ['## Phase B: Subagent TDD Execution'] },
9
- { id: 'sub.build.gap_detection', skill: 'build', includes: ['Gap Detection'] },
11
+ { id: 'sub.apply.phase_a', skill: 'apply', includes: ['## Phase A: Task Rewrite'] },
12
+ { id: 'sub.apply.phase_b', skill: 'apply', includes: ['## Phase B: Subagent TDD Execution'] },
13
+ { id: 'sub.apply.gap_detection', skill: 'apply', includes: ['Gap Detection'] },
10
14
  { id: 'sub.review.spec_regression', skill: 'review', includes: ['regression'] },
11
15
  { id: 'sub.test.full_scope', skill: 'test', includes: ['unit, integration, e2e', 'verification report'] },
12
16
  { id: 'sub.verify.dual_validation', skill: 'verify', includes: ['Dual validation', 'delta specs'] },
13
- { id: 'sub.done.archive_merge', skill: 'done', includes: ['specflow change archive', 'delta specs into main specs'] },
17
+ { id: 'sub.archive.archive_merge', skill: 'archive', includes: ['specflow change archive', 'delta specs into main specs'] },
14
18
  { id: 'sub.fix.urgent_mode', skill: 'fix', includes: ['--urgent', 'skip'] },
15
19
  { id: 'sub.snap.posthoc', skill: 'snap', includes: ['post-hoc', 'archive'] },
16
20
  { id: 'failure.scan.not_implemented_guard', skill: 'scan', includes: ['do NOT attempt to run `specflow scan`'] },
17
- { id: 'failure.build.phase_gate', skill: 'build', includes: ['HARD GATE (prerequisite)', 'phase is not `refined`, REFUSE'] },
21
+ { id: 'failure.explore.proposal_exists_redirect', skill: 'explore', includes: ['explore is too late', 'refine'] },
22
+ { id: 'failure.propose.explore_draft_gate', skill: 'propose', includes: ['Status: draft', 'REFUSE to proceed'] },
23
+ { id: 'failure.apply.phase_gate', skill: 'apply', includes: ['HARD GATE (prerequisite)', 'phase is not `refined`, REFUSE'] },
18
24
  { id: 'failure.verify.explicit_skip_marker', skill: 'verify', includes: ['Pass 2: skipped (no baseline'] },
19
25
  ];
20
26
  export function deriveCapabilityIds(supportedCommandIds, skillContentsByCommand) {
@@ -1,12 +1,13 @@
1
1
  export const COMMAND_CATALOG = [
2
2
  { id: 'scan', description: 'Brownfield project scanner via code-review-graph' },
3
- { id: 'plan', description: 'Create proposal and spec artifacts for a change' },
3
+ { id: 'explore', description: 'Think-before-propose exploration when requirements are fuzzy' },
4
+ { id: 'propose', description: 'Create proposal and spec artifacts for a change' },
4
5
  { id: 'refine', description: 'Refine and iterate on spec artifacts' },
5
- { id: 'build', description: 'Implement tasks from a change spec' },
6
+ { id: 'apply', description: 'Implement tasks from a change spec' },
6
7
  { id: 'review', description: 'Review code changes against spec' },
7
8
  { id: 'test', description: 'Run tests and verify coverage' },
8
9
  { id: 'verify', description: 'Verify implementation matches spec' },
9
- { id: 'done', description: 'Finalize and archive a completed change' },
10
+ { id: 'archive', description: 'Finalize and archive a completed change' },
10
11
  { id: 'fix', description: 'Debug and fix failing tests or builds' },
11
12
  { id: 'snap', description: 'Snapshot current project state' },
12
13
  ];
@@ -7,20 +7,26 @@ export const CAPABILITY_MANIFEST = [
7
7
  required: true,
8
8
  })),
9
9
  { id: 'sub.scan.planned_v03_notice', commandId: 'scan', level: 'sub-capability', required: true },
10
- { id: 'sub.plan.first_iteration', commandId: 'plan', level: 'sub-capability', required: true },
10
+ { id: 'sub.explore.conversation_first', commandId: 'explore', level: 'sub-capability', required: true },
11
+ { id: 'sub.explore.handoff_propose', commandId: 'explore', level: 'sub-capability', required: true },
12
+ { id: 'sub.explore.mid_change_reexplore', commandId: 'explore', level: 'sub-capability', required: true },
13
+ { id: 'sub.propose.explore_handoff', commandId: 'propose', level: 'sub-capability', required: true },
14
+ { id: 'sub.propose.first_iteration', commandId: 'propose', level: 'sub-capability', required: true },
11
15
  { id: 'sub.refine.multi_round', commandId: 'refine', level: 'sub-capability', required: true },
12
16
  { id: 'sub.refine.challenge_scope', commandId: 'refine', level: 'sub-capability', required: true },
13
- { id: 'sub.build.phase_a', commandId: 'build', level: 'sub-capability', required: true },
14
- { id: 'sub.build.phase_b', commandId: 'build', level: 'sub-capability', required: true },
15
- { id: 'sub.build.gap_detection', commandId: 'build', level: 'sub-capability', required: true },
17
+ { id: 'sub.apply.phase_a', commandId: 'apply', level: 'sub-capability', required: true },
18
+ { id: 'sub.apply.phase_b', commandId: 'apply', level: 'sub-capability', required: true },
19
+ { id: 'sub.apply.gap_detection', commandId: 'apply', level: 'sub-capability', required: true },
16
20
  { id: 'sub.review.spec_regression', commandId: 'review', level: 'sub-capability', required: true },
17
21
  { id: 'sub.test.full_scope', commandId: 'test', level: 'sub-capability', required: true },
18
22
  { id: 'sub.verify.dual_validation', commandId: 'verify', level: 'sub-capability', required: true },
19
- { id: 'sub.done.archive_merge', commandId: 'done', level: 'sub-capability', required: true },
23
+ { id: 'sub.archive.archive_merge', commandId: 'archive', level: 'sub-capability', required: true },
20
24
  { id: 'sub.fix.urgent_mode', commandId: 'fix', level: 'sub-capability', required: true },
21
25
  { id: 'sub.snap.posthoc', commandId: 'snap', level: 'sub-capability', required: true },
22
26
  { id: 'failure.scan.not_implemented_guard', commandId: 'scan', level: 'failure-path', required: true },
23
- { id: 'failure.build.phase_gate', commandId: 'build', level: 'failure-path', required: true },
27
+ { id: 'failure.explore.proposal_exists_redirect', commandId: 'explore', level: 'failure-path', required: true },
28
+ { id: 'failure.propose.explore_draft_gate', commandId: 'propose', level: 'failure-path', required: true },
29
+ { id: 'failure.apply.phase_gate', commandId: 'apply', level: 'failure-path', required: true },
24
30
  { id: 'failure.verify.explicit_skip_marker', commandId: 'verify', level: 'failure-path', required: true },
25
31
  ];
26
32
  export const IDE_ASSET_MANIFEST = [
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * - `plan`: Initial phase, created by `specflow change new`.
5
5
  * - `refined`: After `/specflow:refine` iterative deep review completes.
6
- * - `built`: After `/specflow:build` Phase B execution finishes.
6
+ * - `built`: After `/specflow:apply` Phase B execution finishes.
7
7
  * - `archived`: Post-archive state; assigned automatically on successful archive.
8
8
  */
9
9
  export declare const CHANGE_PHASES: readonly ["plan", "refined", "built", "archived"];
@@ -8,7 +8,7 @@ const METADATA_FILENAME = '.specflow.yaml';
8
8
  *
9
9
  * - `plan`: Initial phase, created by `specflow change new`.
10
10
  * - `refined`: After `/specflow:refine` iterative deep review completes.
11
- * - `built`: After `/specflow:build` Phase B execution finishes.
11
+ * - `built`: After `/specflow:apply` Phase B execution finishes.
12
12
  * - `archived`: Post-archive state; assigned automatically on successful archive.
13
13
  */
14
14
  export const CHANGE_PHASES = ['plan', 'refined', 'built', 'archived'];