sillyspec 3.7.33 → 3.8.0
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/.claude/skills/sillyspec-auto/SKILL.md +128 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/design.md +97 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/plan.md +51 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/proposal.md +29 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/requirements.md +34 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/tasks.md +13 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/verify-result.md +43 -0
- package/.sillyspec/changes/auto-mode/design.md +50 -0
- package/.sillyspec/changes/auto-mode/proposal.md +19 -0
- package/.sillyspec/changes/auto-mode/requirements.md +21 -0
- package/.sillyspec/changes/auto-mode/tasks.md +7 -0
- package/package.json +1 -1
- package/src/derive.js +147 -0
- package/src/index.js +27 -1
- package/src/progress.js +68 -1
- package/src/run.js +162 -3
- package/src/stages/brainstorm.js +27 -5
- package/src/stages/doctor.js +10 -1
- package/src/stages/execute.js +5 -0
- package/src/stages/plan.js +9 -0
- package/src/stages/verify.js +6 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 自动模式 — 专精子代理驱动全流程
|
|
3
|
+
argument-hint: "<需求描述>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## 用法
|
|
7
|
+
- /sillyspec:auto 实现用户登录功能
|
|
8
|
+
- /sillyspec:auto 修复搜索结果的排序问题
|
|
9
|
+
|
|
10
|
+
## 任务
|
|
11
|
+
$ARGUMENTS
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 架构
|
|
16
|
+
|
|
17
|
+
你是编排器,不亲自干活。每个阶段启动专精子代理执行,通过文件契约传递信息。
|
|
18
|
+
|
|
19
|
+
## 阶段 personas
|
|
20
|
+
|
|
21
|
+
### brainstorm — 资深架构师
|
|
22
|
+
```
|
|
23
|
+
你是一位有 15 年经验的系统架构师。
|
|
24
|
+
思维模式:先理解业务本质,再设计技术方案。善于从模糊需求中提炼关键约束。
|
|
25
|
+
关注点:系统边界、模块划分、扩展性、技术选型的 trade-off。
|
|
26
|
+
沟通风格:直接、提问犀利、不给模糊方案。不确定就说不确定,不猜。
|
|
27
|
+
输出习惯:决策要附理由,方案要列 trade-off,不用"可能""也许"。
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### plan — 技术项目经理
|
|
31
|
+
```
|
|
32
|
+
你是一位经验丰富的技术项目经理。
|
|
33
|
+
思维模式:任务拆解要粒度均匀(每个任务 1-2 小时可完成),依赖关系要明确。
|
|
34
|
+
关注点:任务优先级、风险点、验证标准、里程碑。
|
|
35
|
+
沟通风格:条理清晰,用 checkbox 和编号,不做模糊描述。
|
|
36
|
+
输出习惯:每个任务有明确的完成标准,Wave 间有依赖说明。
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### execute — 高级工程师
|
|
40
|
+
```
|
|
41
|
+
你是一位严谨的高级工程师。
|
|
42
|
+
思维模式:先读规范再写代码,严格遵循 CONVENTIONS.md。代码要有清晰的职责划分。
|
|
43
|
+
关注点:代码质量、边界处理、错误处理、命名规范。
|
|
44
|
+
沟通风格:少说多做,遇到规范冲突优先问,不自作主张。
|
|
45
|
+
输出习惯:每个函数有注释,改动要解释原因,测试要覆盖边界。
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### verify — QA 专家
|
|
49
|
+
```
|
|
50
|
+
你是一位吹毛求疵的 QA 专家。
|
|
51
|
+
思维模式:假设所有代码都有 bug,用最坏情况测试。关注边界、异常、并发。
|
|
52
|
+
关注点:规范一致性、边界条件、回归风险、性能隐患。
|
|
53
|
+
沟通风格:有问题直说,不放过任何可疑点,用证据说话。
|
|
54
|
+
输出习惯:bug 要有复现步骤,验证要有具体标准,不写"看起来没问题"。
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 执行流程
|
|
58
|
+
|
|
59
|
+
### 启动
|
|
60
|
+
1. 运行 `sillyspec run auto --input "<用户需求>"`
|
|
61
|
+
2. CLI 输出当前状态概览和第一步 prompt
|
|
62
|
+
|
|
63
|
+
### 阶段循环
|
|
64
|
+
|
|
65
|
+
按 brainstorm → plan → execute → verify 顺序,**每个阶段启动子代理**:
|
|
66
|
+
|
|
67
|
+
1. 读取 CLI 输出的 step prompt
|
|
68
|
+
2. **判断是否需要用户确认:**
|
|
69
|
+
- prompt 中包含"请用户选择""等待用户回答""展示给用户""用户确认" → **暂停,等用户回复**
|
|
70
|
+
- 纯内部操作 → **继续**
|
|
71
|
+
3. 启动子代理执行(注入对应 persona + 当前 step prompt)
|
|
72
|
+
4. 子代理完成后,运行 `sillyspec run auto --done --output "<摘要>"`
|
|
73
|
+
5. 读取 CLI 输出的下一步 prompt
|
|
74
|
+
6. 当前阶段所有 step 完成后,自动进入下一阶段,换 persona 启动新子代理
|
|
75
|
+
|
|
76
|
+
### 子代理启动格式
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
## 你的角色
|
|
80
|
+
{对应阶段的 persona}
|
|
81
|
+
|
|
82
|
+
## 当前任务
|
|
83
|
+
{sillyspec CLI 输出的 step prompt}
|
|
84
|
+
|
|
85
|
+
## 项目上下文
|
|
86
|
+
请先读取以下文件了解项目背景:
|
|
87
|
+
- `.sillyspec/docs/<project>/scan/CONVENTIONS.md`
|
|
88
|
+
- `.sillyspec/docs/<project>/scan/ARCHITECTURE.md`
|
|
89
|
+
- `.sillyspec/changes/<变更名>/design.md`(如果存在)
|
|
90
|
+
|
|
91
|
+
## 规则
|
|
92
|
+
- 不要使用 npx
|
|
93
|
+
- 不要编造不存在的 CLI 子命令
|
|
94
|
+
- 不要自动 commit,只 git add
|
|
95
|
+
- 完成后汇报结果,不要自行推进下一步
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 子代理交互协议
|
|
99
|
+
|
|
100
|
+
子代理遇到需要用户确认的问题时,**不要自己猜测**,输出以下标记后暂停:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
### ❓ 需要用户确认
|
|
104
|
+
**问题:** xxx
|
|
105
|
+
**选项:** A) xxx B) xxx C) xxx
|
|
106
|
+
**建议:** B(因为 xxx)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
主代理(你)看到 ❓ 标记后:
|
|
110
|
+
1. 暂停当前流程
|
|
111
|
+
2. 把问题转发给用户
|
|
112
|
+
3. 等用户回复
|
|
113
|
+
4. 把用户回复发送给子代理(sessions_send),子代理继续执行
|
|
114
|
+
|
|
115
|
+
### CLI prompt 中的用户确认点
|
|
116
|
+
|
|
117
|
+
sillyspec CLI 的 step prompt 本身可能包含"请用户选择""等待用户回答"等要求。
|
|
118
|
+
主代理(你)在把 prompt 发给子代理前,先扫描这些关键词。
|
|
119
|
+
如果有 → 先自己处理(询问用户),拿到答案后再发给子代理。
|
|
120
|
+
这样子代理不需要处理交互,只管干活。
|
|
121
|
+
|
|
122
|
+
## 异常处理
|
|
123
|
+
- 命令执行失败 → 展示错误,暂停等用户介入
|
|
124
|
+
- 用户说"停止"/"暂停" → 立即停止
|
|
125
|
+
- 子代理失败 → 展示错误,可重试或跳过
|
|
126
|
+
|
|
127
|
+
## 完成条件
|
|
128
|
+
CLI 输出"全部流程已完成"后,提示用户 `/sillyspec:commit` 提交改动
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# deriveState 状态推导
|
|
2
|
+
|
|
3
|
+
author: qinyi
|
|
4
|
+
created_at: 2026-04-08 07:10:00
|
|
5
|
+
|
|
6
|
+
## 背景
|
|
7
|
+
|
|
8
|
+
当前 sillyspec 的状态管理依赖 `progress.json` 作为唯一数据源。如果 AI 崩溃或异常中断,progress.json 可能与实际产出不一致(如 artifacts 文件已生成但步骤未标记完成)。
|
|
9
|
+
|
|
10
|
+
借鉴 GSD v2 的 deriveState 架构,从文件系统反推状态,与 progress.json 交叉校验。
|
|
11
|
+
|
|
12
|
+
## 需求
|
|
13
|
+
|
|
14
|
+
1. `--done` 完成步骤时轻量校验当前步骤
|
|
15
|
+
2. `doctor` 自检时全量扫描所有阶段
|
|
16
|
+
3. `progress validate --deep` 支持手动触发全量校验
|
|
17
|
+
4. 安全修复策略:明显正确的情况自动修复,有歧义的不动
|
|
18
|
+
|
|
19
|
+
## 设计
|
|
20
|
+
|
|
21
|
+
### 新增文件:`src/derive.js`
|
|
22
|
+
|
|
23
|
+
纯函数模块,零外部依赖(仅 fs/path)。
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
export function deriveState(cwd, options = {}) {
|
|
27
|
+
// options.mode: 'light' | 'full'(默认 light)
|
|
28
|
+
// options.fix: boolean(默认 false,只报告不修复)
|
|
29
|
+
// 返回 { issues: [{type, severity, step, artifact, suggestion}], fixed: number }
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
#### 扫描逻辑
|
|
34
|
+
|
|
35
|
+
1. 读取 progress.json,获取所有阶段步骤状态
|
|
36
|
+
2. 扫描 `.sillyspec/.runtime/artifacts/` 目录
|
|
37
|
+
3. 文件名格式:`{stage}-step{N}-{timestamp}.txt`
|
|
38
|
+
4. 解析文件名提取 stage、stepIndex 信息
|
|
39
|
+
5. 对比规则:
|
|
40
|
+
|
|
41
|
+
| 情况 | 严重度 | 自动修复 |
|
|
42
|
+
|------|--------|----------|
|
|
43
|
+
| artifacts 有文件但 progress 标记未完成 | issue | ✅ 标记为 done |
|
|
44
|
+
| progress 标记已完成但 artifacts 无文件 | warning | ❌ 可能被手动清理 |
|
|
45
|
+
| artifacts 有 step5 但 progress 只到 step3 | issue | ✅ 补齐中间步骤 |
|
|
46
|
+
|
|
47
|
+
#### 模式
|
|
48
|
+
|
|
49
|
+
- **light**:只检查 currentStage 的当前步骤和前一步
|
|
50
|
+
- **full**:检查所有阶段所有步骤
|
|
51
|
+
|
|
52
|
+
### 集成方式
|
|
53
|
+
|
|
54
|
+
#### 1. run.js — `--done` 轻量校验
|
|
55
|
+
|
|
56
|
+
在 `completeStep` 末尾:
|
|
57
|
+
```js
|
|
58
|
+
import { deriveState } from './derive.js';
|
|
59
|
+
const result = deriveState(cwd, { mode: 'light', fix: true });
|
|
60
|
+
if (result.fixed > 0) {
|
|
61
|
+
console.log(`⚠️ 状态修复:${result.fixed} 个步骤已从 artifacts 恢复`);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### 2. doctor.js — 全量扫描
|
|
66
|
+
|
|
67
|
+
第一步(SillySpec 内部检查)调用:
|
|
68
|
+
```js
|
|
69
|
+
const result = deriveState(cwd, { mode: 'full', fix: false });
|
|
70
|
+
// 将 issues 加入自检报告
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### 3. progress.js — `validate --deep`
|
|
74
|
+
|
|
75
|
+
validate 方法支持 deep 参数:
|
|
76
|
+
```js
|
|
77
|
+
validate(cwd, deep = false) {
|
|
78
|
+
// ...现有校验逻辑...
|
|
79
|
+
if (deep) {
|
|
80
|
+
const result = deriveState(cwd, { mode: 'full', fix: true });
|
|
81
|
+
// 输出校验结果
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
CLI:`sillyspec progress validate --deep`
|
|
87
|
+
|
|
88
|
+
## 改动范围
|
|
89
|
+
|
|
90
|
+
- 新增:`src/derive.js`(~80 行)
|
|
91
|
+
- 修改:`src/run.js`(2 行)、`src/stages/doctor.js`(3 行)、`src/progress.js`(5 行)、`src/index.js`(parse --deep)
|
|
92
|
+
|
|
93
|
+
## 不做的事
|
|
94
|
+
|
|
95
|
+
- 不引入 SQLite 或其他新依赖
|
|
96
|
+
- 不改变 progress.json 的数据结构
|
|
97
|
+
- 不自动删除 progress 中有但 artifacts 无的步骤(可能被手动清理)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# deriveState 状态推导 — 实现计划
|
|
2
|
+
|
|
3
|
+
author: qinyi
|
|
4
|
+
created_at: 2026-04-08 07:12:00
|
|
5
|
+
|
|
6
|
+
## Wave 1(核心,无依赖)
|
|
7
|
+
|
|
8
|
+
- [ ] 实现 derive.js 核心函数
|
|
9
|
+
- 新增: `src/derive.js`
|
|
10
|
+
- 步骤:
|
|
11
|
+
1. 实现 `deriveState(cwd, options)` 纯函数
|
|
12
|
+
2. 实现 artifacts 文件名解析(`{stage}-step{N}-{timestamp}.txt`)
|
|
13
|
+
3. 实现 light/full 模式扫描逻辑
|
|
14
|
+
4. 实现安全修复策略(issues 分类 + fix 逻辑)
|
|
15
|
+
5. 验证: 在 sillyspec 项目上手动创建测试 artifacts,运行 `node -e "import('./src/derive.js').then(m => console.log(m.deriveState(process.cwd(), {mode:'full'})))"` 确认输出
|
|
16
|
+
|
|
17
|
+
## Wave 2(集成,依赖 Wave 1)
|
|
18
|
+
|
|
19
|
+
- [ ] 集成 run.js --done 轻量校验
|
|
20
|
+
- 修改: `src/run.js`
|
|
21
|
+
- 参考: `completeStep` 函数末尾
|
|
22
|
+
- 步骤:
|
|
23
|
+
1. 在 completeStep 末尾 import 并调用 deriveState(cwd, {mode:'light', fix:true})
|
|
24
|
+
2. 有修复时输出警告信息
|
|
25
|
+
3. 验证: 运行 brainstorm 完成 --done,确认无报错
|
|
26
|
+
|
|
27
|
+
- [ ] 扩展 validate 支持 --deep
|
|
28
|
+
- 修改: `src/progress.js`
|
|
29
|
+
- 参考: `validate()` 方法
|
|
30
|
+
- 步骤:
|
|
31
|
+
1. validate 方法加 deep 参数
|
|
32
|
+
2. deep=true 时调用 deriveState(cwd, {mode:'full', fix:true})
|
|
33
|
+
3. 验证: `sillyspec progress validate --deep` 确认输出校验结果
|
|
34
|
+
|
|
35
|
+
- [ ] CLI parse --deep 参数
|
|
36
|
+
- 修改: `src/index.js`
|
|
37
|
+
- 参考: progress 子命令的参数解析
|
|
38
|
+
- 步骤:
|
|
39
|
+
1. 在 progress validate 命令中解析 --deep flag
|
|
40
|
+
2. 传递给 validate 方法
|
|
41
|
+
3. 验证: `sillyspec progress validate --deep` 确认 flag 生效
|
|
42
|
+
|
|
43
|
+
## Wave 3(集成,依赖 Wave 1)
|
|
44
|
+
|
|
45
|
+
- [ ] 集成 doctor.js 全量扫描
|
|
46
|
+
- 修改: `src/stages/doctor.js`
|
|
47
|
+
- 参考: doctor 第一步(SillySpec 内部检查)的 prompt
|
|
48
|
+
- 步骤:
|
|
49
|
+
1. 在第一步 prompt 中加入 deriveState 全量扫描指令
|
|
50
|
+
2. 将 issues 列表纳入自检报告
|
|
51
|
+
3. 验证: `sillyspec run doctor` 确认第一步输出包含状态一致性检查
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# deriveState 状态推导 — 提案
|
|
2
|
+
|
|
3
|
+
author: qinyi
|
|
4
|
+
created_at: 2026-04-08 07:11:00
|
|
5
|
+
|
|
6
|
+
## 动机
|
|
7
|
+
|
|
8
|
+
当前 progress.json 是 sillyspec 唯一的状态数据源。AI 崩溃或异常中断时,progress.json 可能与实际产出不一致(artifacts 已生成但步骤未标记完成)。需要从文件系统反推状态,交叉校验。
|
|
9
|
+
|
|
10
|
+
## 变更范围
|
|
11
|
+
|
|
12
|
+
- 新增 `src/derive.js`(状态推导纯函数)
|
|
13
|
+
- 修改 `src/run.js`(--done 轻量校验)
|
|
14
|
+
- 修改 `src/stages/doctor.js`(全量扫描)
|
|
15
|
+
- 修改 `src/progress.js`(validate --deep)
|
|
16
|
+
- 修改 `src/index.js`(parse --deep 参数)
|
|
17
|
+
|
|
18
|
+
## 不在范围内
|
|
19
|
+
|
|
20
|
+
- 不改 progress.json 数据结构
|
|
21
|
+
- 不引入新依赖
|
|
22
|
+
- 不自动删除 progress 中有但 artifacts 无的步骤
|
|
23
|
+
|
|
24
|
+
## 成功标准
|
|
25
|
+
|
|
26
|
+
1. `--done` 完成步骤时自动校验并修复当前步骤
|
|
27
|
+
2. `doctor` 输出全量状态一致性报告
|
|
28
|
+
3. `sillyspec progress validate --deep` 可手动触发全量校验
|
|
29
|
+
4. 所有校验通过现有测试
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# deriveState 状态推导 — 需求
|
|
2
|
+
|
|
3
|
+
author: qinyi
|
|
4
|
+
created_at: 2026-04-08 07:11:00
|
|
5
|
+
|
|
6
|
+
## 功能需求
|
|
7
|
+
|
|
8
|
+
### FR1: deriveState 核心函数
|
|
9
|
+
- 从 artifacts 目录扫描文件,解析 `{stage}-step{N}-{timestamp}.txt` 格式
|
|
10
|
+
- 与 progress.json 步骤状态对比
|
|
11
|
+
- 返回 issues 列表和修复计数
|
|
12
|
+
|
|
13
|
+
### FR2: 轻量模式(light)
|
|
14
|
+
- 只检查 currentStage 的当前步骤和前一步
|
|
15
|
+
- 用于 `--done` 完成时
|
|
16
|
+
|
|
17
|
+
### FR3: 全量模式(full)
|
|
18
|
+
- 检查所有阶段所有步骤
|
|
19
|
+
- 用于 doctor 和 validate --deep
|
|
20
|
+
|
|
21
|
+
### FR4: 安全修复策略
|
|
22
|
+
- artifacts 有但 progress 漏记 → 自动修复为 done
|
|
23
|
+
- artifacts 有 step5 但 progress 只到 step3 → 自动补齐
|
|
24
|
+
- progress 有但 artifacts 无 → 只警告,不修复
|
|
25
|
+
|
|
26
|
+
### FR5: CLI 集成
|
|
27
|
+
- `--done` 时静默调用轻量校验,有修复才输出
|
|
28
|
+
- `doctor` 第一步输出全量报告
|
|
29
|
+
- `sillyspec progress validate --deep` 手动触发
|
|
30
|
+
|
|
31
|
+
## 非功能需求
|
|
32
|
+
- 零外部依赖(仅 fs/path)
|
|
33
|
+
- 纯函数,易于测试
|
|
34
|
+
- 不改变现有 API 行为
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# deriveState 状态推导 — 任务
|
|
2
|
+
|
|
3
|
+
author: qinyi
|
|
4
|
+
created_at: 2026-04-08 07:11:00
|
|
5
|
+
|
|
6
|
+
## 任务列表
|
|
7
|
+
|
|
8
|
+
- [x] 实现 derive.js 核心函数 — `src/derive.js`
|
|
9
|
+
- [x] 集成 run.js --done 轻量校验 — `src/run.js`
|
|
10
|
+
- [x] 集成 doctor.js 全量扫描 — `src/stages/doctor.js`
|
|
11
|
+
- [x] 扩展 validate 支持 --deep — `src/progress.js`
|
|
12
|
+
- [x] CLI parse --deep 参数 — `src/index.js`
|
|
13
|
+
- [ ] 测试验证
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# derive-state 验证报告
|
|
2
|
+
|
|
3
|
+
author: qinyi
|
|
4
|
+
created_at: 2026-04-08 07:21:00
|
|
5
|
+
|
|
6
|
+
## 结论:✅ PASS
|
|
7
|
+
|
|
8
|
+
## 检查结果
|
|
9
|
+
|
|
10
|
+
### 1. 规范文件加载
|
|
11
|
+
- ✅ design.md
|
|
12
|
+
- ✅ proposal.md
|
|
13
|
+
- ✅ requirements.md
|
|
14
|
+
- ✅ tasks.md
|
|
15
|
+
- ✅ plan.md
|
|
16
|
+
|
|
17
|
+
### 2. 任务完成度:5/6 (83%)
|
|
18
|
+
- ✅ 实现 derive.js 核心函数
|
|
19
|
+
- ✅ 集成 run.js --done 轻量校验
|
|
20
|
+
- ✅ 集成 doctor.js 全量扫描
|
|
21
|
+
- ✅ 扩展 validate 支持 --deep
|
|
22
|
+
- ✅ CLI parse --deep 参数
|
|
23
|
+
- ⬜ 测试验证(本项目无自动化测试套件,通过手动验证替代)
|
|
24
|
+
|
|
25
|
+
### 3. 设计一致性
|
|
26
|
+
- ✅ derive.js 纯函数模块,零外部依赖(仅 fs/path)
|
|
27
|
+
- ✅ light/full 模式
|
|
28
|
+
- ✅ fix 参数 + 安全修复策略
|
|
29
|
+
- ✅ run.js --done 轻量校验集成
|
|
30
|
+
- ✅ doctor.js 全量扫描集成
|
|
31
|
+
- ✅ progress.js validate --deep 支持
|
|
32
|
+
- ✅ index.js CLI --deep 参数
|
|
33
|
+
- ✅ 改动文件范围与 design.md 一致
|
|
34
|
+
|
|
35
|
+
### 4. 测试和质量
|
|
36
|
+
- ✅ derive.js 模块导入正常
|
|
37
|
+
- ✅ sillyspec progress validate --deep 通过
|
|
38
|
+
- ✅ 无 TODO/FIXME/HACK/XXX 技术债务
|
|
39
|
+
- ✅ sillyspec run quick --status 正常
|
|
40
|
+
- ✅ sillyspec run doctor --status 正常
|
|
41
|
+
|
|
42
|
+
## 下一步
|
|
43
|
+
sillyspec run archive
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# auto mode — 设计文档
|
|
2
|
+
|
|
3
|
+
author: qinyi
|
|
4
|
+
created_at: 2026-04-08 07:28:00
|
|
5
|
+
|
|
6
|
+
## 背景
|
|
7
|
+
|
|
8
|
+
当前 sillyspec 的每个阶段(brainstorm → plan → execute → verify)需要用户手动执行 `sillyspec run <stage>` 和 `sillyspec run <stage> --done`。用户希望一个自动模式,从 brainstorm 一路推进到 verify 完成。
|
|
9
|
+
|
|
10
|
+
## 需求
|
|
11
|
+
|
|
12
|
+
1. 用户启动一次,AI 自动循环所有阶段和步骤
|
|
13
|
+
2. 步骤内部的用户确认点保留不变
|
|
14
|
+
3. 不修改 CLI 代码,纯 skill 文件实现
|
|
15
|
+
|
|
16
|
+
## 设计
|
|
17
|
+
|
|
18
|
+
### 新增文件:`.claude/skills/sillyspec-auto/SKILL.md`
|
|
19
|
+
|
|
20
|
+
**核心逻辑:**
|
|
21
|
+
|
|
22
|
+
1. 读 `$ARGUMENTS` 作为用户需求
|
|
23
|
+
2. 阶段循环(brainstorm → plan → execute → verify)
|
|
24
|
+
3. 每个阶段内步骤循环:
|
|
25
|
+
- `sillyspec run <stage> --input "需求"` → 读 step prompt
|
|
26
|
+
- 执行 prompt 中的操作
|
|
27
|
+
- 需要用户确认的步骤 → 暂停等回复
|
|
28
|
+
- 完成后自动 `sillyspec run <stage> --done --output "摘要"`
|
|
29
|
+
- 读下一步 prompt,继续
|
|
30
|
+
4. 当前阶段全部完成 → 自动进入下一阶段
|
|
31
|
+
5. verify 完成 → 输出总结,停止
|
|
32
|
+
6. 命令失败 → 暂停,等用户介入
|
|
33
|
+
|
|
34
|
+
**确认点保留规则:**
|
|
35
|
+
- prompt 中有"请用户选择""等待用户回答""展示给用户"等字样 → 暂停
|
|
36
|
+
- prompt 中有"自审""检查"等纯内部操作 → 自动完成
|
|
37
|
+
|
|
38
|
+
### 同步到 npm 包
|
|
39
|
+
|
|
40
|
+
init.js 已有逻辑复制 `sillyspec-*` skills 到项目 `.claude/skills/`,新 skill 自动生效。
|
|
41
|
+
|
|
42
|
+
## 改动范围
|
|
43
|
+
|
|
44
|
+
- 新增:`.claude/skills/sillyspec-auto/SKILL.md`(~60 行)
|
|
45
|
+
|
|
46
|
+
## 不做的事
|
|
47
|
+
|
|
48
|
+
- 不修改任何 JS 源码
|
|
49
|
+
- 不改变现有阶段流程
|
|
50
|
+
- 不自动 commit 或发布
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# auto mode — 提案
|
|
2
|
+
|
|
3
|
+
author: qinyi
|
|
4
|
+
created_at: 2026-04-08 07:29:00
|
|
5
|
+
|
|
6
|
+
## 动机
|
|
7
|
+
用户希望一次启动就自动完成 brainstorm → plan → execute → verify 全流程,不需要手动输入 `sillyspec run <stage>` 和 `--done`。
|
|
8
|
+
|
|
9
|
+
## 变更范围
|
|
10
|
+
新增 `.claude/skills/sillyspec-auto/SKILL.md`
|
|
11
|
+
|
|
12
|
+
## 不在范围内
|
|
13
|
+
- 不修改 JS 源码
|
|
14
|
+
- 不改变阶段流程
|
|
15
|
+
|
|
16
|
+
## 成功标准
|
|
17
|
+
1. `/sillyspec:auto "需求"` 能自动推进全流程
|
|
18
|
+
2. 步骤内部确认点正常暂停
|
|
19
|
+
3. 异常时暂停等用户介入
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# auto mode — 需求
|
|
2
|
+
|
|
3
|
+
author: qinyi
|
|
4
|
+
created_at: 2026-04-08 07:29:00
|
|
5
|
+
|
|
6
|
+
## 功能需求
|
|
7
|
+
|
|
8
|
+
### FR1: 阶段自动推进
|
|
9
|
+
- 按 brainstorm → plan → execute → verify 顺序自动执行
|
|
10
|
+
- 当前阶段完成后自动进入下一阶段
|
|
11
|
+
|
|
12
|
+
### FR2: 步骤自动循环
|
|
13
|
+
- 每个步骤:读 prompt → 执行 → 自动 --done → 读下一步
|
|
14
|
+
- 不需要用户手动触发 --done
|
|
15
|
+
|
|
16
|
+
### FR3: 确认点保留
|
|
17
|
+
- prompt 中有用户确认要求时暂停等回复
|
|
18
|
+
- 纯内部操作步骤自动完成
|
|
19
|
+
|
|
20
|
+
### FR4: 异常处理
|
|
21
|
+
- 命令失败时暂停,展示错误,等用户介入
|
package/package.json
CHANGED
package/src/derive.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { readdirSync, existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 从 artifacts 文件系统反推状态,与 progress.json 交叉校验。
|
|
6
|
+
* 纯函数,零外部副作用(fix 模式除外)。
|
|
7
|
+
*
|
|
8
|
+
* @param {string} cwd - 项目根目录
|
|
9
|
+
* @param {object} options
|
|
10
|
+
* @param {'light'|'full'} options.mode - 轻量(当前步骤)或全量扫描
|
|
11
|
+
* @param {boolean} options.fix - 是否自动修复明显不一致
|
|
12
|
+
* @param {object} options.pm - ProgressManager 实例(fix 模式需要)
|
|
13
|
+
* @param {object} options.progress - 已加载的 progress 数据
|
|
14
|
+
* @returns {{ issues: Array<{type:string, severity:string, stage:string, step:number, message:string, suggestion:string}>, fixed: number }}
|
|
15
|
+
*/
|
|
16
|
+
export function deriveState(cwd, options = {}) {
|
|
17
|
+
const { mode = 'light', fix = false, pm = null, progress = null } = options;
|
|
18
|
+
|
|
19
|
+
const issues = [];
|
|
20
|
+
let fixed = 0;
|
|
21
|
+
|
|
22
|
+
const artifactsDir = join(cwd, '.sillyspec', '.runtime', 'artifacts');
|
|
23
|
+
if (!existsSync(artifactsDir)) {
|
|
24
|
+
return { issues: [{ type: 'no-artifacts', severity: 'info', stage: '-', step: 0, message: 'artifacts 目录不存在', suggestion: '正常,项目刚开始' }], fixed: 0 };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 扫描 artifacts 文件,解析 stage/step 信息
|
|
28
|
+
const artifactMap = {}; // { "stage:stepN": [filenames] }
|
|
29
|
+
const stageStepSet = new Set(); // "stage:stepN"
|
|
30
|
+
|
|
31
|
+
let files;
|
|
32
|
+
try {
|
|
33
|
+
files = readdirSync(artifactsDir).filter(f => f.endsWith('.txt'));
|
|
34
|
+
} catch {
|
|
35
|
+
return { issues: [], fixed: 0 };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const file of files) {
|
|
39
|
+
// 格式: {stage}-step{N}-{timestamp}.txt
|
|
40
|
+
const match = file.match(/^(.+)-step(\d+)-\d+\.txt$/);
|
|
41
|
+
if (match) {
|
|
42
|
+
const [, stage, stepStr] = match;
|
|
43
|
+
const key = `${stage}:${stepStr}`;
|
|
44
|
+
if (!artifactMap[key]) artifactMap[key] = [];
|
|
45
|
+
artifactMap[key].push(file);
|
|
46
|
+
stageStepSet.add(key);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 需要检查的阶段
|
|
51
|
+
let stagesToCheck = [];
|
|
52
|
+
if (progress) {
|
|
53
|
+
if (mode === 'light') {
|
|
54
|
+
// 轻量:只检查 currentStage
|
|
55
|
+
const currentStage = progress.currentStage || '';
|
|
56
|
+
if (currentStage) stagesToCheck.push(currentStage);
|
|
57
|
+
} else {
|
|
58
|
+
// 全量:检查所有阶段
|
|
59
|
+
stagesToCheck = Object.keys(progress.stages || {});
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
// 没有 progress 数据,从 artifacts 推断所有阶段
|
|
63
|
+
for (const key of stageStepSet) {
|
|
64
|
+
const stage = key.split(':')[0];
|
|
65
|
+
if (!stagesToCheck.includes(stage)) stagesToCheck.push(stage);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const stage of stagesToCheck) {
|
|
70
|
+
const stageData = progress?.stages?.[stage];
|
|
71
|
+
const steps = stageData?.steps || [];
|
|
72
|
+
|
|
73
|
+
// 收集 artifacts 中该阶段的步骤编号
|
|
74
|
+
const artifactSteps = new Set();
|
|
75
|
+
for (const key of stageStepSet) {
|
|
76
|
+
const [s, n] = key.split(':');
|
|
77
|
+
if (s === stage) artifactSteps.add(parseInt(n));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 检查1:artifacts 有但 progress 未标记完成
|
|
81
|
+
for (const stepNum of artifactSteps) {
|
|
82
|
+
const stepIdx = stepNum - 1;
|
|
83
|
+
if (stepIdx < steps.length) {
|
|
84
|
+
const step = steps[stepIdx];
|
|
85
|
+
if (step.status !== 'done') {
|
|
86
|
+
issues.push({
|
|
87
|
+
type: 'missing-progress',
|
|
88
|
+
severity: 'issue',
|
|
89
|
+
stage,
|
|
90
|
+
step: stepNum,
|
|
91
|
+
message: `artifacts 有 ${stage}-step${stepNum} 文件但 progress 未标记完成`,
|
|
92
|
+
suggestion: '标记该步骤为 done'
|
|
93
|
+
});
|
|
94
|
+
if (fix && pm && progress) {
|
|
95
|
+
step.status = 'done';
|
|
96
|
+
pm._write(cwd, progress);
|
|
97
|
+
fixed++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 检查2:progress 有但 artifacts 无文件(warning,不修复)
|
|
104
|
+
for (let i = 0; i < steps.length; i++) {
|
|
105
|
+
const step = steps[i];
|
|
106
|
+
if (step.status === 'done' && !artifactSteps.has(i + 1)) {
|
|
107
|
+
issues.push({
|
|
108
|
+
type: 'missing-artifact',
|
|
109
|
+
severity: 'warning',
|
|
110
|
+
stage,
|
|
111
|
+
step: i + 1,
|
|
112
|
+
message: `${stage} step ${i + 1} 标记完成但 artifacts 无对应文件`,
|
|
113
|
+
suggestion: '可能被手动清理,忽略即可'
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 检查3:artifacts 有 step5 但 progress 只到 step3(中间漏记)
|
|
119
|
+
if (artifactSteps.size > 0) {
|
|
120
|
+
const maxArtifactStep = Math.max(...artifactSteps);
|
|
121
|
+
const maxProgressDoneStep = steps.reduce((max, s, i) => s.status === 'done' ? Math.max(max, i + 1) : max, 0);
|
|
122
|
+
|
|
123
|
+
if (maxArtifactStep > maxProgressDoneStep && maxProgressDoneStep > 0) {
|
|
124
|
+
// 检查中间是否有漏记的
|
|
125
|
+
for (let i = maxProgressDoneStep + 1; i <= maxArtifactStep; i++) {
|
|
126
|
+
if (artifactSteps.has(i) && i - 1 < steps.length && steps[i - 1].status !== 'done') {
|
|
127
|
+
// 已在检查1处理,跳过
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// progress 步骤数少于 artifacts 最大步骤号
|
|
133
|
+
if (maxArtifactStep > steps.length) {
|
|
134
|
+
issues.push({
|
|
135
|
+
type: 'missing-steps',
|
|
136
|
+
severity: 'issue',
|
|
137
|
+
stage,
|
|
138
|
+
step: steps.length + 1,
|
|
139
|
+
message: `artifacts 有 step${maxArtifactStep} 但 progress 只有 ${steps.length} 个步骤`,
|
|
140
|
+
suggestion: 'progress 数据可能不完整'
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return { issues, fixed };
|
|
147
|
+
}
|
package/src/index.js
CHANGED
|
@@ -128,7 +128,7 @@ async function main() {
|
|
|
128
128
|
pm.show(dir);
|
|
129
129
|
break;
|
|
130
130
|
case 'validate':
|
|
131
|
-
pm.validate(dir);
|
|
131
|
+
await pm.validate(dir, filteredArgs.includes('--deep'));
|
|
132
132
|
break;
|
|
133
133
|
case 'reset':
|
|
134
134
|
pm.reset(dir, stage);
|
|
@@ -168,6 +168,32 @@ async function main() {
|
|
|
168
168
|
pm.completeStage(dir, compStageName);
|
|
169
169
|
break;
|
|
170
170
|
}
|
|
171
|
+
case 'batch': {
|
|
172
|
+
if (filteredArgs.includes('--status')) {
|
|
173
|
+
const bp = pm.readBatchProgress(dir);
|
|
174
|
+
if (!bp) { console.log('📭 无批量进度数据'); break; }
|
|
175
|
+
const line = pm._renderBatchProgress(bp);
|
|
176
|
+
console.log(line || '📭 无批量进度数据');
|
|
177
|
+
console.log(JSON.stringify(bp, null, 2));
|
|
178
|
+
} else {
|
|
179
|
+
let batchData = {};
|
|
180
|
+
const a = args;
|
|
181
|
+
for (let i = 0; i < a.length; i++) {
|
|
182
|
+
if (a[i] === '--total' && a[i + 1]) { batchData.total = parseInt(a[i + 1]); i++; }
|
|
183
|
+
if (a[i] === '--completed' && a[i + 1]) { batchData.completed = parseInt(a[i + 1]); i++; }
|
|
184
|
+
if (a[i] === '--failed' && a[i + 1]) { batchData.failed = parseInt(a[i + 1]); i++; }
|
|
185
|
+
if (a[i] === '--skipped' && a[i + 1]) { batchData.skipped = parseInt(a[i + 1]); i++; }
|
|
186
|
+
}
|
|
187
|
+
if (Object.keys(batchData).length === 0) {
|
|
188
|
+
console.log('用法: sillyspec progress batch --total 100 --completed 73');
|
|
189
|
+
console.log(' sillyspec progress batch --status');
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
pm.updateBatchProgress(dir, batchData);
|
|
193
|
+
console.log('✅ 批量进度已更新');
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
171
197
|
default:
|
|
172
198
|
console.log('用法: sillyspec progress <init|show|validate|reset|complete|set-stage|add-step|update-step|complete-stage>');
|
|
173
199
|
}
|
package/src/progress.js
CHANGED
|
@@ -289,6 +289,15 @@ export class ProgressManager {
|
|
|
289
289
|
}
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
// 批量进度
|
|
293
|
+
if (data.batchProgress) {
|
|
294
|
+
const batchLine = this._renderBatchProgress(data.batchProgress);
|
|
295
|
+
if (batchLine) {
|
|
296
|
+
console.log('');
|
|
297
|
+
console.log(` ${batchLine}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
292
301
|
console.log('');
|
|
293
302
|
}
|
|
294
303
|
|
|
@@ -296,7 +305,7 @@ export class ProgressManager {
|
|
|
296
305
|
this.show(cwd);
|
|
297
306
|
}
|
|
298
307
|
|
|
299
|
-
validate(cwd) {
|
|
308
|
+
async validate(cwd, deep = false) {
|
|
300
309
|
const data = this.read(cwd);
|
|
301
310
|
if (!data) { console.log('❌ 无法读取 progress.json'); return false; }
|
|
302
311
|
|
|
@@ -328,6 +337,26 @@ export class ProgressManager {
|
|
|
328
337
|
this._write(cwd, fixed);
|
|
329
338
|
console.log('✅ 已修复并备份');
|
|
330
339
|
}
|
|
340
|
+
|
|
341
|
+
if (deep) {
|
|
342
|
+
try {
|
|
343
|
+
const { deriveState } = await import('./derive.js');
|
|
344
|
+
const result = deriveState(cwd, { mode: 'full', fix: true, pm: this, progress: this.read(cwd) });
|
|
345
|
+
if (result.issues.length > 0) {
|
|
346
|
+
console.log(`\n📋 deriveState 深度校验(${result.issues.length} 项):`);
|
|
347
|
+
for (const issue of result.issues) {
|
|
348
|
+
const icon = issue.severity === 'issue' ? '🔴' : issue.severity === 'warning' ? '🟡' : '⚪';
|
|
349
|
+
console.log(` ${icon} ${issue.stage} step ${issue.step}: ${issue.message}`);
|
|
350
|
+
}
|
|
351
|
+
if (result.fixed > 0) console.log(` 🔧 已自动修复 ${result.fixed} 项`);
|
|
352
|
+
} else {
|
|
353
|
+
console.log('✅ deriveState 深度校验通过,无不一致');
|
|
354
|
+
}
|
|
355
|
+
} catch (e) {
|
|
356
|
+
console.log(`⚠️ deriveState 校验失败: ${e.message}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
331
360
|
return true;
|
|
332
361
|
}
|
|
333
362
|
|
|
@@ -414,6 +443,44 @@ export class ProgressManager {
|
|
|
414
443
|
return `${Math.floor(h / 24)} 天前`;
|
|
415
444
|
}
|
|
416
445
|
|
|
446
|
+
// ── 批量进度 ──
|
|
447
|
+
|
|
448
|
+
updateBatchProgress(cwd, batchData) {
|
|
449
|
+
const data = this._readOrInit(cwd);
|
|
450
|
+
if (!data) return;
|
|
451
|
+
|
|
452
|
+
if (!data.batchProgress) {
|
|
453
|
+
data.batchProgress = { total: 0, completed: 0, failed: 0, skipped: 0 };
|
|
454
|
+
}
|
|
455
|
+
if (batchData.total !== undefined) data.batchProgress.total = batchData.total;
|
|
456
|
+
if (batchData.completed !== undefined) data.batchProgress.completed = batchData.completed;
|
|
457
|
+
if (batchData.failed !== undefined) data.batchProgress.failed = batchData.failed;
|
|
458
|
+
if (batchData.skipped !== undefined) data.batchProgress.skipped = batchData.skipped;
|
|
459
|
+
|
|
460
|
+
data.lastActive = new Date().toLocaleString('zh-CN', { hour12: false });
|
|
461
|
+
this._backup(cwd);
|
|
462
|
+
this._write(cwd, data);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
readBatchProgress(cwd) {
|
|
466
|
+
const data = this.read(cwd);
|
|
467
|
+
return data?.batchProgress || null;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
_renderBatchProgress(batchProgress) {
|
|
471
|
+
if (!batchProgress || !batchProgress.total) return null;
|
|
472
|
+
const { total, completed = 0, failed = 0, skipped = 0 } = batchProgress;
|
|
473
|
+
const done = Math.min(completed + failed + skipped, total);
|
|
474
|
+
const barLen = 20;
|
|
475
|
+
const filled = Math.round((completed / total) * barLen);
|
|
476
|
+
const bar = '█'.repeat(filled) + '░'.repeat(barLen - filled);
|
|
477
|
+
const parts = [];
|
|
478
|
+
if (failed > 0) parts.push(`${failed} 失败`);
|
|
479
|
+
if (skipped > 0) parts.push(`${skipped} 跳过`);
|
|
480
|
+
const suffix = parts.length ? ` (${parts.join(', ')})` : '';
|
|
481
|
+
return `📊 批量进度: ${bar} ${completed}/${total}${suffix}`;
|
|
482
|
+
}
|
|
483
|
+
|
|
417
484
|
_ensureGitignore(cwd) {
|
|
418
485
|
const gitignorePath = join(cwd, '.gitignore');
|
|
419
486
|
const rule = '.sillyspec/.runtime/';
|
package/src/run.js
CHANGED
|
@@ -95,12 +95,27 @@ function outputStep(stageName, stepIndex, steps, cwd) {
|
|
|
95
95
|
const total = steps.length
|
|
96
96
|
const projectName = basename(cwd)
|
|
97
97
|
|
|
98
|
+
const personas = {
|
|
99
|
+
brainstorm: `### 🎯 你的角色:资深架构师
|
|
100
|
+
你是一位有 15 年经验的系统架构师。先理解业务本质,再设计技术方案。决策附理由,方案列 trade-off。不确定就说不确定,不猜。`,
|
|
101
|
+
plan: `### 📋 你的角色:技术项目经理
|
|
102
|
+
你是一位经验丰富的技术项目经理。任务拆解粒度均匀,依赖关系明确。每个任务有完成标准,Wave 间有依赖说明。条理清晰,不做模糊描述。`,
|
|
103
|
+
execute: `### 💻 你的角色:高级工程师
|
|
104
|
+
你是一位严谨的高级工程师。先读规范再写代码,严格遵循 CONVENTIONS.md。代码有清晰职责划分,边界处理完善。少说多做,遇到规范冲突优先问。`,
|
|
105
|
+
verify: `### 🔍 你的角色:QA 专家
|
|
106
|
+
你是一位吹毛求疵的 QA 专家。假设所有代码都有 bug,用最坏情况测试。关注边界、异常、并发。有问题直说,用证据说话,不写"看起来没问题"。`
|
|
107
|
+
}
|
|
108
|
+
|
|
98
109
|
console.log(`---`)
|
|
99
110
|
console.log(`stage: ${stageName}`)
|
|
100
111
|
console.log(`step: ${stepIndex + 1}/${total}`)
|
|
101
112
|
console.log(`stepName: ${step.name}`)
|
|
102
113
|
console.log(`project: ${projectName}`)
|
|
103
114
|
console.log(`---\n`)
|
|
115
|
+
if (personas[stageName]) {
|
|
116
|
+
console.log(personas[stageName])
|
|
117
|
+
console.log('')
|
|
118
|
+
}
|
|
104
119
|
console.log(`## Step ${stepIndex + 1}/${total}: ${step.name}\n`)
|
|
105
120
|
console.log(step.prompt)
|
|
106
121
|
console.log(`\n### ⚠️ 铁律`)
|
|
@@ -123,9 +138,15 @@ export async function runCommand(args, cwd) {
|
|
|
123
138
|
const stageName = args[0]
|
|
124
139
|
const flags = args.slice(1)
|
|
125
140
|
|
|
126
|
-
if (!stageName
|
|
127
|
-
console.error(
|
|
128
|
-
console.error(`可选: ${Object.keys(stageRegistry).join(', ')}`)
|
|
141
|
+
if (!stageName) {
|
|
142
|
+
console.error('❌ 请指定阶段,例如: sillyspec run brainstorm')
|
|
143
|
+
console.error(`可选: ${Object.keys(stageRegistry).join(', ')}, auto`)
|
|
144
|
+
process.exit(1)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!stageRegistry[stageName] && stageName !== 'auto') {
|
|
148
|
+
console.error(`❌ 未知阶段: ${stageName}`)
|
|
149
|
+
console.error(`可选: ${Object.keys(stageRegistry).join(', ')}, auto`)
|
|
129
150
|
process.exit(1)
|
|
130
151
|
}
|
|
131
152
|
|
|
@@ -169,6 +190,11 @@ export async function runCommand(args, cwd) {
|
|
|
169
190
|
progress = pm.init(cwd)
|
|
170
191
|
}
|
|
171
192
|
|
|
193
|
+
// -- auto 模式:自动推进所有流程阶段
|
|
194
|
+
if (stageName === 'auto') {
|
|
195
|
+
return await runAutoMode(pm, progress, cwd, flags)
|
|
196
|
+
}
|
|
197
|
+
|
|
172
198
|
// --change 设置当前变更名
|
|
173
199
|
if (changeName) {
|
|
174
200
|
progress.currentChange = changeName
|
|
@@ -329,6 +355,15 @@ async function completeStep(pm, progress, stageName, cwd, outputText, inputText
|
|
|
329
355
|
progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
|
|
330
356
|
pm._write(cwd, progress)
|
|
331
357
|
|
|
358
|
+
// deriveState 轻量校验
|
|
359
|
+
try {
|
|
360
|
+
const { deriveState } = await import('./derive.js')
|
|
361
|
+
const result = deriveState(cwd, { mode: 'light', fix: true, pm, progress })
|
|
362
|
+
if (result.fixed > 0) {
|
|
363
|
+
console.log(`⚠️ 状态修复:${result.fixed} 个步骤已从 artifacts 恢复`)
|
|
364
|
+
}
|
|
365
|
+
} catch {}
|
|
366
|
+
|
|
332
367
|
// Append to user-inputs.md
|
|
333
368
|
if (outputText) {
|
|
334
369
|
const inputsPath = join(cwd, '.sillyspec', '.runtime', 'user-inputs.md')
|
|
@@ -419,6 +454,23 @@ function showStatus(progress, stageName) {
|
|
|
419
454
|
|
|
420
455
|
const firstPending = steps.findIndex(s => s.status === 'pending')
|
|
421
456
|
|
|
457
|
+
// 批量进度
|
|
458
|
+
if (progress.batchProgress) {
|
|
459
|
+
const bp = progress.batchProgress
|
|
460
|
+
const total = bp.total || 0
|
|
461
|
+
const completed = bp.completed || 0
|
|
462
|
+
const failed = bp.failed || 0
|
|
463
|
+
const skipped = bp.skipped || 0
|
|
464
|
+
const barLen = 20
|
|
465
|
+
const filled = Math.round((completed / Math.max(total, 1)) * barLen)
|
|
466
|
+
const bar = '█'.repeat(filled) + '░'.repeat(barLen - filled)
|
|
467
|
+
const parts = []
|
|
468
|
+
if (failed > 0) parts.push(`${failed} 失败`)
|
|
469
|
+
if (skipped > 0) parts.push(`${skipped} 跳过`)
|
|
470
|
+
const suffix = parts.length ? ` (${parts.join(', ')})` : ''
|
|
471
|
+
console.log(`\n📊 批量进度: ${bar} ${completed}/${total}${suffix}\n`)
|
|
472
|
+
}
|
|
473
|
+
|
|
422
474
|
steps.forEach((step, i) => {
|
|
423
475
|
const icon = step.status === 'completed' ? '✅' : step.status === 'skipped' ? '⏭️' : '⬜'
|
|
424
476
|
const isCurrent = step.status === 'pending' && i === firstPending
|
|
@@ -438,3 +490,110 @@ async function resetStage(pm, progress, stageName, cwd) {
|
|
|
438
490
|
pm._write(cwd, progress)
|
|
439
491
|
console.log(`🔄 ${stageName} 阶段已重置`)
|
|
440
492
|
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* auto 模式:自动推进 brainstorm → plan → execute → verify
|
|
496
|
+
*/
|
|
497
|
+
async function runAutoMode(pm, progress, cwd, flags) {
|
|
498
|
+
const flowStages = ['brainstorm', 'plan', 'execute', 'verify']
|
|
499
|
+
const isDone = flags.includes('--done')
|
|
500
|
+
let outputText = null
|
|
501
|
+
const outputIdx = flags.indexOf('--output')
|
|
502
|
+
if (outputIdx !== -1 && flags[outputIdx + 1]) outputText = flags[outputIdx + 1]
|
|
503
|
+
let inputText = null
|
|
504
|
+
const inputIdx = flags.indexOf('--input')
|
|
505
|
+
if (inputIdx !== -1 && flags[inputIdx + 1]) inputText = flags[inputIdx + 1]
|
|
506
|
+
|
|
507
|
+
if (!isDone) {
|
|
508
|
+
// 首次启动:显示当前状态和下一步
|
|
509
|
+
const currentStage = progress.currentStage || flowStages[0]
|
|
510
|
+
const stageIdx = flowStages.indexOf(currentStage)
|
|
511
|
+
if (stageIdx === -1) {
|
|
512
|
+
console.error(`❌ 当前阶段 ${currentStage} 不在 auto 流程中`)
|
|
513
|
+
console.error(`auto 流程: ${flowStages.join(' → ')}`)
|
|
514
|
+
process.exit(1)
|
|
515
|
+
}
|
|
516
|
+
// 显示进度概览
|
|
517
|
+
console.log('════════════════════════════════════════')
|
|
518
|
+
console.log(' 🤖 SillySpec Auto Mode')
|
|
519
|
+
console.log('════════════════════════════════════════')
|
|
520
|
+
console.log(` 流程: ${flowStages.join(' → ')}`)
|
|
521
|
+
console.log(` 当前: ${currentStage}`)
|
|
522
|
+
for (let i = 0; i < flowStages.length; i++) {
|
|
523
|
+
const s = flowStages[i]
|
|
524
|
+
const stageData = progress.stages[s]
|
|
525
|
+
const done = stageData?.status === 'completed'
|
|
526
|
+
const active = s === currentStage
|
|
527
|
+
const total = stageData?.steps?.length || '?'
|
|
528
|
+
const completed = stageData?.steps?.filter(st => st.status === 'completed').length || 0
|
|
529
|
+
const icon = done ? '✅' : active ? '🔵' : '⬜'
|
|
530
|
+
console.log(` ${icon} ${s} (${completed}/${total})`)
|
|
531
|
+
}
|
|
532
|
+
console.log('')
|
|
533
|
+
// 输出当前步骤 prompt
|
|
534
|
+
const steps = await getStageSteps(currentStage, cwd, progress)
|
|
535
|
+
if (!steps) {
|
|
536
|
+
console.error(`❌ 无法获取 ${currentStage} 步骤`)
|
|
537
|
+
process.exit(1)
|
|
538
|
+
}
|
|
539
|
+
const pendingIdx = steps.findIndex(s => s.status === 'pending')
|
|
540
|
+
if (pendingIdx === -1) {
|
|
541
|
+
// 阶段已完成,提示进入下一阶段
|
|
542
|
+
const next = getNextStage(currentStage)
|
|
543
|
+
if (next) {
|
|
544
|
+
console.log(`✅ ${currentStage} 已完成,下一步:sillyspec run auto --done --output "${currentStage} 完成"`)
|
|
545
|
+
} else {
|
|
546
|
+
console.log('🎉 全部流程已完成!')
|
|
547
|
+
}
|
|
548
|
+
return
|
|
549
|
+
}
|
|
550
|
+
outputStepPrompt(steps, pendingIdx, currentStage, cwd, progress)
|
|
551
|
+
return
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// --done:完成当前步骤,如果阶段完成则自动推进
|
|
555
|
+
if (!outputText) {
|
|
556
|
+
console.error('❌ auto --done 需要 --output 参数')
|
|
557
|
+
process.exit(1)
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const currentStage = progress.currentStage
|
|
561
|
+
const stageIdx = flowStages.indexOf(currentStage)
|
|
562
|
+
if (stageIdx === -1) {
|
|
563
|
+
console.error(`❌ 当前阶段 ${currentStage} 不在 auto 流程中`)
|
|
564
|
+
process.exit(1)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// 完成当前步骤
|
|
568
|
+
const completed = await completeStep(pm, progress, currentStage, cwd, outputText, inputText)
|
|
569
|
+
if (!completed) return
|
|
570
|
+
|
|
571
|
+
// 检查阶段是否完成
|
|
572
|
+
const nextPendingIdx = progress.stages[currentStage]?.steps?.findIndex(s => s.status === 'pending')
|
|
573
|
+
if (nextPendingIdx === -1) {
|
|
574
|
+
// 阶段已完成
|
|
575
|
+
const next = getNextStage(currentStage)
|
|
576
|
+
if (next) {
|
|
577
|
+
console.log(`\n✅ ${currentStage} 阶段完成,自动进入 ${next}`)
|
|
578
|
+
// 输出下一阶段第一步 prompt
|
|
579
|
+
const nextSteps = await getStageSteps(next, cwd, progress)
|
|
580
|
+
if (nextSteps) {
|
|
581
|
+
const firstPending = nextSteps.findIndex(s => s.status === 'pending')
|
|
582
|
+
if (firstPending !== -1) {
|
|
583
|
+
outputStepPrompt(nextSteps, firstPending, next, cwd, progress)
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
} else {
|
|
587
|
+
console.log('\n🎉 全部流程已完成!建议运行 /sillyspec:commit 提交改动')
|
|
588
|
+
}
|
|
589
|
+
} else {
|
|
590
|
+
// 阶段内下一步
|
|
591
|
+
const steps = await getStageSteps(currentStage, cwd, progress)
|
|
592
|
+
if (steps) {
|
|
593
|
+
const firstPending = steps.findIndex(s => s.status === 'pending')
|
|
594
|
+
if (firstPending !== -1) {
|
|
595
|
+
outputStepPrompt(steps, firstPending, currentStage, cwd, progress)
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
package/src/stages/brainstorm.js
CHANGED
|
@@ -80,7 +80,7 @@ export const definition = {
|
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
82
|
name: '需求范围评估',
|
|
83
|
-
prompt:
|
|
83
|
+
prompt: `评估需求复杂度,判断是否需要拆分或走批量模式。
|
|
84
84
|
|
|
85
85
|
### 操作
|
|
86
86
|
1. 根据分析结果判断复杂度
|
|
@@ -89,15 +89,37 @@ export const definition = {
|
|
|
89
89
|
- 3+ 种角色有不同权限和视图
|
|
90
90
|
- 跨页面状态流转(审批流、多步表单)
|
|
91
91
|
- 模块间耦合度低可独立开发
|
|
92
|
-
3.
|
|
93
|
-
|
|
92
|
+
3. 满足以下条件建议走**批量模式**:
|
|
93
|
+
- 任务数量 > 10 且任务间有重复模式(如 100 个报表、50 个表单、N 个相似页面)
|
|
94
|
+
- 本质是「模板 × 数据」而非 N 个独立功能
|
|
95
|
+
- 直接逐个开发会导致 plan.md 膨胀和上下文溢出
|
|
96
|
+
4. 需要拆分 → 生成 MASTER.md,规划子阶段
|
|
97
|
+
5. 检测到批量模式 → 输出提示并建议用户确认
|
|
98
|
+
6. 都不需要 → 继续
|
|
99
|
+
|
|
100
|
+
### 批量模式指引
|
|
101
|
+
确认后,后续 plan/execute 按以下原则调整:
|
|
102
|
+
- **不要**把每个实例列为独立任务(不要写 100 个 checkbox)
|
|
103
|
+
- plan 设计通用架构(引擎/模板/配置格式),任务数控制在 10 个以内
|
|
104
|
+
- 数据转换用脚本完成(Excel → 配置文件),不消耗 AI 上下文
|
|
105
|
+
- execute 每个 Wave 独立模块,Wave 间通过接口定义解耦
|
|
106
|
+
- verify 用脚本全量验证 + AI 抽查边界案例
|
|
107
|
+
|
|
108
|
+
### 半批量场景
|
|
109
|
+
如果任务中大部分相似但有少量特殊任务(如 20 个任务中 15 个相似、5 个特殊):
|
|
110
|
+
- **主簇**(>10 个相似)→ 走批量模式(引擎 + 配置)
|
|
111
|
+
- **小簇**(2-5 个相似)→ 走简化版批量(基于主簇模板扩展)
|
|
112
|
+
- **孤立任务**(1 个)→ 走标准开发流程
|
|
113
|
+
- 建议用「继承 + override」配置解决特殊任务,配置解决不了的才写定制代码
|
|
114
|
+
- 架构设计时预留扩展点(hooks/overrides),让特殊任务能"挂上去"而不是"另起炉灶"
|
|
94
115
|
|
|
95
116
|
### 输出
|
|
96
|
-
|
|
117
|
+
拆分方案 / 批量模式确认 / "无需拆分"确认
|
|
97
118
|
|
|
98
119
|
### 注意
|
|
99
120
|
- 简单 CRUD 不拆
|
|
100
|
-
-
|
|
121
|
+
- 拆分方案需用户确认
|
|
122
|
+
- 批量模式需用户确认`,
|
|
101
123
|
outputHint: '拆分方案或无需拆分确认',
|
|
102
124
|
optional: true
|
|
103
125
|
},
|
package/src/stages/doctor.js
CHANGED
|
@@ -108,7 +108,16 @@ done
|
|
|
108
108
|
|
|
109
109
|
### 注意
|
|
110
110
|
- 不要编造路径或结果,严格基于命令输出
|
|
111
|
-
- 如果 .sillyspec/ 不存在,直接输出 ❌
|
|
111
|
+
- 如果 .sillyspec/ 不存在,直接输出 ❌ 并跳过后续检查
|
|
112
|
+
- 额外运行 deriveState 全量校验:
|
|
113
|
+
\
|
|
114
|
+
\
|
|
115
|
+
\
|
|
116
|
+
node -e "import('./src/derive.js').then(m => { const pm = require('./progress.js'); const r = m.deriveState('.', {mode:'full',fix:false,progress:pm.read('.')}); console.log(JSON.stringify(r, null, 2)); })" 2>/dev/null || echo "deriveState 不可用"
|
|
117
|
+
\
|
|
118
|
+
\
|
|
119
|
+
\
|
|
120
|
+
将 deriveState 的 issues 列表纳入 SillySpec 内部检查结果中`,
|
|
112
121
|
outputHint: 'SillySpec 内部检查结果',
|
|
113
122
|
optional: false
|
|
114
123
|
},
|
package/src/stages/execute.js
CHANGED
|
@@ -177,6 +177,11 @@ function buildWavePrompt(wave, waveIndex) {
|
|
|
177
177
|
}).join('\n')
|
|
178
178
|
return `## Wave ${waveIndex}: 执行以下任务
|
|
179
179
|
|
|
180
|
+
### Wave 开始前
|
|
181
|
+
1. 读取 design.md 的「编码铁律」章节(如果存在),严格遵守
|
|
182
|
+
2. 确认本 Wave 的输入/输出契约(前置 Wave 产出了什么,本 Wave 需要消费什么)
|
|
183
|
+
3. 检查前置 Wave 的产出是否完整(文件是否存在、测试是否通过)
|
|
184
|
+
|
|
180
185
|
### 本 Wave 任务
|
|
181
186
|
${taskList}
|
|
182
187
|
|
package/src/stages/plan.js
CHANGED
|
@@ -112,6 +112,15 @@ export const definition = {
|
|
|
112
112
|
3. 有依赖的 Task 按顺序排列
|
|
113
113
|
4. Wave 编号从 1 开始
|
|
114
114
|
|
|
115
|
+
### 批量模式指引
|
|
116
|
+
如果 design.md 或需求中包含批量特征(关键词:批量/模板/引擎/100个/50个/N个相似/Excel×数据),按以下原则规划:
|
|
117
|
+
❌ 不要列出每个实例作为独立任务(不要写 100 个 checkbox)
|
|
118
|
+
❌ 不要在 plan.md 中嵌入数据(Excel 内容、表单字段列表等)
|
|
119
|
+
✅ 设计通用架构(引擎/模板/配置格式),Wave 1 聚焦架构
|
|
120
|
+
✅ 数据转换用脚本完成(Excel → 配置文件),单独一个 Wave
|
|
121
|
+
✅ 总任务数控制在 10 个以内
|
|
122
|
+
✅ 在 design.md 中增加「编码铁律」章节(3-5 条不可违反的约束)
|
|
123
|
+
|
|
115
124
|
### 操作
|
|
116
125
|
1. 读取 tasks.md 获取任务列表
|
|
117
126
|
2. 读取 design.md 获取文件变更清单
|
package/src/stages/verify.js
CHANGED
|
@@ -39,6 +39,12 @@ export const definition = {
|
|
|
39
39
|
2. 检查代码是否实现了描述的功能
|
|
40
40
|
3. 标记:✅ 已完成 / ❌ 未完成 / ⚠️ 部分完成
|
|
41
41
|
|
|
42
|
+
### 批量模式验证指引
|
|
43
|
+
如果 tasks.md 中有批量特征(引擎/模板/配置/批量生成),采用分层验证:
|
|
44
|
+
- **L1 自动化(100%)**:运行验证脚本(如有),检查所有实例的文件存在、格式正确、Schema 校验通过
|
|
45
|
+
- **L2 AI 抽查(5-10 个)**:选择最复杂的 3 个 + 最简单的 2 个 + 有特殊逻辑的,检查业务逻辑正确性
|
|
46
|
+
- **L3 模式性 bug 检测**:L2 发现 bug → 判断是否为系统性问题 → 系统性 bug 则回退修复引擎并重新生成所有实例
|
|
47
|
+
|
|
42
48
|
### 输出
|
|
43
49
|
任务完成度列表 + 完成率
|
|
44
50
|
|